Using the Pause Script Demo



Created January 27, 2005 © Copyright SuzShook
Made "Version-Independent" March 2009
Property of SuzShook

This tutorial is my own creation;
however, most of the techniques used in this tutorial, I have learned from others!
Therefore, if you recognize any contribution you have made, I thank you!
And I thank you as well for respecting this as my work by not posting it,
in whole or in part,
in any other location without written permission from me!

Individuals and PSP graphics groups are invited to share my tutorials with others with TEXT LINKS ONLY.
You can e-mail me to let me know you are adding one or more of my tutorials to your list if you like -
it's always fun to know who is doing them!

This tutorial describes Gary Barton's "Pause Script Demo" script. This script is essentially a script skeleton which allows the script author to create strategic pauses within a script so the user can prepare data, set options, or otherwise alter the image in preparation for further processing. The "Pause Script" technique is copyrighted to Gary Barton of Pixelnook, and the original script, along with Gary's documentation, can be found at his site.



I make my tutorials as brief as possible, without the customary paths, details, and how-to's. For those veterans among you, this will be welcome! But for those less familiar with PSP, I included a "Glossary" that contains all the details omitted in the tutorial. If you need a little extra help, check the Glossary section. Just click on the button below - the Glossary will open in a new window.

PSP glossary button


This tutorial assumes you have a working knowledge of Paint Shop Pro at the intermediate level (or advanced beginner level with the Glossary). It was originally written in and for PSP Version 8, and now made "version-independent". Screen shots for this tutorial can come from any version of PSP - where there are significant differences from version to version, a green "Version Note" will be included, along with multiple screen shots if necessary.

Where a note/tip refers to a version of PSP and all higher versions, a + sign will be used to indicate this. For example, if a note/tip applies to PSP X and higher versions, I will use the convention "PSP X+".

If you try this tutorial, and find something is inaccurate for your version of PSP, please EMAIL ME to let me know so I can fix it!

Screen shots in this tutorial are resized - your work will be larger than this!



Supplies - For this tutorial, you will need the following:

~     ~     ~     ~     ~     ~     ~     ~     ~     ~

OK, now we're ready to begin. Grab your mouse and let's get started.

Remember to save often.

~     ~     ~     ~     ~     ~     ~     ~     ~     ~


What is the "Pause Script Demo" Script?

The "Pause Script Demo" script is actually a script composed of several scripts. The "Pause Script" format allows the user to chain together several scripts, inserting pauses between the individual scripts so that the user can complete certain tasks before continuing on to the next script. For example, the user may have two scripts, one that makes a tag, and a second that adds text to the tag. Perhaps the user wants to chain these scripts together, with a pause after the tag is complete to allow the user to choose new materials for the text and/or set the Text tool options before continuing to add the text. This is a perfect candidate for a "Pause Script".

The "Pause Script" also provides the user with a vehicle for passing data or information from one script to another - perhaps one of the biggest reasons for using the "Pause Script" format.


How the "Pause Script Demo" Script Works

The "Pause Script Demo" script works essentially in the following manner:

  1. The script runs a series of commands, followed by a pause during which the user performs certain tasks in preparation for the next section.

  2. The user completes the tasks as instructed, and then clicks the Run Selected Script button ( run selected script button ) on the Script toolbar to resume running the script. This time, the script runs the next series of commands, followed by either a pause, or the completion of the script.

  3. This is repeated as needed until the script completes.

Basic Components of the "Pause Script Demo" Script

In the next few sections, I am going to go through Gary Barton's "Pause Script Demo" script virtually line by line. The official script is available at Gary's site, and the content is reproduced here only by way of illustration. In order to get the most from this tutorial, download Gary's script (link provided above) and print out the "Pause Script Demo" script so you can follow along.

The "Pause Script Demo" script consists of a number of parts. Its basic structure is a series of def DoSteps, and an engine that makes them all work. Gary has included lots of documentation right within his script, so it should be rather easy to follow along.

Note: All lines beginning with "#" are comment lines - they are NOT an essential part of the script coding, but serve as notes or documentation. The comment lines have been italicized throughout this tutorial.

