Calling MainWindow's update_() causes script to hang

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?

Yes.

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. :stuck_out_tongue: