Converting list of specifier to string

Hi,

the result of

set u to entire contents of menu bar

is displayed in the editors’ result window, but I can’t convert it to a string.

Any ideas?

{menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item “About This Mac” of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item “System Information…” of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item 3 of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item “System Preferences…, 1 update” of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item “Location” of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu “Location” of menu item “Location” of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item “----Wlan 2021” of menu “Location” of menu item “Location” of menu “Apple” of menu bar item “Apple” of menu bar 1 of application process “Glyphs” of application “System Events”, menu item “+++Ethernet 2021” of menu “Location” of menu item “Location” of menu “Apple” of menu bar item “Apple” of

One way I see is retrieving, then parsing the text info from error message.


property theClasses : {"«class pcap»", "«class mbar»", "«class mbri»", "«class menE»", "«class menI»"}
property textReferences : {"application process", "menu bar", "menu bar item", "menu", "menu item"}

tell application "System Events" to tell process "Finder" to tell menu bar 1
	try
		entire contents as text
	on error errorMessage
		set entireContentsAsText to text 13 thru -18 of errorMessage
	end try
end tell

set ATID to AppleScript's text item delimiters
repeat with i from 1 to count theClasses
	set entireContentsAsText to ¬
		findAndReplaceInText(entireContentsAsText, item i of theClasses, item i of textReferences)
end repeat
set AppleScript's text item delimiters to ATID
return entireContentsAsText

on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to text items of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
end findAndReplaceInText

@Fredrik71,

Your code relies heavily on the Script Editor document, so it doesn’t work as an application. As a script, it won’t work in another script editor either. It is better to not involve script editors at all. Only the examined process, and the AppleScript itself.

This is helpful and wonderfully simple. I have tried working with ‘replies’ (as well as just ‘result’) and of course, it never went well. Thanks.

I added these two lines above the last line to separate the other elements of the events log (although sometimes it grabs the first text item).

set AppleScript's text item delimiters to "--> "
set theLog to last text item of theLog

Which gets me thinking… I wonder if you couldn’t script Script Editor to just comment the results and use that.

I replaced the lines above with this:

set AppleScript's text item delimiters to {"--> {", "}"}
set theLog to middle text item of theLog as text

That strips both the beginning and ending braces. I then write it to a text file and during that process, the quotes are de-escaped. This is good. Thanks again.

Even though this can’t be run as an application, I often run scripts from SE. When I’m trying to figure something out, I often want to compare sets of properties, or just review them, but it’s an awkward thing to do. With this though, I can easily save for a variety of objects (e.g. window, document, paragraph, word), or to continue with the OP’s menu bar example, for each menu’s ui elements. Maybe even collect them in a spreadsheet.

Basically, I see no point in viewing the structure of UI elements this way. Because the Script Debugger makes it easier to browse the structure of UI elements.

I see a great benefit in the ability to automatically determine the reference to the desired UI element using the Automator application. You click on a UI element of interest while recording (red button) in Automator is activated. Turn off the recording, and copy the reference that Automator graciously wrote down for you…

Automator will record any of your manual actions. To view the recorded action as a script, you drag the action to the “Drag actions or files here to…” area. I know that many users do not even know that this is possible. Therefore, I think that my post will be quite useful.

Here, the Automator graciously wrote my 3 manual actions. Then I dragged “Click the “File” menu” action to the “Drag actions or files here to…” area. Especially for users, I have highlighted (in blue) a reference for copying.

I have used this method on occasion and it is a useful tool to have. But for me, when I’m trying to learn something, I like to be able to see things at varying resolutions. At one end, ‘every ui element’ can be overwhelming (but is still useful to have). At the other end, ‘set uiscript’ is too narrow. It involves an awkward process and provides too sparse results… a single click will generate a clumsy, 20-line script. It takes just a handful of clicks to scroll off the screen. And given the clutter it generates, it is nigh on impossible to compare results.

So, while it is useful, it is not universal.

Very helpful. Thanks a lot!

You are right, the result of “entire contents” is not perfect. But I need an overview of all menu entries. And doing it with an iteration is really slow.

If someone has a solution to get all menu entries (including short cuts (AXMenuItemCmdChar & AXMenuItemCmdModifiers) in a list without waiting a looong time for the return, this would be great.

You cannot avoid iterations for 3 reasons: 1) retrieving menu items is a recursive process, 2) not all menu items have names, for example, dividing lines, 3) not all menu items have a keyboard shortcut.

With this in mind, you can get the names of menu items that have a keyboard shortcut and the keyboard shortcuts themselves, in the following recursive way:


-- Get shorcuts of menu items of application

property appProcessName : "Finder"
property entireMenuItems : ""
property modifierCombinations : {"Command", "Command + Shift", "Command + Option", "Command + Shift + Option", "Command + Option", "Command + Control", "Command + Option + Control", "Command + Option + Control + Shift"}

-- optional code line 
tell application "TextEdit" to make new document with properties {name:"Menu Items Shortcuts"}

tell application "System Events" to tell process appProcessName
	-- except of "Apple" menu bar item (if need)
	-- otherway, set menuBarItems to menu bar items of menu bar 1
	set menuBarItems to menu bar items 2 thru -1 of menu bar 1
	repeat with menuBarItem in menuBarItems
		my recurseMenuItems(menuBarItem)
	end repeat
end tell

return entireMenuItems

on recurseMenuItems(anUIElement)
	tell application "System Events" to tell process appProcessName
		set uiElements to UI elements of anUIElement
		repeat with uiElement in uiElements
			set menuItems to menu items of uiElement
			repeat with menuItem in menuItems
				try
					set cmdChar to value of attribute "AXMenuItemCmdChar" of menuItem
				on error
					set cmdChar to missing value
				end try
				if cmdChar is missing value then
					-- do nothing
				else
					set entireMenuItems to entireMenuItems & title of menuItem & linefeed
					set cmdModifiers to value of attribute "AXMenuItemCmdModifiers" of menuItem
					set cmdModifiers to item (cmdModifiers + 1) of modifierCombinations
					set entireMenuItems to entireMenuItems & cmdModifiers & " + \"" & cmdChar & "\""
					set entireMenuItems to entireMenuItems & linefeed & linefeed
					my addToTextDocument(entireMenuItems) -- optional
				end if
			end repeat
			my recurseMenuItems(uiElement)
		end repeat
	end tell
end recurseMenuItems

on addToTextDocument(theText) -- optional handler
	tell application "TextEdit"
		activate
		set text of document 1 to theText
		delay 0.1
	end tell
end addToTextDocument