Here are the basics components:

  1. The from JascApp import * command - this is always the first command in a PSP script. This command imports the JascApp module, which takes care of all communication between the script and PSP.
    Version Note: Versions of PSP after version 9 use the from PSPApp import * command rather than the from JascApp import * command. However, if you want your scripts to run in any version of PSP, it is essential that you use the from JascApp import * command here.

  2. The def ScriptProperties(): command:
    def ScriptProperties():
        return {
            # Change this to be your own information
            'Author': u'Gary Barton',
            'Copyright': u'Copyright 2003 Gary Barton',
            'Description': u'Demo of pausing a script',
            'Host': u'Paint Shop Pro',
            'Host Version': u'8.00'
            }
  3. This command is found at the beginning of all scripts. Populate this command with your own information.

  4. Definition of a variable called scriptName:
    # Put the name of your script here
    scriptName = "Pause Script Demo"

    This variable is used to build information about your script and is also used in generated messages. Change the part between the quotes to reflect the name of your script.

  5. A def DoStepx(Environment): step - Gary's notes here are quite clear:
  6. # Do part one of the script here. It is just like the normal Do routine
    # except for the name. You can copy the Do routines from recorded scripts
    # and use them as steps. The name of each step must begin with "DoStep".
    # The script steps will be done in alphabetical order. You can have any
    # number of scripts

    def DoStep1(Environment):

  7. User inserted commands. In Gary's Demo, he has just a few lines of code, where he asks the user to input some text, and saves the text for use in a later step:
        result = App.Do(Environment, 'GetString', {
            'Prompt': 'Enter some text',
            'DefaultText': 'Testing 1 2 3...'
            })
        if not result['OKButton']:
            return
        # Save the text for use in a later step
        scriptData['myText'] = result['EnteredText']

    All of the above would be replaced by your code - the commands you want to run before the first pause.

  8. Note: The last 2 lines above show you how to pass information from one part of the "Pause Script Demo" script to another part of the script. We'll look at that later in this tutorial.

  9. A message instructing the user what actions are to be taken before resuming the script. Change the part between the quotes (indicated in red below) to tell the user what to do while the script is paused:
        # Display a message to let the user know what to do
        result = App.Do(Environment, 'MsgBox', {
            'Buttons': App.Constants.MsgButtons.OK,
            'Icon': App.Constants.MsgIcons.Info,
            'Text': 'Use the text tool to select a font, point size, etc. then run the script again to continue.'
            })
  10. A pause - the script is suspended until restarted by the user.

  11. Repeat of items 3 through 6 until all sections of the script have been included.

  12. The "Pause Script" Support section - a section of copyrighted code at the end of the script that makes it all work (much code suppressed here - replaced by lines of . . .).
    # ----- Pause support start -----
    # Author: Gary Barton
    # Revision: 0.3
            . . .
            . . .
            . . .
    # ----- Pause support end -----

The scriptName Variable

You'll see the scriptName you choose in the messages generated by the "Pause Script" script. For instance, when you press the Run Selected Script (or play) button to resume the script after a pause, you'll see a message like this - note the use of the name assigned to the scriptName variable (underlined here for emphasis):


The def DoStepx(Environment): Steps

The "Pause Script" script is set up as a series of def DoStepx(Environment): steps. As Gary notes, each section of the "Pause Script" must begin with one of these steps. Each def DoStep section is actually a script in itself - it contains all the commands of a script, up to the point where some user interaction is required. So, essentially, a "Pause Script" is a series of def DoSteps, followed by pauses. And each DoStep section can contain many commands - all the commands needed before user input is required.

The "Pause Script" sorts the DoSteps and executes them in alphanumeric order.

Note: Alphanumeric sorting places numbers (0 - 9) before upper case letters (A - Z), before lower case letters (a - z).
This means DoStep1 will be executed before DoStep2, which will be executed before DoStepA, which will be executed before DoStepa. You can have any number of DoSteps in the "Pause Script" script, but if you have more than 9, you might want to use DoStep01, DoStep02, DoStep03, etc., or even DoStepA, DoStepB, DoStepC, etc. (which would allow you to have 26 steps), rather than DoStep1, DoStep2, DoStep3, etc.

Be very careful how you name the DoSteps - they will be executed in alphanumeric order, no matter in what order they appear in the script. If the first step in your script is called DoStep5, and the second one is called DoStep3, the commands following DoStep3 will execute first, and then after the pause, those following DoStep5! Remember, the "Pause Script" sorts the DoSteps before deciding where to start. Just a heads up.

Note: Most scripts have a single
def Do(Environment):

statement before any of the commands of the script. On the other hand, "Pause Scripts" have a series of

def DoStepx(Environment):

lines, which divide the script into sections (containing user-inserted commands) interspersed with pauses.

The user-inserted commands represent what was formerly an entire script. Before Gary's "Pause Script" script, authors had to create a series of scripts to get the job done. The user was instructed to run the first script, then do some tasks to get ready for the second script, then run the second script, etc. Now, both scripts can be combined into one script. Just copy all the commands (the App.Do's) from the first script and paste them right after the DoStep1 line. Then open the second script, and copy all the commands and paste them right after the DoStep2 line, etc. Just be very careful of the alignment of those App.Do's in the "Pause Script" script - they should all be indented four spaces from the left margin, just like they are in the original script.

