Donations Welcome

I am currently saving toward a new version of Paint Shop Pro. If you wish to help me out, then please donate.

Larger versions of images can be seen by clicking on the image. Also, thank you for your kind and encouraging comments. I love reading the comments. If you use my any of freebies, I would be overjoyed with a comment containing a link to your item. I love seeing any of my freebies in use.

Wednesday, August 27, 2008

Troubleshooting Scripts

These are screenshots of common problems that occur when using scripts in Paint Shop Pro. I have assembled screenshots that I use when helping people.

Problem A - JascApp vs. PSPApp
A common problem with PSP scripts is version compliance. Scripts written in PSP 8 and PSP 9 use a module called JascApp. Scripts after PSP X use a module called PSPApp. When a PSP X or higher script is placed in the Scripts-Restricted folder and used in PSP 8 or 9, it will produce an error "This script has attempted an operation that is not permitted in a restricted script. This script may be run by moving it to a trusted directory."

When the script is moved to the Scripts-Trusted directory and the person tries to use it, another error is produced, "This script could not be loaded."

This error can be easily fixed by changing the PSPApp module to the JascApp module. This is steps 5, 6 and 7 in my Color Customization tutorial.

Problem B - Case Sensitivity
Paint Shop Pro scripts use the Python interpreter. This means that Paint Shop Pro scripts are case-sensitive. You will also get Problem A if you use some variation of Jasc like jasc or JASC or even jASC.

Problem C - Editor Problems
Sometimes, in foreign versions of Paint Shop Pro (like Dutch, Swedish and German), after the script has been editted in Notepad, it will not run. In some languages, Notepad will leave strange characters in the script file that will prevent it from working in PSP. The solution to this problem is to change the default Script Editor in Paint Shop Pro to WordPad.

The Script Editor is located in File > Preferences > File Locations. In the dialog on the left-hand side, find Python Source Editor and click on it.

Change the executable pathfrom "C:\WINDOWS\NOTEPAD.EXE" to "C:\Program Files\Windows NT\Accessories\wordpad.exe"

Once the default editor has been changed to WordPad, the user needs to remember to save the script files as text.

Sunday, August 3, 2008

Parts of a Script



The first line in a script 'from JascApp import *' allows for communication between the script and PSP.  It is essential.  When the wrong module is loaded (PSPApp, for example), then the script cannot function.  The next word in the script is 'def' which defines a function.  In this case, it is the ScriptProperties function which provides information such as the author of the script, copyright, and a description of the script.



The name of a function appears after the word def.  The second function in any script is the Do function.  Most of the work of the script occurs inside the Do function.



All scripts are called with the App.Do method.  App refers to a variable name inside of JascApp.  Do is the function.  App.Do passes the Environment argument, the Command name (circled in blue) and a dictionary to PSP to perform a command.



The dictionary is composed of keys which can be used to summon information from the dictionary and values which is the information needed to perform the command.



Finally, there are pound or number (#) signs which are used to designate comments.  Anything with a pound sign to the left is a comment and is ignored by the script and PSP.  Comments are used to for notations in the script so that the script author can tell future users what is happening in the script.

The following is a complex script which I broke down for Deb of DeeBe Designs.  It details the script modifications seen in a lot of my script tutorials.



Circled is the Fill Variable.  Once the GetMaterial command has been executed, it contains all the information for Material in a dictionary which has the keys: Color, Gradient, Pattern, Texture and Art.

In the Custom Colors in Effects tutorial, the script snippet uses a while loop.  There are several elements in the code snippet which are explained here.  First, the user is asked to choose a color so that the Fill variable is set prior to execution of the loop.  Every action in the loop is indented inside the loop.  I recommend at least 3 spaces and the first line of new App.Do function calls should be indented the same amount.  The while loop sets two variables: Fill and MyColor.



If the Fill variable is unset, then if the user clicks cancel in the Materials palette, MyColor can end up with a null value which will cause PSP to cycle endlessly in a loop.  The only to stop the loop is to abort the program.  In the while loop, we ask the user to choose a color if they happen to choose a gradient or pattern.  The while loop forces the user to choose a color instead of a different option.  So, as long as there is not a color, the loop will cycle.  Once the user chooses a color, we call the color out of the Fill dictionary and place the color inside the MyColor variable.



This example shows how the MyColor variable is set and then used in the Blinds Effect.

However, if a color choice is forced and then the resulting variable is used in Fill or any other command which accepts a material, such a preset shape, then it is important to use the correct variable.  If a dictionary is improperly defined, the App.Do method will send the incorrect information to PSP and the script will not work correctly.

For example, the FlowerFill variable is being used to store all the information resulting from the GetMaterial command.  The FlowerFill variable can be used by itself as the data for the Fill key in the Preset Shape command.

 

However, if the color information is placed in a new variable,  FlowerColor, the variable FlowerColor cannot be used as the entire data for the Fill key.  That is because FlowerColor is a tuple and Fill requires a dictionary.  FlowerColor can only substitute as data for the Color key inside the dictionary.


Saturday, August 2, 2008

Checking Program Version in Scripts

One of the problems with scripts is that Paint Shop Pro is not completely backwards compatible.  Between PSP X and X1, Corel changed the names of brushes, gradients and preset shapes.  Therefore, a script which works in X1 and X2 may not work correctly in 9 and X.  It is possible to have the script check for the program version and then switch between the old name for a brush, gradient or preset shape and the new name using an if statement.  The GetVersionInfo command returns the following keys: BuildType, MinorVersion, MajorVersion, MicroVersion, BuildNumber, VersionString and Program.

The following code snippet will provide the version of the program and place it in the variable 'version'.

   VersionInfo = App.Do(Environment, 'GetVersionInfo')
   version = VersionInfo['MajorVersion']

Insert the snippet after Do(Environment) at the beginning of the script.

def Do(Environment):

The variable 'version' can then be used to set variables for use in the script.  For example, to switch between custom brushes.

   if version == 9:
      brush = u'Marble2'
   elif version == 10:
      brush = u'Marble2'
   elif version == 11:
      brush = u'Corel_01_029'
   else:
      brush = u'Corel_01_029'

Then, replace the CustomBrush string in the brush command with the 'brush' variable.

    # PaintBrush
    App.Do( Environment, 'PaintBrush', {
            'BrushTip': {
                'Shape': App.Constants.BrushShape.Custom, 
                'CustomBrush': brush, 
                'Size': 500, 
                'Hardness': 100, 
                'Step': 32, 
                'Density': 100, 
                'Thickness': 100, 
                'Rotation': 0, 
                'BrushVariance': {
                    'SizeVariance': App.Constants.VarianceMethod.Pressure, 
                    'SizeJitter': 0, 
                    'OpacityVariance': App.Constants.VarianceMethod.None, 
                    'OpacityJitter': 0, 
                    'DensityVariance': App.Constants.VarianceMethod.None, 
                    'DensityJitter': 0, 
                    'ThicknessVariance': App.Constants.VarianceMethod.None, 
                    'ThicknessJitter': 0, 
                    'RotationVariance': App.Constants.VarianceMethod.None, 
                    'RotationJitter': 0, 
                    'ColorBlendVariance': App.Constants.VarianceMethod.None, 
                    'ColorBlendJitter': 0, 
                    'HueVariance': App.Constants.VarianceMethod.None, 
                    'HueJitter': 0, 
                    'SaturationVariance': App.Constants.VarianceMethod.None, 
                    'SaturationJitter': 0, 
                    'LightnessVariance': App.Constants.VarianceMethod.None, 
                    'LightnessJitter': 0, 
                    'PositionJitter': 0, 
                    'UseScaledPositionJitter': False, 
                    'ImpressionsPerStep': 1, 
                    'FadeRate': 100
                    }
                }, 
            'Brush': {
                'Opacity': 87, 
                'ContinuousPaint': True, 
                'WetLookPaint': True, 
                'BlendMode': App.Constants.BlendMode.Multiply
                }, 
            'PrimaryMaterial': App.Constants.MaterialRef.Foreground, 
            'ForegroundMaterial': {
                'Color': (128,128,128), 
                'Pattern': None, 
                'Gradient': None, 
                'Texture': None, 
                'Art': None
                },
            'BackgroundMaterial': {
                'Color': (128,128,128), 
                'Pattern': None, 
                'Gradient': None, 
                'Texture': None, 
                'Art': None
                }, 
            'Stroke': [
                (App.Constants.PathEntryInterpretation.Absolute,(880.5,1680.5),0),
                (App.Constants.PathEntryInterpretation.Absolute,(880.5,1680.5),188)
            ], 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'RandomSeed': 77296609, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((12,0,1),1)
                }
            })

