Thursday, September 20, 2018

#1 2018-09-04 11:55:19 am

ScriptLover
Member
Registered: 2018-09-04
Posts: 4

Iterating Through Different UI Elements Until a Match is Found

Hi Folks,

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:

Gci90rU.png

(it's the second button from the right)

I can refer the button directly, like so:

Applescript:


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.

Applescript:


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

Help would be extremely appreciated!

Thanks!

Last edited by ScriptLover (2018-09-04 11:56:27 am)

Offline

 

#2 2018-09-05 06:20:39 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4653

Re: Iterating Through Different UI Elements Until a Match is Found

Hi. Welcome to MacScripter.

Does this work for you?

Applescript:

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


NG

Offline

 

#3 2018-09-05 09:29:29 am

ScriptLover
Member
Registered: 2018-09-04
Posts: 4

Re: Iterating Through Different UI Elements Until a Match is Found

Amazing. It works incredibly well. Thank you so much!
I wish I could've accomplished that myself. sad

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?

Thanks!
Roy

Last edited by ScriptLover (2018-09-05 09:41:04 am)

Offline

 

#4 2018-09-05 02:20:40 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4653

Re: Iterating Through Different UI Elements Until a Match is Found

ScriptLover wrote:

In your script, what is return for? Is it AS's version of "break from loop"?


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.

And if you could elaborate some more on your script and tell me where did I go wrong, it would be extremely appreciated.


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.  smile

Last edited by Nigel Garvey (2018-09-05 02:24:35 pm)


NG

Offline

 

#5 2018-09-06 08:41:25 am

ScriptLover
Member
Registered: 2018-09-04
Posts: 4

Re: Iterating Through Different UI Elements Until a Match is Found

Amazing. Thank you so much.

One more question regarding those parts of your code:

Applescript:

repeat with g from 1 to (count groupList)

I thought that was just syntactic sugar for repeats, and one might as well use this version, which is the one I'm familiar with.

Applescript:

repeat with g in groupList

I did some testing, and in your specific script, the latter produces errors, while in some of my other scripts, the FORMER produces errors.

What's going on here?

Thanks!

Last edited by ScriptLover (2018-09-06 08:42:13 am)

Offline

 

#6 2018-09-06 11:22:08 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4653

Re: Iterating Through Different UI Elements Until a Match is Found

ScriptLover wrote:

What's going on here?


OK. There are three different types of repeat in AppleScript. The simplest simply repeats a specified number of times, say:

Applescript:

repeat 10 times

Then there's your example from my script:

Applescript:

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:

Applescript:

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:

Applescript:

repeat with g from 5 to 1 by -1
   -- g will be 5, 4, 3, 2, and 1.

With your other example:

Applescript:

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:

Applescript:

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:

Applescript:

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:

Applescript:

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:

Applescript:

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.  wink


NG

Offline

 

#7 2018-09-13 10:22:40 am

ScriptLover
Member
Registered: 2018-09-04
Posts: 4

Re: Iterating Through Different UI Elements Until a Match is Found

Wow, thank you so much for the amazing explanation!

You're now an expert on AppleScript repeats.  wink



I would call that a wild exaggeration, but I'll sure do my best smile

I'll try to get my head around this, step by step:

About this kind of repeats:

Applescript:

repeat with g from 1 to (count groupList)

So you're basically suggesting that it is beneficial to get familiar with this kind of repeats, since they're more versatile?

And regarding what you said here:

This is because g is never actually 4 (which is what the test asks) but a reference to the item in the list.



What's the true value of g here?

Again, thank you so much.

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)