Problem with Progress bar

I have been trying to get a progress bar to work. I have tried using the method from various sources, but I cannot get the label or the progress bar to update until it goes thru the repeat loop. Once it finishes the repeat it will update to say “processing image 4 of 4” and then the bar jumps to the end. All I get while it is going thru the repeat is “processing 1 of 4” and the progress bar disappears.

and Help is appreciated. Here is my code…



	property parent : class "NSObject"
    
    property myFile: missing value
    property hfsSavePath: missing value
    
    property theCounter: 0
    property statusMessage: "App is idle"
    property progressCount: 0
    property theWindow: missing value
    property myProgressBar : missing value -- connected to the progress bar
    property myTextField : missing value -- connected to the text field

    on imageCreatePSD_(sender)
       
        -->select the group of corrected image files (all blouses, pants, jackets etc...)
        my showModalOpen_()
        delay 1
        -->select the file name and where to save them, will automatically be at the proper folder, just need to name and hit save.
        tell application "Finder"
            set this_folder to (path to desktop folder)
            -->create the folder to save the layered files to
            if not (exists folder "Product PSD" of this_folder) then
                make new folder at this_folder with properties {name:"Product PSD"}
            end if
            set myFilePath to (((path to desktop folder) as text) & "Product PSD")
        end tell
        my showModalSave_()
        delay 1
        tell myProgressBar
            setMaxValue_(progressCount)
            setUsesThreadedAnimation_(true)
            setIndeterminate_(false)
        end tell
        tell theWindow to displayIfNeeded()

        tell application "Adobe Photoshop CS6"
           -- activate
            my setPixelUnits_()
            make new document with properties {name:"Product_Sizing", width:2000, height:2000}
            set testDoc to document "Product_Sizing"
            tell testDoc
                set background layer of layer 1 to false
            end tell
            log myFile
            set my theCounter to 1
            repeat with oneDrop in myFile
                my incrementMyProgress_("Processing image " & theCounter & " of " & progressCount, 1)
                set my theCounter to (theCounter + 1 as integer)
                log theCounter
                open file oneDrop
                set docRef to current document
--                flatten docRef
                set current document to docRef
                set theLayer to layer 2 in current document
                my setPixelUnits_()
                resize image docRef height 2500 resolution 72
                set theLayer to layer 2 in current document
                duplicate theLayer to testDoc
                set current document to docRef
                close current document saving no
                tell theWindow to displayIfNeeded()
                do shell script "sleep 0.25"
            end repeat
            set my theCounter to 0
            myTextField's setStringValue_("Finished")
            set myOptions to {class:TIFF save options, image compression:none, save layers:true, transparency:true}
            set myLayers to every layer in testDoc
            make new layer set in testDoc
            tell testDoc
                move myLayers to layer set 1
            end tell
            save testDoc in file hfsSavePath as TIFF with options myOptions
            close current document saving no
        end tell
        my sendGrowlNotification_("Product", "Product PSD file is Complete", "Now get to work on it!")
        tell application "UITest" to activate
    end imageCreatePSD_
   
    on incrementMyProgress_(theText, incBy)
        myTextField's setStringValue_(theText)
        
        tell myProgressBar
            incrementBy_(incBy)
            display()
        end tell
    end incrementMyProgress_


Hi,

run the working handler in the background and the UI relevant lines on the main thread,
that means:

¢ write separate handlers for updating the UI e.g. startProgressbar(), updateProgressbar() and stopProgressbar()
¢ call imageCreatePSD from somewhere else with performSelectorInBackground_withObject_()
¢ call each handler to update the UI with performSelectorOnMainThread_withObject_waitUntilDone_()

Sorry StefanK, I guess I don’t understand exactly what you are talking about. I am not a seasoned vet at this but more of an advanced beginner. Can you explain a little further of what you mean for each part you described?

When you do a UI update during a repeat cycle you have to the the text field to update itself. Something like this:

myTextField's displayIfNeeded()

.after you change the text field’s contents, of course. This should help.

What Stefan showed you is more advanced stuff but much more efficient. I would encourage you to look into this eventually. Sending a process like yours to a background thread is releasing the UI and makes your app more responsive. For example you could add a cancel button with methods such as this.

example code (not tested)


property parent : class "NSObject"

-- .

on imageCreatePSD_(sender)
	my performSelectorInBackground_withObject_("performImageCreatePSD", missing value)
end imageCreatePSD_

on performImageCreatePSD()
	-- .
	my performSelectorOnMainThread_withObject_waitUntilDone_("startProgress:", progressCount, false)
	repeat
		-- .
		my performSelectorOnMainThread_withObject_waitUntilDone_("incrementMyProgress:", "Processing image " & theCounter & " of " & progressCount, true)
		-- .
	end repeat
	my performSelectorOnMainThread_withObject_waitUntilDone_("stopProgress", nil, true)
	
end performImageCreatePSD

on startProgress_(maxV)
	tell myProgressBar
		setMaxValue_(maxV)
		setUsesThreadedAnimation_(true)
		setIndeterminate_(false)
	end tell
end startProgress

on stopProgress()
	-- do something to stop the progress bar
end stopProgress


on incrementMyProgress_(theText)
	myTextField's setStringValue_(theText)
	
	tell myProgressBar
		incrementBy_(1)
		display()
	end tell
end incrementMyProgress_


Thanks StefanK, with a little trial and error, I got this to work.

So with this method, I need to have every process that I want the progress bar for I will need to create a “on perform…” handler like you created here?

and 1 last stupid question what does the (performSelectorInBackground_withObject) do? Is it as simple as it just runs the process in the background and keeps the focus on my application?

Thanks again for the help.

Unlike classical AppleScript which runs on a single thread, AppleScriptObjC with its Cocoa fundament is able to run code on multiple threads asynchronously.

According to Apple’s guidelines the user interface runs always on the main thread.
To keep the application responsive move all expensive work to a background thread.
With GCD (Grand Central Dispatch) it’s even possible to run multiple threads concurrently on multiple cores of a CPU

Hi Scooter61

The performSelectorInBackground:withObject: is a method from NSObject which allows you to run Selectors “Handler” as threads in the background. Take a look at the Docs and you will see the following:

performSelectorInBackground:withObject:

Invokes a method of the receiver on a new background thread.

  • (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
    Parameters

aSelector
A selector that identifies the method to invoke. The method should not have a significant return value and should take a single argument of type id, or no arguments.

There are examples on this forum on this method as well as Shane Stanley’s book.

t.

Up to a point. Although you can run AS on multiple threads, the application only has one instance of AppleScript to deal with its code, and can therefore only really deal with one thread at a time. So if code on a background thread is called, any code already running on the main thread is suspended until it is finished.

The net result is that putting AS on a background thread not only does not let code on the main thread run at the same time, but you also lose control of the order in which code is run. This can mean things like dialogs being out of sync with what’s actually happening, and in some cases a lot worse.

In most cases the best you can do in an AS project, bar doing one thread in some Objective-C code, is to regularly service the event queue, this forcing a screen update and stopping the queue from backing up. Using something like fordEvent() from Myriad Helpers is an efficient way of doing that.

FWIW, this is covered in a fair amount of detail in chapter 13 of my book.

Shane, thanks for the reply.

I have your book and this is why I had the question bout the progress bar and the label not updating. I was trying to do the same thing that you had in your book, which all worked until everything was supposed to update. So i do not know what I was doing wrong, but it just was not working for me. I will read more on chapter 13 though!

I have been able to get Stefans method to work, and everything seems to be working smoothly now.

Thanks to everyone for their help, i have a little bit of a better understanding of this method now.

Thanks,
Scott