#!/usr/local/bin/pythono """ Special PoserPython edition of Poser Compressor script. This will not run fully in Python 2.4/Tk 8.4. There seems to have been some change in the way tuples and strings are handled, somehow. It runs on P5 PoserPython, however. Differences between this and Python 2.4 version: - This uses string, rather than tuple, handling for reassignment of the listbox contents in handleGo. (Thanks to Tromnek for pointing me to that.) - This uses askopenfilename for single file browsing, rather than the more flexible askopenfilenames. PoserPython's version of the TkFileDialog.py does not include askopenfilenames. - Changed the handling of string.lstrip for the add box entries, to remove the chars option. I am not sure what version of Python you need to run in order to use this from the desktop, with Tk. I can tell you that it is not version 2.4. This script is an adaptation of the compression and decompression scripts which shipped with Poser 5. I have made use of Tromnek's altered version of the Poser 5 compression script, which can be found in the archives at the Renderosity Python forum. Other people have done the hard work (and the more effective coding, I'm sure). I have tried to implement the compression functions in ways that are more flexible and easier to use. Mainly that means I slapped a GUI on it. :) I have tried to annotate my changes to the scipts from which this is derived. You should assume that any errors are mine, not those of Tromnek or the original programmer of the script. This script allows you to browse for single files or entire folders to process using PoserPython's Gzip compression. You can also select an option to process all subfolders, as with the default Poser 5 scripts. This script also allows control over the file types which will be processed, without the need to edit the script to change the file tables. Run the program and press "Help" to get basic instructions. This is untested on Macintosh. The original scripts are set up for Mac compatability, but there is no way for me to determine whether I may have broken Mac functionality, or failed to actually implement my new functions for the Mac. The Mac code has been retained untouched, although it has been moved around together with its surrounding code. Once again, thanks to Tromnek. Cagedrei@aol.com """ from Tkinter import * import sys import tkFileDialog import gzip import string import os import tkMessageBox chunksize = 4096 fileToCompress = 0 fileToUncompress = 0 deleteOriginals = 0 onameC = 0 onameU = 0 proceed = 0 message = 0 AddTo = " " AddTo2 = " " addElem = " " KillVar = 0 root = Tk() #-------------------------------------------------------------------------------- # Suffixes. #-------------------------------------------------------------------------------- suffixTableC = { '.pz3' : '.pzz', '.pz2' : '.p2z', '.cr2' : '.crz', '.cm2' : '.cmz', '.lt2' : '.ltz', '.hd2' : '.hdz', '.hr2' : '.hrz', '.pp2' : '.ppz', '.mt5' : '.mz5', '.fc2' : '.fcz', } suffixTableU = { '.pzz' : '.pz3', '.p2z' : '.pz2', '.crz' : '.cr2', '.cmz' : '.cm2', '.ltz' : '.lt2', '.hdz' : '.hd2', '.hrz' : '.hr2', '.ppz' : '.pp2', '.mz5' : '.mt5', '.fcz' : '.fc2', } openListC = '*.pz3 *.pz2 *.cr2 *.cm2 *.lt2 *.hd2 *.hr2 *.pp2 *.mt5 *.fc2' openListU = '*.pzz *.p2z *.crz *.cmz *.ltz *.hdz *.ppz *.mz5 *.fcz' #-------------------------------------------------------------------------------- # Figure out which OS we're running #-------------------------------------------------------------------------------- macver = 0 winver = 0 if(os.name == 'mac'): macver = 1 separator = ':' elif(os.name == 'dos' or os.name == 'nt'): winver = 1 separator = '\\' else: raise 'unrecognized operating system\n' if(macver): #---------------------------------------------------------------------------- # Keep a table for type #---------------------------------------------------------------------------- typeTable = { '.pzz' : 'PZZ ', '.p2z' : 'pz2Z', '.crz' : 'cr2Z', '.cmz' : 'cm2Z', '.ltz' : 'lt2Z', '.hdz' : 'hd2Z', '.hrz' : 'fc2Z', '.ppz' : 'pp2Z', '.mz5' : 'mt5Z', '.fcz' : 'fc2Z', '.pz3':'PZ3 ', '.pz2':'pz2 ', '.cr2':'cr2 ', '.cm2':'cm2 ', '.lt2':'lt2 ', '.hd2':'hd2 ', '.hr2':'fc2 ', '.pp2':'pp2 ', '.mt5':'mt5 ', '.fc2':'fc2 ' } #-------------------------------------------------------------------------------- def Stoppit(KillVar): ##Loop killer for recursion if KillVar ==1: #Without this it can go berserk and start dangerously tearing through the hard drive.... root.destroy()#Mainly a problem when "cancel" is pressed when selecting a directory.... #----------------------------------------------------------------------------------------- def compressFile(fileToCompress, suffix, newSuffix, deleteOriginals): #P5 and Tromnek #if(string.find(fileToCompress, 'Default Guy.cr2') != -1): if(string.find(string.lower(fileToCompress), 'default guy.cr2') != -1): #TromNek case insensitive test return 0 newFileName = fileToCompress #extIndex = string.find(fileToCompress, suffix) extIndex = string.rfind(string.lower(fileToCompress), suffix) #TromNek, a more exact file extension search #if (extIndex == -1): if ( (extIndex + len(suffix)) != len(fileToCompress)): #TromNek, is it really the extension return -1 else: newFileName = newFileName[:extIndex] + newSuffix # change .*z3 to .*zz try: pz3 = open(fileToCompress, 'rb') except: return -2 try: #TromNek open a file object first pzzfo = open(newFileName, 'w+b') pzzfo.truncate(0) except: return -2 #pzz = gzip.GzipFile(newFileName, 'wb') pzz = gzip.GzipFile(fileobj=pzzfo, mode='wb') #TromNek pass gzip the file object instead of the name while 1: chunk = pz3.read(chunksize) # do the compression if(chunk == ""): break pzz.write(chunk) pz3.close() pzz.close() #TromNek next 13 lines hdrNameOffset = 10 # If there's a file name, where is is pzzfo.seek(hdrNameOffset + extIndex,0) # Seek the beginning of the file extension chunk = pzzfo.read( len(suffix) ) if chunk == newSuffix: # Make extra sure we want to do this pzzfo.seek(hdrNameOffset + extIndex,0) # Seek the beginning of the file extension, again pzzfo.write( suffix ) else: print "!!! Couldn't find file extension in header !!!" import struct pzzfo.seek( -4, 2 ) # original file size stored by GzipFile origfsize = struct.unpack("', newFileName if(deleteOriginals == 1): import os if os.stat(fileToCompress)[6] != origfsize: #TromNek minimum sanity check before deletion print '!!!Original file size does not equal value stored in gzip file!!! NOT DELETING ORIGINAL !!! ', os.stat(fileToCompress)[6], " != ",origfsize else: try: os.remove(fileToCompress) print ' (', fileToCompress, 'deleted)' except: print ' (Could not delete', fileToCompress print ' Permissions set to Read-Only?' #------------------------------------------------------------------------------------------------------ def uncompressFile(fileToUnCompress, suffix, newSuffix, deleteOriginals): #P5 and Tromnek if(string.find(fileToUnCompress, 'Default Guy.cr2') != -1): #if(string.find(string.lower(fileToCompress), 'default guy.cr2') != -1): #TromNek case insensitive test return 0 newFileName = fileToUnCompress extIndex = string.find(fileToUnCompress, suffix) #extIndex = string.rfind(string.lower(fileToCompress), suffix) #TromNek, a more exact file extension search if (extIndex == -1): #if ( (extIndex + len(suffix)) != len(fileToCompress)): #TromNek, is it really the extension return -1 else: newFileName = newFileName[:extIndex] + newSuffix # change .*z3 to .*zz try: pzz = gzip.GzipFile(fileToUnCompress, 'rb') except: return -2 pz3 = open(newFileName, 'wb') while 1: chunk = pzz.read(chunksize) # do the uncompression if(chunk == ""): break pz3.write(chunk) pz3.close() pzz.close() if(macver): # for mac version, we need to change file's import macfs # type and creator fs = macfs.FSSpec(newFileName) fs.SetCreatorType('PZ3A', typeTable[newSuffix]) print ' *** ', fileToUnCompress, ' ***' print ' -->', newFileName if(deleteOriginals == 1): import os try: os.remove(fileToUnCompress) print ' (', fileToUnCompress, 'deleted)' except: print ' (Could not delete', fileToUnCompress print ' Permissions set to Read-Only?' #------------------------------------------------------------------------------------------------ def visitC(something, dirname, names): #P5, Tromnek, and "but it's my only line!" if (recursive == 1) and (KillVar == 0): if fileToUncompress == 0: print 'Looking under directory:', dirname, '------' for name in names: for key in suffixTableC.keys(): #if (string.find(name, key) != -1): extIndex = string.rfind(string.lower(name), key ) #TromNek make sure it really is the extension. if ( (extIndex + len(key)) == len(name)): compressFile(dirname + separator + name, key, suffixTableC[key], deleteOriginals) break print from Tkinter import * #------------------------------------------------------------------------------------------ def visitU(something, dirname, names): #P5 and Tromnek w/ one line added by Cage if (recursive == 1) and (KillVar == 0): if fileToCompress == 0: print 'Looking under directory:', dirname, '------' for name in names: for key in suffixTableU.keys(): #if (string.find(name, key) != -1): extIndex = string.rfind(string.lower(name), key ) #TromNek make sure it really is the extension. if ( (extIndex + len(key)) == len(name)): uncompressFile(dirname + separator + name, key, suffixTableU[key], deleteOriginals) break print from Tkinter import * #----------------------------------------------------------------------------------------------- #Ockham likes to point out that this part is Tkinter, so: this part is Tkinter. Except for the middle. Well, some. A bit. class App: def __init__(self, master, textMessage): global recurse, recursive, deleteOriginals, custUse recurse = IntVar() recurse.set(0) recursive = IntVar() recursive.set(0) custUse = IntVar() custUse.set(0) self.safety = 1 #To prevent "custom file list" checkbutton from looping at start self.refresh = 0 #To enable custom file list refresh w/ open buttons self.master = master master.title("Poser Compressor") self.ButtonFrame = Frame(self.master,borderwidth=2,relief=RIDGE) self.ButtonFrame.grid(row=0,column=2) self.ListFrame = Frame(self.master,borderwidth=2,relief=RIDGE) self.ListFrame.grid(row = 0, column = 0) self.Label = Label(self.ListFrame,text="File Types:").grid(row=1, column=1) #Listbox self.ListScroll = Scrollbar(self.ListFrame, orient=VERTICAL) self.ListScroll.grid( row=2, column=0,sticky=N+S+E) self.List = Listbox(self.ListFrame, height=20, width=10,selectmode=MULTIPLE,yscrollcommand = self.ListScroll.set) self.List.grid( row=2, column=1) self.ListScroll["command"] = self.List.yview TypeList = dict.items(suffixTableC) #Fill the listbox from the compress table for i in TypeList: self.List.insert(END,i) #Buttons self.Label = Label(self.ButtonFrame,text="Browse For Files:").grid(row=0, column=0) self.Label = Label(self.ButtonFrame,text="Browse for a single file,").grid(row=2,column=0) self.Label = Label(self.ButtonFrame,text="or select checkbuttons to process").grid(row=3,column=0) self.Label = Label(self.ButtonFrame,text="entire folders and subfolders.").grid(row=4,column=0) self.buttonCompress = Button(self.ButtonFrame, text="Compress", command=self.openfileC) self.buttonCompress.grid(row = 10, column = 0) self.buttonUncompress = Button(self.ButtonFrame, text="Uncompress", command=self.openfileU) self.buttonUncompress.grid(row = 20, column = 0) self.buttonAddFile = Button(self.ButtonFrame, text="Add File Type", command=self.AddFile) self.buttonAddFile.grid(row = 30, column = 0) self.buttonClose = Button(self.ButtonFrame, text="Close", command=self.die) self.buttonClose.grid(row = 70, column = 0) self.buttonHelp = Button(self.ButtonFrame, text="Help", command=self.HelpMe) self.buttonHelp.grid(row=60, column=0) self.List.bind("",self.handleSelect) #Checkboxes self.deleteOrig = Checkbutton(self.ButtonFrame,text="Delete Original Files After Compressing", variable=deleteOriginals, command=self.delFiles) self.deleteOrig.grid(row = 35, column = 0) self.EntireFolder = Checkbutton(self.ButtonFrame,text="Process Entire Folder", variable=recurse, command=self.openfileR) self.EntireFolder.grid(row = 40, column = 0) self.Subfolders = Checkbutton(self.ButtonFrame, text="Include Subfolders", variable=recursive, command=self.openfileS) self.Subfolders.grid(row=45, column = 0) self.UseCustom = Checkbutton(self.ButtonFrame,text="Use Only Selected File Types", variable=custUse, command=self.handleCustom) self.UseCustom.grid(row=50, column = 0) self.Label = Label(self.master,text="Double-click to remove file types").grid(row = 2, column = 0) self.openfileR() #Priming the system to fend off IntVar value problems self.openfileS() #Emulate first clicks of checkbuttons self.handleCustom() #Okay, so it's a kludgy workaround.... def handleSelect(self,event): self.List.delete(ACTIVE) #Delete listbox entries w/ double-click def HelpMe(self): tkMessageBox.showinfo("Instructions:","-Browse for single files by pressing 'Compress' or 'Uncompress'.\012" "-Browse for a folder by selecting 'Process Entire Folder' before pressing 'Compress'\012" " or 'Uncompress'.\012" "-Select 'Include Subfolders' together with 'Process Entire Folder' to include all subfolders.\012" "-Toggle 'Delete Original Files After Compressing' to delete or retain originals.\012" "-Highlight file types in the 'File Types' list and select 'Use Only Selected File Types' to\012" " specify file types to compress or uncompress.\012" "-Press 'Add File Type' to add a custom file type to the list.\012" "-Double click on a file type to remove it from the list.") def handleGo(self): #handleGo converts listbox info to file table info global openListC, openListU q = " " p = " " Selecteds = self.List.curselection() #You can see, here, that I have been learning from Ockham's code.... #Sels = self.List.get(ACTIVE) #Hopefully you can't copyright variable names.... :) NumSel = len(Selecteds) if NumSel != 0: for i in Selecteds: Name = self.List.get(i) q,p = string.split(Name) #PoserPython handling. Thanks, Tromnek. suffixTableC[q] = p #Re-fill the suffix tables suffixTableU[p] = q openListC = " " #Make sure file open lists are emptied openListU = " " for i in Selecteds: Name = self.List.get(i) #Repeat loop to handle open file lists (All of this can be in one "for" loop....) q,p = string.split(Name) q = "*" + q p = "*" + p openListC = (openListC + " " + q) #Re-fill the file open lists openListU = (openListU + " " + p) def die(self): #Game Over self.master.destroy() self.master.quit() def handleCustom(self): #handleCustom handles user interaction w/ listbox global custUse, openListC, openListU, backupC, backupU, openCback, openUback if self.refresh == 0: if custUse ==0: custUse = 1 else: custUse = 0 if self.refresh == 1: #Switch to allow compress and uncompress buttons to update the file self.refresh = 0 #tables if custom file use is selected. if custUse == 1: NumSel = 0 if self.safety == 1: backupC = dict.copy(suffixTableC) #Back up original file tables only at start backupU = dict.copy(suffixTableU) openCback = openListC openUback = openListU Selecteds = self.List.curselection() NumSel = len(Selecteds) #Don't empty the file tables when there's nothing selected to replace them. if NumSel != 0: dict.clear(suffixTableC) #Empty the file tables each time dict.clear(suffixTableU) openListC = " " #Clear the open file lists each time openListU = " " self.safety = 0 #A switch to prevent zero looping and to create backups only at start self.handleGo() if (custUse == 0) and (self.safety ==0): #When custom file types checkbox is unchecked dict.clear(suffixTableC) suffixTableC.update(backupC) #Restore old file tables from backup dict.clear(suffixTableU) suffixTableU.update(backupU) openListC = openCback #Restore old file open lists from backup openListU = openUback def openfileR(self): #Switch for "entire folder" handling. Bad variable name. global recurse if recurse == 0: recurse = 1 else: recurse = 0 def openfileS(self): #Switch for recursive folder handling. global recursive if recursive == 0: recursive = 1 else: recursive = 0 def delFiles(self): #Switch for file delete option. global deleteOriginals if deleteOriginals == 0: deleteOriginals = 1 else: deleteOriginals = 0 def AddFile(self): #Part 1 of Addfile box function. global AddTo, root #Part 2 is a separate Class, below. d = self.MyDialog(root,AddTo) root.wait_window(d.top) self.boxAdd(addElem) def boxAdd(self,addElem): #Part 3 of Addfile box. global AddTo addElem = AddTo AddTo = " " if addElem != " ": self.List.insert(END,addElem) #----------------------------------------------------------------------------------------- def openfileC(self): #Compression operations. This is my part. You can tell, because it's kludgy. :) global message, names, dirname, listed, recurse, recursive, KillVar if custUse == 1: self.refresh = 1 self.handleCustom() if recurse != 1: recurse = 0 if recursive != 1: recursive = 0 if recurse == 0: onameC = tkFileDialog.askopenfilename(filetypes=[("All Associated Files", openListC)]) if recurse == 1: message = "" message = tkFileDialog.askdirectory() print message if message != "": dirname = message listed = os.listdir (message) if recursive == 0: #I suspect some of this may not be necessary.... names = listed if recursive == 1: if message != 0: dirname = message names = os.listdir (dirname) for i in names : onameC = i if onameC: proceed = 1 print onameC fileToCompress = onameC #---------------------------------------------------------------------------------------------- if recurse == 0: #This part is P5 original, lightly edited. for key in suffixTableC.keys(): if (string.find(fileToCompress, key) != -1): compressFile(fileToCompress, key, suffixTableC[key], deleteOriginals) break #--------------------------------------------------------------------------------------------------- elif (recurse ==1) and (recursive ==1): #P5 original, as altered by Tromnek, plus my conditional at top i = 0 for key in suffixTableC.keys(): if (i==0): message = message + key else: message = message + ', ' + key i = i + 1 try: #TromNek this allows us to run the script outside of poser initDir = poser.AppLocation() # <-this line was in the original script except: initDir = os.getcwd() # we must be running in native python fileIndex = string.rfind(initDir, separator) initDir = initDir[:fileIndex] dirToCompress = dirname #(proceed, dirToCompress, deleteOriginals) = message if proceed == 0: raise 'Script cancelled' print 'Recursing from ', dirToCompress, '\n\n' arg = None #TromNek next 15 lines reldir = separator+'Runtime'+separator # get path starting with first 'Runtime' folder #reldir = '' # or not dirToCompress = os.path.normpath( dirToCompress ) begpth = string.find( string.lower(dirToCompress+separator), string.lower(reldir) ) if (begpth < 1): begpth = 0 if ((winver==1) and (dirToCompress[1]==':')): begpth = 2 while (begpth < len(dirToCompress)) and (dirToCompress[begpth] == separator): begpth = begpth+1 # skip over path separators to get to the path if begpth > 0: os.chdir( dirToCompress[:begpth] ) # change to drive and initial folder of dirToCompress dirToCompress = dirToCompress[begpth:] # strip 'driveletter:\' and initial folder from dirToCompress os.path.walk(dirToCompress, visitC, arg) message = 0 print "Compression complete" #KillVar = 1 Stoppit(1) #This is Cage's, too. Keep recusrion from running amok. #----------------------------------------------------------------------------------------------- elif (recurse == 1) and (recursive==0): #P5 and Tromnek. print 'Looking under directory:', dirname, '------' for name in names: for key in suffixTableC.keys(): #if (string.find(name, key) != -1): extIndex = string.rfind(string.lower(name), key ) #TromNek make sure it really is the extension. if ( (extIndex + len(key)) == len(name)): compressFile(dirname + separator + name, key, suffixTableC[key], deleteOriginals) break #------------------------------------------------------------------------------------------------- def openfileU(self): #Uncompression functions. This is my part. global recurse, message, names, dirname, recursive, KillVar if custUse == 1: self.refresh = 1 self.handleCustom() if recurse != 1: recurse = 0 if recursive != 1: recursive = 0 if recurse == 0: onameU = tkFileDialog.askopenfilename(filetypes=[("All Associated Files", openListU)]) if recurse == 1: message = "" message = tkFileDialog.askdirectory() print len(message) if message != "": dirname = message listed = os.listdir (message) if recursive == 0: names = listed if recursive == 1: if message != 0: dirname = message names = os.listdir (dirname) for i in names : onameU = i if onameU: proceed = 1 print onameU fileToUncompress = onameU #--------------------------------------------------------------------------------------- if recurse == 0: #P5 original w/ minor alterations. for key in suffixTableU.keys(): if (string.find(fileToUncompress, key) != -1): uncompressFile(fileToUncompress, key, suffixTableU[key], deleteOriginals) break #----------------------------------------------------------------------------------------- elif (recurse ==1) and (recursive ==1): #P5 and Tromnek. i = 0 for key in suffixTableU.keys(): if (i==0): message = message + key else: message = message + ', ' + key i = i + 1 try: #TromNek this allows us to run the script outside of poser initDir = poser.AppLocation() # <-this line was in the original script except: initDir = os.getcwd() # we must be running in native python #initDir = poser.AppLocation() fileIndex = string.rfind(initDir, separator) initDir = initDir[:fileIndex] dirToUnCompress = dirname #(proceed, dirToUnCompress, deleteOriginals) = dialog(message, initDir) if proceed == 0: raise 'Script cancelled' print 'Recursing from ', dirToUnCompress, '\n\n' arg = None os.path.walk(dirToUnCompress, visitU, arg) message = 0 print "Uncompression complete" #KillVar = 1 Stoppit(1) #----------------------------------------------------------------------------------------- elif (recurse == 1) and (recursive == 0): #P5 and Tromnek. print 'Looking under directory:', dirname, '------' for name in names: for key in suffixTableU.keys(): #if (string.find(name, key) != -1): extIndex = string.rfind(string.lower(name), key ) #TromNek make sure it really is the extension. if ( (extIndex + len(key)) == len(name)): uncompressFile(dirname + separator + name, key, suffixTableU[key], deleteOriginals) break #----------------------------------------------------------------------------------------------- class MyDialog: #Part 3 of the Addfile box function. def __init__(self,parent,txt): top = self.top = Toplevel(parent) Label(top,text="Enter New Extensions").pack() self.e = Entry(top) self.e.insert(0,txt) self.e.pack(padx=5) Label(top,text="uncompressed extension").pack() self.e2 =Entry(top) self.e2.insert(10,txt) self.e2.pack(padx=5) Label(top,text="compressed extension").pack() b = Button(top, text="OK", command=self.ok) b.pack(pady=5) b2 = Button(top, text="Cancel", command=self.cancel) b2.pack(pady=5) def ok(self): global AddTo, root, parent fixit = self.e.get() fixit = string.lstrip(fixit) fixit2 = self.e2.get() fixit2 = string.lstrip(fixit2) nAddTo = ((fixit) ,(fixit2)) self.e = " " #Clear the boxes for the next entry self.e2 = " " if nAddTo and nAddTo != " ": AddTo = nAddTo nAddTo = " " self.top.destroy() if nAddTo == " ": self.top.destroy() def cancel(self): self.e = " " self.e2 = " " AddTo = " " nAddTo = " " self.top.destroy() #-------------------------------------------------------------------------------------------- app = App(root, "") root.mainloop() #--------------------------------------------------------------------------