Friday, August 1, 2008

Scripts - If Statement

Scripts in Paint Shop Pro use a scripting language called Python. In order to perform advanced tasks in PSP scripts, it is necessary to understand Python. This tutorial will cover the if clause (also known as the if statement). Sometimes it is beneficial in a script to allow a user to choose to perform an action or not. This choice occurs using the if clause. I will show how to manipulate a simple script so that a user can choose whether or not to apply a plugin.

Supplies:

Simple Ribbon Tutorial

1. Have my Simple Ribbon Tutorial available (either printed or open in a web browser).

2. Go to the script toolbar (or menu) and press the circle to start recording.

Toolbar

Menu

3. Follow the directions in the Simple Ribbon Tutorial, including the optional steps. Do not worry if you make a mistake. If you make a mistake, just undo the action (Ctrl-Z).

4. Once you have finished the tutorial, save the script by going to the script toolbar (or menu) and pressing the disk icon.

A window will pop up asking you for a name (and location) to save the script. Make sure 'Remove Undone Commands' is checked and 'Save Dialog Positions' is unchecked. To make sure that your script will be identical to the one in this tutorial, make sure 'Save Materials' is checked. You now have a script that will make a ribbon in only one set of colors. If you save the script with 'Save Materials' unchecked, then the script will make a ribbon in your existing foreground and background colors.

5. Now we will edit the script so that the user can choose whether or not to apply the plugin, Greg's Factory Output Vol. II: Pool Shadow. Open up the script editor by clicking the scroll button on the script toolbar.

A pop up window for the Script Editor will appear. Go to the bottom and click 'Text Editor.'

6. The script looks like this:
from JascApp import *

