Общесистемный ярлык для Mac OS X
Поэтому меня попросили портировать некоторые внутренние вспомогательные приложения на Mac OS X 10.7.
Работает все довольно хорошо, поскольку зависимый от платформы код в любом случае минимален, но для работы одного приложения необходим общесистемный ярлык (т. Е. Функциональность RegisterHotkey), и я не могу найти никакой документации о том, как бы я это делал на Mac.
Программа использует PyQt GUI с Python 3.2. и соответствующий код для Windows в основном:
def register_hotkey(self):
hwnd = int(self.winId())
modifiers, key = self._get_hotkey()
user32.RegisterHotKey(hwnd, self._MESSAGE_ID, modifiers, key)
а затем получать события горячих клавиш:
def winEvent(self, msg):
if msg.message == w32.WM_HOTKEY:
self.handle_hotkey()
return True, id(msg)
return False, id(msg)
Обратите внимание, что мне не нужен вариант Python, я легко могу написать простое расширение c - так что приветствуются также решения C/ target-c.
4 ответа
Недавно я кодировал расширение для захвата мультимедиа-ключей quodlibet (так как впитывается в сам quodlibet); для вашей настройки применяется тот же процесс.
Я использовал Кварц CGEventTapCreate
ловушка и цикл обработки событий, а также инфраструктура Cocoa AppKit для расшифровки кодов клавиш для достижения этой цели.
Следующий код регистрирует обратный вызов Python, которому передаются глобальные нажатия клавиш, и запускает цикл обработки событий:
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()
Я определил касание только для системных клавиш (мультимедийные клавиши); вам нужно будет указать другую маску события (CGEventMaskBit
с одним или несколькими типами событий); например Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp)
для ключевых событий.
Обратный вызов должен иметь следующую подпись (он реализует CGEventTapCallBack
метод из Кварцевого API:
def keyboardTapCallback(proxy, type_, event, refcon):
# Convert the Quartz CGEvent into something more useful
keyEvent = NSEvent.eventWithCGEvent_(event)
Я превратил событие Кварц в NSEvent
потому что вся информация, которую я мог найти на мультимедийных клавишах Mac, имела отношение к этому классу.
В принципе, вы можете добиться того же с API-интерфейсами AppKit, но тогда ваше приложение Python рассматривается как приложение Mac (видимое в Dock со значком и всем остальным), хотя я хотел, чтобы это вообще оставалось в фоновом режиме.
Позвольте мне сначала перейти к хорошему: я написал небольшой модуль Python, чтобы упростить это: QuickMacHotKey.
Использование его выглядит следующим образом:
@quickHotKey(virtualKey=kVK_ANSI_X,
modifierMask=mask(cmdKey, controlKey, optionKey))
def handler() -> None:
print("handled ⌘⌃⌥X")
Вы можете получить это с
Теперь история и подробности того, почему это необходимо:
К сожалению, принятый ответ использует документированный общедоступный API, для использования которого требуются неприятные запросы безопасности и полный доступ. API более высокого уровня, такие как addGlobalMonitorForEventsMatchingMask:handler:
также требуют такого рода доступ к специальным возможностям. Как сказано в документации к этому методу:
События, связанные с ключами, можно отслеживать только в том случае, если специальные возможности включены или если вашему приложению доверяют доступ к специальным возможностям (см. AXIsProcessTrusted).
Способ регистрации глобальных горячих клавиш без вызова подсказок доступности по-прежнему заключается в использовании древнего Carbon API.
Раньше этот API был доступен для Python через встроенный
Итак, мой модуль просто использует API PyObjC, который сам по себе устарел и не имеет замены , хотя его сопровождающий несколько более отзывчив, чем Apple, поэтому я ожидаю, что мои проблемы действительно будут решены до выпуска платформы. что полностью ломается, то выходит :).
Используя возможности Google, я нашел этот фрагмент кода, который позволяет регистрировать глобальные горячие клавиши для Mac OS X
Вам нужно будет добавить Carbon
рамки, и, вероятно, мостовой актерский состав для ARC
при передаче собственного указателя Objective-C на функцию C.
Как минимум, вам также необходимо:
#import <Carbon/Carbon.h>
Коды клавиш можно увидеть на этой странице, объясняющие коды виртуальных клавиш.
Почему никто никогда не упоминал молоток, который поддерживает настраиваемые глобальные системные ярлыки, которые могут вызывать команду оболочки или запускать приложение пользовательского интерфейса, такое как Safari, PHOTOSHOP.
Ниже приведен пример, написанный мной, демонстрирующий, как вызывать функцию оболочки с помощью глобальных горячих клавиш. https://gist.github.com/BigSully/0e59ab97f148bc167ea19dbd42ebef4b
Используйте hs.execute для выполнения команды оболочки, как неинтерактивной, так и интерактивной.
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "P", function()
local output = hs.execute("toggleProxy", true)
hs.alert.show(output)
end)
или Используйте hs.application.launchOrFocus для запуска приложения hs.application.launchOrFocus("Safari")