Understanding AppleScript UI-Scripting to Click Menus

There is a bug with the click command. It’ll work immediately the first time round which is fine if you’re just toggling a button like low latency mode

Ie


Tell application “System Events” to tell process “Logic Pro”
       Set tracks_window to title of first window whose title contains “_Tracks”
       set front most to true
       Tell checkbox “Low Latency Monitoring Mode” of group 1 of window tracks_window
             Click
        End tell
End tell

But if you need to send more events after the click, I would download a command line program called cliclick (command line interface click)

Then, find the ui element, note that the ui element can change depending on the window layout in logic. so if you can find the accessibility description rather than entering button 2 you could say

Here’s an example using cliclick

`Tell application “System Events” to tell process “Logic Pro”
       Set tracks_window to title of first window whose title contains “_Tracks”
       set front most to true 
Set theImage to (The first button of UI element 1 of group 2 of list 1 of window tracks_window whose accessibility description = “xxx”
Set {x, y} to position of theImage
Do shell script “/user/local/Cellar/cliclick/5.x/bin/cliclick dc:” & x & “,” & y `

After that, in the same tell block, you can use keystrokes and key codes to navigate through a pop up menu. With accessibility descriptions you can do a lot with gui scripting in Logic Pro. Here’s some examples from apple scripts I’ve worked into better touch tool

Here’s an AppleScript that on run will give you a 5 second head start to position your mouse on an element in the gui then it will list all the available info of the ui element under the cursor.


delay 5
#!/usr/bin/osascript
--------------------------------------------------------------------------------
# pnam: GET MOUSE POSITION | IDENTIFY UI ELEMENT
# nmxt: .applescript
# pDSC: Identifies the UI element reference located under the mouse cursor

# plst: -/Users/dh/AppleScriptive/scripts/Get Mouse Position | Identify UI Element.applescript

# rslt: «text» : A script containing a reference to the UI element's properties
#       -      : Element not accessible
--------------------------------------------------------------------------------
# sown: CK
# ascd: 2018-09-02
# asmo: 2019-06-09
--------------------------------------------------------------------------------
use application "System Events"
use scripting additions
--------------------------------------------------------------------------------
# IMPLEMENTATION:
set s to __string__({UIElement:it ¬
	, UIProperties:properties ¬
	, UIAttributes:name of attributes ¬
	, UIActions:name of actions} of ¬
	(click at my mouseLocation()))

-- Text manipulation -- 
set [a, b, c] to [length of "{UIElement:", ¬
	offset of "of application \"System Events\", " in s, ¬
	length of "of application \"System Events\", "]
set UIElement to text (1 + a) thru (b - 2) of s
set UIRecord to ["{", text (b + c) thru -1 of s]

-- Composite string --
set UIString to the contents of [¬
	"use application \"System Events\"", ¬
	linefeed, linefeed, UIElement, ¬
	linefeed, linefeed, UIRecord] as text


set the clipboard to the UIString
return the UIString
--------------------------------------------------------------------------------
# HANDLERS:
# __string__()
#   Returns a string representation of an AppleScript object
to __string__(object)
	local object
	
	try
		set s to object as missing value
	on error E --> "Can’t make %object% into type missing value."
		set tid to the text item delimiters
		set the text item delimiters to "Can’t make "
		set s to text items 2 thru -1 of E as text
		
		set the text item delimiters to " into type missing value."
		set s to text items 1 thru -2 of s as text
		
		set the text item delimiters to tid
	end try
	
	return s
end __string__

# mouseLocation()
#   Gets the current cursor position relative to the top-left corner of the
#   screen
on mouseLocation()
	script
		use framework "Foundation"
		use framework "AppKit"
		
		property this : a reference to current application
		property parent : this
		
		property NSEvent : a reference to NSEvent of this
		property NSScreen : a reference to NSScreen of this
		
		on mouseLocation()
			set [list, [number, height]] to ¬
				NSScreen's ¬
				mainScreen()'s ¬
				frame()
			
			tell NSEvent's mouseLocation() to ¬
				{its x, height - (its y)}
		end mouseLocation
	end script
	
	result's mouseLocation()
