我已经玩了一整天了。目标是生成一个 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
Run Code Online (Sandbox Code Playgroud)
每次都无一例外地吐出以下错误:
error "System Events got an error: Can’t get application \"Finder\"." number -1728 from application "Finder"
Run Code Online (Sandbox Code Playgroud)
我不是要求有人为我解决整个问题。尽管对此事的任何建议总是受到赞赏。当前的麻烦只是将每个窗口的边界设置为变量,以便在脚本的其他地方使用。
此答案侧重于 下所述的问题What I'm Currently Trying To Overcome
。我已经解释The Desired End Result
为背景信息,为您紧迫的问题提供上下文(这真的很有趣/有用,所以谢谢您)。
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
Run Code Online (Sandbox Code Playgroud)
这将让你的列表size
,并position
为每个属性application process
。下面是一个相当冗长的解构,说明你的脚本出错的地方和原因;在确定上述基本代码之前,在考虑了其他同样可行的解决方案之后,提出了一个建议的解决方案。改天当我不那么累时,我会尝试减少这个答案的冗长,但就目前而言,我希望更深入的洞察力有所帮助。
? 从脚本抛出的特定错误开始,有必要指出,一般来说,tell application
块不经常也不应该很少需要嵌套。您打开了一个tell
目标System Events的块,这是获取process
名称所必需的;这就是您应该关闭tell
块或在一行上使用简单 tell
命令的时候:
tell application "System Events" to set openApps to the name of every process...
Run Code Online (Sandbox Code Playgroud)
(end tell
在这种情况下不需要)。
但是,当您的tell
块保持打开状态时,接下来的命令也被定向到System Events。application process
您的脚本显然找到的第一个属于Finder,并且当您的脚本(在重复循环内)被指示tell application "Finder"
(通过checkApp
变量)时,会抛出错误,因为您实际上已经告诉系统事件告诉Finder做某事,而系统事件不了解如何与application
对象进行交互。
? 这将我们引向以下行,其中有几个与您的脚本相关的问题(加上一个更普遍的值得注意的†,我已经留下了脚注):
tell application checkApp to get the bounds of the front window
Run Code Online (Sandbox Code Playgroud)
此行仅适用于 (Apple)可编写脚本的应用程序。并非所有应用程序都可以由 AppleScript 控制——这是应用程序制造商在为 macOS 开发软件时选择实施(或选择不实施,这种情况越来越多)的功能。
那些编写脚本的意志(如果他们效仿苹果的指导方针)定义window
对象都包含一个bounds
属性。那些不可编写脚本的将不会有这些。这时候会抛出另一个错误。
另一个“次要”问题是并非所有进程都background only
必须有窗口,因此front window
. Finder从不只是背景,但有时没有打开的窗口。因此,即使您遇到的错误已得到修复,如果没有打开的Finder窗口,这也是下一个出现的错误。
尽管您无法获得bounds
属于不可编写脚本的应用程序的窗口的属性,但系统事件可以检索属于application process
. 这与进程所属的应用程序本身是否可编写脚本无关,因为系统事件是我们所针对的应用程序,它是可编写脚本的,并且恰好可以访问与每个进程的window
对象有关的类似信息(注意,请参阅脚注下面,但window
属于对象process
是不一样的window
,属于一个对象application
,所以不能互换使用,并且也不可以它们的属性)。
虽然没有bounds
对财产window
所拥有的对象processes
的系统事件,还有其他两个属性,它们共同等同于bounds
:position
和size
。的position
给出了{X, Y}
相对于屏幕(在此上下文中定义为原点在的左上角的窗口的左上角的坐标{0, 0}
); 的size
给出了{X, Y}
一对分别代表窗口的宽度和高度,尺寸。
因此,给定特定窗口的假设bounds
属性值 ,{, , , }
与size: {, }
和的关系position: {, ?}
可以表示为:
{, , , } = {, , + , + ?}
Run Code Online (Sandbox Code Playgroud)
另一个考虑是获取实际具有窗口的进程列表。有多种方法可以做到这一点,每种方法都有优点和缺点,包括代码的简洁性、执行时间和检索到的窗口列表的准确性。
您原来的检索进程列表的方法鉴别通过background only
最快的国家之一,只有在那里漏报导致遗漏从列表中少数情况下(即,菜单栏,作为注册申请background only
尚未明确有window
;在Instagram的应用程序水槽是例子)。
您可以改为通过visible
属性进行区分,这同样快,但在应用程序被隐藏并且需要在记录其窗口属性之前取消隐藏的情况下,我感觉不太合适;或者,还可以是注册一些菜单栏的应用程序为background only
,没有visible
,但显然是在前台窗口visible_。
遗憾的是,在任何情况下检索所有窗口最可靠的方法都非常慢,但确实会生成一个易于使用且不需要进一步处理的列表 a。
然而,在我们目前的情况下,我认为选择提供速度并适用于大多数应用程序的选项是明智的,这就是您的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
Run Code Online (Sandbox Code Playgroud)
如果您关闭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
Run Code Online (Sandbox Code Playgroud)
在我的系统上运行需要 20 倍的时间,产生一个解决方案,尽管它可以识别菜单栏应用程序、常规应用程序和带有窗口的隐藏应用程序,这可能最终对您的最终目标至关重要。但是,如果您最常使用更常规的应用程序,那么哪种方法更合适就很明显了。
†更普遍的潜在问题——它实际上并不真正适用于你的脚本,但如果你尝试使用类似的技术,那么了解未来的脚本会很有用——是使用变量作为引用一个application
在编译时(在脚本运行之前)具有未确定的名称。
bounds
是所有可编写脚本的应用程序的一个相当普遍的属性windows
,这就是为什么您(几乎)在这里摆脱了这种技术。但是,如果你选择了一个属性或对象类,脚本编辑器要注意,它不包含的AppleScript将无法识别的术语,并假设它只是一个变量名。为了识别特定于应用程序的术语,需要以某种形式引用该特定应用程序,或者通过 atell application "Finder" to...
或将相关行括在一个using terms from application "Finder"
块中。
一个好的经验法则是,应用程序通常需要在编译时已知并指定,以便接收 AppleScript 命令。如果不if...then...else if...
为每个可能的应用程序使用一系列条件块,就没有简单的方法来满足不同的选项。
这是一个令人沮丧的根源,尤其是当涉及到看似性质相似的应用程序时,而且具有类似的 AppleScript 字典,但仍然没有相互共享它们的术语以供一般使用。我正在特别考虑Safari和Chrome,它们都有被称为 的对象tabs
,很容易忘记Safari tab
仍然是与Chrome 不同的对象类tab
,以及任何将通用代码编写为脚本的尝试或两者会遇到失败。