def ScriptProperties():
    return {
        'Author': u'',
        'Copyright': u'',
        'Description': u'',
        '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': 3600, 
            'Height': 150, 
            'ColorDepth': App.Constants.Colordepth.SixteenMillionColor, 
            'DimensionUnits': App.Constants.DimensionType.Pixels, 
            'ResolutionUnits': App.Constants.ResolutionUnits.PixelsPerIn, 
            'Resolution': 300, 
            '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'Asphalt', 
                'EnableFill': True, 
                '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)
                }
            })

    # Fill
    App.Do( Environment, 'Fill', {
            'BlendMode': App.Constants.BlendMode.Normal, 
            'MatchMode': App.Constants.MatchMode.None, 
            'Material': {
                'Color': (54,146,171), 
                'Pattern': None, 
                'Gradient': None, 
                'Texture': None, 
                'Art': None
                }, 
            'UseForeground': True, 
            'Opacity': 100, 
            'Point': (2376.5,66.5), 
            'SampleMerged': False, 
            'Tolerance': 10, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

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

    # Select Selection Borders
    App.Do( Environment, 'SelectSelectionBorders', {
            'Antialias': True, 
            'BordersType': App.Constants.BordersType.Inside, 
            'BorderWidth': 10, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

    # Fill
    App.Do( Environment, 'Fill', {
            'BlendMode': App.Constants.BlendMode.Normal, 
            'MatchMode': App.Constants.MatchMode.None, 
            'Material': {
                'Color': (141,204,213), 
                'Pattern': None, 
                'Gradient': None, 
                'Texture': None, 
                'Art': None
                }, 
            'UseForeground': True, 
            'Opacity': 100, 
            'Point': (2079.5,3.5), 
            'SampleMerged': False, 
            'Tolerance': 10, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

    # ResizeCanvas
    App.Do( Environment, 'ResizeCanvas', {
            'AspectRatio': 24, 
            'FillColor': (255,255,255), 
            'HoriPlace': App.Constants.HorizontalType.Center, 
            'MaintainAspect': False, 
            'NewDimUnits': App.Constants.UnitsOfMeasure.Pixels, 
            'NewHeight': 150, 
            'NewWidth': 3580, 
            'PlaceBottom': 0, 
            'PlaceLeft': -10, 
            'PlaceRight': -10, 
            'PlaceTop': 0, 
            'VertPlace': App.Constants.VerticalType.Center, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

    # Blinds
    App.Do( Environment, 'Blinds', {
            'Width': 5, 
            'Opacity': 25, 
            'Horizontal': False, 
            'LightFromLeftTop': True, 
            'Color': (62,105,148), 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

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

    # Halftone
    App.Do( Environment, 'Halftone', {
            'BackgroundColor': (255,255,255), 
            'ColorScheme': App.Constants.ColorScheme.Greyscale, 
            'HalftonePattern': App.Constants.HalftonePattern.Line, 
            'Overlay': True, 
            'OverlayBlendMode': App.Constants.BlendMode.Screen, 
            'OverlayOpacity': 48, 
            'PatternColor': (0,0,0), 
            'ScreenAngles': (90,137,137), 
            'Size': 5, 
            'TransparentBackground': False, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

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

    # Magic Wand
    App.Do( Environment, 'MagicWand', {
            'General': {
                'Mode': App.Constants.SelectionOperation.Replace, 
                'Antialias': True, 
                'Feather': 0, 
                'SampleMerged': False
                }, 
            'MatchMode': App.Constants.MatchMode.RGBValue, 
            'Contiguous': False, 
            'Point': (1482.5,90.5), 
            'Tolerance': 1, 
            'AntialiasType': App.Constants.AntialiasType.Outside, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })

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

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

    # Gregs Factory Output Vol. II_Pool Shadow
    App.Do( Environment, 'Gregs Factory Output Vol. II_Pool Shadow', {
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }, 
            'DefaultProperties': []
            })
We are going to place the section in purple inside an if clause. First, we need to ask the user a question using a Message Box.
    result = App.Do(Environment,  'MsgBox', {
                'Buttons': App.Constants.MsgButtons.YesNo, 
                'Icon': App.Constants.MsgIcons.Question, 
                'Text': 'Do you wish to apply Greg\'s Factory Output\n'
                        'Vol. II Pool Shadow?',
                })
The \n in the text section tells the Message Box to insert a return. Special characters, such as the apostrophe, need to be escaped with a backslash (\) so that the text does not end prematurely.

7. This will create a question message box with two options: Yes and No. (Read more about message boxes.)

The answer to the question is stored in the variable 'result'. The value of 'result' will either be 1 or 0. If the answer is Yes, then 'result' will be 1. If the answer is No, then 'result' will be 0. By itself, the answer to the question does not cause the script to pause, skip the plugin or perform any action. The answer to the question, 'result' must be used by a conditional (in this case 'if') to produce the desired effect.

8. To make the script use the plugin if the user chooses yes, but skip the plugin if the user chooses no, insert the 'if' statement before the plugin command.
    if result == 1:
       # Gregs Factory Output Vol. II_Pool Shadow
       App.Do( Environment, 'Gregs Factory Output Vol. II_Pool Shadow', {
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }, 
            'DefaultProperties': []
            })
    else:
       # MsgBox
       Result = App.Do(Environment,'MsgBox',{
           'Buttons':App.Constants.MsgButtons.OK,
           'Icon':App.Constants.MsgIcons.Info,
           'Text':'Script Complete\n\n'
                  'http://humbuggraphicsgalore.blogspot.com/',
       })

This conditional says, if the result is equal to one (meaning Yes) then use the plugin. Else, if the result is not equal to one then produce a message box saying that the script is complete.

Note that both the plugin command and the message box are nested inside the if clause. Indentation matters.


If the block is not indented, then the script will not load and the script output window will produce an error stating, "IndentationError: expected an indented block"

9. The 'else' part of the if clause is optional. It does not need to exist and it only occurs when the user answers 'No' to the question. The following code snippet would produce the same effect for the plugin, but the final message would always appear, not just when the user clicked 'No'.
    if result == 1:
       # Gregs Factory Output Vol. II_Pool Shadow
       App.Do( Environment, 'Gregs Factory Output Vol. II_Pool Shadow', {
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }, 
            'DefaultProperties': []
            })

    # MsgBox
    Result = App.Do(Environment,'MsgBox',{
        'Buttons':App.Constants.MsgButtons.OK,
        'Icon':App.Constants.MsgIcons.Info,
        'Text':'Script Complete\n\n'
               'http://humbuggraphicsgalore.blogspot.com/',
    })

In this case, what happens is that after the script reaches the end of the if clause, it continues with the rest of the script.

10. Finally, multiple if statements can be nested inside each other. For example,
    EC5 = App.Do(Environment,  'MsgBox', {
                'Buttons': App.Constants.MsgButtons.YesNo, 
                'Icon': App.Constants.MsgIcons.Question, 
                'Text': 'Do you have Eye Candy 5 Impact?',
                })
    if EC5 == 1:
       # Alien Skin Eye Candy 5: Impact_Bevel
       App.Do( Environment, 'Alien Skin Eye Candy 5: Impact_Bevel', {
         'GeneralSettings': {
             'ExecutionMode': App.Constants.ExecutionMode.Default,
             'AutoActionMode': App.Constants.AutoActionMode.Match,
             'Version': ((9,0,1),1)
             },
         'DefaultProperties': [(1313631852,1651470188,0,(1,r'''AQ==''')),(1466201192,1413830740,0,(1,
             r'''NDIgcGl4ZWxzAA==''')),(1131312242,1685026146,0,(1,r'''AAAAAACAQUA=''')),(1399682152,
             1685026146,0,(1,r'''AAAAAAAAWUA=''')),(1111847523,1685026146,0,(1,r'''AAAAAAAAAAA=''')),
             (1111848057,1701737837,0,(1,r'''aXRlQjBUZWI=''')),(1148349294,1685026146,0,(1,r'''AAAAAAAAAAA='''
             )),(1181491232,1651470188,0,(1,r'''AQ==''')),(1111843443,1701737837,0,(1,r'''eXR1UzBSdXM='''
             )),(1114468417,1685026146,0,(1,r'''AAAAAAAAOUA=''')),(1399874414,1413830740,0,(1,r'''OS4wODMgcGl4ZWxzAA=='''
             )),(1382966355,1819242087,0,(1,r'''1x8AAA==''')),(1148347252,1685026146,0,(1,r'''AAAAAADgcEA='''
             )),(1229874028,1685026146,0,(1,r'''AAAAAABAUEA=''')),(1114793832,1685026146,0,(1,r'''AAAAAAAAREA='''
             )),(1399353968,1685026146,0,(1,r'''AAAAAACAUUA=''')),(1131180576,1413830740,0,(1,r'''UkdCOiAyNTUgMjU1IDI1NSAA'''
             )),(1935963971,1413830740,0,(1,r'''UkdCOiA2NCA2NCA2NCAA''')),(1111843683,1413830740,0,(1
             ,r'''Q3VzdG9tQ3VydmU6IDMgMCAxIDAgMC41IDAuNjYgMCAxIDAgMAA=''')),(1111843700,1701737837,0,
             (1,r'''dGNlQjBUY2I=''')),(1111843444,1701737837,0,(1,r'''Y2JlQjBVYmI='''))]
         })
    else:
       EC4000 = App.Do(Environment,  'MsgBox', {
             'Buttons': App.Constants.MsgButtons.YesNo, 
             'Icon': App.Constants.MsgIcons.Question, 
             'Text': 'Do you have Eye Candy 4000?',
             })
       if EC4000 == 1:
          # Eye Candy 4000_Bevel Boss
          App.Do( Environment, 'Eye Candy 4000_Bevel Boss', {
            'GeneralSettings': {
               'ExecutionMode': App.Constants.ExecutionMode.Interactive,
               'AutoActionMode': App.Constants.AutoActionMode.Match,
               'Version': ((12,0,1),1)
               },
             'DefaultProperties': [(1466201192,1413830740,0,(1,r'''MjQuMDAgcGl4ZWxzAA==''')),(1131312242,
                1685026146,0,(1,r'''AAAAAAAASUA=''')),(1399682152,1685026146,0,(1,r'''AAAAAADAUkA=''')),
                (1651928147,1701737837,0,(1,r'''aXRlQjBUZWI=''')),(1148349294,1685026146,0,(1,r'''AAAAAAAAAAA='''
                )),(1181491232,1651470188,0,(1,r'''AQ==''')),(1148347252,1685026146,0,(1,r'''AAAAAADgYEA='''
                )),(1229874028,1685026146,0,(1,r'''AAAAAACARkA=''')),(1114793832,1685026146,0,(1,r'''AAAAAAAAWUA='''
                )),(1399353968,1685026146,0,(1,r'''AAAAAAAAOUA=''')),(1131180576,1413830740,0,(1,r'''UkdCOiAxMDAuMCUgMTAwLjAlIDEwMC4wJSAoMjU1IDI1NSAyNTUpAA=='''
                )),(1935963971,1413830740,0,(1,r'''UkdCOiAwLjAlIDAuMCUgMC4wJSAoMCAwIDApAA==''')),(1111848556
                ,1413830740,0,(1,r'''QmV2ZWw6IDMvMC4wMDAgMS4wMDAgMC8wLjUwMCAwLjY2MCAwLzEuMDAwIDAuMDAwIDAA'''
                ))]
          })
       else:
          result = App.Do(Environment,  'MsgBox', {
                'Buttons': App.Constants.MsgButtons.OK, 
                'Icon': App.Constants.MsgIcons.Info, 
                'Text': 'You need a version of Eye Candy.',
                })   
Notice that the if statements are placed (left indented) inside the previous if statements, like so:
Code
   |----Code
              |----Code
                         |----Code

This is known as nesting. The indentation tells Python when various pieces of code end.

Message Boxes in Scripts

There are three types of message boxes in Paint Shop Pro scripts: the information box, the question box, and the stop box.


Code Snippet:

result = App.Do(Environment, 'MsgBox', {
'Buttons': App.Constants.MsgButtons.OK,
'Icon': App.Constants.MsgIcons.Info,
'Text': 'This is an OK button with an informational icon.',
})




Code Snippet:

result = App.Do(Environment, 'MsgBox', {
'Buttons': App.Constants.MsgButtons.YesNo,
'Icon': App.Constants.MsgIcons.Question,
'Text': 'These are Yes and No buttons with a question mark icon.',
})




Code Snippet:

result = App.Do(Environment, 'MsgBox', {
'Buttons': App.Constants.MsgButtons.OKCancel,
'Icon': App.Constants.MsgIcons.Stop,
'Text': 'These are Ok and Cancel buttons with a stop sign icon.',
})


The results from the message box are stored in the variable 'result'. The MsgBox produces one value. The value will either be 1 or 0. The 'result' variable can be used in the if clause to allow switching between choices.

To change the type of buttons on the message box, you change the word after .MsgButtons. The options are OK, OKCancel, or YesNo.
   result = App.Do(Environment,  'MsgBox', {
'Buttons': App.Constants.MsgButtons.YesNo,
'Icon': App.Constants.MsgIcons.Question,
'Text': 'These are Yes and No buttons with a question mark icon.',
})
To change the picture on the message box, you change the word after .MsgIcons. The options are Info, Question, or Stop.
   result = App.Do(Environment,  'MsgBox', {
'Buttons': App.Constants.MsgButtons.YesNo,
'Icon': App.Constants.MsgIcons.Question,
'Text': 'These are Yes and No buttons with a question mark icon.',
})
To change the text in a message box you may edit anything between the two single quotes after Text.
   result = App.Do(Environment,  'MsgBox', {
'Buttons': App.Constants.MsgButtons.YesNo,
'Icon': App.Constants.MsgIcons.Question,
'Text': 'These are Yes and No buttons with a question mark icon.',
})

Scripts - Variables

Python stores information in variables. A variable is something whose value changes over time. A variable stores information. Variables do not perform actions. Common things stored by Paint Shop Pro in a variable are selections from the materials palette and answers to questions from message boxes. In general, the variable is the word to the left of the equal sign.

In the example script for the dotted ric rac, MyFill is the variable. It stores the color, gradient or pattern information from the materials palette. This information is then used in the fill command.

Common variables seen in commercially available PSP scripts are: result, Fill, KLEUR and VULLING. A variable can have any name.  A variable can be re-used. If a variable does not have a unique name (such as result), then whenever the variable is used again, the old value is replaced with the new value.  Paint Shop Pro does not care what you name the variable, but the variable needs to be a single word. Using spaces will produce syntax errors. Variables are case-sensitive. Result is not equal to result. Result and result are two different variables.

Variables can store any type of information. Two common types of information stored by a Paint Shop Pro script are material and text. Material is a selection from the materials palette. A material is a type of data known as an array. In computer programming languages, an array is a group of objects with the same attributes. The Material array contains several sub-variables (also known as elements) inside it: color, gradient, pattern, art, and texture. Some of these elements are also arrays. So, it is possible to store arrays inside arrays. These elements are referred by name, not number. This makes the Material array a "dictionary". To fetch an element out of a dictionary, you have to call it by name.   This is known as a key.  The data stored is known as a value.



In the Fill example, Material is a 'key' which can be used to find the value for the color, pattern, gradient, texture, and art.  However, Color is a 'key' inside the Material dictionary which holds the value '(0,0,255)'. 

In the example below, after Fill is executed with the GetMaterial command, it will point to all the Material information.



To store the color information from the Fill variable in a new variable, you need to name a new variable, MyColor, and place the color information in it.
    Fill = App.Do(Environment,'GetMaterial',{
        'IsPrimary':App.Constants.Boolean.true,
        'GeneralSettings': {
            'ExecutionMode':App.Constants.ExecutionMode.Interactive
        }
    })
    MyColor = MyFill['Color']

The Color 'key' is being used to grab the color data and place it in the new variable 'MyColor'.  An array is always in curly brackets and the individual elements are retrieved from it using square brackets. Therefore, a dictionary is always in curly brackets and the individual elements are retrieved from it using square brackets.

Color is a data type known as a tuple. Color is a three element tuple consisting of 3 numbers which are the red, green and blue values for the color. A tuple is always in parenthesis. For example in this Fill command, the material element (in italics and bold) is an dictionary.
    # Fill
    App.Do( Environment, 'Fill', {
            'BlendMode': App.Constants.BlendMode.Normal, 
            'MatchMode': App.Constants.MatchMode.None, 
            'Material': {
                'Color': (141,204,213), 
                'Pattern': None, 
                'Gradient': None, 
                'Texture': None, 
                'Art': None
                }, 
            'UseForeground': True, 
            'Opacity': 100, 
            'Point': (2079.5,3.5), 
            'SampleMerged': False, 
            'Tolerance': 10, 
            'GeneralSettings': {
                'ExecutionMode': App.Constants.ExecutionMode.Default, 
                'AutoActionMode': App.Constants.AutoActionMode.Match, 
                'Version': ((9,0,1),1)
                }
            })
The color element (141,204,213) in the Material dictionary is a tuple.

Text is a data type known as a string. In Paint Shop Pro, a string is always prefixed with a 'u'. This is because PSP uses Unicode strings. Unicode strings use the Unicode character set as defined by the Unicode Consortium and ISO 10646. An example of text use can be see in the Customizing Text tutorial. In that tutorial you can see that the GetString command stores the answer to the dialog in the variable 'Result'. The 'Result' variable is an dictionary.

    Result = App.Do( Environment, 'GetString', {
        'DefaultText': 'Name',
        'DialogTitle': 'Name for Tag',
        'Prompt': 'Enter Name',
        'MaxLength': 25,
        'GeneralSettings': {
            'ExecutionMode': App.Constants.ExecutionMode.Interactive
            }
        })

    MyText = Result['EnteredText']
The text needed for the Text command is the stored in 'EnteredText'. The EnteredText element is a string. It is retrieved from the dictionary and stored in MyText.

This tutorial does not cover all the information about variables, or data types in Python. There are many excellent Python programming books which provides excellent detail on this subject. I recommend Programming Python by Mark Lutz. This tutorial only covers the aspects of variables and data types pertinent to intermediate-level scripting in Paint Shop Pro. For advanced scripting in Paint Shop Pro, read the scripting API.