繁体   English   中英

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

[英]AppleScript - Get the Bounds of Every open Window

我已经玩了一整天了。 目标是生成一个 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"

我不是要求有人为我解决整个问题。 尽管对此事的任何建议总是受到赞赏。 当前的麻烦只是将每个窗口的边界设置为变量,以便在脚本的其他地方使用。

此答案侧重于What I'm Currently Trying To Overcome的问题下所述的问题。 我已经将The Desired End Result解释为背景信息,为您紧迫的问题提供背景信息(这真的很有趣/有用,所以谢谢您)。

TL; 博士

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

这将为您提供每个application processsizeposition属性列表。 下面是一个相当冗长的解构,说明你的脚本出错的地方和原因; 在确定上述基本代码之前,在考虑了其他同样可行的解决方案之后,提出了一个建议的解决方案。 改天,当我不那么累时,我会尝试减少这个答案的冗长,但就目前而言,我希望更深入的洞察力有所帮助。

问题

▸ 从你的脚本抛出的特定错误开始,有必要指出,一般来说, tell application块不经常也不应该很少需要嵌套。 您打开了一个tell块以定位System Events ,这是获取process名称所必需的; 这就是您应该关闭tell块或在一行上使用简单的tell命令的时候:

tell application "System Events" to set openApps to the name of every process...

(在这种情况下不需要end tell )。

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

▸ 这将我们引向以下行,其中有几个与您的脚本相关的问题(加上一个更普遍的值得注意的我已经留下了脚注):

tell application checkApp to get the bounds of the front window

此行仅适用于 (Apple)可编写脚本的应用程序。 并非所有应用程序都可以由 AppleScript 控制——这是应用程序制造商在为 macOS 开发软件时选择实施(或选择不实施,这种情况越来越多)的功能。

那些可编写脚本的(如果他们遵循 Apple 的指导方针)定义了每个包含bounds属性的window对象。 那些不可编写脚本的将不会有这些。 这时候会抛出另一个错误。

另一个“次要”问题是并非所有background only进程都必须有窗口,因此有front window Finder从不只是背景,但有时没有打开的窗口。 因此,即使您遇到的错误已得到修复,如果没有打开的Finder窗口,这也是下一个出现的错误。

一个办法

尽管您无法获得属于不可编写脚本的应用程序的窗口的bounds属性,但系统事件可以检索属于application process对象的一些属性。 这与进程所属的应用程序本身是否可编写脚本无关,因为系统事件是我们所针对的应用程序,它可编写脚本的,并且碰巧可以访问与每个进程的window对象有关的类似信息(注意,请参阅下面的脚注,但window属于对象process一样的window属于一个对象application ,所以不能互换使用,并且也不可以它们的属性)。

尽管System Events processes拥有的window对象没有bounds属性,但还有另外两个属性,它们一起等价于boundspositionsize position给出了窗口左上角相对于屏幕左上角的{X, Y}坐标(在此上下文中定义为{0, 0}处的原点); size给出了{X, Y}对分别代表窗口宽度和高度的尺寸。

因此,给定一个特定窗口的假设bounds属性值{𝑎, 𝑏, 𝑐, 𝑑} ,与size: {𝑥, 𝑦}的关系size: {𝑥, 𝑦}position: {𝑤, ℎ}可以表示为:

{𝑎, 𝑏, 𝑐, 𝑑} = {𝑥, 𝑦, 𝑥 + 𝑤, 𝑦 + ℎ}

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

您检索background onlybackground only区分的进程列表的原始方法是最快的方法之一,并且只有少数情况下漏报会导致列表中的遗漏(即background only注册为background only但显然有一个window菜单栏应用程序; Instagram 应用程序Flume就是一个例子)。

您可以改为通过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

如果您关闭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

在我的系统上运行需要 20 倍的时间,尽管它可以识别菜单栏应用程序、常规应用程序和具有窗口的隐藏应用程序,但最终可能对您的最终目标至关重要。 但是,如果您最常使用更常规的应用程序,那么哪种方法更合适就很明显了。


更普遍的潜在问题——它并不真正适用于你的脚本,但如果你尝试使用类似的技术,那么了解未来的脚本会很有用——是使用变量作为引用一个在编译时(在脚本运行之前)具有未确定名称的application

bounds是所有可编写脚本的应用程序windows一个相当普遍的属性,这就是为什么你(几乎)在这里摆脱这种技术。 但是,如果您选择了脚本编辑器包含的属性或对象类,AppleScript 将无法识别该术语并假定它只是一个变量名称。 为了识别特定于应用程序的术语,需要以某种形式引用该特定应用程序,或者通过tell application "Finder" to...或将相关行括在using terms from application "Finder"using terms from application "Finder"块。

一个好的经验法则是,应用程序通常需要在编译时已知并指定,以便接收 AppleScript 命令。 if...then...else if...不使用if...then...else if...为每个可能的应用程序设置一系列条件块,就没有简单的方法可以满足不同的选项。

这是一个令人沮丧的根源,尤其是当涉及到看似性质相似的应用程序时,而且具有类似的 AppleScript 字典,但仍然没有相互共享它们的通用术语。 我正在特别考虑SafariChrome ,它们都有被称为tabs对象,很容易忘记Safari tab仍然是Chrome tab不同的对象类,以及任何将通用代码编写到脚本的尝试一个或两个都会失败。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM