Hello everyone,
The script I am writing copies files from one directory to another. I also display an NSProgressIndicator to let the user know how far along the copy progress is.
The problem that I am having, is that when I call my mainWindow’s update_(), the script’s execution hangs. If I remove the update_() call, then the script executes but the window isn’t updated until the process is finished.
Here is the script in question:
script LibrarianAppDelegate
property parent : class "NSObject"
-- Properties
property kProgressText : missing value
property kProgressBar : missing value
property kProgressWindow : missing value
property aLibraryList : {}
-- applicationWillFinishLaunching_
on applicationWillFinishLaunching_(aNotification)
-- Do nothing for now
end applicationWillFinishLaunching_
-- applicationDidFinishLaunching_
on applicationDidFinishLaunching_(aNotification)
launch_(aNotification)
end applicationDidFinishLaunching_
-- copyFile: Copies the file specified to the given path
on copyFile(szPathToCopyTo, szPathToFile)
do shell script "cp " & szPathToFile & " " & szPathToCopyTo
end copyFile
-- getFileNames: Asks user for location of files to copy
on getFileNames()
tell application "Finder"
activate
set szSourceFolder to choose folder
set aLibraryList to (every file in szSourceFolder) as alias list
end tell
end getFileNames
-- launch_
on launch_(aNotification)
-- Start the animation of the progress bar
kProgressBar's startAnimation_(kProgressBar)
-- Set paths to copy to and from
set szFolderToCopyFrom to "Libraries/Debug/"
set szFolderToCopyTo to "/Users/Shared/Projects/Sandbox/cto/"
getFileNames()
-- Set the max value of the progress indicator to the number of items in the list
set iNumItems to count aLibraryList
kProgressBar's setMaxValue_(iNumItems)
kProgressBar's setIndeterminate_(false)
-- Loop through the files and copy them to their destination
repeat with i from 1 to iNumItems
set szFileName to POSIX path of item i of aLibraryList
kProgressText's setStringValue_("Copying " & szFileName & " To: " & szFolderToCopyTo)
copyFile(szFolderToCopyTo, szFileName)
-- Update progress bar and window
kProgressBar's incrementBy_(1)
-- !!! If I remove this call, the script executes, otherwise it hangs !!!
kProgressWindow's update_()
end repeat
quit
end launch_
-- applicationShouldTerminate_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return current application's NSTerminateNow
end applicationShouldTerminate_
end script
The update command takes no parameters, so there shouldn’t be an underscore. You’ll probably also need pipes to make it compile, so |update|(). Let us know if that makes it update properly (I have a suspicion it might not).
Hey Shane,
Thanks for the reply.
I’ve tried writing the update call in the following ways:
kProgressWindow's update_()
kProgressWindow's update()
kProgressWindow's |update|()
Out of those three ways, all of them compile. The underscore version updates the window after the first file is copied, but causes the script to hang. The version without the underscore and the version with the pipes don’t update the window until the entire process has finished.
Perhaps there is something I missed in Interface Builder?
All of the properties declared in the script file are connected to the AppDelegate in the “Outlets” panel in the IB inspector.
Other than that, something very fishy is afoot in my script that I can’t put my finger on.
I don’t think there’s anything fishy in your script, just that perhaps we need to do things a bit differently from AS. First, try this: take out the update line and replace it with “delay 0.00001”, and tell me if it works.
I removed the call to update and replaced it with the delay command. The results were the same in that the window didn’t update until all of the files were copied.
Edit:
Thanks for the help.
I extended the delay time and that got it working. The window won’t accept the focus now until it’s completed copying the files, which seems to be a different issue.
OK, a little light bulb just went on.
Instead of telling the window to update(), which happens in the next event loop, you can tell it to diplayIfNeeded() (or display(), but the former is probably more efficient) and it seems it will do it straight away. That’s much cleaner than using delay – and would probably have been the answer to several other queries here before.
No more performSelector_withObject_afterDelay_ for me …
Hey Shane,
Thanks for all of the help.
Calling “displayIfNeeded()” instead of using “delay” or “update()” worked.
I have a progress bar and it’s not moving, but everything else is going smoothly. Can someone help. I’ve tried:
Can someone help me. No errors are coming up in the console. Nothing is wrong except the progress bar.
Not without seeing the relevant code. Have you logged progressbar to make sure it’s connected properly in IB?
Post the relevant progressbar code.
Oh. Sorry. Here’s the whole project. It’s a test project, not a acual application. Click Here for the project.
You’re using an indeterminate progress bar – turn off Indeterminate in IB.
Oh, duh! Thanks, but how do I make it animated? Instead of having it choppy, is there a way to make it animated like normal, like in Software Update (for example).
Change the max value from 10 to 1000 and it will smooth out.
OK. I have everything fixed except one thing, my script smooths the progress bar out, the max value is 100 on the progress bar, but when the script is telling it to change it 50, its just fills up in one second instead of only filling up to the half mark. Heres the project. The script is
script TestingAppDelegate
property Progressbar : missing value
property stepperfield : missing value
property textfield : missing value
property mainwin : missing value
on awakeFromNib()
textfield's setStringValue_("0")
end awakeFromNib
on runs_(sender)
say "Adding 50"
set stepvaluenumb to 1
repeat
Progressbar's incrementBy_(stepvaluenumb)
Progressbar's displayIfNeeded()
set stepvaluenumb to stepvaluenumb + 1
log "Added. Number is " & stepvaluenumb
if stepvaluenumb is 50 then
log "Done."
exit repeat
end if
end repeat
end runs_
end script
repeat loops are very fast. Repeating 50 times is nearly instant. If you were processing some information or files, the loop would have time between iteration completions. If you want to test things out without actually doing anything then you need to simulate that something is happening. Put a delay in the loop.
No, that’s not my problem. The problem is that when I set the max value to 100 and tell the progress bar to set it to 50, it fills up completely, in one try. It’s not too fast, that’s not my problem.
You need to increment by 1. Each time through the loop you are incrementing by 1, 2, 3, 4 and so on. That is why it fills up on the 14th iteration.
Progressbar's incrementBy_(1)
Oh. Duh. Now I feel stupid.