11 Replies Latest reply: Jul 8, 2011 6:21 PM by Steve Herman1
s1er Level 1 Level 1 (0 points)

Hopefully someone here can give me a hand.

 

I have a very simple script that I would like to share with some of my coworkers who aren't so comfortable with the command line. So I would like to make a GUI with a progress bar using Interface Builder. I've searched high and low but can not find anything current that helps me. I think I might just be misunderstanding how outlets work and such, as I can get the script to run and the window shows, but the bar does not progress.

 

As a note, I'm using Xcode 4. Any help is greatly appreciated. Thanks in advance.


MacBook Pro, Mac OS X (10.6.8)
  • red_menace Level 6 Level 6 (14,905 points)

    How is your progress bar set up and how are you using it?  If it is a determinate bar with a maximum value set and connected to an outlet property (for example myProgressBar), you should be able to increment it by using

     

    myProgressBar's incrementBy_(1)

  • s1er Level 1 Level 1 (0 points)

    That's the thing, I'm not sure where or how to properly declare the progress bar and then address it with the applescript delegate afterwards.

  • red_menace Level 6 Level 6 (14,905 points)

    OK, in your application delegate script, you will need to define a property that will get connected to your progress bar:

     

      property myProgressBar : missing value

     

    I don't have Xcode 4, but the principles are the same - Interface Builder (or the equivalent part in Xcode 4) will look for properties with an initial value of missing value so that you can use them as outlets (see the AppleScriptObjC Release Notes).  Once you connect the progress bar object to your AppDelegate property, it will be the instance variable for your progress bar when the application is run, and you can then use it with the various NSProgressIndicator methods.

  • s1er Level 1 Level 1 (0 points)

    Thanks! Also, is there a way to get the bar to update its own progress by reading the commands from the script, instead of having to increment by a value after each one? I've found setDoubleValue but I'm not sure that's what I'm looking for or even how to use it.

  • red_menace Level 6 Level 6 (14,905 points)

    Not sure what you mean by "reading commands from the script", but usually the progress of your task can be broken up into steps, for example items in a list of files.  Your application would then set the maximum value of the progress bar to the number of items, and increment it each time through your process loop.

     

    The setDoubleValue method can be used to set the progress bar to a specific value, for example resetting it to 0, but normally you would use the incrementBy method to bump your progress forward (or back).

  • s1er Level 1 Level 1 (0 points)

    incrementBy seems simple enough. However I can not seem to get the bar to show any animation. Instead, whenever it's run, the bar is just full.

     

    Since the task that I want to perform is very simple, I just have a do shell script followed by the incrementBy all within an on awakeFromNib. Am I doing something wrong, or is there a specific way to make the bar progress smoothly? Here's a similar example to what I have.

     

     

        on awakeFromNib()

            do shell script "mkdir ~/Desktop/dir1"

            myProgressBar's incrementBy_(20)

            do shell script "mkdir ~/Desktop/dir2"

            myProgressBar's incrementBy_(20)

            do shell script "mkdir ~/Desktop/dir3"

            myProgressBar's incrementBy_(20)

            do shell script "mkdir ~/Desktop/dir1/dir4"

            myProgressBar's incrementBy_(20)

            do shell script "mkdir ~/Desktop/dir2/dir5"

            myProgressBar's incrementBy_(20)

        end awakeFromNib

     

  • Steve Herman1 Level 4 Level 4 (2,545 points)

    A couple of things...

     

    Your window and it's contents (including your progress indicator) normally do not redraw while you're within a handler. So chances are that the progress indicator is not getting redrawn until you exit the awakeFromNib handler... at which time your progress indicator has already been incremented to it's max value and so it would redraw as already full.

     

    So, one option is to insert calls to tell your window to redraw after each update. But progress indicators have the ability to update themselves on a background thread. So, in this case, an easier option might be to tell your progress indicator to use threaded animation. So try inserting this at the top of your routine:

     

    myProgressBar's setUsesThreadedAnimation_(true)

     

    Another thing is that the "do shell script..." calls that you're doing are probably happening very quickly since all you're doing is a call to "mkdir". So even after adding the above line of code you may not see much if any difference in how quickly the progress indicator fills up.

     

    You might try adding a small "sleep" to each do shell script call to slow things down a little. For example, for each of your shell script calls you could do something like this:

     

    Change this:

    do shell script "mkdir ~/Desktop/dir1"

     

    To this:

    do shell script "mkdir ~/Desktop/dir1 ; sleep 0.5"

     

    That will add a half second delay to each shell script call and possibly allow you to see the progress indicator fill up more slowly.

     

    Steve

  • s1er Level 1 Level 1 (0 points)

    Unfortunately the threaded animation method is not working for me. Also, adding the sleep command causes the window to show after the task completes

     

    Since the widow does not redraw while it's within a handler, is there a better way to perform this task, with the animated progress bar?

  • Steve Herman1 Level 4 Level 4 (2,545 points)

    Ahhh....

     

    Part of the problem is also that you're doing this inside "awakeFromNib". That gets called when your nib/xib file has been loaded... but your app is not completely up and running at that point (at least from the end user's perspective). So all your work is occurring before the window appears on screen.  I believe your window was _always_ showing after the task completed, it's just more apparent now because of the sleep delays that you've added.

     

    Take your code out of "on awakeFromNib()" and put it into "on applicationDidFinishLaunching_(aNotification)".

     

    Steve

  • s1er Level 1 Level 1 (0 points)

    Perfect! Thanks much to the both of you!

  • Steve Herman1 Level 4 Level 4 (2,545 points)

    For what it's worth, and since you asked...

     

    One way you can force your window to redraw within a handler is to call your window's "displayIfNeeded()" method. Create an outlet for your window similar to how you did for the progress bar.

     

    property myWindow : missing value

     

    Then wire it up in Interface Builder so that it's connected to the window (by dragging the wire to the window's title bar). Then whenever you need to force a redraw do this:

     

    tell myWindow to displayIfNeeded()

     

    You might need to go this route if you want to display some status text that changes with each script step or update some other window items that don't have the option of using threaded animation like the progress bar does.

     

    By the way, you might also be interested in "AppleScriptObjC Explored" here:  http://www.macosxautomation.com/applescript/apps/index.html

     

    I have no association with the author and mention the book only because I happen to be working thru it myself and the very first example he goes thru in the book is about this very subject.

     

    Steve