I'm trying to create script for application which doesn't have dictionary. How to get selected list item?
I need something like this
tell application "System Events"
tell process "process"
get selected item of list 1 of splitter group 1 of window 1
end tell
end tell
_
choose from list list 1... fails with 1700 error (can't convert to string)
Unfortunately the selected property of each of those particular elements (which are static text UI Elements) seems to be inaccessible.
Doing the following:
activate application "SongOfGod_1_2"
--(this line above is just to make sure
-- we don't miss anything by not having the app frontmost;
--for many commands it is unnecessary)
tell application "System Events"
tell process "SongOfGod"
properties of static text 2 of list 1 of splitter group 1 of group 1 of splitter group 1 of window 1
end tell
end tell
returns:
{class:static text, minimum value:missing value, orientation:missing value, position:{595, 259}, accessibility description:"2. And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.", role description:"text", focused:false, title:missing value, size:{1605, 18}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXStaticText", value:missing value, subrole:missing value, selected:missing value, name:missing value, description:"2. And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters."}
You'll notice, if you scroll way out there, that the selected property returns missing value, which means you cannot determine if it is selected. I also tried focused, which doesn't work. Nor do click or select work. Trying to get the selected property of the list containing it also doesn't work.
You apparently cannot get what you want out of this app, I'm sorry to say.
Related
I'm trying to get the position of a UI element from the Notification Center. I can do so when I get a list of coordinates (an x and y posn) and then reference each one individually. However when I try to do it directly, I get a type coercion error.
System Events got an error: Can’t make item 1 of position of group 1 of UI element 1 of scroll area 1 of window "Notification Center" of process "Notification Center" into type specifier..
Here's the example code:
activate application "NotificationCenter"
tell application "System Events"
tell process "Notification Center"
-- works
set xyPositions to position of group 1 of UI element 1 of scroll area 1 of window "Notification Center"
set xPos to item 1 of xyPositions
-- fails with error
set xPos to item 1 of position of group 1 of UI element 1 of scroll area 1 of window "Notification Center"
end tell
end tell
I'd like to know what's going on, since the two methods seem equivalent.
They look equivalent but they are not:
set xyPositions to position of group 1 of UI element 1 of scroll area 1 of window "Notification Center"
is really shorthand for:
set xyPositions to get position of group 1 of UI element 1 of scroll area 1 of window "Notification Center"
When AppleScript sees an object specifier, it determines if that specifier points to a property or element(s) of a local AppleScript value (string, list, record, etc) or a remote application (in this case, System Events).
If the specifier points to an application, and the specifier isn’t already a parameter to an application command (move, duplicate, delete, etc), AppleScript automatically performs a get command (aka “implicit get”) to resolve that object specifier. In your example, this returns the value of the position property, which is an AppleScript list. The second line then works as you’d expect:
set xPos to item 1 of xyPositions
as AppleScript extracts the first item of that list.
When you combine the two lines into one, you are telling the application to get item 1 of the position property:
set xPos to [get] item 1 of position of group 1 of UI element 1 of scroll area 1 of window "Notification Center"
However the application doesn’t know how to do this—it can only access objects within its object model. It doesn’t know how to manipulate the contents of primitive strings, lists, records, etc; only AppleScript can do that. Thus the application command fails.
If you wish to combine the two lines into one, you must use an explicit get command to resolve the application specifier before manipulating its result:
set xPos to item 1 of (get position of group 1 of UI element 1 of scroll area 1 of window "Notification Center")
Yes, it’s confusing. But that’s AppleScript for you: it tries to be “simple” and “helpful” to make it easy for non-experts to use, but really all it does is obfuscate what’s actually going on underneath that superficially friendly syntax. When it works, it’s like arcane magic. When it confounds, it’s like arcane magic. So it goes.
I have a functioning script that looks at the activity state in a specific app, and time spent in that state, then shows it in the status bar of macOS. It works as expected, but I'd like to add some coloring so that if you go over a certain amount of time in a certain state, the text will turn yellow or red. For example, I might be in the Idle status for 8 minutes, which shows in the status bar, but when it reaches 10 minutes, I'd like for the text to change from white to red.
I've done some research on the 'attribute range' function, but I'm unsure of how that might be applied (or if it can be applied) to my script since I'm not working with text in Pages, Microsoft Word, Text Edit, or something similar, just a value that is returned to the status bar.
on idle
-- Update the status item's text here.
tell application "System Events"
if not (exists process appName) then
display alert "Application " & appName & " is not running" as warning giving up after 6
quit me
end if
tell process appName
-- assume the window and toolbar are always going to be there
repeat until exists of first window's first toolbar's fourth group's first group's first menu button
delay 0.2
end repeat
tell first window's first toolbar's fourth group's first group's first menu button
set activityState to first item of (value as list) as text
end tell
end tell
end tell
set statusItem's button's title to activityState
(*
The return value gives the time in seconds
*)
return 1
end idle
I'm wondering if I can use a command to set the 'activityState' attribute to a certain color since that variable has been defined to the appropriate area of that affected app's GUI, then to set conditions for that to change depending on the type of activity state, and time spent there.
An attributed string can be used for the status item title:
set greenAttr to current application's NSDictionary's dictionaryWithObjects:{current application's NSColor's greenColor()} forKeys:{current application's NSForegroundColorAttributeName}
set redAttr to current application's NSDictionary's dictionaryWithObjects:{current application's NSColor's redColor()} forKeys:{current application's NSForegroundColorAttributeName}
set attrText to current application's NSMutableAttributedString's alloc's initWithString:"whatever"
attrText's setAttributes:greenAttr range:{0, attrText's |length|()}
statusItem's button's setAttributedTitle:attrText
Then at some point the attributes can be changed or replaced and the title set with the new attributed string:
if elapsedTime > someTime then attrText's setAttributes:redAttr range:{0, attrText's |length|()}
statusItem's button's setAttributedTitle:attrText
I am creating an AppleScript to control some basic standard actions in SAP and everything as written works fine from the main menu page of available transactions. The current script fills out the Command Text Field and opens a new existing transaction code. However, once SAP has gone to the new transaction code page, when I run the AppleScript a second time it no longer recognizes the index of the Command Text Field. Instead it pastes the transaction code into whatever text field happens to be the current focus. When I run the script, it should always go to the same text field because the overall index address hasn't changed, but it's not.
I've attempted to open the Script Dictionary for SAP, but it appears that my company's flavor of SAP has had the scripting dictionary disabled, so I am forced to use indexing addresses to fill text fields and click buttons
on chooseSAPTransaction()
global sapTransaction, transactionCode
set transactionCode to "" -- intialize the variable
set transactionList to choose from list {"Find Document", "Change Document", "Look up Product", "Correct Product"} with prompt "SAP Action:" default items {""}
if transactionList is false then
error number -128
else
set choosenTransaction to item 1 of transactionList
end if
if choosenTransaction is "Find Document" then
set transactionCode to "code1"
chooseCode1()
end if
if choosenTransaction is "Change Document" then
set transactionCode to "code2"
chooseCode2()
end if
if choosenTransaction is "Look up Product" then
set transactionCode to "code3"
chooseCode3()
end if
if choosenTransaction is "Correct Product" then
set transactionCode to "code4"
chooseCode4()
end if
set sapTransaction to "/n" & transactionCode
activate application "SAP"
delay 0.1
tell application "System Events"
tell process "SAP"
tell front window to tell toolbar 1 to tell text field 1
keystroke sapTransaction
end tell
tell front window to tell toolbar 1 to tell button 1 to perform action "AXPress"
end tell
end tell
end chooseSAPTransaction
It's skipping tell front window to tell toolbar 1 to tell text field 1 and going right to keystroke sapTransaction on subsequent script executions
("/n" & transactionCode creates the string that tells SAP the new transaction to go to. When typing it manually, it always takes you to the new transaction, regardless of how many layers deep you might be in the current transaction)
I expect the indexing tell to go to the same element on every new instance of running the script, but once it's run once, AppleScript isn't finding the element address anymore.
edit:
I just tried moving the pasting action into its own unique handler so that it can be called before attempting to go to the next step, but that didn't work either.
--snip--
if choosenTransaction is "Find Document" then
set transactionCode to "code1"
set sapTransaction to "/n" & transactionCode
pasteTransaction()
chooseCode1()
end if
--snip--
on pasteTransaction()
global serialNumber, sapTransaction, transactionCode
activate application "SAPGUI"
delay 0.1
tell application "System Events"
tell process "SAPGUI"
tell front window to tell toolbar 1 to tell text field 1
keystroke sapTransaction
end tell
tell front window to tell toolbar 1 to tell button 1 to perform action "AXPress"
end tell
end tell
end pasteTransaction
edit 2:
I got it working by forcing SAP to go through the menu. Woo!
on pasteTransaction()
global serialNumber, sapTransaction, transactionCode
activate application "SAPGUI"
delay 0.1
tell application "System Events"
click menu item ¬
"Target Command Field" of menu 1 of menu bar item ¬
"Edit" of menu bar 1 of application process "SAPGUI"
keystroke sapTransaction
tell process "SAPGUI"
tell front window to tell toolbar 1 to tell button 1 to perform action "AXPress"
end tell
end tell
end pasteTransaction
unfortunately, that doesn't fix the bigger issue of AppleScript not finding the correct index address for text fields, because getting this part working is only step one. Now I have to get text strings into various text fields within each transaction.
So if anyone knows how to address that original Index Address issue, that would be great.
I wish I knew how to actually write AppleScript instead of cobbling code together one action at a time from various sources. It's not elegant, and functionality is hit-or-miss.
GUI scripting is always a hack, and without seeing the application in action it's hard to diagnose problems. Obviously something is changing in the interface that is throwing off the view hierarchy you're trying to leverage, but... I will point out that menus and toolbars tend to be dynamic, which makes things dicier. But here's a couple of diagnostics and alternate approaches you can try.
First, let me rewrite the GUI code like this, which I find clearer:
tell application "System Events"
tell process "SAP"
tell front window
tell toolbar 1
tell text field 1
keystroke sapTransaction
end tell
tell button 1
perform action "AXPress"
end tell
end tell
end tell
end tell
end tell
So first — assuming you are targeting the correct textfield — you might try this instead:
tell toolbar 1
tell text field 1
set value to sapTransaction
end tell
tell button 1
perform action "AXPress"
end tell
end tell
keystroke can be intercepted by whatever UI element has the focus, but setting the text field's value ought to go directly into the correct element. If that doesn't work, you can try this diagnostic in Script Editor:
tell toolbar 1
properties of every text field
end tell
This should give you a list in the Script Editor's log of all of the text fields in toolbar 1. With that, you can see if the text field has a name property (which would make addressing easier: i.e. tell text field "someName"), or if some other text field has been added to the toolbar higher in the hierarchy, changing the index of the text field you're after. If worse comes to worst, you can try this:
tell toolbar 1
entire contents
end tell
Which will list out all the UI elements of the toolbar. You can do a before/after comparison to see what (if anything) changed.
Hope that helps.
I've successfully managed to get AppleScript to manipulate the background colors in my Numbers document by refering to the background color property. However, I would like to set the borders of a selection or range of cells as well.
I've looked in the Dictionary in AppleScript Editor for a command or property that could help me out here, but I haven't found anything. Is it the case that it's not possible to create borders in Numbers with AppleScript?
There is nothing in the AppleScript dictionary to allow this functionality (which is awful, IMO, but perhaps Apple will add this in the future if enough people complain).
You can use SystemEvents to interact with the interface of Numbers to add the borders:
-- Set the selection range
tell application "Numbers"
make new document
tell front document to tell active sheet to tell table 1
set selection range to range "A3:C6"
end tell
end tell
-- Asks System Events to interact with the interface
tell application "System Events" to tell application process "Numbers"
-- Asks the main window to click on the button called 'Cell' (the name depends of your system language)
tell window 1
click radio button "Cell" of radio group 1
-- Scroll area 4 is the inspector area
tell scroll area 4
-- The first incrementor of the inspector is the borders' one.
increment incrementor 1
end tell
end tell
end tell
If you want to manipulate other elements of the interface, I recommend using the Accessibility Inspector app that comes with Xcode.
10.7.4 OSX Lion
Applescript
I am working with an application (built in house and has no Applescript dictionary) that has a static text element I want to copy to the clipboard and send to another app but I'm having a hard time getting it to work.
The script I was using for targeting the element looked like this:
Tell application "System Events" to set frontmost of process "*application*" to true
Tell application "System Events"
Tell process "*application*"
Tell static text 1 of tab view 1 scroll area 1 of splitter group 1 of splitter group 1 of splitter group 1 of window 1
keystroke "a" using command down
delay 0.1
keystroke "c" using command down
delay 0.1
end tell
end tell
end tell
end tell
What would happen was that the wrong text from the wrong element was copied to the clipboard every time I clicked in a different spot on the application (there are numerous text fields).
I noticed in UI Accessor/Accessibility Accessor that each UI element in the application has a unique AXIdentifier value when you mouse over them.
Is there anyway to accomplishing what I am trying to do, using AXIdentifier values to target that element and copy the text from it?
Thanks for all the help this is my first post and I hope it was worthy! ~TheLarkInn
You can do this by using AppleScript's filtering. For example, to get the From: pop-up menu in a message composition window in Apple Mail, there is no accessibility description you can match on, however there is a unique AXIdentifier which you can match as follows:
tell application "System Events"
tell application process "Mail"
tell window 1
get first pop up button whose value of attribute "AXIdentifier" is "popup_from"
end tell
end tell
end tell
This is more efficient than looping in AppleScript as it only involves sending one Apple Event to System Events.
I don't think there is a way to directly do what you're trying to do. It seems like you can only access attributes once you have a handle on an element via selector. Here is a very ugly solution that does what you're asking by iterating over all UI elements, but it is really slow with bigger UIs and probably not ideal for any production level code.
tell application "System Events"
tell process "Some Process"
set tElements to entire contents of window "Some Window"
repeat with tElement in tElements
if (exists attribute "AXIdentifier" of tElement) then
if value of attribute "AXIdentifier" of tElement = "Some AXIdentifier" then set tText to value of tElement
end if
end repeat
end tell
end tell
tText
I think using UIElementInspector or Accessibility Inspector from Xcode to build a selector string is the way to go!
Tell application "*application*" to activate
Tell application "System Events"
Tell application process "*application*"
set textStaticTextValue to value of static text 1 of tab view 1 scroll area 1 of splitter group 1 of splitter group 1 of splitter group 1 of window 1
end tell
end tell