So I really want to fix this applescript I'm working on. I would like to be able to click an arbitrary item in an arbitrary submenu of an arbitrary menu in any application. Here's where I've gotten:
tell application "System Events"
set activeApp to name of first application process whose frontmost is true
end tell
tell application "System Events" to tell process activeApp
tell menu bar item 1 of menu bar item 1 of menu bar 1
click
end tell
end tell
The problem I'm getting is when I add that second "of menu bar item 1". It worked just fine up until I got to that point and now I don't know what to do? I'm pretty new to UI scripting and applescript.
Best Answer
The hierarchy of the
menu bar
object goes like this:where the square brackets indicate possible sub-menus, should they exist.
Therefore, if you change this line:
to this:
it will work.
This, of course, will always click the Apple () menu, which is
menu bar item 1 of menu bar 1
for all applications in which the menu bar exists and is visible (although it belongs to whichever application is frontmost, so attempting to ask another application process to click this item will throw an error).If you wish to click an arbitrary menu item in the menu bar to reveal its menu, you need to replace the index number
1
of themenu bar item
with a random value, but one that does not exceed the number of availablemenu bar items
.To get the number of menu bar items, use the
count
command:(You can omit
of activeApp
if the command is placed inside atell process activeApp
block.)To get a random number between 1 and
N
, use therandom number
command:Putting that all together, your new command will look something like this:
Now each time you run the script, a random menu bar item will be clicked causing its menu to appear.
To go even further, and get your script to click a random
menu item
, you can apply exactly the same principles as above, bearing in mind the hierarchy I laid out at the top.So, after retrieving a random menu bar item, next determine the maximum number of
menu items
and generate a random number less than or equal to this maximum:Then you can click it:
Applying the same principles to any sub-menus that exist for that particular menu item, you'll end up with this:
However, you'll find that this—more often than not—will throw an error when trying to
set L
. This is because not all menu items will have sub-menus, and in the instances where no sub-menu exists, asking AppleScript to count the number of menu items [in a non-existent sub-menu] is not a sensible command.There are two ways to handle this. Either wrap those lines in a
try...end try
error-catching block, like this:or
count
the number of (sub-)menus ofmenu item j
, and only proceed if this number is greater than 0:And so on, and so on...
Finally...
It's unlikely you will know precisely how many levels deep a particular menu tree goes: how many sub-menus within sub-menus exist for a particular menu item ? The answer is at least 0, but with no given maximum unless you check in advance yourself.
Luckily, we can AppleScript to do the checking for us. After picking a random
menu bar item
, every child that spawns from this (looking at the hierarchy I showed you at the top) is amenu
followed by amenu item
. Themenu
object, as you'll have noticed, only comes in singular count, so will always be referenced asmenu 1
. Themenu items
come in plural, and these are the items that need counting.So, quite simply, having chosen a random
menu bar item
, we get AppleScript to do the following:count
the number ofmenu items
it contains;count
the number ofmenu items
this one contains;This process is a loop that can keep going for as many sub-menus that it finds. And that's what this code does:
Caveat: The horizontal lines that separate sections of a menu are also classed as
menu items
, as are any menu items that are disabled. These, when clicked, don't do anything.BE CAREFUL: Clicking a menu item randomly before you know what it does can result in some undesirable outcomes. For instance, you may accidentally quit the front application; you may delete a file or eject a disk if the script runs whilst Finder is at the front; or you may log out or shut down your computer.