AppleScript-获取每个打开的窗口的界限

时间:2018-09-12 18:52:52

标签: macos applescript

我整天都在玩这个游戏。目标是生成可生成更多AppleScript的AppleScript。我将详细解释。

所需的最终结果:安排好窗口后,按照自己的喜好启动此脚本。这会将必需的脚本复制到剪贴板,以自动启动,定位应用程序窗口并调整其大小,使其适应当前配置。这样一来,我便可以将脚本发送给其他人,然后他们可以在启动此脚本时设计自己的自定义布局,然后将其粘贴到“脚本编辑器”中,或者可以将其制成服务,并使用Automator绑定到热键。

我目前想克服的问题:我似乎无法让它列出每个窗口的边界。我当前正在运行此脚本。

  tell application "System Events"
    set openApps to name of every process whose background only is false

    repeat with theItem in openApps
        set checkApp to theItem
        tell application checkApp to get the bounds of the front window
    end repeat
end tell

这每次都会毫无例外地发出以下错误:

error "System Events got an error: Can’t get application \"Finder\"." number -1728 from application "Finder"

我并不是要有人为我解决整个问题。尽管对此事有任何建议总是很感激。当前的麻烦只是将每个窗口的边界设置为变量,以供脚本中的其他地方使用。

1 个答案:

答案 0 :(得分:2)

此答案的重点是 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的{​​{1}}和position属性的列表。以下是对脚本错误的位置和原因的详细解构;在考虑其他基本可行的解决方案之后,再提出建议的解决方案,然后再采用上述基本代码。当我不那么累的时候,我将尝试在另一天整理一下此答案的含义,但现在,我希望更深入的了解会有所帮助。

问题

▸从脚本抛出的特定错误开始,有必要指出,通常来说,application process块不经常也不应该嵌套。您打开了一个tell application块以定位系统事件,这对于获得tell名称是必需的;这就是您应该关闭process块或在一行上使用 simple tell命令的时候所在的点:

tell

(在这种情况下,不需要tell application "System Events" to set openApps to the name of every process...

但是,当end tell块保持打开状态时,接下来出现的命令也将定向到系统事件。您的脚本显然找到的第一个tell属于 Finder ,并且当您的脚本(在重复循环内)被指示application process时(通过{{1} }变量),因为您实际上已经告诉系统事件告诉 Finder 做某事,并且系统事件不了解,所以引发了错误如何与tell application "Finder"对象进行交互。

▸这将导致我们进入以下几行,其中涉及与您的脚本有关的几个问题(加上一个更一般的值得注意的地方,除了,我为此留下了脚注):

checkApp

该行仅适用于(Apple)可编写脚本的应用程序。并非所有应用程序都可以由AppleScript进行控制-这是应用程序制造商选择的一项功能,可以在为macOS开发其软件时实施(或选择不这样做,这种情况更为常见)。

可编写脚本的 (如果遵循Apple的准则)将定义application个对象,每个对象都包含一个tell application checkApp to get the bounds of the front window 属性。那些无法编写脚本的程序将不会具有任何一种。那是在抛出另一个错误的时候。

另一个“次要”问题是,并非所有window的进程都必须具有窗口,因此也没有bounds Finder 绝不仅是背景,而且有时没有打开的窗口。因此,即使您修复了所得到的错误,如果没有打开的 Finder 窗口,这也是出现的下一个错误。

解决方案

尽管您无法获得属于非脚本应用程序的窗口的background only属性,但系统事件可以检索属于{{1}对象的某些属性}。这与该进程所属的应用程序本身是否可编写脚本无关,因为 System Events 是我们要定位的应用程序,它可编写并且恰好具有访问与每个进程的front window对象有关的类似信息( NB 。请参见下面的脚注,但是属于bounds的{​​{1}}对象不是 与属于application process的{​​{1}}对象相同,因此不能互换使用,它们的属性也不能使用。

尽管系统事件window所拥有的window对象没有process属性,但还有两个其他属性,它们一起是等效于windowapplicationboundswindow给出了窗口左上角相对于屏幕左上角的processes坐标(在此上下文中,其定义为bounds的原点); position给出size对尺寸,分别代表窗口的宽度和高度。

因此,给定特定窗口的position的假设{X, Y}属性值,可以表示与{0, 0}size的关系:

{X, Y}

另一个考虑因素是获取实际上具有Windows的进程的列表。有多种方法可以做到这一点,每种方法都有其优点和缺点,其中包括代码的简洁性,执行时间以及所检索的Windows列表的准确性。

检索由bounds区分的进程列表的原始方法是最快的方法之一,并且在少数情况下,误报导致从列表中遗漏(即,注册为{{1 }},但显然有一个{, , , }; Instagram应用程序 Flume 是一个示例)。

您可以通过size: {, }属性来进行区分,该属性同样快,但是在隐藏了应用程序且需要在记录其窗口属性之前将其取消隐藏的情况下,我不太适应。还是再次,一些菜单栏应用程序注册为position: {, ℎ},而不是{, , , } = {, , + , + ℎ} ,但显然是 且前景是一个窗口。

在任何情况下,检索所有窗口最可靠的方法令人遗憾的是,它相当慢,但是确实生成了一个易于使用且不需要进一步处理的列表。

但是,在我们目前的情况下,我认为选择提供速度并适用于大多数应用程序的选项是明智的,这是您的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 窗口,您会看到它仍按名称显示在第一个列表中,但是第二个和第三个列表的窗口background only处为空,大小和位置将否则。因此,在尝试引用每个子列表的项目之前,请务必进行一些检查。

将它与这种较慢但更准确的解决方案进行比较和对比:

    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

在我的系统上运行需要二十倍的时间,这产生了一个可以识别菜单栏应用程序,常规应用程序和具有窗口的隐藏应用程序的解决方案,但这最终可能对于您的最终目标至关重要。但是,如果您最常使用更常规的应用程序,那么很显然哪种方法更合适。


更广泛的,潜在的问题—实际上并没有真正适用于您的脚本,但是如果您尝试使用类似的技术,则对于以后的脚本了解很有用-是使用变量作为引用在编译时(脚本运行之前)名称不确定的window的一种方式。

visible是所有可编写脚本的应用程序background only的普遍存在的属性,这就是为什么您(几乎)在这里不使用此技术。但是,如果选择了脚本编辑器特别包含不包含的属性或对象类,AppleScript将无法识别该术语,并假定它只是一个变量名。为了识别特定于应用程序的术语,需要以某种形式引用该特定应用程序,或者通过visible或将相关行括在background only块内。 / sub>

一个很好的经验法则是,通常需要在编译时知道和指定应用程序才能接收AppleScript命令。没有为每个可能的应用程序使用{}系列条件块的简单方法来满足各种选项。

这是令人沮丧的根源,特别是对于本质上看似相似,并且具有相似的AppleScript词典的应用程序,但仍不相互共享其通用术语。我专门考虑的是 Safari Chrome ,它们都具有被称为application的对象,因此很容易忘记 Safari < / em> bounds仍然是 Chrome windows的对象的不同类,并且任何尝试编写通用代码以编写脚本或脚本将会失败。