Manipulating specific controls in a window using AppleScript

Holy Crap! Maybe everyone already knows this, but I certainly didn’t. Thanks to @ccstone’s script about getting window information, I started playing around in AppleScript.

I discovered it is possible to manipulate controls in a window, using AppleScript. For example:

tell application "System Events"
	tell process "HandBrake"
		get value of text field 1 of window 1
	end tell
end tell

To get info on all controls in a window:

tell application "System Events"
	tell process "HandBrake"
		get entire contents of window 1
	end tell
end tell

Paste the contents into an editor of some kind and replace commas with newlines, for something a little more readable.

This opens up a “crap-ton” of possibilities (as a young friend of mine would say). I’m going to try to use this knowledge to change some of my macros that rely on found images to something better (hopefully).

But I’m throwing this out there so all you people with more technical knowledge than I have can come up with some methods of doing cool things. Have at it, people. Show us your brilliance!

@peternlewis - It would be cool if we could do something like “pause until control becomes enabled” - something that wouldn’t be quite as easy to do in AppleScript. Of course, having the full gamut of window control manipulation tools would be awesome… Here’s where you tell me why it’s too complicated or just not feasible - not complaining, just anticipating what usually happens when I come up with what I think is a brilliant idea. :stuck_out_tongue:

Hey Dan,

If you search for “System Events” on the forum you’ll find a bunch of stuff I and a few others have posted.

Xcode comes with the Accessibility Inspector.app. It doesn’t do a good job at providing human-readable information, but it will comprehensively describe the UI tree of elements it’s pointed at.

The best tool for UI exploration is UI Browser ($55.00 U.S.). I bought a copy back around 2005, and it’s saved me from pulling my hair out many times.

As you’ve discovered it’s possible to work your way through the UI by hand as well.

My macro makes use of the fact that Keyboard Maestro transforms a AppleScript lists and record to text on output.

This is messy and hard to parse with complete accuracy, but it’s useful.

You surely have BBEdit (if not use TextWrangler its free sibling)

tell application "System Events"
  tell application process "BBEdit"
    tell (first window whose subrole is "AXStandardWindow")
      attributes of button 3
    end tell
  end tell
end tell

Yes, there’s a wealth of information available.

-Chris

1 Like

Holy Moley! You guys have had some seriously long discussions! And they look to be incredibly useful. I’m going to have to PDF some them and start reading. Looking forward to it.

Thanks a million!

Dan, yep, there are many things you can do with AppleScript System Events to inspect and control the UI.

KM can do some of this. For example, the KM Button condition and Press Button action cover more than just a simple button -- it also covers checkboxes (any maybe radio buttons).

If KM can do it, it is almost always easier than figuring out the UI elements to use with AppleScript.

Here's an example I developed to check a certain checkbox in SnagIT Preferences:

For comparison, here's the AppleScript I ended up writing, for other reasons, that does the same thing:

activate application "Snagit"
tell application "System Events"
  tell process "Snagit"
    
    --- OPEN THE SNAGIT PREFERENCES WINDOW ---
    
    tell menu item 6 of menu 1 of menu bar item 2 of menu bar 1 --prefMenu
      --menu item "Preferences…" of menu 1 of menu bar item "Snagit" of menu bar 1
      perform action "AXPress"
    end tell
    
    --delay 0.5   ## don't seem to need a delay
    
    set oWin to window 1 -- SnagIT main window
    
    --- CLICK ON CAPTURE TAB ---
    tell button "Capture" of toolbar 1 of oWin
      perform action "AXPress"
    end tell
    
    tell oWin
      set nameWin to name
      --set actionsList to actions
      --set uielemList to UI elements
      --set arrtList to attributes
      
      --- MAKE SURE "Preview in Editor" is CHECKED ---
      
      set ckbPreview to checkbox "Preview in Editor"
      if value of ckbPreview is 0 then
        --set value of ckbPreview to 1
        tell ckbPreview
          perform action "AXPress"
        end tell
      end if
      
      
      --- CLOSE THE PREFERENCES WINDOW ---
      tell button 3 of oWin
        perform action "AXPress"
      end tell
    end tell
    
  end tell
end tell

I like the KM version better. :stuck_out_tongue:

If only KM handled Text Boxes, and checking for Enabled/Disabled. Then I’d be happy as a pig in… You know, that’s a lie. I’m sure I’d find something else to belly ache about!

“I want it all, and I want it NOW!”

It does handle that.

Not for Text Boxes, though, right?

I haven't tried that, but I would NOT expect it to, since I can't construe a "text box" as a button.

1 Like

Hey Dan,

Right. Keyboard Maestro can't see them.

System Events usually can though – you can usually set the focus and the value.

-Chris

As with status menu items, the problem tends to be how you specify them. Controls don't always have names, text fields almost never have any kind of name, etc.

So while you can specify them by some mechanism in AppleScript, you cannot specify them simply in general.

Yeah, I thought of that. It really opens up a whole can of worms, from the KM UI point of view. If you support this, then people would want help from the KM UI in identifying controls, etc. This is probably a good place for a plugin action or two.

So let's say I created a plugin action called "Get Control Enabled State" (or a better name if you've got one). I want to use it in a "pause until control is enabled" sort of way. How do you recommend doing this type of "pause until"? Obviously I want to put a delay inside the loop - how much of a delay inside the loop do you recommend, or is this just a personal preference?

There is no plugin for Pause Until per se, but you can have a script condition. There is no way to add a pause to limit the speed of the condition testing, though you could artificially put a delay in your script which would have some limiting affect on the usage.

It is personal preference as to how long to delay, it depends entirely on the situation. Often you (as in the person) are not really doing anything except waiting for the macro to complete, so the only downside of spinning fast is a) you might actually slow down the process that will light up the control, or b) you will use more battery.

1 Like