Открываем и скрываем приложение по хоткею
Начнем с самого простого. Задача: по нажатию на глобальный хоткей из любой программы передать фокус Skype и этим же действием его скрыть. Существует целый парк программ, которые позволяют сделать это очень просто. Однако мы не ищем простых путей и не готовы устанавливать дополнительные программки для этих целей. В данном случае на помощь приходит Automator с возможностью создать сервис и повесить хоткей на его вызов из любого приложения.
Запускаем Automator, выбираем тип документа Служба. В списке Служба получает выбираем нет входных данных в соседнем списке оставляем в любой программе. Добавляем в нашу службу действие Запустить AppleScript и вставляем следующий код:
set appName to "Skype" set startIt to false tell application "System Events" if not (exists process appName) then set startIt to true else if frontmost of process appName then set visible of process appName to false else set frontmost of process appName to true end if end tell if startIt then tell application appName to activate end if
Данный код честно позаимствован отсюда.
Здесь все просто: скрипт проверяет, запущено ли приложение, если нет, то запускает его, иначе либо сворачивает его (если окно приложения находится в фокусе), либо отдает ему фокус (если в данный момент активно другое приложение).
Сохраняем нашу службу, переходим в Системные настройки > Клавиатура > Сочетание клавиш в списке слева выбираем Службы, в списке справа находим только что созданную нами службу и привязываем к ней хоткей. Цель достигнута, однако без одного но здесь не обойдется.
Проблема в том, что вызов службы не привязывается к глобальному хоткею. Службы вызываются из каждого приложения «локально». Например, находясь в данный момент в Safari, в меню программы мы найдем подменю Службы (т.е. Safari > Службы), где нам будет доступна наша служба для запуска. Соответственно запустить ее мы можем вручную из любого приложения, а назначенный нами хоткей имеет меньший приоритет по сравнению с настройками конкретного приложения. Отсюда имеем два выхода: либо назначить для вызова нашей службы сочетание клавиш, которое не используется больше нигде в системе, ни в одной программе, либо прибегнуть к помощи тех самых сторонних программ, которые дадут возможность повесить глобальный хоткей на вызов службы. Лично я пошел по третьему пути и воспользовался возможностями Alfred, который позволяет не прибегать к выше описанным действиям и дает возможность показывать и скрывать приложение по нажатию на заданный глобальный хоткей.
Что там с автохайдингом?
Итак, мы научились показывать и скрывать приложение по нажатию на глобальный хоткей, но жать на кнопку, прежде чем переключиться в другое приложение, напряжно. Решим и эту проблему.
Открываем Редактор AppleScript и вставляем следующий код:
property appName : "Skype" on idle tell application "System Events" set focusedApp to (name of the first process whose frontmost is true) if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then set visible of process appName to false end if end tell return 0.5 end idle
Жмем Файл > Сохранить, выбираем формат файла — Программа, ставим галку рядом с Оставлять открытым после запуска обработчика, сохраняем программу. После этого в редакторе доступна кнопка Содержание пакета, нажав на нее, справа выползет окно, в котором ищем кнопку с шестерней, в появившемся меню выбираем Показать в Finder:
В открывшейся папке будет лежать файл Info.plist, открываем его в текстовом редакторе, после четвертой строки вставляем:
<key>LSBackgroundOnly</key> <string>1</string>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>LSBackgroundOnly</key> <string>1</string> <key>CFBundleAllowMixedLocalizations</key> <true/> <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleExecutable</key> <string>applet</string> <key>CFBundleIconFile</key> <string>applet</string> <key>CFBundleIdentifier</key> <string>com.apple.ScriptEditor.id.HideSkype</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>HideSkype</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleSignature</key> <string>aplt</string> <key>LSMinimumSystemVersionByArchitecture</key> <dict> <key>x86_64</key> <string>10.6</string> </dict> <key>LSRequiresCarbon</key> <true/> <key>WindowState</key> <dict> <key>dividerCollapsed</key> <false/> <key>eventLogLevel</key> <integer>2</integer> <key>name</key> <string>ScriptWindowState</string> <key>positionOfDivider</key> <real>333</real> <key>savedFrame</key> <string>55 281 602 597 0 0 1440 878 </string> <key>selectedTabView</key> <string>event log</string> </dict> </dict> </plist>
Сохраняем файл.
Вот и все, запускаем наше приложение, добавляем в автозагрузку и радуемся полученному результату. Отныне скайп глаза не мозолит.
Если вам интересны особенности реализации, то читаем дальше.
Разбор полетов
AppleScript для меня оказался открытием, так что буквально пару дней назад я и не знал о существовании этого «чуда» (уж очень он смахивает на язык для домохозяек). Зато предоставляет достаточно возможностей для автоматизации процессов в вашем рабочем окружении. Для выше описанных действий не понадобилась установка инструментов разработчика, а учитывая полученный результат, можно говорить о наличии мощного инструмента, который всегда под рукой.
Попробую разложить по полочкам решение задачи про автоскрытию приложения.
Сначала научимся определять, находится ли конкретное приложение в фокусе, или нет:
set appName to "Skype" --думаю, комментарии не уместны tell application "System Events" --начинаем работу с объектом application, в данном случае с программой System Events set focusedApp to (name of the first process whose frontmost is true) --записываем в focusedApp имя приложения, которое в данный момент в фокусе if focusedApp is appName then --вот и выяснили, что приложение с именем appName находится в фокусе end if end tell
Очень непривычно читать такой код, зато приятно — сомодокументированный код.
Если фокус приложения потерян, то скрываем его:
set appName to "Skype" tell application "System Events" set focusedApp to (name of the first process whose frontmost is true) if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then set visible of process appName to false end if end tell
Вот и решение задачи. Осталось завернуть его в бесконечный цикл.
set appName to "Skype" repeat while true tell application "System Events" set focusedApp to (name of the first process whose frontmost is true) if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then set visible of process appName to false end if end tell delay 0.5 --делаем задержку в пол секунды перед следующей итерацией end repeat
И это уже рабочее решение. Однако если мы оставим его в таком виде, то скрипт просто-напросто зависнет, хоть и будет работать корректно. Решение — вынести цикл в отдельный поток. Но увы, потоков в AppleScript нет. Идем в документацию и открываем для себя хэндлер idle. Хэндлерами в AppleScript называются, по сути, привычные нам процедуры. Особенность хэндлера idle в том, что он вызывается системой каждые 30 секунд, если не возвращает никакого значения, если же он вернет, допустим, 5, то будет вызываться каждые 5 секунд.
on idle set appName to "Skype" tell application "System Events" set focusedApp to (name of the first process whose frontmost is true) if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then set visible of process appName to false end if end tell return 0.5 end idle
Также можно отказаться от использования idle и работать с хендлерами run и quit:
property appName: "Skype" property running: true on run repeat while running tell application "System Events" set focusedApp to (name of the first process whose frontmost is true) if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then set visible of process appName to false end if end tell delay 1 end repeat end run on quit set running to false end quit
Хочу один обработчик для нескольких программ
Да без проблем:
property appsToHide: {"Skype", "Adium", "Sublime Text 2"} on idle tell application "System Events" set focusedApp to name of the first process whose frontmost is true repeat with appToHide in appsToHide if (focusedApp is not in appToHide) and (exists process appToHide) and (visible of process appToHide) then set visible of process appToHide to false end if end repeat end tell return 0.5 end idle
Стоит отметить следующий момент: focusedApp is not in appToHide
. Здесь проверяется, содержится ли значение focusedApp
в appToHide
, хотя правильно бы было просто сравнить строки (операторы =, is equal, equals, [is] equal to, is not isn't, isn't equal [to], is not equal [to], doesn't equal, does not equal
). Но в данном примере строки (тип данных text
) так и не захотели корректно сравниваться.
На этом я заканчиваю свой рассказ. Много подробностей об AppleScript можно найти в официальной документации. Если кто сомневается в актуальности данного инструмента, то посмотрите в release notes — возможности языка расширяют с каждым релизом OS X. Также поисковики пестрят уже готовыми howto по решению задач с помощью AppleScript.
Спасибо за внимание.
ссылка на оригинал статьи http://habrahabr.ru/post/202864/
Добавить комментарий