В этом кратком совете, взятом из книги «Useful Python».
Работая на Mac, мы можем контролировать почти все в системе, используя pyobjc, мост Python-to-Objective-C. Apple делает большую часть своей ОС управляемой через модуль AppKit и pyobjcпредоставляет доступ ко всему этому Python. Это будет наиболее полезно, если мы уже знаем, как AppKit делает то, что мы хотим, но с небольшим исследованием можно пробиться через API-интерфейсы операционной системы.
Давайте попробуем пример. Во-первых, нам понадобится pyobjc, который можно установить с помощью pip install pyobjc. Это установит целый список мостов API операционной системы, предоставляя доступ ко всем видам аспектов macOS. Сейчас мы рассмотрим AppKit — инструмент, используемый для создания и управления запущенными приложениями на рабочем столе Mac.
Мы можем перечислить все приложения, запущенные в настоящее время с помощью AppKit:
Python 3.9.6 (default, Oct 18 2022, 12:41:40) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from AppKit import NSWorkspace >>> NSWorkspace.sharedWorkspace().runningApplications() ( "<NSRunningApplication: 0x60000145c000 (com.apple.loginwindow - 148) LSASN:{hi=0x0;lo=0x6006}>", "<NSRunningApplication: 0x60000145c080 (com.apple.backgroundtaskmanagement.agent - 475) LSASN:{hi=0x0;lo=0xb00b}>", "<NSRunningApplication: 0x60000145c100 (com.apple.WindowManager - 474) LSASN:{hi=0x0;lo=0xc00c}>", "<NSRunningApplication: 0x60000145c180 (com.apple.CoreLocationAgent - 500) LSASN:{hi=0x0;lo=0xe00e}>", "<NSRunningApplication: 0x60000145c980 (com.apple.Terminal - 1302) LSASN:{hi=0x0;lo=0x24024}>", "<NSRunningApplication: 0x60000145ca00 (com.apple.Safari - 1303) LSASN:{hi=0x0;lo=0x25025}>", "<NSRunningApplication: 0x60000145cb80 (com.apple.Spotlight - 1310) LSASN:{hi=0x0;lo=0x28028}>", "<NSRunningApplication: 0x60000145cc00 (com.apple.finder - 1306) LSASN:{hi=0x0;lo=0x29029}>", ) >>>
Это даст длинный список NSRunningApplicationобъектов. Каждый из них соответствует определенному приложению, работающему в данный момент на рабочем столе. Многие из них являются «невидимыми» приложениями (вещи, которые работают, но не обязательно показывают окно), но есть и такие вещи, которые мы могли бы считать реальными приложениями, которые мы можем видеть, например Safari, Terminal и т. д. NSRunningApplicationзадокументирован на сайте developer.apple.com, где можно увидеть его свойства. Например, у каждого приложения есть localizedNameи bundleIdentifier:
>>> for nsapp in NSWorkspace.sharedWorkspace().runningApplications(): ... print(f"{nsapp.localizedName()} -> {nsapp.bundleIdentifier()}") ... loginwindow -> com.apple.loginwindow BackgroundTaskManagementAgent -> com.apple.backgroundtaskmanagement.agent WindowManager -> com.apple.WindowManager CoreLocationAgent -> com.apple.CoreLocationAgent Terminal -> com.apple.Terminal Safari -> com.apple.Safari Spotlight -> com.apple.Spotlight Finder -> com.apple.finder
Мы также можем видеть, что у NSRunningApplicationобъекта есть функция активации, которую мы можем вызвать, чтобы активировать это приложение, как если бы мы щелкнули его значок в Dock. Итак, чтобы найти Safari, а затем активировать его, мы будем использовать эту функцию активации. Для вызова activateтребуется значение для options, как описано в документации, и его также необходимо импортировать из AppKit:
>>> from AppKit import NSWorkspace, NSApplicationActivateIgnoringOtherApps >>> safari_list = [x for x in NSWorkspace.sharedWorkspace().runningApplications() if x.bundleIdentifier() == 'com.apple.Safari'] >>> safari = safari_list[0] >>> safari.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
Теперь Safari активирован.
Поиск версий Python для API macOS
Найти имя чего-то в Python, которое соответствует имени Objective-C, может быть немного сложно. Как показано в приведенном выше коде, activateфункция Objective-C вызывается activateWithOptions_в Python. Существует набор правил для перевода этого имени, который объясняется в документации pyobjc, но иногда может быть быстрее использовать собственную функцию Python, dir()чтобы показать все свойства объекта, а затем выбрать то, которое выглядит наиболее правдоподобно:
>>> print(len(dir(safari))) 452
Ой! Наш safariэкземпляр NSRunningApplicationимеет 452 свойства! Ну, тот, который нам нужен, вероятно, называется что-то вроде «активировать», поэтому:
>>> print([x for x in dir(safari) if "activate" in x.lower()]) ['activateWithOptions_', 'activateWithOptions_']
Ага! Так же activateWithOptions_как и имя функции, которую нам нужно вызвать. Точно так же имя опции, которую мы хотим передать этой функции, находится в самом AppKit:
>>> [x for x in dir(AppKit) if "ignoringotherapps" in x.lower()] ['NSApplicationActivateIgnoringOtherApps']
Иногда этот процесс может показаться немного исследовательским, но все, что Objective-C может сделать, можно сделать и из Python.