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