end mouseLocation

In answer to your question the following script would select humanize


Tell application “System Events” to tell process “Logic Pro”
       Set tracks_window to title of first window whose title contains “_Tracks”
       set front most to true 
Set theImage to («class menB» "Functions" of «class sgrp» 1 of «class sgrp» 1 of «class sgrp» 3 of window tracks_winndow
Set {x, y} to position of theImage
Do shell script “/user/local/Cellar/cliclick/5.x/bin/cliclick dc:” & x & “,” & y
Keystroke “mi”
Key code 125
Key code 124
Keystroke “hu”
Key code 34
End tell

you might have to add some values to the x, y data as cliclick doesn’t always move the mouse to the expected position. Eg:


Do shell script “/usr/local/cellar/cliclick/5.x/bin/cliclick dc:” & x + 10 & “,” & y + 7 

It's a timing issue rather than a bug. You need to wait for a popup menu to actually appear before you can act upon it. So if you want all the keystrokes in one script (i.e. without using native KM actions), you can do this (no cliclick required):

Script
try
	with timeout of 0.3 seconds
		activate application "Logic Pro X"
		tell application "System Events"
			tell process "Logic Pro"
				
				set tracks_window to title of first window whose title contains "- Tracks"
				
				click menu button 2 of group 1 of group 1 of group 4 of window tracks_window
				
				delay 0.1

keystroke "MIDI"
key code 124
keystroke "Humanize"
key code 36

		end tell
		end tell
	end timeout
end try

I’m not sure what you mean by timing issue. But I’m pretty sure it’s a bug that’s been around for years. If you tell a pop up menu to click, it will click and reveal the pop up menu pretty much instantly but it won’t carry out any more events until about 5 seconds after click command. This is an issue with the click command. Look it up online. Most posts discussing the topic recommend using cliclick as a work around.

I understood this to mean that you were unable to interact beyond the first click. The script I posted above will fail if you don't include the 0.1s delay as it affords the popup time to appear. Once the delay is in there, subsequent interaction is successful.

Have I misunderstood?

I didn’t say you can’t interact with the interface after using a click command. I said any following events would be delayed by several seconds even if you use ignore application response

Sorry, Include delay after what part of what script? I run some pretty complex and long scripts interacting with pop up menus in logic successfully without any delay. But if your script needs it then fair enough. But it doesn’t change the fact there’s a bug with the delay command. It applies to click and AXPress. Take my word for it if you want to write UI scripting routines using AppleScript that require a click that’s more than toggling a button then use cliclick as a do shell script.

Not in this post, hence my misunderstanding. All good though.


(In this post)

I'm sure you're right. However I've personally never run into any such problem with Logic, so I'm interested to know the circumstances under which that bug would present itself.

Could you give a real-world example of something that will fail without cliclick?

If click command worked correctly I doubt I’d need the delay before keystrokes. In any event it’s neither here nor there because the click command in any instance will leave a several second delay before it will process any future actions. So in response to your question in what instance would the issue occur the answer is every instance click is used. So unless you’re just toggling a button if you don’t want to wait 5 seconds before your next action, then use something like cliclick. Gui automation is meant to spread processes up not slow them down. By the time you’ve waited for the script to start responding again you may as well have just performed the task manually. Theres also an error in your script to select the correct sub menu you would need to add key code 125 after keystroke midi so it selects midi transform rather than midi region parameters. (Also the less text you type in keystrokes the better) in this instance keystroke “M” followed by key code 125, 124 keystroke “hu” key code 34. Would suffice “m” rather than midi and “hu” instead of humanise As if the script failed after the first character of your keystroke text the ‘I” would open the track info d would open event list and I again would open or close the track ui element. You can test what the least amount of characters needed in keystrokes is by clicking on the menu and typing the subfolder name letter by letter until it selects the correct subfolder.

If you're into UI scripting, don't forget that UIElementInspector still works with the current OS. Not as user friendly as the sadly now defunct UI Browser, but easier for most than manually digging through menu items.

Sorry, I'm not sure I understand. There's little or no delay between simulated clicks in this, for example:

tell application "Safari"
	activate
end tell

tell application "System Events"
	tell process "Safari"
		click menu "File" of menu bar 1
		click menu "Edit" of menu bar 1
	end tell
end tell

Or are you talking specifically about Logic? In which case the bug isn't in the click command but in the way Logic handles it.

That must be quite frustrating but it certainly isn't an issue for everyone, and I suspect, as @Nige_S points out, that it isn't, in essence, an AS 'bug', as such.

Actually it's not an error; it's using the Functions menu in the Piano Roll as an example to demonstrate the use of a short delay. For the other menu it would be:

Script
try
	with timeout of 0.3 seconds
		activate application "Logic Pro X"
		tell application "System Events"
			tell process "Logic Pro"
				
				set tracks_window to title of first window whose title contains "- Tracks"
				
				click menu button 2 of group 1 of group 1 of group 3 of window tracks_window
				delay 0.1
				
				keystroke "MIDI Transform"
				key code 124
				keystroke "Humanize"
				key code 36
				
			end tell
		end tell
	end timeout
end try

If you run that on your setup, do you still get a 5s delay?

Agreed, but in my experience, the difference when dealing with short strings is negligible, and my script was an example, so I wanted it to be plainly readable. It runs extremely fast, but sure, you could probably shave off a few nanoseconds by typing "hu" instead of humanize.

you’re absolutely right Nige, thanks for that information and the correction, it was a fairly widely documented general issue in the past. When I wrote the bulk of my UI scripts. There were lots of posts on forums acknowledging the issue and it was considered a bug, that’s obviously since been ironed out. I’ve just used click repeatedly in several different apps without any delay issues. Ironically the issue still persists in logic. So the best way to mouse click in a logic automation script would still be to use cliclick. Re logics ability to handle the command; I wonder if it has something to do with the absence of an AppleScript dictionary in logic.

No, because I don't have that issue in Logic.

Could you please post a script (or macro) that you're having issues with so we can see if there's something else at play?

Scripts do fail and the less chance of numbers being entered outside of a script resulting in window rearranging r record button being pressed things being solo’d etc it’s best to use as little text as possible.

I’m not having any issues. Because I don’t use click command in logic. I sell complex pro app automation scripts and am not in need of scripting advice. Thanks though. You’re being a bit pernickety Neil :slight_smile: I know because I just tried the click command in logic in a script debugger script. Ui scripts do fail maybe your scripts are just very basic. Not necessarily a bad thing.

I can't remember the last time a UI script failed in Logic without simply aborting... but as I already said, I agree. Anyway, enough nitpicking...

Well what do you mean by this then?:point_down:t3:

How would you know if you don't have any scripts with that issue?

I'm not offering advice. I'm trying to clarify whether there is indeed an AppleScript 'bug'. If there were one, I'd like to know how to recognise it so that I'm able to work around it myself.

Please humour me and try it using KM's Execute an AppleScript action instead. Script Debugger is great but I find that it tends to interrupt UI scripting in Logic by stealing focus before the script has completed during testing:

Humanize.kmmacros (20 KB)

Macro screenshot

Or maybe I just try to write them very carefully...?

Logic is infamous for its lack of proper Accessibility support. That likely includes it not returning anything to System Events after a menu click -- which is why AS has wait, sitting and wondering if Logic had heard the command at all. ignoring application responses can result in your script racing ahead of the app, but IIRC using a try block (to catch the errors) containing a with timeout of 1 second block often does the trick -- enough of a pause for the app to respond to the click and not so long that doing it manually is faster.

cliclick works round things similarly, but faster -- it is already ignoring application responses and has a default 20ms delay between actions.

And at the risk of teaching Granny to suck eggs...

Try upping cliclick's "easing value" a little -- for slower-responding apps (and no, I don't know why that might be) it often clicks before the app has registered the full pointer movement. Slowing the movement down can help in those situations.