home *** CD-ROM | disk | FTP | other *** search
Wrap
from JascApp import * from Tkinter import * import tkMessageBox import JascUtils import string import sys #This script brings up a dialog that prompts the user for the x/y spacing of #the grid (in pixels). The grid will be drawn using the paintbrush, using #whatever foreground material is set on the materials palette. # #When the script completes the grid will be saved in the patterns directory #so it can be used at any time in the future without having to run the script. # #The sequence is: # - prompt the user for the grid settings # - call the NameFromMaterial method in JascUtils to a name that # describes the foreground material. # - call the CreateGrid function to make a new image containing exactly # one repetition of the pattern. It does this by creating an image that # is as big as the pattern, and drawing lines down the right and bottom # edges. # - call SavePatternFile to save the pattern image as a file in the patterns # directory. Since PSP maintains a cache of all the patterns, before # we can use it we have to notify the cache that we have made a change. # That is done with a call to EventNotify. # - Finally we make a new layer, and flood fill it with the pattern we # just created. As the pattern is tiled it creates the grid we want. def ScriptProperties(): return { 'Author': 'Joe Fromm', 'Copyright': 'Copyright (C) 2002-2003, Jasc Software Inc., All Rights Reserved. Permission to create derivate works of this script is granted provided this copyright notice is included', 'Description': 'Add a layer to the image with a grid defined by the user.', 'Host': 'Paint Shop Pro', 'Host Version': '8.00' } class GridMakerDialog(Frame): ''' define the dialog used to prompt the user for grid information''' def __init__( self, parent, title ): Frame.__init__(self) # init our parent # if we exit with OK this will be set to 1. A zero means we pressed cancel self.OKPressed = 0 # define all the variables attached to the controls self.XSpacing = IntVar() self.XSpacing.set( 50 ) self.YSpacing = IntVar() self.YSpacing.set( 50 ) # define the basics of the window self.pack(expand=YES, fill=BOTH) self.master.title('Define Grid') # put some explanatory text on the window Label( self, text = 'This will add a new layer to your image ' 'and draw a grid at the pixel spacing you ' 'specify.\nThe color of the grid will be the ' 'foreground material of the application', justify=LEFT ).pack(expand=YES, fill=BOTH, side=TOP) # make a subframe to hold the horizontal size controls XFrame = Frame( self ) XLabel = Label( XFrame, text='Horizontal spacing (pixels):', width=30 ) XLabel.pack( expand=YES, fill=BOTH, side=LEFT ) self.XEntry = Entry( XFrame, textvariable=self.XSpacing ) self.XEntry.pack( expand=YES, fill=BOTH, side=RIGHT ) XFrame.pack(side=TOP) # do the same thing for the vertical size controls YFrame = Frame( self ) YLabel = Label( YFrame, text='Vertical spacing (pixels):', width=30 ) YLabel.pack( expand=YES, fill=BOTH, side=LEFT ) self.YEntry = Entry( YFrame, textvariable=self.YSpacing ) self.YEntry.pack( expand=YES, fill=BOTH, side=RIGHT ) YFrame.pack(side=TOP) # put OK/Cancel buttons on the dialog - parts of this lifted from TkSimpleDialog ButtonFrame = Frame(self) OKButton = Button( ButtonFrame, text="OK", width=10, command=self.OnOK, default=ACTIVE ) OKButton.pack(side=LEFT, padx=5, pady=5) CancelButton = Button( ButtonFrame, text="Cancel", width=10, command=self.OnCancel ) CancelButton.pack(side=LEFT, padx=5, pady=5) ButtonFrame.pack() self.bind("<Return>", self.OnOK) self.bind("<Escape>", self.OnCancel) def OnOK(self, event=None): ''' called by pressing the OK button - validates data, and if no error sets a good return code and dismisses the dialog by calling OnCancel ''' try: X = self.XSpacing.get() except ValueError: X = 0 try: Y = self.YSpacing.get() except ValueError: Y = 0 if X < 2 or X > 1000: tkMessageBox.showerror( 'Horizontal Spacing invalid', 'The horizontal spacing value must be between 2 and 1000' ) return elif Y < 2 or Y > 1000: tkMessageBox.showerror( 'Vertical Spacing invalid', 'The vertical spacing value must be between 2 and 1000' ) return # if we got here we passed validation self.OKPressed = 1 # finish by pressing the Cancel button self.OnCancel() def OnCancel(self, event=None): # on cancel we simply terminate the message loop self.quit() def CreateGrid( Environment, XSpacing, YSpacing ): ''' Create a grid that can be used as a pattern fill. We do this by creating a new document of the size desired, and then drawing down the right hand and bottom edges. When tiled, this results in a grid of the proper dimensions. The current foreground material is used for the paint stroke. returns the document created ''' # first create a new image App.Do( Environment, 'NewFile', { 'Width': XSpacing, 'Height': YSpacing, 'ColorDepth': App.Constants.Colordepth.SixteenMillionColor, 'DimensionUnits': App.Constants.DimensionType.Pixels, 'ResolutionUnits': App.Constants.ResolutionUnits.PixelsPerIn, 'Resolution': 300, 'FillMaterial': { 'Color': (0,0,0), 'Pattern': None, 'Gradient': None, 'Texture': None }, 'Transparent': App.Constants.Boolean.true, 'VectorBackground': App.Constants.Boolean.false, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.Match } }) TargetDoc = App.ActiveDocument # now draw on it at the right and bottom edges using the foreground material App.Do( Environment, 'PaintBrush', { 'BrushTip': { 'BrushVariance': { 'SizeVariance': App.Constants.VarianceMethod.None, 'SizeJitter': 0, 'OpacityVariance': App.Constants.VarianceMethod.None, 'OpacityJitter': 0, 'DensityVariance': App.Constants.VarianceMethod.None, 'DensityJitter': 0, 'RotationVariance': App.Constants.VarianceMethod.None, 'RotationJitter': 0, 'ThicknessVariance': App.Constants.VarianceMethod.None, 'ThicknessJitter': 0, 'FadeRate': 100, 'ColorBlendVariance': App.Constants.VarianceMethod.None, 'ColorBlendJitter': 0, 'PositionJitter': 0, 'UseScaledPositionJitter': App.Constants.Boolean.false, 'ImpressionsPerStep': 1 }, 'Shape': App.Constants.BrushShape.Round, 'CustomBrush': None, 'Size': 1, 'Hardness': 100, 'Density': 100, 'Rotation': 0, 'Thickness': 100, 'Step': 25 }, 'Brush': { 'Opacity': 100, 'ContinuousPaint': App.Constants.Boolean.false, 'WetLookPaint': App.Constants.Boolean.false, 'BlendMode': App.Constants.BlendMode.Normal }, 'PrimaryMaterial': App.Constants.MaterialRef.Foreground, 'ForegroundMaterial': None, 'BackgroundMaterial': { # we don't use a background, but make sure it is not null 'Color': (0,0,0), 'Pattern': None, 'Gradient': None, 'Texture': None }, 'Stroke': [ (App.Constants.PathEntryInterpretation.Absolute,(0, YSpacing-1),0,1,0,1,0,1,0), (App.Constants.PathEntryInterpretation.Absolute,(XSpacing-1, YSpacing-1),0,1,0,1,0,1,0), (App.Constants.PathEntryInterpretation.Absolute,(XSpacing-1, 0),0,1,0,1,0,1,0) ], 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Default, 'AutoActionMode': App.Constants.AutoActionMode.Match } }, TargetDoc ) return TargetDoc def SavePatternFile( Environment, PatternName, Doc ): 'save the active image as a pattern file, using the specified name.' # we need to get the file locations info and look up the pattern directory Dirs = App.Do( Environment, 'ReturnFileLocations' ) PatternPath = Dirs['Patterns'][0] # data returned from ReturnFileLocations is in a form intended to be # script syntax, so all \ characters are doubled so that they don't # count as delimiters. In this case we are using them as python strings # and not running them back through the interpreter, so we turn all the # double backslashes into singles. PatternPath = string.replace( PatternPath, '\\\\', '\\' ) FullPath = PatternPath + '\\' + PatternName + '.PspImage' # save the file to the pattern directory App.Do( Environment, 'FileSaveAs', { 'Encoding': { 'PSP': { 'Compression': App.Constants.PspCompression.LZ77, 'Version': App.Constants.PspVersion.PSP8 } }, 'FileName': FullPath, 'FileFormat': App.Constants.FileFormat.PSP, 'FormatDesc': 'Paint Shop Pro Image', 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.AllAlways }, 'DefaultProperties': [] }, Doc ) # tell PSP we just invalidated the pattern cache App.Do( Environment, 'EventNotify', { 'EventType': App.Constants.Event.PatternCache } ) # don't need the pattern file anymore - we'll let the material palette load it for us App.Do( Environment, 'FileClose', { 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.Match } }, Doc) def Do(Environment): if JascUtils.RequireADoc( Environment ) == App.Constants.Boolean.false: return Target = App.TargetDocument ' Create a grid on a new layer, using spacing entered by the user' # we need a foreground material - error if we don't have one Foreground = App.Do( Environment, 'GetMaterial',{ 'IsPrimary': App.Constants.Boolean.true, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent }, }) if JascUtils.IsNullMaterial( Foreground ): App.Do(Environment, 'MsgBox', { 'Buttons': App.Constants.MsgButtons.OK, 'Icon': App.Constants.MsgIcons.Stop, 'Text': 'This script requires a non-null foreground material.', }) return # create the root TK window root = Tk() # create the dialog and show the dialog Dlg = GridMakerDialog( root, 'Enter Grid Spacing') # tell PSP that a foreign dialog is running. This causes PSP to do some additional # work to keep the UI updating properly and to prevent the script window from going # behind PSP. App.Do( Environment, 'StartForeignWindow', { 'WindowHandle': int(Dlg.winfo_id()) } ) root.mainloop() root.destroy() App.Do( Environment, 'StartForeignWindow', { 'WindowHandle': 0 } ) # if the user pressed cancel in the dialog just return if not Dlg.OKPressed: print 'Cancel pressed - aborting' return XSpacing = Dlg.XSpacing.get() YSpacing = Dlg.YSpacing.get() # since we are going to save the grid as a pattern file, lets try # to name it in a quasi-descriptive manner. We'll embed the grid # size and the material description in the name. PatternName = JascUtils.NameFromMaterial( Foreground ) PatternFile = 'Grid%dx%d_%s' % ( XSpacing, YSpacing, PatternName ) print 'Patternfile = ', PatternFile # Create the grid using the parameters we got from the dilaog PatternDoc = CreateGrid( Environment, XSpacing, YSpacing ) # save the grid as a pattern file SavePatternFile( Environment, PatternFile, PatternDoc ) # finally on to the heart of the matter - make a new layer, and then # flood fill it with the pattern we went to all the trouble to create. App.Do( Environment, 'NewRasterLayer', { 'General': { 'Name': 'Grid Overlay', }, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.Match } }, Target ) # now flood fill it with the grid we created App.Do( Environment, 'Fill', { 'BlendMode': 0, 'MatchMode': 1, 'Material': { 'Color': None, 'Pattern': { 'Name': PatternFile, 'Image': None, 'Angle': 0.000000, 'Scale': 100.000000 }, 'Gradient': None, 'Texture': None }, 'UseForground': App.Constants.Boolean.true, 'Opacity': 100, 'Point': (0,0), 'SampleMerged': 0, 'Tolerance': 20, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Default, 'AutoActionMode': App.Constants.AutoActionMode.Match } }, Target )