I’m trying to write a script that will press the “Translate” Safari extension button. The representing UI element is settled among other buttons of various other extensions, as shown here:
(it’s the second button from the right)
I can refer the button directly, like so:
click button 1 of UI element 1 of group 4 of tool bar 1 of window 1 of process "Safari"
The “button 1” and “UI element 1” are static, but since I tend to mess with the installed extensions constantly, the group number tends to change, and I want this script to work regardless of the group’s number. This is what I’ve been up to, but it doesn’t work.
tell application "Safari"
activate
end tell
tell application "System Events"
tell process "Safari"
tell window 1
tell toolbar 1
set theButtons to every button of every UI element of every group
repeat with i in theButtons
try
tell (first button whose description is "Translate")
perform action "AXPress"
end tell
end try
end repeat
end tell
end tell
end tell
end tell
tell application "System Events"
tell process "Safari"
set frontmost to true
tell window 1
tell toolbar 1
set groupList to (description of every button of every UI element of every group)
--> A list of lists of lists of texts representing the toolbar's groups' UI elements' (buttons' descriptions).
repeat with g from 1 to (count groupList)
set UIElementList to item g of groupList
repeat with u from 1 to (count UIElementList)
set buttonDescriptionList to item u of UIElementList
repeat with b from 1 to (count buttonDescriptionList)
if (item b of buttonDescriptionList is "Translate") then
perform action "AXPress" of button b of UI element u of group g
return
end if
end repeat
end repeat
end repeat
end tell
end tell
end tell
end tell
Amazing. It works incredibly well. Thank you so much!
I wish I could’ve accomplished that myself.
In your script, what is return for? Is it AS’s version of “break from loop”?
And if you could elaborate some more on your script and tell me where did I go wrong, it would be extremely appreciated.
And thanks again for the warm (and helpful) welcome. Glad to be here.
I’m new to AppleScript and I’m already in awe of its possibilities. I wish to learn as much as possible and, from the best of the best. What would be the definitive source to learn AS?
That’s its function here, yes. return jumps right out of the handler (subroutine or script run handler) where it occurs. There’s an exit repeat command which explicitly jumps out of the repeat in which it occurs, but here all three of the nested repeats, and the script itself, have to stop once the button’s clicked. An alternative would be error number -128. This is the error that’s generated when a “Cancel” button’s clicked and simply stops the script. return can also be used to return a result, if needed. If you change the line to return “Success!”, you should see “Success!” appear in Script Editor’s “Result” pane after the button’s clicked.
Your line set theButtons to every button of every UI element of every group doesn’t return a straight list of button references. It returns a list of lists of lists of button references which corresponds to the stucture of the groups, UI elements, and buttons found. So:
every group → Returns a list of groups. every UI element of every group → Returns a list of lists (corresponding to groups) of UI elements. every button of every UI element of every group → Returns a list of lists (corresponding to groups) of lists (corresponding to UI elements) of button references.
So your repeat’s iterating through the lists corresponding the groups, not through the buttons themselves. Also, your reference (first button whose description is “Translate”) occurs directly within the context of the tell toolbar 1 statement, so the script thinks you mean a button of the toolbar when you actually mean a button of a UI element of a group of the toolbar.
I usually find whose filters a bit slow, so I started with the list structure returned by your every button of every UI element of every group reference, sticking description of on the front so that the innermost lists would contain the buttons’ descriptions rather than the buttons themselves. The outermost repeat cycles through the lists representing the groups, the middle repeat cycles through the lists (representing UI elements) in each list found by the outermost repeat, and the innermost repeat checks the description texts in each list found by the middle repeat. If item b of item u of item g of the list structure is the required description, it means that button b of UI element u of group g of toolbar 1 is the required button.
It can be mind-bending at first, but it doesn’t take long to get the hang of it.
OK. There are three different types of repeat in AppleScript. The simplest simply repeats a specified number of times, say:
repeat 10 times
Then there’s your example from my script:
repeat with g from 1 to (count groupList)
In this, the given variable (here g) is automatically set to the from value (an integer) for the first iteration of the repeat and is augmented by 1 on each subsequent iteration until, for the last iteration, it has the to value (also an integer). I used this in my script above because the variable is used to index positions in the lists and the script needs to use the same numbers to index the groups, UI elements and buttons in the application.
It’s optionally possible with this kind of repeat to specify different increment steps:
repeat with g from 1 to 11 by 2
-- g will be successively 1, 3, 5, 7, 9, and 11.
repeat with g from 1 to 11 by 3
-- g will be 1, 4, 7, and 10. (13 would go past 11.)
And descending variable values:
repeat with g from 5 to 1 by -1
-- g will be 5, 4, 3, 2, and 1.
With your other example:
repeat with g in groupList
The variable (g again) is set to a reference to each item in the list (or application object) in turn. So say groupList contains integers instead of lists:
set groupList to {5, 7, 4, 10, 3}
repeat with g in groupList
get g
-- Successively:
--> item 1 of {5, 7, 4, 10, 3}
--> item 2 of {5, 7, 4, 10, 3}
--> item 3 of {5, 7, 4, 10, 3}
--> item 4 of {5, 7, 4, 10, 3}
--> item 5 of {5, 7, 4, 10, 3}
end repeat
In most cases, this means you can treat the variable as if it contained the item in the list:
set groupList to {5, 7, 4, 10, 3}
repeat with g in groupList
get g + 1
-- Successively:
--> 6
--> 8
--> 5
--> 11
--> 4
end repeat
The exception to this, which often catches people out, is in tests for equality:
set groupList to {5, 7, 4, 10, 3}
repeat with g in groupList
if (g = 4) then beep -- Never beeps.
end repeat
This is because g is never actually 4 (which is what the test asks) but a reference to the item in the list. In such cases, the reference has to be explicitly resolved with the contents operator:
set groupList to {5, 7, 4, 10, 3}
repeat with g in groupList
if (contents of g = 4) then beep -- :)
end repeat
You’re now an expert on AppleScript repeats.
Edit: Oops. I neglected to mention ‘while’ and ‘until’ repeats, which are fairly self-explanatory:
set g to 1
repeat while (g < 6)
say g
set g to g + 1
end repeat
set g to 1
repeat until (g = 6)
say g
set g to g + 1
end repeat
Edit 2: And of course simple repeats with (or without!) exit conditions written into the code they contain:
set g to 1
repeat
say g
set g to g + 1
if (g > 5) then exit repeat
end repeat