Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
122 views
in Technique[技术] by (71.8m points)

python - system wide shortcut for Mac OS X

So I was asked to port some internal helper applications to Mac OS X 10.7.

Works all quite welll as the platform dependent code is minimal anyhow, but one application needs a system wide shortcut to function (i.e. RegisterHotkey functionality) and I can't find any documentation on how I'd do this on a Mac.

The program is using a PyQt gui with Python 3.2. and the corresponding code for windows is basically:

def register_hotkey(self):
    hwnd = int(self.winId())
    modifiers, key = self._get_hotkey()
    user32.RegisterHotKey(hwnd, self._MESSAGE_ID, modifiers, key)

and then to receive the hotkey events:

def winEvent(self, msg):
    if msg.message == w32.WM_HOTKEY:
        self.handle_hotkey()
        return True, id(msg)
    return False, id(msg)

Note that I don't need a python variant, I can easily write a simple c extension - so C/objective-c solutions are welcome as well.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I recently coded up an extension to quodlibet capturing multimedia keys (since absorbed into quodlibet itself); for your setup the same process applies.

I used the Quartz CGEventTapCreate hook and event loop, and the Cocoa AppKit framework to decipher key codes to achieve this.

The following code registers a python callback which is passed global key presses, and starts the event loop:

import Quartz
from AppKit import NSKeyUp, NSSystemDefined, NSEvent

# Set up a tap, with type of tap, location, options and event mask
tap = Quartz.CGEventTapCreate(
    Quartz.kCGSessionEventTap, # Session level is enough for our needs
    Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter
    Quartz.kCGEventTapOptionListenOnly, # Listening is enough
    Quartz.CGEventMaskBit(NSSystemDefined), # NSSystemDefined for media keys
    keyboardTapCallback,
    None
)

runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0)
Quartz.CFRunLoopAddSource(
    Quartz.CFRunLoopGetCurrent(),
    runLoopSource,
    Quartz.kCFRunLoopDefaultMode
)
# Enable the tap
Quartz.CGEventTapEnable(tap, True)
# and run! This won't return until we exit or are terminated.
Quartz.CFRunLoopRun()

I defined a tap for system defined keys only (media keys); you'll have to specify a different event mask (CGEventMaskBit with one or more Event Types); e.g. Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp) for key up events.

The callback should have the following signature (it implements the CGEventTapCallBack method from the Quartz API:

def keyboardTapCallback(proxy, type_, event, refcon):
    # Convert the Quartz CGEvent into something more useful
    keyEvent = NSEvent.eventWithCGEvent_(event)

I converted the Quartz event into a NSEvent, because all the information I could find on Mac multimedia keys was referring to that class.

In principle you can achieve the same thing with the AppKit APIs too, but then your Python application is treated as a Mac Application (visible in the Dock with an icon and everything), while I wanted this to be kept in the background altogether.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...