Since the sections, or DoSteps, of the "Pause Script" script are essentially scripts themselves, the best way to test these separate sections is as independent scripts. Then, when you get them working exactly like you want them to work, the commands (App.Do's) can be copied and pasted into your new "Pause Script" script. Otherwise, the testing can get quite complicated, and the error messages a little harder to follow.


Passing Data In A "Pause Script"

Gary has included a vehicle to pass data from one "Pause Script" DoStep to another. This is accomplished in the following lines in his "Pause Script Demo" script, where the first 2 lines, in DoStep1 are for passing the data, and the next 2 lines, in DoStep2, are for retrieving the saved data:

# Save the text for use in a later step
scriptData['myText'] = result['EnteredText']

# Use the text saved in previous step
segment['Characters'] = scriptData['myText']

The "Pause Script" stores the items you want to pass to subsequent DoSteps in a dictionary called scriptData. You can pass anything you want from section to section. Just store the information in a variable, and pass it along using the scriptData dictionary to "hold" the data for you. When you want to use the saved data, you just have to "fetch" if from the location where it was saved.

Python Dictionaries Primer
It might be a good idea at this point to expose you to some concepts about dictionaries. If you know all about Python dictionaries, you can skip this primer box. For the rest of you, here's all you ever wanted to know about dictionaries and were afraid to ask!

A Python dictionary is a set of 'key:value' pairs contained in curly braces, a "mapping" whose values are stored and retrieved using keys. A Python dictionary consists of pairs separated by a colon (:) - the first part of the pair is the key, and the second part of the pair is the value associated with that key. One such pair in PSP might be 'Width':300. In this case the key is 'Width' and the associated value is 300. Another example might be 'Height':200, and a third might be 'Size':(300,200).

We could store those values in the scriptData dictionary this way:

scriptData['Width'] = 300
scriptData['Height'] = 200
scriptData['Size'] = (300,200)

The resulting scriptData dictionary might look like this:

scriptData = {'Width':300, 'Height':200, 'Size':(300,200)}

We could print the dictionary if we wanted to, using the print command:

print scriptData

which would produce this in the Script Output Palette (SOP):

{'Width':300, 'Size':(300,200), 'Height':200}
Note: The items in a dictionary are "unordered" - hence they may appear in any order in a listing. The SOP results listed above might just as well be any of the following:
{'Width':300, 'Height':200, 'Size':(300,200)}
{'Size':(300,200), 'Width':300, 'Height':200}
{'Size':(300,200), 'Height':200, 'Width':300}
{'Height':200, 'Width':300, 'Size':(300,200)}
{'Height':200, 'Size':(300,200), 'Width':300}

We can also retrieve values from a Python dictionary. Python uses a system called indexing to get to individual elements in a dictionary. In a real dictionary, you would use a word to look up its meaning. In Python, you use a key (like a word) to look up or find the associated value (meaning). The format that's used to return a value from a dictionary is:

dictionaryname[key]

Returning to the sample scriptData dictionary above, to retrieve the width, code scriptData['Width']. This says: "Go into the scriptData dictionary, find the key called 'Width', and return its associated value". Therefore, if you code:

print
print scriptData['Width']

the SOP would list:

300

To print the height from this dictionary, code:

print
print scriptData['Height']

and the SOP will list:

200

In summary, then, here's what you need to know about dictionaries:

  • The dictionary elements are enclosed in curly braces {}.

  • Each "key-value" pair in the dictionary is separated by a colon (:).

  • The individual key-value pairs are separated from other key-value pairs by commas.

  • To access any value in a dictionary, code the dictionary name, followed by the key associated with the value you want, in square brackets. Be very careful to code the key exactly as you see it in the dictionary, including the quotes, or you'll get an error.

That's it for dictionary basics. If this is new to you, reread the Primer. It's a good idea to understand dictionaries in order to effectively use the "Pause Script" to pass data from DoStep to DoStep.

The basic format for passing data in a "Pause Script" is the following:

# Save xxxxx for use in a later DoStep
scriptData['xxxxx'] = xxxxx

When you want to retrieve that data in a subsequent DoStep, use this basic format:

# Retrieve xxxxx saved in a previous DoStep
xxxxx = scriptData['xxxxx']

In the sending or saving DoStep, xxxxx is a variable name where you've stored data that you want to pass on to another DoStep. It is very good practice to use this same name when you store the data in the scriptData dictionary. Then, in the receiving DoStep, the same name, xxxxx, is used once again to store the retrieved data. This makes the whole process clear and uncomplicated.

Let's look at a very simple example to see how this works. Suppose you calculate the width for an image in a "Pause Script" DoStep, calling the calculated width NewWidth. And suppose you want to use that same calculated width in a later DoStep for some reason or other. In the DoStep where you calculate the width, you would insert this code to pass the data:

# Save NewWidth for use in a later DoStep
scriptData['NewWidth'] = NewWidth

This code says "Save the value stored in the NewWidth variable to the scriptData dictionary, giving it the same name (key) in that dictionary, NewWidth, so I can find it later".

The value you've stored will be "remembered" by the "Pause Script". Later, when you want to retrieve that saved information in a subsequent DoStep, you would insert this code:

# Retrieve NewWidth value saved in a previous DoStep
NewWidth = scriptData['NewWidth']

This code says "Retrieve the value stored in the in scriptData dictionary using the key NewWidth, and store it in a variable called NewWidth, so I can use it in this DoStep".

Note: Though both the name in the scriptData dictionary, and the name you use to store the retrieved value in a later DoStep can be different from the original variable name, it's a lot clearer and less confusing to use the same name throughout. This is the procedure I always follow when I pass and retrieve data between DoSteps in my scripts. If you follow this policy when passing and retrieving values, you won't forget which variable name to use in the retrieving DoStep - it will be the same as it was when you sent it forward.

Let's look at one more example. Suppose you have a "Pause Script" in which you have the user supply the name of the font to use for a tag, and the point size of that font. You could store those values in variables named FontName and FontSize. Perhaps you have another Text or TextEx command later in the script, in a subsequent DoStep, and you want to use the same font as before. All you have to do is pass that information before the end of the DoStep in which it was defined, using the scriptData dictionary, like this:

# Save FontName and FontSize for use in a later DoStep
scriptData['FontName'] = FontName
scriptData['FontSize'] = FontSize

This says "Save the values stored in the FontName and FontSize variables to the scriptData dictionary, giving them their original names (keys) in that dictionary so I can find them later".

The values you've stored will be "remembered" by the "Pause Script". Later, in the DoStep where you want to retrieve that saved information, you would insert this code:

# Retrieve FontName and FontSize values saved in a previous DoStep
FontName = scriptData['FontName']
FontSize = scriptData['FontSize']

That's all there is to it. It's a marvelous vehicle and makes for great versatility in your scripts. Just remember the basic format for passing data:

# Save xxxxx for use in a later DoStep
scriptData['xxxxx'] = xxxxx

and for retrieving data:

# Retrieve xxxxx saved in a previous DoStep
xxxxx = scriptData['xxxxx']
Note: Once data has been stored in the scriptData dictionary in any "Pause Script" DoStep, it can be retrieved in any subsequent DoStep.

Message (MsgBox) Commands

As indicated above, each section of a "Pause Script" script should end with a message instructing the user what actions to take during the pause. This message can be as long as the author wants, and can contain as much detail as the author wants to include. Here's what that message will look like to the user, using Gary's "Pause Script Demo" script:

Note that once users click the OK button, they'll probably forget what they just read, and won't remember what they're supposed to do! Here are some suggestions for making that information easier to remember:

  1. Make sure the message is clear and complete. Users must know exactly what to do, and what state the image is to be left in when the script is restarted.

  2. Complicated instructions should be repeated within the ReadMe file for the script. That way, if users forget what to do, they can read the instructions again before continuing.

  3. A better option is to echo the instructions into the Script Output palette (SOP) using print statements. Here's an example of a message followed by reinforcing print statements - again, red is used for the information you would code:
    # Display a message to let the user know what to do
    result = App.Do(Environment, 'MsgBox', {
            'Buttons': App.Constants.MsgButtons.OK,
            'Icon': App.Constants.MsgIcons.Info,
            'Text': 'Use the text tool to select a font, point size, etc. then run the script again to continue.'
            })

    print " "
    print " Use the text tool to select a font, point size, etc."
    print " Then run the script again to continue."
    print " "
  4. Here's what this would look like in the SOP - notice how inserting blank lines before and after your message, and leaving a few blank spaces before each line of the message, makes it stand out in the SOP:

    Note: Starting in PSP 9, a termination message is generated at the conclusion of each script. Because the "Pause Script" chains together several scripts, that termination message will still be generated when each section of the script finishes - that is, at each of the pauses - even though the entire script has not completed. Be aware of this, and be sure you warn users of your scripts that this message is generated by PSP independently of the "Pause Script" format, and in this context, may indicate that one of the "subscripts" or DoSteps of the "Pause Script" has completed, rather than the entire "Pause Script".

  5. If you need to insert a second pause before a third set of commands, add another message telling the user what to do before continuing the script, as above. Gary's sample script has only one pause, so there is no message at the end of this section. However, you could add a message informing the user that the script has completed, if you wish:
    # Display a message to let the user know what to do
    result = App.Do(Environment, 'MsgBox', {
            'Buttons': App.Constants.MsgButtons.OK,
            'Icon': App.Constants.MsgIcons.Info,
            'Text': 'Congratulations - the script has completed successfully!'
            })

    print " "
    print " Congratulations - the script has completed successfully!"
    print " "

The "Pause Script" Pauses

A couple of messages are associated with the pauses in the "Pause Script" script. First of all, after a pause, when the user presses the Run Selected Script (play) button to resume the script, the following message will be generated:

To resume the script, the user clicks the YES button.

If the NO button is clicked instead, this message is displayed:



  • Clicking the OK button terminates the script.
  • Clicking the CANCEL button continues the pause. This is handy if the user suddenly realizes not all the tasks assigned for the pause have been completed.

When the script resumes, it will begin with the next section, denoted by the following lines in Gary's "Pause Script Demo" script:

# Do part two of the script here...
def DoStep2(Environment):
# Your script steps go here, indented four spaces from the left margin.

Undo's and the "Pause Script Demo" Script

If you press the UNDO button (or choose Edit...Undo, or press CTRL + Z) after a "Pause Script" script completes, the UNDO actions will happen in reverse order. Say your script has three sections, and after the first section, you instruct the user to add a layer and insert some text, while after the second section, the user is to move the text to a new position and add a drop shadow. The script outline would look something like this:

Section 1 commands - DoStep1    
    Add Layer
    Insert Text
\
/
Done during the 1st pause
Section 2 commands - DoStep2    
    Move Text
    Drop Shadow
\
/
Done during the 2nd pause
Section 3 commands - DoStep3    

Once the script has completed, if the user chooses to UNDO some or all of the script and pause actions performed, here's what actually happens:

  • The first time the user presses UNDO, everything accomplished by the Section 3 (DoStep3) commands is all undone.
  • The second time the user presses UNDO, the Drop Shadow is removed.
  • The third time, the Move Text is undone.
  • The fourth time, all the commands in Section 2 (DoStep2) of the script are undone.
  • The fifth time, Insert Text is undone.
  • The sixth time, Add layer is undone.
  • The seventh time, all actions accomplished by the Section 1 (DoStep1) commands are all undone.
Note: You need to notice that individual commands executed outside scripts are "undone" one at a time. But commands performed in a script are "undone" all at once - the entire script (or section of a "Pause Script") is "undone" in one click. As the "Pause Script" is actually made up of several individual scripts separated by pauses, each of those scripts, or sections, will be totally undone by a click of the UNDO button. As illustrated above, totally "undoing" the above "Pause Script" would take seven UNDOs. Be sure you understand this, as it's vital if you ever need to "undo" part of a "Pause Script".

Let's say, for example, the user has "undone" all the commands up to and including the Section 2 (DoStep2) commands. What happens if at this point the user again presses the Play button? The script will resume section 2, or DoStep2, with the question:

Interesting, huh? So users can back a paused script up part way, and resume the commands/DoSteps from that point on! And you don't need to wait until the end of a "Pause Script" to "undo" something. Using the above example, say you complete the second part of the "Pause Script" - the DoStep2 section - and suddenly remember that you forgot to place the text on a new layer. During the pause after DoStep2 you can "undo" that entire script (the DoStep2 part of the "Pause Script") with one click of the UNDO button, and then "undo" the "Insert Text" command. Now you can insert the layer, add the text again, and then restart the script, which will ask you if you want to continue with step 2 of the script.

Note: There is no need to code the EnableOptimizedScriptUndo command in any of the sections of a "Pause Script" script. Gary includes that important command in the Pause Support section.

The Playback Mode and "Pause Script" Scripts

You can choose the playback mode for each section of the "Pause Script" script independently. For instance, you can run one part of a script in Silent mode, and during the next pause, change the mode to Interactive mode to run the rest of the script. This can be very handy if you want to change the settings in only one part of a script, but want the rest to run silently.


The Pause Support Section

At the end of the script there's a section beginning with this line:

# ----- Pause support start -----

and ending with this line:

# ----- Pause support end -----

These lines (there are about 137 of them) must be included in any script with pauses. They are the "engine" which makes it all work, and are copyrighted to Gary Barton. You should not, under any circumstances, change any of the lines in this section.


The Open Image Requirement

Any "Pause Script" script requires that a document be open in the PSP workspace before the first pause. This is required because the "Pause Script" uses the Creator Information Description field of the Current Image Information to store information about the progress of the "Pause Script", including the next DoStep to run, and any passed data. Without an open image, there is no place to store the script information, so the script will fail with this error message:

We'll take a look at the information stored by a "Pause Script" later. For now, just be sure when you set up your scripts in the "Pause Script" format that you either start with an open image, or create or open an image during the first part of the script.


Preparing For A "Pause Script" Script

Now it's time to create our first "Pause Script". Are you ready? What we want to do in this first "Pause Script" is very simple - we'll just create a new image, pause for the user to add a tube to the image, and then resume the script so that a drop shadow can be added to the tube. This is a very simple "Pause Script", you're saying - and you're right. But if you can get it together for a simple set of scripts, you'll be able to do it for a more complex example. So let's try this very simple example.

Here's what we need to do first - let's record these two very simple scripts:

  • The first script should create a new image, with a transparent background.
  • The second script should add a drop shadow using settings 2, 2, 50, 4, black, and Shadow on new layer UNchecked.

That's all there is to it - just record these simple scripts now.

Note: If you've never recorded a script, you might want to take a look at my Recording A Simple Script tutorial.

Call the first one PauseScriptPart1.PspScript, and call the second one PauseScriptPart2.PspScript. Go ahead and record those scripts now. Test both scripts to be sure they work, and come back when you are done. Be sure to test the second script on some tubes, so you can be sure it's applying the drop shadow correctly.

Now that we have two scripts, let's insert them into the "Pause Script" format. First of all, the only part you want of each script is the part containing the commands of the script - that is, everything that follows the def Do(Environment): line to the end. Here's what my PauseScriptPart1 script looks like (yours might be slightly different, depending on the settings you have in your New Image dialog, and the version of PSP you are using):

from JascApp import *

def ScriptProperties():
    return {
        'Author': u'SuzShook',
        'Copyright': u'SuzShook 2003-2005',
        'Description': u'PauseScriptPart1',
        'Host': u'Paint Shop Pro 9',
        'Host Version': u'9.01'
        }
def Do(Environment):
    # EnableOptimizedScriptUndo
    App.Do( Environment, 'EnableOptimizedScriptUndo', {
        'GeneralSettings': {
            'ExecutionMode': App.Constants.ExecutionMode.Default,
            'AutoActionMode': App.Constants.AutoActionMode.Match,
            'Version': ((9,0,1),1)
            }
        })

# FileNew
App.Do( Environment, 'NewFile', {
    'Width': 200,
    'Height': 200,
    'ColorDepth': App.Constants.Colordepth.SixteenMillionColor,
    'DimensionUnits': App.Constants.DimensionType.Pixels,
    'ResolutionUnits': App.Constants.ResolutionUnits.PixelsPerIn,
    'Resolution': 72,
    'FillMaterial': {
        'Color': (255,255,255),
        'Pattern': None,
        'Gradient': None,
        'Texture': None,
        'Art': None
        },
    'Transparent': True,
    'LayerType': App.Constants.NewLayerType.Raster,
    'ArtMediaTexture': {
        'Category': u'Art Media',
        'Name': u'Canvas coarse',
        'EnableFill': False,
        'FillColor': (255,255,255)
        },
    'GeneralSettings': {
        'ExecutionMode': App.Constants.ExecutionMode.Default,
        'AutoActionMode': App.Constants.AutoActionMode.Match,
        'Version': ((9,0,1),1)
        }
    })

# SelectDocument
App.Do( Environment, 'SelectDocument', {
    'SelectedImage': 0,
    'Strict': False,
    'GeneralSettings': {
        'ExecutionMode': App.Constants.ExecutionMode.Default,
        'AutoActionMode': App.Constants.AutoActionMode.Match,
        'Version': ((9,0,1),1)
        }
    })

Note: In the above code snippet, I boxed the part you need to copy into the "Pause Script" skeleton. Notice also that I left out the EnableOptimizedScriptUndo command. As I mentioned before, there is no need to include this command in any of the sections of a "Pause Script" script, as Gary includes that important command in the Pause Support section.

Here's what my PauseScriptPart2 script looks like (yours should be similar):

from JascApp import *

def ScriptProperties():
    return {
        'Author': u'SuzShook',
        'Copyright': u'SuzShook 2003-2005',
        'Description': u'PauseScriptPart1',
        'Host': u'Paint Shop Pro 9',
        'Host Version': u'9.01'
        }
def Do(Environment):
    # EnableOptimizedScriptUndo
    App.Do( Environment, 'EnableOptimizedScriptUndo', {
        'GeneralSettings': {
            'ExecutionMode': App.Constants.ExecutionMode.Default,
            'AutoActionMode': App.Constants.AutoActionMode.Match,
            'Version': ((9,0,1),1)
            }
        })

# Drop Shadow
App.Do( Environment, 'DropShadow', {
    'Blur': 4,
    'Color': (0,0,0),
    'Horizontal': 2,
    'NewLayer': False,
    'Opacity': 50,
    'Vertical': 2,
        'GeneralSettings': {
            'ExecutionMode': App.Constants.ExecutionMode.Default,
            'AutoActionMode': App.Constants.AutoActionMode.Match,
            'Version': ((9,0,1),1)
            }
        })

Note: Again, I boxed the part you will need to copy into the "Pause Script" skeleton.

Creating the "Pause Script" Script

We're ready to put it all together now. To make things easy for you to build your first "Pause Script", I have edited Gary's "Pause Script Demo" script, removing all but the required lines. Open the ss-PauseScriptModel.PspScript script provided in the ZIP for this tutorial in your Text Editor. You might want to save this script using a different name, so you'll still have the model script when you go to build your next "Pause Script". Call your copy of this script MyFirstPauseScript.PspScript - that's descriptive!

Open your first script (PauseScriptPart1) in your Text Editor. Copy the lines following the def Do(Environment): line (and the EnableOptimizedScriptUndo step, if there is one in your script) in that script, all the way to the end (those boxed in the example above), and paste those lines into your MyFirstPauseScript.PspScript script, replacing the line which says ### INSERT YOUR FIRST SCRIPT COMMANDS HERE.

Open your second script (PauseScriptPart2) in your Text Editor. Copy the lines following the def Do(Environment): line (and the EnableOptimizedScriptUndo step, if there is one in your script) in that script, all the way to the end, and paste those lines into the MyFirstPauseScript.PspScript script, replacing the line which says ### INSERT YOUR SECOND SCRIPT COMMANDS HERE.

Note: Be very careful when you copy and paste script lines - the App.Do( Environment,... lines must always be indented four spaces from the left edge. Spacing is critical in Python/PSP scripts - being "off" by a single space will cause your script to generate errors!

Now, let's put the finishing touches on our "Pause Script":

  • Find the first MsgBox command - update it to tell the user to choose a tube during the pause and center it on the image, and then run the script again. To do this, replace the string of x's in the Text parameter of this command with the appropriate message, being careful to retain the opening and closing quotes.

  • Add print statements echoing the message to the SOP. Those print statements might look something like this:
    print
    print " The script will now pause so you can choose a tube."
    print " Center the tube in the image."
    print " Run the script again when you are ready."
    print

  • Find the second MsgBox command - update it to thank the user for trying your script. To do this, replace the string of y's in the Text parameter of this command with the appropriate message, being careful to retain the opening and closing quotes.

  • Add print statements echoing the message to the SOP. Those print statements might look something like this:
    print
    print " Thank you for trying my script!"
    print

  • Find the definition of the scriptName variable at the beginning of the script - update it to contain the name of your script (replace the string of z's in the model with the name of your script - most probably for this tutorial that will be MyFirstPauseScript).

Looks like you're in business now! Save the script and try running it. Chances are you'll have errors at first, but correct those and get it to run.

You did it! You've created your first script using the "Pause Script" format! Now you can do this for any of your scripts where it's necessary to stop somewhere for users to complete some required tasks. Just use the Model script each time, placing your script commands in the proper places, and updating the MsgBox commands to reflect the appropriate messages for the user.

If you had problems getting your "Pause Script" to work, I included a completed script in the ZIP for this lesson. Compare your script with mine (ss-MyFirstPauseScript.PspScript) and find your errors, so they won't occur again. I'm giving you my script only so you can compare notes and figure out why yours doesn't work.


Practicing Passing Data In A "Pause Script" Script

There's only one more thing you have to practice before you're a full-fledged, board-certified, top-of-the-line "Pause Script" aficionado! Let's try passing data in a "Pause Script".

We can use our MyFirstPauseScript script to practice. Open this script in your Text Editor and save it as MyFirstPauseScript-PassingData.PspScript. Insert the following lines just before the first MsgBox command - the one in DoStep1:

Width = App.ActiveDocument.Width
print
print " The image width is ", Width
print

These lines extract the width of the image from the system, assign that value to the variable Width, and then print that information to the SOP. Save the script and run it again - did the width print in the SOP? Great.

Before continuing, delete the image your script just created.

To verify that the information stored in the Width variable is not available in the second part of the script, insert the following lines into the second part of the script, again right before the MsgBox command will be fine:

print
print " The image width is ", Width
print

Save the script and run it, restarting the script again after the pause so the second part of your "Pause Script" runs. An error should be generated in the SOP which is several lines long - the last line of that error message says:

NameError: global name 'Width' is not defined

This error is generated because the second part of the script does not know about the Width parameter. It was defined in the first section of the "Pause Script", which is actually a separate script entirely.

To pass that information from the first part of the script, use the following line - insert this line anywhere after you define the variable Width, or as the very last thing in this section (DoStep01) of the script:

scriptData['Width'] = Width

To retrieve, extract or fetch the stored information, use the following line - insert this line in the second DoStep of the script, at the beginning of that step (right after the def DoStep2 line is a good place):

Width = scriptData['Width']
Note: I like to pass information as soon as it's created, and retrieve it at the start of the section that needs it. You will soon develop your own techniques. Just be sure you pass the data after it's created, and retrieve the data before you try to use in a subsequent DoStep.

Now rerun your script - this time it should complete without errors! If you have any problems coding these statements see the ss-MyFirstPauseScript-PassingData.PspScript script in the ZIP that accompanies this tutorial.

That's basically the process required to pass data. In summary:

  • In the sending DoStep, place this line:
    scriptData['xxxxx'] = xxxxx

  • In the receiving DoStep - the step where you want to "fetch" the stored data - place this line:
    xxxxx = scriptData['xxxxx']
Note: In both lines, xxxxx is the variable name of the data being passed/received. It's much simpler and clearer if you use the same name throughout.

I've included one more script in the ZIP that accompanies this tutorial, called ss-PauseScriptFullModel.PspScript. This script gives you a complete "Pause Script" model that includes the following:

  • The lines needed to pass and retrieve data - these lines are commented out in this model script, with documentation reminding you how to use this code to pass and retrieve data).

  • Print lines to echo MsgBox messages to the SOP.

  • Three DoSteps instead of only 2, in case you need some help setting up a "Pause Script" with more than two DoSteps.

The ss-PauseScriptFullModel.PspScript is your friend!


How The "Pause Script" Keeps Track of Things (Optional)

Before we review all we've learned in this tutorial, let's just take a quick look at the information stored by Gary's marvelous "Pause Script" script. This section is marked "optional", but is included here for those "inquiring minds" who "want to know"!

To take a look at the information stored in a "Pause Script":

  1. Create a new image, any image, and then take a look at the Current Image Information:
    • Choose Image...Image Information, or use the shortcut keys SHIFT + I.
    • Click on the Creator Information tab.
    • Examine the Description field - for a new image, this field is blank.

  2. Now, run your last script again - the one that passes data. When you get to the first pause, take a look at the Current Image Information for the image created by the script. Now there's data in the Description field, because the "Pause Script" stores information about its progress in that field. You should see something like this (you will see the data in a single line - in fact, you have to run your cursor to the end to see all the data):
    {'ScriptStack': ['MyFirstPauseScript-PassingData'],'MyFirstPauseScript-PassingData': {'Width': 200,'DoStep': 1}} # Script data

    You don't really have to understand this data -but it's a rather ingenious way to pass data that Gary came up with. Notice the data is stored in a dictionary, evidenced by the curly brackets ({}) and the key:data format characteristic of dictionaries. Also notice the Width parameter that's stored in the dictionary - mine says 200, but yours will be whatever the width of your image is. This data is retained, and updated as the script progresses - "Pause Scripts" can have many, many DoSteps - but when the script terminates, this information is removed from the description field. To prove this, close the Current Image Information dialog and complete the script - add the tube to the image, and run the script again. Now look at the Current Image Information again - you will notice that the Description field has been cleared.

    One last point I'd like to make here - do you remember several pages back I told you to "delete the image created by the script before continuing"? There was a reason for that. Had you tried to run the script again, without first removing the image that had the scriptData information stored in its Description field, PSP would have tried to complete the original script. One of the things it stores is the "next DoStep to run" information. The "Pause Script" maintains a list of all script DoSteps, and stores the index pointer for the next step to run in that Description field we just examined. In our case, the list of steps to run would have looked like this:

    ['DoStep1','DoStep2']

    Since we just ran the DoStep1 step, the index pointer is set to 1, which means that the next step to run is the DoStep2 step, or the step at index 1 in the list. In Python - the scripting language of PSP - counting starts at 0, so 0 points to the first item in the list, and 1 points to the second item in the list. Yes, confusing if you're used to starting your counting at 1, but that's just the way programming languages work - programmers are strange fellows!. Do you see the entry in the Description field that tells the system which step to run next? That's right, it's this entry:

    'DoStep1': 1

    If you're not familiar with dictionaries and indexing, this may be more than a little Greek, but for those of you who've delved into Python a bit, this just might make sense! Anyway, I just wanted to show you a bit about how the "Pause Script" works. File it away, or throw it away - you don't really have to understand this part to use the "Pause Script" format! Thank heavens, huh! But perhaps the next time you try to start a "Pause Script" from the beginning, and it wants to continue with the one you already started, you'll remember this discussion, and you might even vaguely recall why that's happening! You can restart the script from the beginning by either deleting the image with the scriptData information stored within its Current Image Information field, or just delete the information in that field! Either way works.


Other Hints and Tips for Creating Effective and Efficient "Pause Scripts"

Always echo messages from MsgBox commands in the SOP - users invariably forget what they just read.

Don't insert pauses unnecessarily - the fewer stops in a script, the better! Think things through and stop only when necessary to obtain user input of some kind that's impossible to obtain while the script is running.

Make your scripts user-friendly and if possible, "people-proof"! In other words, anticipate problems users might have with your scripts, and code so that those areas are clearly addressed.


Review

In review, here's how to set up a "Pause Script" script, using the ss-PauseScriptFullModel.PspScript model script:

  1. Record each part of the "Pause Script" as a separate script. Test to be sure the separate scripts run successfully.

  2. Insert the first script commands into the DoStep01 step of the "Pause Script".
    Note: Remember to insert only those commands following the def Do(Environment): line (and the EnableOptimizedScriptUndo step, if there is one in your script).

  3. Insert the second script commands into the DoStep02 step of the "Pause Script". Same note as above.

  4. Insert the third script commands into the DoStep99 step of the "Pause Script". Same note as above.

  5. If you need more than three DoSteps, copy DoStep02 and paste the new steps after DoStep02, renumbering the DoSteps accordingly. You will not have to renumber the last step - the one that has the "Thank you for trying my script" message - unless you have more than 99 DoSteps in your "Pause Script"!

  6. Update all MsgBox commands.

  7. Update print statements so that they echo MsgBox information to the SOP.

  8. Update the scriptName variable at the beginning of the script.

And that's it! You're ready now to create your own "Pause Scripts" - use this format any time you have a script you want to share that requires some user input or actions during the script, or you need to pass data from one script to another. It's a wonderful tool - use it wisely! I hope I have explained the "Pause Script" format in enough detail for you to feel comfortable implementing it. Please let me know if there is anything that I can do to make it clearer!


A wonderful new resource was introduced in PSP 9 which actually expands on the "Pause Script's" ability to pass data between scripts, and allows you to pass data from script to script throughout the entire PSP session. This may make the "Pause Script" format unnecessary, but I still think Gary Barton's "Pause Script" is a tidy way to present a script!

The undocumented PSP 9 gem, which has persisted through subsequent releases of PSP, is known as the scriptData Communication Area. For more information, see my Using the ScriptData Communications Area in PSP tutorial.




If you have any problems, comments, or questions, please do not hesitate to Email me.

Email


Version Independent Tutorials ~ About Me ~ Home ~ Email

All graphics and content 2002-present by SuzShook