Can AppleScript understand that an application that the script’s code is “handling” is busy with something else - busy with exporting for example - and only upon finishing its task to execute a part of the script?
Bare in mind I do not want to use the ‘wait’ command.
I am using tell “System Events” to handle an otherwise non-scriptable application and I would be interested to be able to set a loop when exporting (to perform multiple exports). But I’d need the script to understand that the application has finished an export first before going for the next.
Again, the ‘wait’ command cannot be used in this specific instance.
I guess you could see if it’s active process has stopped doing something by checking it’s PID
then let your script carry on if it has, worth a crack.
tell application "System Events"
set PID to unix id of process "non-scriptable application"
set pidIdle to do shell script "ps aux | grep " & PID & " | grep -v grep | awk '{print $3}'"
set pidIdle to result
if pidIdle is "0.0" as text then
-- do something --
end if
end tell
tell application "System Events"
set PID to unix id of process "Photos"
log PID (*780*)
end tell
set pidIdle to do shell script "ps aux | grep " & PID & " | grep -v grep | awk '{print $3}'"
set pidIdle to result
(*"0.0
0.0
0.0
0.0
0.0"*)
the result was not a single string but a 5 paragraphs one.
During the test, Photos was open but did nothing.
I ran this other one
tell application "System Events"
set PID to unix id of process "Preview"
log PID (*825*)
end tell
set pidIdle to do shell script "ps aux | grep " & PID & " | grep -v grep | awk '{print $3}'"
set pidIdle to result
(*"0.5"*)
And although Preview was doing nothing the result was “0.5”
Maybe the fact that English is not my native language is the explanation of my misunderstanding.
Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) mercredi 11 mai 2016 12:00:33
That is indeed the BSD way to go however keep in mind we’re talking about applications which is an windowserver object and not a process. A process gets the idle state after 20 seconds and not immediately. So getting the process by it’s state is not really accurate because when the process is doing nothing for 15 seconds it’s still have the active state. Getting the process’s cpu time as you did is returning the time of the user time + system time of the process, however this value is accumulative and is not the value since the last time the process went idle which makes this value hardly useful.
But there are more flaws to this, when the process is waiting for something like an event or reading from an network connection (waiting for the server to give results back over a socket) the process itself can turn into an inactive state while in fact UI of the application is showing it is quite busy. Also running XPC services, which is handled by launchd on behalf of the application, can make the application wait for the process to finish. While in fact the processes is inactive, the application itself is not. This list of exceptions can go on and on.
The best way to see if an application (not a process) is busy is simply by doing the same as the end-user does. Look at the GUI if there are any UI elements be visible, animated or enabled that indicates that there is something going on.
I ran your first script and only got back “0.0” not 5 paragraphs.
then I ran your second script and got back “0.0”, even when preview was doing nothing.
wonder why this is happening this way for you and not me.
Hi DJ
Thank you for the explanation, adds a lot of clarity, it appears that checking the UI is definitely
the correct way to go here.
I have no idea about what makes the differences.
Today the code testing Photos (with every window hidden) returned PID = 509 and pidIdle = “0.0
0.0
0.0” (Yes, three paragraphs) Same result with a photo open and the thumbnails disabled.
With the library open it returned “0.0
0.0
0.0
0.0
0.0” ( Yes, five paragraphs )
Preview returned PID = 487 and pidIdle = “0.0
0.0” (Yes, two paragraphs)
So I wouldn’t rely about that to determine the state of an application.
Yvan KOENIG running El Capitan 10.11.4 in French (VALLAURIS, France) jeudi 12 mai 2016 10:24:31
tell application "System Events"
if exists (window 1 of process "Preview") then
display dialog "Exists"
else
display dialog "Does Not Exist"
end if
end tell
Many thanks for the reply back. Sorry it took me a while to reply back. New baby in the house so scripting had to take a back seat.
I am currently using the “Accessibility Inspector” off XCode and “UI Browser” to identify scriptable elements in this app I am building.
One of my current issues that I cannot resolve is that:
The scripted app brings up another window (a progress bar window) when it is performing an export.
Once the export has finished, that progress bar window automatically disappears.
The “Accessibility Inspector” sees this window as a new element
I want my script to understand that this window is open and hold the script (delay) until the progress bar window disappears.
In this particular case I cannot - I repeat, I cannot - put a manual delay value, as the exporting time is never fixed and varies depending on numerous factors.
Try to identify the window with something, it can be size, location, name or elements it contains like labels. I have successfully identified windows by it’s elements it contained while the window itself looked anonymous.
Update: here an simple example of an script that waits until the the preference window of the Finder closes within 20 minutes. There are easier ways but it shows how you can identify a window based on it’s contents rather than the window properties itself.
IMPORTANT: update the line if btns contains “Algemeen” and btns contains “Tags” then with the toolbar button names of your preferences window.
set start to current date
repeat
if not isFinderPreferenceWindowOpen() then exit repeat
delay 1
-- timeout after 20 minutes
if (current date) - start > 1200 then error "Timed out" number -128
end repeat
return "window is closed"
on isFinderPreferenceWindowOpen()
tell application "System Events"
tell process "Finder"
set windowRef to missing value
repeat with win in every window
repeat 1 times
if (count of toolbars of win) = 0 then exit repeat
set btns to name of buttons of toolbar 1 of win
-- note "Algemeen" and "Tags" are localized Dutch strings
if btns contains "Algemeen" and btns contains "Tags" then
set windowRef to win
end if
end repeat
end repeat
end tell
end tell
return windowRef is not missing value
end isFinderPreferenceWindowOpen