Этот ответ сфокусирован на проблеме, указанной в What I'm Currently Trying To Overcome
. Я интерпретировал The Desired End Result
как справочную информацию, которая обеспечивает контекст для вашей неотложной проблемы (и это действительно интересно / полезно предоставить, так что спасибо).
TL; DR
tell application "System Events"
set _P to a reference to (processes whose background only = false)
set _W to a reference to windows of _P
[_P's name, _W's size, _W's position]
end tell
Это даст вам список size
и position
свойств для каждого application process
. Ниже приведена довольно многословная деконструкция того, где и почему ваш скрипт пошёл не так; затем предлагаемое решение после рассмотрения других столь же жизнеспособных решений, прежде чем остановиться на базовом коде выше. Я постараюсь урезать многословность этого ответа в другой день, когда буду немного менее уставшим, но сейчас, я надеюсь, поможет более глубокое понимание.
Проблемы
▸ Начиная с конкретной ошибки, которую выдает ваш скрипт, необходимо отметить, что, вообще говоря, блоки tell application
не часто и не должны быть вложенными. Вы открыли блок tell
для цели Системные события , что было необходимо для получения имен process
; в этот момент вы должны были либо закрыть блок tell
, либо использовать команду simple tell
в одной строке:
tell application "System Events" to set openApps to the name of every process...
(в этом случае end tell
не требуется).
Однако, поскольку ваш блок tell
остается открытым, команды, которые следуют далее, также направляются на Системные события . Первый application process
, который, очевидно, находит ваш скрипт, относится к Finder , и когда ваш скрипт (внутри цикла повторения) получает команду tell application "Finder"
(посредством переменной checkApp
), ошибка брошен потому, что вы на самом деле сказали Системные события сказать Finder что-то сделать, а Системные события не понимают, как взаимодействовать с application
объект.
▸ Это приводит нас к следующей строке, с которой связана пара проблем, относящихся к вашему сценарию (плюс более общий примечательный момент в стороне † , о котором я оставил сноску):
tell application checkApp to get the bounds of the front window
Эта строка будет работать только для приложений (Apple) с поддержкой сценариев . Не все приложения могут контролироваться AppleScript - разработчики приложений выбирают для реализации (или не делают, как это часто бывает) при разработке программного обеспечения для macOS.
Те из них, для которых возможен сценарий , будут (если они будут следовать рекомендациям Apple), определив window
объекты, каждый из которых содержит свойство bounds
. Те, которые не являются сценариями, не будут иметь ни одного из них. Вот тогда будет еще одна ошибка.
Другая «второстепенная» проблема заключается в том, что не все процессы, которые background only
обязательно имеют окна, и, таким образом, front window
. Finder никогда не бывает только фоновым, но иногда не имеет открытых окон. Поэтому, даже если ошибка, которую вы получаете, исправлена, это следующая ошибка, которая появляется, если нет открытых Finder окон.
Решение
Хотя вы не можете получить свойство bounds
окна, принадлежащего приложению без сценариев, Системные события может получить некоторые свойства, которые принадлежат объектам application process
. Это не зависит от того, является ли приложение, которому принадлежит процесс, самим сценарием или нет, потому что Системные события - это приложение, на которое мы нацелены, которое является сценарием и имеет доступ к аналогичная информация, относящаяся к window
объектам каждого процесса ( NB. , см. сноску ниже, но объект window
, принадлежащий process
, не такой же, как window
объект, принадлежащий application
, поэтому не может использоваться взаимозаменяемо, как и их свойства).
Alхотя для window
объектов, принадлежащих processes
из системных событий , нет свойства bounds
, есть два других свойства, которые вместе эквивалентны bounds
: position
иsize
.position
дает координату {X, Y}
левого верхнего угла окна относительно левого верхнего угла экрана (который определяется в этом контексте как источник в {0, 0}
);size
дает пару {X, Y}
измерений, которые представляют ширину и высоту окна соответственно.
Таким образом, учитывая гипотетическое значение свойства bounds
{?, ?, ?, ?}
для конкретного окна, связь сsize: {?, ?}
и position: {?, ℎ}
можно выразить так:
{?, ?, ?, ?} = {?, ?, ? + ?, ? + ℎ}
Другим соображением является получение списка процессов, которые фактически имеют окна.Это можно сделать разными способами, каждый из которых имеет свои преимущества и недостатки, включая краткость кода, время выполнения и точность полученного списка окон.
Ваш оригинальный метод получения списка процессов, различаемыйbackground only
- одна из самых быстрых, и есть только несколько ситуаций, когда ложные отрицания приводят к упущениям в списке (а именно, приложения с меню, которые регистрируются как background only
, но явно имеют window
; приложение Instagram Flume является примером).
Вместо этого вы можете различить по свойству visible
, которое так же быстро, но я чувствую себя менее подходящим в ситуациях, когда приложение скрыто и должно быть незаметно передзапись его свойств окна;или, опять же, некоторые приложения меню, которые регистрируются как background only
, а не visible
, но все же ясно , visible_ с окном на переднем плане.
Метод, который является наиболее надежным при получениивсе окна в любых обстоятельствах, к сожалению, довольно медленные, но создают список, с которым легко работать и который не требует дальнейшей обработки.
В нашей текущей ситуации, однако, я думаю, что разумно выбратьопция, которая дает скорость и будет работать для большинства приложений, которая является вашим background only
фильтром.Поскольку список, полученный из этого, дает некоторые ложные срабатывания (например, Finder ), нам нужно немного обработать список, прежде чем его можно будет надежно использовать.
Вот код для получения вложенногосписок, содержащий a) список именованных процессов; b) список размеров окон для каждого из названных процессов;и c) список позиций окна для каждого из названных процессов:
tell application "System Events"
set _P to a reference to (processes whose background only = false)
set _W to a reference to windows of _P
[_P's name, _W's size, _W's position]
end tell
Если вы закроете окна Finder , вы увидите, что оно все еще появляетсяв первом списке по имени, но во втором и третьем списках есть пустой список {}
, где иначе были бы размеры и позиции его окон.Поэтому просто обязательно сделайте некоторую проверку, прежде чем пытаться ссылаться на элементы каждого подсписка.
Сравните и сопоставьте это с этим более медленным, но более точным решением:
tell application "System Events"
set _P to a reference to (processes whose class of window 1 is window)
set _W to a reference to windows of _P
[_P's name, _W's size, _W's position]
end tell
Требуется двадцатьзапускать в моей системе в два раза больше времени, предоставляя хотя бы одно решение, которое может определять приложения на панели меню, обычные приложения и скрытые приложения с окнами, которые могут оказаться необходимыми для вашей конечной цели.Но если вы чаще всего работаете с более обычными приложениями, то вполне понятно, какой метод больше подходит.
† Более обобщенная, потенциальная проблема - которая недействительно применимо к вашему сценарию в его нынешнем виде, но полезно знать о будущих сценариях, если вы пытаетесь использовать подобную технику - это использование переменной в качестве средства ссылки на application
, который имеет неопределенное имя при компиляциивремя (до запуска скрипта).
bounds
является довольно вездесущим свойством windows
всех скриптовых приложений, поэтому вы (почти) избегаете этой техники здесь. Однако, если вы выбрали свойство или класс объектов, которые Script Editor специально содержит , а не , AppleScript не распознает терминологию и предположит, что это просто имя переменной. Для того чтобы терминология конкретного приложения была распознана, необходимо сделать ссылку на это конкретное приложение в какой-либо форме, либо с помощью tell application "Finder" to...
, либо путем включения соответствующих строк внутри блока using terms from application "Finder"
.
Хорошее практическое правило заключается в том, что приложения обычно должны быть известны и указаны во время компиляции, чтобы получать команды AppleScript. Нет простого способа удовлетворить различные варианты без использования условных блоков if...then...else if...
для каждого возможного применения.
Это вызывает разочарование, особенно когда речь идет о приложениях, которые кажутся схожими по природе и, к тому же, имеют похожий словарь AppleScript, но при этом все еще не делятся друг с другом терминологией для общего использования. Я имею в виду Safari и Chrome , оба из которых имеют объекты, называемые tabs
, что позволяет легко забыть, что Safari tab
по-прежнему отличается * классом объекта от Chrome tab
, и любая попытка записать обобщенный код в сценарий одного или обоих встретится с ошибкой.