dougscripts.com

April 19 2021 - 8:55 pm

Can't Believe I'm Posting About GUI Scripting

It is a fact universally acknowledged—in my house—that GUI Scripting stinks. But the flip side of that is that sometimes it can be the only way to automate something with AppleScript. Especially so if the developers of the app you are 'scripting have long been ignoring your suggestions. Again, my house.

GUI Scripting is a way to write AppleScript that works by simulating mouse clicks, selections, key presses and other "touches" of the GUI elements of an app in order to get the app to do what those clicks and presses do. For instance, you can simulate a mouseclick on a menu item in a menu and whatever happens when you usually click that menu item will happen. GUI Scripting is clunky to work with for a number of reasons, but the two most discouraging are that it's tough to know what and how and where to click and press for the Thing you want to do; and that if the developer changes an app's layout in the future the scripts could break because something isn't where it was before.

Despite all that, I've been playing with GUI Scripting lately. It's what I use to change playlist views in Playlist Manager and how Needle Drop clicks the "Clear" button in the Playing Next panel. If you've used those scripts, you've had to give permission in "Accessibility" to allow those scripts to use the "System Events" app. System Events is what enables AppleScript to target GUI elements.

Here's a little scripting project that uses some GUI Scripting to do three things that I like to do somewhat regularly in the Music app: open the selected playlist in its own window; change the playlist View to "as Songs"; reposition the playlist window to a standard place, rather than BANG right over the main browser window. (Note that I could only get it to work on the latest version of Big Sur with the latest version of Music, which bears out my thoughts above about clunkiness.)

I'll explain a little about what's happening below the listing.

tell application "Music"

try

set v to (get view of front browser window)

if kind of container of v is not library then error

if special kind of v is not in {folder, none} then error

on error

return

end try

end tell

tell application "System Events"

tell application process "Music"

set frontmost to true

tell outline 1 of splitter group 1 of window 1

perform action "AXShowMenu"

ignoring application responses

tell (some UI element whose role is "AXMenu")

click menu item "Open in New Window"

end tell

end ignoring

end tell

tell menu 1 of menu bar item 6 of menu bar 1

repeat until (get title of menu item 7 is "")

delay 0.1

end repeat

click menu item "as Songs"

end tell

end tell

end tell

tell application "Music"

tell front window to set bounds to {1155, 45, 2135, 1375}

end tell

The first tell block is ascertaining whether or not the currently selected playlist is suitable. If an error occurs trying to target a playlist or if any of the unsuitable conditions is met, the error handler will dismiss the script. This should filter out all Sidebar items except user-made local playlists.

The second tell block uses "System Events" to control the Music app. And to do so, Music has to be frontmost. The first thing we want to do is simulate a right-click on the selected playlist in order to display its contextual menu. The thing is, the contextual menu is actually part of the Outline View (the list of playlists) and when you right-mouseclick on a playlist, the Outline View "knows" which of its rows (playlists) you've selected and what to show in the menu. We have to fake that activity. So the script targets the outline and tells it to show the menu. Then, with the menu visible, the script simulates a click on the "Open in New Window" menu item. The Outline View "knows" which playlist the commands from the menu are supposed to affect and thus, a playlist window is opened. I enclosed it in an ignoring block because sometimes click will compel a script to wait for a response for as long as 5 or 6 seconds; the ignoring application responses defeats that behavior.

Now I want to view the playlist "as Songs". To do that, I have to simulate a click on the "as Songs" menu item in the View menu (feel free to use your preferred "as Whatever" view that appears in the View menu). However, the View menu is also contextual and, believe it or not, it might not be completely finished being built when the script is ready to click on it. I worked around this by putting in a repeat loop that waits until the seventh menu item is a separator menu item and then it clicks "as Songs". I could take up a lot of space with the logic of why I used this as the determining factor, but the best reason is that it seemed to work right every time.

Finally, I reposition the playlist window. I figured out the bounds I wanted by using this old-timey scripting trick:

tell application "Music" to get bounds of front window

Run that in a separate Script Editor document after positioning a Music playlist window where you want it. The result will be the bounds that you can copy-and-paste into your script.

Oh, but wait. We're not done, unless you always want to run this script from Script Editor. Save this script as "Application" in your Music Scripts folder. Then launch it. The first thing that happens is that the script will ask for permission to target the Music app. Then it will tell you it needs Accessibility permission. It will offer to open Security & Privacy settings so you can give the applet permission to "control your computer". Once that's done you shouldn't have to do it again. Now re-launch the script and it should work as described.

Site contents © 2001 - 2021 (that's right: 2001) Doug Adams and weblished by Doug Adams. Contact support AT dougscripts DOT com. About.
All rights reserved. Privacy.
AppleScript, iTunes, iPod, iPad, and iPhone are registered trademarks of Apple Inc. This site has no direct affiliation with Apple, Inc.
The one who says "it cannot be done" should not be interrupting the one who is doing it.