home *** CD-ROM | disk | FTP | other *** search
Wrap
from JascApp import * from Tkinter import * from tkMessageBox import * from stat import * import os.path import os import sys import glob ScriptVersion = '1.1' # Revision log # 1.1 Added call to StartForeignWindow # 1.0 Removed the run and gui edit buttons, since there is a problem running nested Tk dialogs. # 0.3 Added word wrapping on the description, copyright and location fields. # Added ability to send the script to the GUI editor # 0.2 Fixed bug in rename. GetString was not using interactive mode. # Added ability to run either silently or interactively # 0.1 Initial # ScriptManager exists to help people manage and organize their scripts. # It provides the following functions: # 1. Displays all scripts found on the configured paths # 2. For the selected script, shows the author, copyright and description strings # 3. Allows Delete, Rename, Edit and Run of the selected script # 4. Can move a script to any configured directory # I use a tuple to encapsulate everything I know about a script: # 0 - script name # 1 - script path # 2 - 1 if trusted, 0 if restricted # 3 - dictionary returned by script properties, None if not initialized def ScriptProperties(): return { 'Author': 'Joe Fromm', 'Copyright': 'Copyright (C) 2002-2003, Jasc Software Inc., All Rights Reserved. Permission to create derivative works of this script is granted provided this copyright notice is included', 'Description': 'Manage existing script files - delete, rename, move, edit.', 'Host': 'Paint Shop Pro', 'Host Version': '8.00' } class ChooseDir(Toplevel): 'dialog class for selecting one of the defined file locations' def __init__(self, Current, Trusted, Restricted ): Toplevel.__init__(self) self.title( 'Move Script' ) # mark the current dialog as the foreign window again App.Do( GlobalEnv, 'StartForeignWindow', { 'WindowHandle': int(self.winfo_id()) } ) self.NewDir = Current # on exit, this will be set to the selected dir self.AllDirs = Restricted + Trusted # save the complete set so I can do the move later Label( self, text='Choose a directory to move to:' ).pack() ListFrame = Frame(self) ListFrame.pack(side=TOP, expand=YES,fill=BOTH) self.DirList = Listbox(ListFrame, height=10, width=75, selectmode=SINGLE, exportselection=FALSE) self.DirList.pack(side=LEFT, expand=YES, fill=BOTH) DirListScroll = Scrollbar(ListFrame, command = self.DirList.yview) DirListScroll.pack(side=LEFT, fill=Y) self.DirList.configure(yscrollcommand=DirListScroll.set) self.DirList.bind("<Button-1>", self.OnSelectDir) ButtonFrame = Frame( self ) ButtonFrame.pack( expand=YES, fill = X) Button(ButtonFrame, text='OK', width = 10, command=self.OnOK ).pack(side=LEFT, padx=10, pady=10) Button(ButtonFrame, text='Cancel', width= 10, command=self.OnCancel ).pack(side=RIGHT, padx=10, pady=10) self.index = 0 for dir in Restricted: text = 'Restricted: %s' % dir self.DirList.insert( END, text ) if dir == Current: self.index = self.DirList.size() - 1 for dir in Trusted: text = 'Trusted: %s' % dir self.DirList.insert( END, text ) if dir == Current: self.index = self.DirList.size() - 1 self.DirList.select_set(self.index) self.focus_set() self.grab_set() self.wait_window() def OnSelectDir( self, event ): 'just update the directory in the listbox' self.index = self.DirList.nearest(event.y) def OnOK( self ): 'on OK we record the selected dir in self.NewDir' self.NewDir = self.AllDirs[ self.index ] self.destroy() pass def OnCancel( self ): 'on cancel, just leave without setting a new dir' self.destroy() pass class ScriptManager(Frame): ''' Script manager dialog. Puts list of script names in the left column, with details and command buttons on the right hand side ''' def __init__(self, TrustedDirs, RestrictedDirs, AllScripts): Frame.__init__(self) self.pack(expand=YES, fill=BOTH) self.master.title('Script Manager Version %s' % ScriptVersion ) self.TrustedDirs = TrustedDirs self.RestrictedDirs = RestrictedDirs self.AllScripts = AllScripts self.index = 0 UpperFrame = Frame(self) UpperFrame.pack(side=TOP, expand=YES, fill=BOTH) # Add a label field telling the user what to do. Label(UpperFrame, text= 'Select a script to work with:', justify=LEFT).pack(side=TOP, expand=NO, anchor=W) # left side of the dialog is a listbox containing the scripts self.ScriptList = Listbox(UpperFrame, height=15, width=35, selectmode=SINGLE, exportselection=FALSE) self.ScriptList.pack(side=LEFT, expand=YES, fill=BOTH) ScriptListScroll = Scrollbar(UpperFrame, command = self.ScriptList.yview) ScriptListScroll.pack(side=LEFT, fill=Y) self.ScriptList.configure(yscrollcommand=ScriptListScroll.set) # The user can select a script by clicking on it self.ScriptList.bind("<Button-1>", self.OnSelectScript) # add command buttons CommandFrame = Frame(UpperFrame) CommandFrame.pack(side=RIGHT) Button(CommandFrame, text='Delete', command=self.OnDelete, width = 20 ).pack(side=TOP, padx=10, pady=5) Button(CommandFrame, text='Rename', command=self.OnRename, width = 20 ).pack(side=TOP, padx=10, pady=5) Button(CommandFrame, text='Move', command=self.OnMoveScript, width = 20 ).pack(side=TOP, padx=10, pady=5) ## Button(CommandFrame, text='Run Silent', command=self.OnRunSilent, width = 20 ).pack(side=TOP, padx=10, pady=5) ## Button(CommandFrame, text='Run Interactive', command=self.OnRunInteractive, width = 20 ).pack(side=TOP, padx=5, pady=5) Button(CommandFrame, text='Text Edit', command=self.OnSourceEdit, width = 20 ).pack(side=TOP, padx=10, pady=5) ## Button(CommandFrame, text='Edit w/PSP', command=self.OnGUIEdit, width = 20 ).pack(side=TOP, padx=10, pady=5) # Put the details on the bottom InfoFrame = Frame(self) InfoFrame.pack(side=TOP, anchor=W, expand=YES, fill=BOTH) self.Author = Label(InfoFrame, text='Author:', width=80, anchor=W) self.Author.pack(side=TOP, padx = 5, expand=YES, fill=X) self.Copyright = Label(InfoFrame, text='Copyright:', width = 80, height=2, anchor=W, justify=LEFT) self.Copyright.pack(side=TOP, padx = 5, expand=YES, fill=X) self.Description = Label(InfoFrame, text='Description:', width=80, height=2, anchor=W, justify=LEFT) self.Description.pack(side=TOP, padx = 5, expand=YES, fill=X) self.Location = Label( InfoFrame, text='Location:', width = 80, height=2,anchor=W, justify=LEFT) self.Location.pack(side=TOP, padx = 5, expand=YES, fill=X) self.Trusted = Label( InfoFrame, text='Trusted:', width = 80, anchor=W) self.Trusted.pack(side=TOP, padx = 5, expand=YES, fill=X) self.Description.bind("<Configure>", self.UpdateWidth) self.Copyright.bind("<Configure>", self.UpdateWidth) self.Location.bind("<Configure>", self.UpdateWidth) # now drop the names into the list box for Script in self.AllScripts: self.ScriptList.insert(END, Script[0]) # select the first entry self.ScriptList.select_set(0) self.SelectIndex(0) def UpdateWidth(self, e): 'Resize the message widgets based on the width of the window (thanks to Gary Barton)' WidgetWidth = e.widget.winfo_width() - 2*int(e.widget['bd']) e.widget.config(wraplength = WidgetWidth) def OnSelectScript(self, event): ' respond to a click event in the listbox - just determines the index and call SelectIndex' if self.ScriptList.size() > 0: # Find out which one is selected self.SelectIndex( self.ScriptList.nearest(event.y) ) def SelectIndex( self, index ): 'select a new script in the list box' self.index = index if self.AllScripts[index][3] is None: # lookup the properties if we don't have them already self.GetScriptProperties( index ) Selected = self.AllScripts[index] self.Author.config( text = 'Author: %s' % Selected[3]['Author'] ) self.Copyright.config( text = 'Copyright: %s' % Selected[3]['Copyright'] ) self.Description.config( text = 'Description: %s' % Selected[3]['Description'] ) self.Location.config( text = 'Location: %s' % Selected[1] ) self.Trusted.config( text= 'Trusted: %s' % ( ('No', 'Yes')[ Selected[2] ] ) ) def FullPath( self, index ): 'return the full path of a given index' Path = os.path.join( self.AllScripts[index][1], self.AllScripts[index][0] ) Path += '.PspScript' return Path def OnRename(self): ''' prompt for a new scriptname. I use the PSP GetString command just because it is easier to specify a default value that way ''' OldName = self.AllScripts[ self.index ][0] OldPath = self.FullPath( self.index ) Result = App.Do( GlobalEnv, 'GetString', { 'DefaultText': OldName, 'DialogTitle': 'Rename script:', 'Prompt': 'New name:', 'MaxLength': 80, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Interactive, 'AutoActionMode': App.Constants.AutoActionMode.Match } }) NewName = Result[ 'EnteredText' ] if Result[ 'OKButton' ] and NewName != OldName: # replace the name in our AllScripts list Current = self.AllScripts[self.index] NewScript = ( NewName, Current[1], Current[2], Current[3] ) self.AllScripts[ self.index ] = NewScript # replace the entry in the list box self.ScriptList.delete( self.index ) self.ScriptList.insert( self.index, NewName ) self.ScriptList.select_set(self.index) # finally rename the file on disk NewPath = os.path.join( self.AllScripts[self.index][1], NewName ) NewPath += '.PspScript' os.rename( OldPath, NewPath ) def OnDelete(self): 'delete the current selected script' # better confirm it index = self.index mode = os.stat( self.FullPath( index ) ) if mode[0] & S_IWRITE: MakeWriteable = 0 prompt = 'OK to delete script %s?' % self.AllScripts[index][0] else: MakeWriteable = 1 prompt = 'OK to delete readonly script %s?' % self.AllScripts[index][0] if askyesno( 'Delete Script', prompt ): # remove it from disk path = self.FullPath( index ) if MakeWriteable: os.chmod( path, S_IMODE(mode[0]) | S_IWRITE ) os.remove( path ) self.AllScripts.remove( self.AllScripts[index] ) # remove it from our list self.ScriptList.delete(index) # remove it from the list box index = max(0, index-1) # move the selection up self.SelectIndex( index ) # select the next script self.ScriptList.select_set(index) def OnMoveScript(self): ''' bring up a list box with all of the configured directories and let the user designate a new directory for the script ''' DirDlg = ChooseDir( self.AllScripts[ self.index ][1], self.TrustedDirs, self.RestrictedDirs ) # mark the current dialog as the foreign window again App.Do( GlobalEnv, 'StartForeignWindow', { 'WindowHandle': int(self.winfo_id()) } ) if DirDlg.NewDir != self.AllScripts[ self.index ][1]: # we are moving the file SrcPath = self.FullPath( self.index ) # where it starts DstPath = os.path.join( DirDlg.NewDir, self.AllScripts[ self.index ][0] ) # where it goes DstPath += '.PspScript' # update the list Current = self.AllScripts[self.index] NewScript = ( Current[0], DirDlg.NewDir, Current[2], Current[3] ) self.AllScripts[ self.index ] = NewScript os.rename( SrcPath, DstPath ) def OnSourceEdit(self): 'launch the configured source editor on the selected file' path = '"%s"' % self.FullPath( self.index ) os.spawnl( os.P_NOWAIT, SourceEditor, SourceEditor, path ) def OnGUIEdit(self): 'launch the PSP editor on the current file' App.Do( GlobalEnv, 'EditScript', { 'ScriptPath': self.FullPath( self.index ), 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.Match } }) def OnRunInteractive(self): ''' run the current script. This is not intended to be a good way to launch scripts, but having the option here makes it easier to determine if a script should be kept or deleted. This variant runs interactively. ''' App.Do( GlobalEnv, 'RunScript', { 'FileName': self.FullPath( self.index ), 'ScriptExecutionMode': App.Constants.ExecutionMode.Interactive, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.Match } }) def OnRunSilent(self): ''' run the current script. This is not intended to be a good way to launch scripts, but having the option here makes it easier to determine if a script should be kept or deleted. This variant runs silently. ''' App.Do( GlobalEnv, 'RunScript', { 'FileName': self.FullPath( self.index ), 'ScriptExecutionMode': App.Constants.ExecutionMode.Silent, 'GeneralSettings': { 'ExecutionMode': App.Constants.ExecutionMode.Silent, 'AutoActionMode': App.Constants.AutoActionMode.Match } }) def GetScriptProperties( self, index ): ''' open the script file from disk and extract the data from the script properties method. Updates the self.AllScripts member to include the properties ''' script = self.AllScripts[index] sname = self.FullPath( index ) file = open( sname, 'r' ) ScriptContents = '' lines = file.readlines() file.close() for line in lines: trimmed = line.rstrip() trimmed += '\n' ScriptContents += trimmed # load the script and try executing the script properties method try: gn = {} ln = {} Props = {} Props[ 'Author' ] = 'Unknown' Props[ 'Copyright' ] = 'Unknown' Props[ 'Description' ] = 'Unknown' exec( ScriptContents, gn, ln ) if ln.has_key( 'ScriptProperties' ): Props = ln[ 'ScriptProperties' ]() else: raise ValueError except: print 'Unable to determine script properties for %s' % script[0] NewEntry = ( script[0], script[1], script[2], Props ) self.AllScripts[index] = NewEntry def Do(Environment): global GlobalEnv global SourceEditor GlobalEnv = Environment # start by getting all the script directories. The behavior of the file locations object # is that for locations that include subdirectories it will return all of the subdirectories # as well as the parent location. So there is no need for us to search recursively. DirSet = App.Do( Environment, 'ReturnFileLocations' ) SourceEditor = DirSet[ 'PythonSourceEditor'][0].replace( '\\\\', '\\' ) TrustedDirs = [] RestrictedDirs = [] AllScripts = [] # the return value of Return file locations has double backslashes, so lets convert # them to singles for dir in DirSet[ 'TrustedScripts' ]: TrustedDirs.append( dir.replace( '\\\\', '\\' ) ) for dir in DirSet[ 'RestrictedScripts' ]: RestrictedDirs.append( dir.replace( '\\\\', '\\' ) ) AllDirs = TrustedDirs + RestrictedDirs for SearchDir in AllDirs: # concatenate the dir and file spec together SearchPath = os.path.join( SearchDir, '*.PspScript' ) # glob will search the directory and return a list GlobbedFiles = glob.glob( SearchPath ) for FileSpec in GlobbedFiles: # now iterate the list # break the full pathspec apart (Path, ScriptName) = os.path.split(FileSpec) BareName = os.path.splitext(ScriptName)[0] # keep track of what is restricted vs trusted if Path in TrustedDirs: Trusted = 1 else: Trusted = 0 # assemble what we know about the script so far ScriptInfo = ( BareName, Path, Trusted, None ) AllScripts.append( ScriptInfo ) # and add each one to our script list if len(AllScripts) == 0: # going to have a problem if there aren't any scripts App.Do(Environment, 'MsgBox', { 'Buttons': App.Constants.MsgButtons.OK, 'Icon': App.Constants.MsgIcons.Stop, 'Text': 'No scripts found to manage', }) return # sort the scripts by name AllScripts.sort( lambda a, b: cmp( a[0].lower(), b[0].lower() )) Mgr = ScriptManager( TrustedDirs, RestrictedDirs, AllScripts ) # 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(Mgr.winfo_id()) } ) Mgr.mainloop() App.Do( Environment, 'StartForeignWindow', { 'WindowHandle': 0 } )