i have a loop that shows a progress bar. That loop updates the progress bar, then does a shell command that takes about 4 seconds to complete. Then it repeats. It is really hard to click that stop button, the applescript’s GUI is mostly locked up most of this time.
Is there a way to make it more clickable? One option might be a delay, but this loop is running thousands of times so I really don’t want to slow it down. Even a 1 second delay is going to reduce the amount of work this script does in an hour by 25%. Is there any other technique?
Apples implementation of this progress bar has been so disappointing. Nothing like Mike’s Scriptable Progress Bar!!!
If you are using repeat for the loop, that is preventing events from being processed, so the UI is getting blocked. Using a separate application avoids the issue because it has its own run loop that you aren’t blocking.
So if I broke apart my repeat loop and put it’s contents in an idle handler that returned 0, would that make it a continuously running loop but with a more responsive ‘stop’ button on the progress bar?
Is an idle 0 loop even possible? And I would have to control it so it wouldn’t just run randomly, only when called.
I don’t have it handy but its pretty simple. Its currently a repeat loop that updates the progress bar, then runs a shell script that takes about 4 seconds to complete, then repeats to infinity.
It looks like the built-in progress isn’t going to work with idle, timer, or notification - it dismisses when the run handler completes, even in a stay-open app. Progress indicators, for whatever reason, are a royal pain to implement unless your application is built for them. It looks like you might have to go with processing the events yourself, but seeing a basic code layout or minimal example would go a long way in figuring out the best place to squeeze in a progress indicator that works the way you want.
on run
set progress total steps to 100
set progress description to "Doing the Thing!..."
set i to 1
repeat
try
set progress completed steps to i
set progress additional description to "Specific sub-task in process"
do shell script "rm 'path/to/file'; cp 'file1' 'file2';"
on error
if err_num = -128 then
display dialog "You have quit the script, have a good day" buttons "Quit" default button 1 with icon stop with title MyName
quit me
end if
end try
end repeat
end run
This is basically the setup. The shell command takes about 4 seconds. And with this setup, its hard to click on the stop button in the progress bar. Its very laggy.
I agree that apples implementation of the progress bar is terrible. And strangely very different from all of the other interact-able windows AppleScript can create.
Its not a deal breaker, but it would be nice if there were a way to make it easier to stop this script - nicely - without slowing it down.
Your example showed files. Are there files or different shell scripts? If so, how are you handling those? I have a sample that doesn’t use repeat statements, idle handlers, or timers, but I still don’t know what you are doing.
My sample code above is the whole thing. I’m deleting and duplicating files, lots of files. Hundreds of GB worth. If the files are small, the stop button becomes very responsive. If the files are large like 1 GB each, the stop button is very hard to press.
OK, now we are getting somewhere. My current solution uses NSTask to launch a task in the background and a notification when it completes, so the UI stays responsive. NSTask is not quite the same set up as do shell script but the commands would be the same. How are you dealing with the file paths to put in the shell script? Reading from a file or something?
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
property myFiles : {}
property fileNames : {}
on run
local pid, errMsg, errNum
tell application "System Events"
set {myFiles, fileNames} to {POSIX path, name} of (files of folder "Crucial SSD:" whose name extension is "mp4")
end tell
set progress total steps to count myFiles
set progress description to "Doing the Thing!..."
try
repeat with i from 1 to count myFiles
set progress completed steps to i
set progress additional description to item i of fileNames
set pid to do shell script "cp '" & (item i of myFiles) & "' '/Volumes/Test SSD/" & (item i of fileNames) & "' &>/dev/null & echo $!"
repeat until pid is not in (words of (do shell script "ps -x -o pid= "))
delay 0.5
end repeat
end repeat
on error errMsg number errNum
if errNum = -128 then
display dialog "You have quit the script, have a good day" buttons "Quit" default button 1 with icon stop with title MyName
return
end if
end try
end run
This will copy 1 file at a time, and has a timer loop to check if copy is done by looking for the copy processes Process-ID.
The issue stems from the fact that when you invoke the shell script in your original script, AppleScript waits for that to complete before proceeding. It has to do this in case you want to wait for the result of the shell command (you might want to check for an error code, wait for completion status, etc.)
By appending " >/dev/null 2>&1" to the end of the shell script, this suppresses the output of the command and returns control to the script immediately. This means that you don’t have to wait for the shell command to finish before displaying the next dialog (and thus opportunity for the user to click Stop).
Now, this might be a problem if iteration 2 requires that iteration 1 is complete. If that’s the case there are also options such as having the shell script return the PID of the process it launched, then you can amend the second iteration to check if the previous PID is done before it kicks off the second copy. It all depends on the workflow and your intent/needs.
Now, this might be a problem if iteration 2 requires that iteration 1 is complete. If that’s the case there are also options such as having the shell script return the PID of the process it launched, then you can amend the second iteration to check if the previous PID is done before it kicks off the second copy.
Right… I was just pointing out that there are multiple levels of ‘solution’ here, from the quick-and-dirty, through the more robust/flexible, through a complete rewrite.