Storing and recalling UI element paths using variables?

Is it possible to store the full path to a UI element (obtained using UI Browser) in a variable, then use that variable later on in a “tell” statement in order to obtain the properties of that UI element?

For example, this script returns null, when attempting to use a UI element path that’s stored in the variable named uiElementPath:

set uiElementPath to "checkbox 9 of group 1 of window 1"

activate application "DaVinci Resolve"
tell application "System Events"
	tell application process "DaVinci Resolve"
		set xPos to ""
		set yPos to ""

		try
		    tell uiElementPath
				set {xPosition, yPosition} to position
				set {xSize, ySize} to size
				-- modify offsets if hot spot is not centered:
				set xPos to xPosition + (xSize div 2)
				set yPos to yPosition + (ySize div 2)
			end tell
		end try
		
	end tell
end tell

return {xPos, yPos}

However, I can get this routine to work as expected if I separate the UI Element index numbers into their own variables, and then build the tell statement like this:

set uiElementPath_checkbox to 9
set uiElementPath_group to 1
set uiElementPath_window to 1

activate application "DaVinci Resolve"
tell application "System Events"
	tell application process "DaVinci Resolve"
		set xPos to ""
		set yPos to ""	
		try
			tell checkbox uiElementPath_checkbox of group uiElementPath_group of window uiElementPath_window
				set {xPosition, yPosition} to position
				set {xSize, ySize} to size
				-- modify offsets if hot spot is not centered:
				set xPos to xPosition + (xSize div 2)
				set yPos to yPosition + (ySize div 2)
			end tell
		end try   		
	end tell
end tell

return {xPos, yPos}

But this is not ideal for the script I’m trying to build, because not all of the UI Element indexes adhere to the same type of hierarchy, and I don’t want to have to manually write tell statements for every possible permutation of the element paths that exist in the app I’m trying to UI Script. I already have a map of all the UI element paths in a JSON file, and just want to be able to iterate over all the keys in the JSON file and feed the UI Element path value for each key into the AppleScript tell statement.

I come from a Bash scripting background, so I was hoping there was an AppleScript equivalent to Bash parameter expansion for a scenario like this. But it seems like AppleScript doesn’t work this way? Any advice is greatly appreciated.

Yes, you can store the path to UI elements in references (which essentially delay the normally-transparent resolution of the reference until later in the script). The full keyword is a reference to, but you can type ref as shorthand.

I have a couple of complex UI scripts that use references extensively. Here’s a snippet:

tell application "System Events"
	-- Determine if Mail should be scripted.
	set use_system_settings to true
	tell application "Mail" to set mail_active to application "Mail" is frontmost and (count message viewers) > 0
	if mail_active then
		set mail_sheet_heading to a reference to static text 1 of sheet 1 of window 1 of application process "Mail"
		set mail_sheet_exists to (exists mail_sheet_heading) and value of mail_sheet_heading contains "Enter the password for the account"
		if not mail_sheet_exists then
			set account_row to a reference to (first row of outline 1 of scroll area 1 of splitter group 1 of window 1 of application process "Mail" where (value of static text 1 of its UI element 1 is account_name and value of its attribute "AXDisclosureLevel" is 0)) -- Main heading (not the inbox).
			set connect_button to a reference to (first button of UI element 1 of account_row whose description is "disconnect")
			
			-- Check if connect button exists.
			set connect_button_exists to exists connect_button
			if not connect_button_exists then
				-- Attempt to display the connect button.
				tell application "Mail" to check for new mail for account account_name
				set connect_button_exists to SL's waitUntilUIElementExists(connect_button, 3, 0.1)
			end if
			
			-- Attempt to trigger sheet.
			if connect_button_exists then
				click connect_button
				set mail_sheet_exists to SL's waitUntilUIElementAttributeContains(a reference to value of mail_sheet_heading, 10, 0.1, "Enter the password for the account")
			end if
		end if
		
		-- Determine whether to use Mail or System Settings.
		set use_system_settings to not mail_sheet_exists
	end if
	
	if use_system_settings then
		set target_process to "System Settings"
		tell application "System Settings"
			-- Get application state & activate Internet Accounts pane.
			set was_running to running
			activate
			if not was_running or current pane is missing value then
				set previous_pane_id to missing value
			else
				set previous_pane_id to id of current pane
			end if
			
			set internet_accounts_pane_was_selected to previous_pane_id is internet_accounts_pane_id
			if not internet_accounts_pane_was_selected then reveal pane id internet_accounts_pane_id
			delay 0.1
		end tell
	else -- Using Mail directly.
		set target_process to "Mail"
	end if
	
	-- Script either System Settings or Mail.
	tell process target_process
		set sheet_heading to a reference to static text 1 of sheet 1 of window 1
		set web_form_scroll_area to a reference to scroll area 1 of group 1 of group 1 of group 1 of sheet 1 of window 1
		set web_form_group to a reference to group 2 of UI element 1 of web_form_scroll_area
(*continued*)
end tell
end tell

Thanks for the heads up on the a reference to thing.

However, I can’t seem to get this to work by storing the UI element path in a variable first (uiElementPath in this example), and then recalling it later using the to a reference syntax:

activate application "DaVinci Resolve"
tell application "System Events"
	tell application process "DaVinci Resolve"
		set uiElementPath to "checkbox 9 of group 1 of window 1"
		set uiElementPathRef to a reference to uiElementPath
		set xPos to ""
		set yPos to ""
		
		try
			set {xPosition, yPosition} to position of uiElementPathRef
			set {xSize, ySize} to size of uiElementPathRef
			
			-- get center coordinates of UI element
			set xPos to xPosition + (xSize div 2)
			set yPos to yPosition + (ySize div 2)
		end try
	end tell
end tell

return {xPos, yPos}

This code does not error out, but it still returns null. I did discover that it works if I remove the quotes around the initial value assignment to the uiElementPath variable.

How would I get this to work if I wanted to feed the script UI Element paths from a pre-existing JSON dictionary like this (I’m running this script from Keyboard Maestro)?

{ 
    "cameraraw": "checkbox 9 of group 1 of window 1",
    "colormatch": "checkbox 10 of group 1 of window 1",
    "primaries": "checkbox 11 of group 1 of window 1",
    "hdr": "checkbox 12 of group 1 of window 1",
    "rgbmixer": "checkbox 13 of group 1 of window 1",
    "motioneffects": "checkbox 14 of group 1 of window 1",
    "curves": "checkbox 15 of group 1 of window 1"
}

You can’t do that; uiElementPathRef ends up being a reference to the uiElementPath variable itself.

You can’t do it directly or easily. You have the following options:

  1. Do a second level of compilation at runtime to convert the UI element paths into references (slow, but straightforward).
  2. Parse the strings yourself and build the references in a repeat loop.