I have a totally stupid problem that's driving me nuts. I need to access a node in a plist file, and if there's a problem I want a dialog displayed with the 'stop' icon. Here's the code:
tell application "System Events"
tell property list file (plistFile as text)
try
tell contents
set backupDayTemp to value of property list item plistElementBackupDay of property list item chosenBackupDisk
end tell
on error m number n from f to t partial result p
if n = -1728 then
display dialog "Can't find " & plistElementBackupDay & " key for disk " & chosenBackupDisk & " in plist file." buttons {"Damn", "Oh dear"} default button "Damn" with title myName with icon stop
return
else
-- otherwise, pass the error on
error m number n from f to t partial result p
end if
end try
end tell
end tell
It seems the compiler thinks System Events is the one that should understand the token "stop", since it's in bold blue font, not the purple italics that it would normally be in. And when the error handler is run I get told "System Events got an error: property list file [path to file] doesn't understand the "stop" message".
My question is: how on earth do I tell System Events that the token is not for it? I've tried putting a tell me to in front of the display dialog, but that didn't help. I also tried using terms from application "Standard Additions" but when I run it, I just get asked to locate the dictionary for Standard Additions.
Tearing my hair out!!!
The easiest solution is to put the error handling out of the application tell block
try
tell application "System Events"
tell property list file (plistFile as text)
tell contents
set backupDayTemp to value of property list item plistElementBackupDay of property list item chosenBackupDisk
end tell
end tell
end tell
on error m number n from f to t partial result p
if n = -1728 then
display dialog "Can't find " & plistElementBackupDay & " key for disk " & chosenBackupDisk & " in plist file." buttons {"Damn", "Oh dear"} default button "Damn" with title myName with icon stop
return
else
-- otherwise, pass the error on
error m number n from f to t partial result p
end if
end try
Related
I am encountering an index error that appears when the app in use has an overlay or notification appear. To provide a better description, the app will occasionally show an alert if something needs to be acknowledged or dismissed. When that happens, the script is unable to return the value from the designated location in the GUI, and returns the following error message: "Can’t get group 4 of toolbar 1 of window 1 of process "App I'm Using". Invalid index.System Events got an error: Can’t get group 4 of toolbar 1 of window 1 of process "App I'm Using". Invalid index. (-1719)"
The behavior is expected, but I would like to adjust the script to where it will either delay trying again for 30 seconds or so, or just not display said error at all.
I've been toying around with using an 'on error' statement, but I can't get it to take with the 'tell' statement that it's referring to, for example:
on error error_message number error_number
if error_number = -1719 then
wait 30
end if
I'm unsure of how I can use the 'on error' function with the section of the script below, but if I can make it try again in 30 - 45 seconds without displaying an error, it would be perfect.
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
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
end idle
I believe the error is encountered when the script reaches "tell window's first toolbar's fourth group's..." before it is supposed to "set activityState to first item...".
I have used the 'on error' function with 'try' statements successfully, but I'm having issues moving forward with this one.
Or you can try this approach which will remain in the repeat loop until first window's first toolbar's fourth group's first group's first menu button becomes available.
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
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
end idle
You can’t split statements (such as if or tell). The try statement needs to wrap around the complete statement(s) you want it to work with, for example, the statement telling the app process:
on idle
tell application "System Events"
if not (exists process appName) then
---
end if
try
tell process appName
---
end tell
on error errmess number errnum
return 30 -- try the idle handler again in 30 seconds
end try
end tell
end idle
You can use the exists keyword to check if an element is present before trying to access it. exists won't through an error if the element isn't there, and will let you skip over the problematic lines:
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
-- assuming the window and toolbar are always going to be there
try
tell first window's first toolbar
-- check to see if the UI element exists
if exists fourth group's first group's first menu button then
-- only get the activity state if it does
tell fourth group's first group's first menu button
set activityState to first item of (value as list) as text
end tell
end if
end tell
on error errstr
(*
this code is in an 'idle' handler, so on any error we
just return 30 to idle for another 30 seconds and try again.
*)
return 30
end try
end tell
end tell
end idle
We have an automation tool for Photoshop, using a control app, which calls Applescripts controlling Photoshop.
One situation is that we must open a RAW image in the CameraRAW plug-in, and then open it in Photoshop. This part is handled, via an applet using System Events. When that applet terminates, we run the processing script for Photoshop.
As some pictures take quite a bit of time to open, we have to make sure that the picture is really open before the script can run. … and that's where I am stuck.
At the moment, I am using the following code which is intended to wait until the image is open (the criterion for "open" is correct (and tested manually), so that's not the issue here).
tell application "Adobe Photoshop CC 2015"
activate
tell application "System Events"
tell application process "Photoshop CC"
--
display alert "Waiting for Window"
--
repeat
try
set wn to name of window 1 as text
try
if (wn contains "(RGB/16") then
set wn to "image is open: " & wn
end if
end try
if (wn contains "(RGB/16") then
display alert "We are there, quitting now… " & wn
exit repeat
end if
end try
delay 1
end repeat
end tell
end tell
--
display alert "Ready for process"
--
-- and here comes the processing code
end tell
I also tried to set a variable which is tested as argument for repeat, and changed when the exit condition is fulfilled.
Trying to create even alerts within the repeat loop, does not lead to any effect; the script ends up in an infinite loop.
It is well possible that I miss the obvious… So, I am grateful for any helpful hint.
Thanks in advance.
I think you have a few small issues with your script that are causing your problem.
You are using name of window 1 when I believe you need name of document 1. With your first try block structured as it was you weren't realizing it was actually giving an error on name of window 1
The name that is returned doesn't contain the color space and bit count, so I've changed the result test to an empty string
Notice the modifications to the try block around getting the document name
I don't believe it's necessary or see a reason to use "System Events" in this case, so I've modified the version below without it.
Example Script
tell application "Adobe Photoshop CC 2015"
display alert "Waiting for Window"
repeat
try
set wn to name of document 1 as text
on error
set wn to ""
end try
try
if wn is not equal to "" then
set wn to "image is open: " & wn
end if
end try
if wn is not equal to "" then
display alert "We are there, quitting now… " & wn
exit repeat
end if
delay 1
end repeat
display alert "Ready for process"
end tell
The general question from my specific problem is how to interact WITH a running Applescript without leaving the program you a currently in. Its kind of the opposite question of how to USE Applescript to interact with other programs.
I need to go through a lot of web pages and input information from them into a spreadsheet. I have somewhat automated the process with an Applescript that takes an webpage as input and then opens its relevant links one by one, waiting for a click in a dialog box before proceeding to the next.
If you want to run the script, use http://www.resultat.dk/tennis/challenger-singler/champaign/resultater/ as input to the dialog box
--Let the user input the the main webpage
display dialog "Linkzor" default answer ""
set tunering to text returned of result
--get the javascript of the web page
tell application "Safari"
tell window 1
open location tunering
end tell
delay 5
set listen to "" as string
tell front document to set ¬
startText to {name, do JavaScript "window.document.documentElement.outerHTML"}
end tell
set listen to {}
set teksten to startText as string
--Gets all the relevant link variables of the web page
repeat with i from 1 to 500
set forekomst to text (nthOffset(teksten, "g_2_", i) + 4) thru (nthOffset(teksten, "g_2_", i) + 11) of teksten
if nthOffset(teksten, "g_2_", i) = 0 then exit repeat
set punkt to "http://www.resultat.dk/kamp/" & forekomst & "/#kampreferat"
set listen to listen & punkt
end repeat
set lengde to count of listen
--opens the links one by one.
repeat with x from 1 to lengde
tell application "Safari"
tell window 1
set URL of document 1 to item x of listen
end tell
end tell
--THIS IS THE PART I WANT TO INTERACT WITH FROM OUTSIDE THE SCRIPT
set question to display dialog "forsæt?" buttons {"Ja", "nej"} default button 1
set answer to button returned of question
if answer is "nej" then exit repeat
end repeat
on nthOffset(myText, subString, n)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to subString
-- There will be one more text item than there are instances of the substring in the string, so:
if (count myText's text items) > n then
-- There are at least n instances of the substring in the text.
-- The first character of the nth instance comes immediately after the last
-- character of the nth text item.
set o to (count text from text item 1 to text item n of myText) + 1
else
-- There isn't an nth instance of the substring in this text.
set o to 0
end if
set AppleScript's text item delimiters to astid
return o
end nthOffset
Now I would like to automate it a bit further so I didn´t have to leave the spreadsheet to click the dialog box when going to the next webpage. Is there a way to force the Applescript proceed without me actually clicking the dialog box? I have tried to save the script as an application ("hent2") and made another script to sent an "enter" keystroke to it while it is running (and then activate the new script through some kind of hotkey):
tell application "System Events"
tell application "hent2" to activate
key code 36
end tell
It does´t do anything.
Any suggestions on how to proceed? Either with the "sent keystroke to Applescript" or a more elegant solution?
EDIT:
I go it to work with the "Hitting a button" solution from Jerry below.
But I can´t get the handler solution to work. It clearly communicating with the main script because it tells me it can´t find the list "listen" of urls:
"error "hent3 got an error: The variable listen is not defined." number -2753 from "listen""
I define and assign values to "listen" in the part that is autorun when I save and run the script as a program. But when I call the subroutine from another Applescript it knows nothing about "listen"
Here is my attempt:
--Let the user input the the main webpage
display dialog "Linkzor" default answer ""
set tunering to text returned of result
--get the javascript of the web page
tell application "Safari"
tell window 1
open location tunering
end tell
delay 5
tell front document to set ¬
startText to {name, do JavaScript "window.document.documentElement.outerHTML"}
end tell
set listen to {} --HERE I DEFINE THE VARIABLE
set teksten to startText as string
--Gets all the relevant link variables of the web page
repeat with i from 1 to 500
set forekomst to text (nthOffset(teksten, "g_2_", i) + 4) thru (nthOffset(teksten, "g_2_", i) + 11) of teksten
if nthOffset(teksten, "g_2_", i) = 0 then exit repeat
set punkt to "http://www.resultat.dk/kamp/" & forekomst & "/#kampreferat"
set listen to listen & punkt -HERE I ASSIGN VALUES TO IT
end repeat
set lengde to count of listen
set i to 1
on ContinueOnward()
tell application "Safari"
tell window 1
set URL of document 1 to item i of listen --HERE IT ISN`T RECOGNISED
end tell
end tell
set i to i + 1
if i > lengde then
display notification "slut"
end if
end ContinueOnward
on nthOffset(myText, subString, n)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to subString
-- There will be one more text item than there are instances of the substring in the string, so:
if (count myText's text items) > n then
-- There are at least n instances of the substring in the text.
-- The first character of the nth instance comes immediately after the last
-- character of the nth text item.
set o to (count text from text item 1 to text item n of myText) + 1
else
-- There isn't an nth instance of the substring in this text.
set o to 0
end if
set AppleScript's text item delimiters to astid
return o
end nthOffset
Hitting a button
First, when scripting dialogs, if you want to press a button, use System Event’s click button:
tell application "System Events"
tell application process "hent2"
tell window 1
click button "Ja"
end tell
end tell
end tell
This works even if the process being hit is not front-most, so it might be all you need.
Activating via handler
You can also activate handlers in one AppleScript via a tell in another AppleScript. For example, save this as Test Handler, as Application and Stay Open After Run:
on ContinueOnward()
display notification "Continuing Onward"
end ContinueOnward
Then, in Script Editor, write the following script:
tell application "Test Handler"
ContinueOnward()
end tell
You should see a notification pop up saying “Continuing Onward” when you run that script. In your own script, of course, you could use that handler to move on to the next URL.
In order to maintain variables across different handlers, you will need to use either properties or globals or some combination. Properties maintain their values across runs of the application; globals only maintain their values across one run of the application. As I read your needs, you will need globals. In every handler that needs access to the global, use:
global variablename
And you’ll also need to initialize the variable outside of the handler.
For example, this application (Save As Application, Stay Open) will change its notification message, and then quit, after the fourth time:
set currentCount to 0
on ContinueOnward()
global currentCount
set currentCount to currentCount + 1
if currentCount < 5 then
display notification "Continuing with item " & currentCount
else
display notification "Quitting"
quit
end if
end ContinueOnward
In your case, you will want to make i, listen, and lengde be global variables. Put:
global i, listen, lengde
at the top of your handler, as I did with currentCount at the top of the example ContinueOnward handler.
I'm trying to different events depending on what application is currently the "open" application - The one which is foremost of the screen. I have managed to save the name of the application using a variable. Using this code.
tell application "System Events"
item 1 of (get name of processes whose frontmost is true)
set openWindow to (get name of processes whose frontmost is true)
do shell script "echo " & openWindow & " > /Users/name/temp/currentWindow.txt"
end tell
I then tried to use this code do different events for each open application
tell application "System Events"
if openWindow = "AppleScript Editor" then
display dialog "my variable: " & openWindow
end if
end tell
However this code does not apper to do anything, I don't have any error messages or anything however the code doesn't display the dialog box. If I place the code for the dialog box in the first section of code it will display the name of the open application.
Any ideas on how to get this to work, it would be very helpful
To explain your problem, it's because of this code...
set openWindow to (get name of processes whose frontmost is true)
That code returns a list of items. Notice you asked for processes (plural), so you can get more than one so applescript gives it to you as a list whether there's one or more items found. What's strange is that in the line above this you do ask for "item 1" of the list but for some reason you don't do anything with that line of code. Normally I would write that line of code like this so I only get item 1...
set openWindow to name of first process whose frontmost is true
Anyway, you can't compare the string "AppleScript Editor" to a list {"AppleScript Editor"}. They are not equal so your if statement is never true.
Display dialog displays a string. So when you move that code outside the if statement, applescript is smart enough to convert your list into a string so it can be displayed.
So bottom line is you are getting a list and you must access the items of the list. The items are strings so get one (in your case you want item 1) and use that in the if statement.
Hopefully you can learn from this explanation. Good luck.
In the first script cast the openWindow variable to a string:
set openWindow to (get name of processes whose frontmost is true) as string
I'm trying to enable or disable all the control in a window as the programme changes from interactive to non-interactive mode. How can I ask a window to give me all its contents?
every control of window "mainWindow"
doesn't work, nor does
contents of window "mainWindow"
Actually, I haven't been able to find any good documentation for interacting with menu items from interface builder at all. Things like how to set the contents of popups, and buttons and so on.
thanks
The way I do it at the moment is:
property onlineControls: {"maxLength", "speed", "accelerationSlider", "accelerationField", "showInfo"} --and so on, listing all the controls by name
on enableControls(theList, enableState)
tell window "mainWindow"
repeat with theControl in theList
set the enabled of control theControl to enableState
end repeat
end tell
enableControls(onlineControls, true)
I've made several lists of controls tht get turned on or off depending on the state the programme is in. But it has to be hard coded, which I don't see as being the best way.
tell application "System Events"
tell process "Adium"
get entire contents of window 1
end tell
end tell
This script will give you as result all contents of front window of Adium: butons of window, tool bars of window, buttons of tool bars, etc. Enjoy =]
I haven't been able to find a way to get all the controls in a window, but here's an example of interacting with the menu of a popup button:
tell menu of popup button "somePopupButton" of window "mainWindow"
delete every menu item
repeat with i in someItems
make new menu item at end of menu items ¬
with properties {title:i, enabled:true}
end repeat
end tell
Is the same script as "BoB1990" with the possibility of getting back the information given by get entire contents of window in a string of whom you can observe or modify all the items listed :
tell application "System Events" to tell process "Adium"
set this_info to {}
try
display alert ((get entire contents of window (x as integer)))
on error errMsg set theText to errMsg
set this_info to do shell script " echo " & theText & " | sed 's#System Events got an error: Can’t make ##g;s# into type string.##g'"
end try
set info to {}
set info to do shell script " echo " & this_info
display alert (info)
end tell