簡體   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