One very common use for AppleScript is batch processing. Scripts of this nature will often loop through multiple items, performing some type of automated task for each item being processed. An example of this might be a script that loops through a folder of image files, converting each one to another format.
A script of this nature that is created with Script Editor, and then run, may work just fine. However, visually, it is less than spectacular. Other than a spinning cursor, and perhaps a dialog message displayed here or there during script execution, the user does not usually receive a very good visual representation of what is occurring.
This is where AppleScript Studio can come in handy. In this month’s column, we will discuss adding progress bars and spinners to an AppleScript, in order to provide a visual representation of what is being processed, how much processing is complete, and how much processing remains during script execution.
Preparing to Follow Along
To follow along with this month’s column, you will need to create a new AppleScript Studio project. Launch Xcode, and select New Project from the File menu. Next, choose the AppleScript Application template, provided by Apple. Name the project Progress Example, and save it into the desired location. Double click on MainMenu.nib to begin editing the project’s interface.
Begin by dragging a text field, progress spinner, and progress bar to the main window of the interface from the Cocoa Objects Palette window. Arrange the objects as shown in the example window in figure 1. Next, select the window, and enable the awake from nib event handler in the AppleScript pane of the Inspector window. Link the event handler to the main Progress Example.applescript file in your project. See figure 1.
Figure 1. Linking the Window to the awake from nib Event Handler
Next, select the progress spinner in the interface, and assign it a name of spinner in the AppleScript pane of the Inspector palette. See figure 2. Also, disable the progress spinner’s Display When Stopped checkbox in the Attributes pane of the Inspector palette.
Figure 2. Naming the Progress Spinner
Because our interface will have two progress indicators, assigning a name to each one will allow us to target each progress indicator specifically by its name in our code. Select the progress bar and assign it an AppleScript name of bar. See figure 3. Also, disable the progress bar’s Display When Stopped checkbox in the Attributes pane of the Inspector palette.
Figure 3. Naming the Progress Bar
Save the interface. To begin editing the code of the project, select the main interface window again, and then click the Edit button at the bottom of the AppleScript pane of the Inspector palette.
Working with Progress Spinners
A progress spinner can be used to indicate that processing is occurring without indicating the amount of processing completed or remaining. A progress spinner will simply spin in a circular motion until stopped. Progress spinners are useful when interface space is too limited to include other progress indicators, or in situations where it is difficult to determine the amount of data being processed.
A progress spinner will begin spinning when it is started. To start a progress spinner, use the start command. For example:
start progress indicator "spinner" of window 1
To stop a progress spinner, use the stop command. For example:
stop progress indicator "spinner" of window 1
Working with Progress Bars
A progress bar can be a very user friendly way of providing progress during script execution. A progress bar can be used to display incremental progress, or, like a progress spinner, indeterminate progress.
Like progress spinners, progress bars may be started and stopped. For example:
start progress indicator "bar" of window 1
A progress bar is indeterminate by default, but may be changed to be incremental via your project’s AppleScript code, or from the Attributes pane of the Inspector palette in Interface Builder. To set a progress bar to be incremental, rather than indeterminate, from your project’s code, change the boolean value of the progress indicator’s indeterminate property. For example:
set indeterminate of progress indicator "bar" of window 1 to false
Incremental progress bars are used to display the current progress of a process. Because of this, they have a maximum value and a current value. These values may be adjusted as needed through out your code. When starting a progress bar, you will probably want to set the maximum value of the progress bar to the desired amount. This is done by setting the maximum value property of the progress indicator. For example:
set maximum value of progress indicator "bar" of window 1 to 10
To increment the progress bar, you will change its current value as needed, to reflect actual progress as it occurs. To do this, set the value of the content property of the progress indicator to the current progress increment. For example:
set content of progress indicator "bar" of window 1 to 5
Displaying Progress in Text Fields
Text fields can also be used as an effective way to display progress. By updating the content of the text field at various times during processing, you can provide a user with important information about what is occurring as the script executes.
Pulling things Together
Now we’re going to pull together some the techniques that we have discussed to show different ways that progress can be indicated.
Displaying Progress Using a Progress Spinner and a Text Field
Figure 4. Displaying Progress using a Progress Spinner and a Text Field
Add the following code to your main project window. Then, build and run the project.
on awake from nib theObject
set theFolder to choose folder with prompt "Please select a folder of items to process:"
set theFolderItems to list folder theFolder without invisibles
start progress indicator "spinner" of window 1
repeat with a from 1 to length of theFolderItems
set content of text field 1 of window 1 to "Processing item " & a & " of " & (length of theFolderItems) & "..." & return & (item a of theFolderItems) as string
-- This code delays long enough to show you the results
set delayUntil to (current date) + 1
repeat until (current date) is greater than or equal to delayUntil
end repeat
end repeat
stop progress indicator "spinner" of window 1
display dialog "Done!"
quit
end awake from nib
The code above begins by prompting the user to select a folder to process. After a folder has been selected, a list of items in the folder is retrieved. Next, the progress spinner in the interface is started. The script then proceeds to loop through the items detected in the folder. The text field in the interface is updated for each item in the loop. The loop also includes some code to delay long enough to allow you to see the interface update during each iteration. Once all items in the folder have been processed, the progress spinner is stopped, and a dialog is displayed to indicate that processing is complete. The following examples will all perform in a similar fashion.
Displaying Progress Using an Indeterminate Progress Bar and a Text Field
Figure 5. Displaying Progress using an Indeterminate Progress Bar and a Text Field
This example code will perform the same process as above, but will use an indeterminate progress bar, rather than a progress spinner.
on awake from nib theObject
set theFolder to choose folder with prompt "Please select a folder of items to process:"
set theFolderItems to list folder theFolder without invisibles
start progress indicator "bar" of window 1
repeat with a from 1 to length of theFolderItems
set content of text field 1 of window 1 to "Processing item " & a & " of " & (length of theFolderItems) & "..." & return & (item a of theFolderItems) as string
-- This code delays long enough to show you the results
set delayUntil to (current date) + 1
repeat until (current date) is greater than or equal to delayUntil
end repeat
end repeat
stop progress indicator "bar" of window 1
display dialog "Done!"
quit
end awake from nib
Displaying Progress Using an Incremental Progress Bar and a Text Field
Figure 6. Displaying Progress using an Incremental Progress Bar and a Text Field
This example code will perform the same process as above, but will use an incremental progress bar. Take note of how this code sets the maximum value of the progress bar to the number of items in the folder. It then sets the current value of the progress bar to 0 before the repeat loop is entered. Then, the current value of the progress bar is incremented with each repeat.
on awake from nib theObject
set theFolder to choose folder with prompt "Please select a folder of items to process:"
set theFolderItems to list folder theFolder without invisibles
set maximum value of progress indicator "bar" of window 1 to (length of theFolderItems)
set content of progress indicator "bar" of window 1 to 0
set indeterminate of progress indicator "bar" of window 1 to false
start progress indicator "bar" of window 1
repeat with a from 1 to length of theFolderItems
set content of text field 1 of window 1 to "Processing item " & a & " of " & (length of theFolderItems) & "..." & return & (item a of theFolderItems) as string
-- This code delays long enough to show you the results
set delayUntil to (current date) + 1
repeat until (current date) is greater than or equal to delayUntil
end repeat
set content of progress indicator "bar" of window 1 to a
end repeat
stop progress indicator "bar" of window 1
display dialog "Done!"
quit
end awake from nib
Displaying Progress Using a Progress Spinner, an Incremental Progress Bar, and a Text Field
Figure 7. Displaying Progress using a Progress Spinner, an Incremental Progress Bar, and a Text Field
This example code will perform the same process as above, but will also display a spinner, in addition to the incremental progress bar and text field.
on awake from nib theObject
set theFolder to choose folder with prompt "Please select a folder of items to process:"
set theFolderItems to list folder theFolder without invisibles
start progress indicator "spinner" of window 1
set maximum value of progress indicator "bar" of window 1 to (length of theFolderItems)
set content of progress indicator "bar" of window 1 to 0
set indeterminate of progress indicator "bar" of window 1 to false
start progress indicator "bar" of window 1
repeat with a from 1 to length of theFolderItems
set content of text field 1 of window 1 to "Processing item " & a & " of " & (length of theFolderItems) & "..." & return & (item a of theFolderItems) as string
-- This code delays long enough to show you the results
set delayUntil to (current date) + 1
repeat until (current date) is greater than or equal to delayUntil
end repeat
set content of progress indicator "bar" of window 1 to a
end repeat
stop progress indicator "bar" of window 1
stop progress indicator "spinner" of window 1
display dialog "Done!"
quit
end awake from nib
In Conclusion
If you had trouble following along with the previous examples, you can download the example Xcode project used in the examples from here.
Until next time, just script it!
-Ben Waldie