#!/usr/bin/env python ###################################################################### # Try to access some of the newer Tk dialogs. # This module is inspired by the Python 1.[34] Dialog.py module. # # Mitch Chapman #--------------------------------------------------------------------- # $Log: TkDlgWrapper.py,v $ # Revision 1.1 1996/12/01 22:58:54 mchapman # Initial revision # ###################################################################### __version__ = "$Revision: 1.1 $" import Tkinter; Tk=Tkinter import os, copy VersionError = "VersionError" if Tk.TkVersion < 4.2: raise VersionError, "This module requires Tk 4.2 or greater." ###################################################################### # This is an abstract wrapper for all classes of built-in Tk dialogs. # It's subclassed from Tk.Widget to gain access to widget # configuration methods. ###################################################################### class T(Tk.Widget): ################################################################## # Initialize a new instance. ################################################################## def __init__(self, widgetName, master=None, **kw): self.widgetName = "__%s__" % widgetName # The unadorned widgetName is the Tk command/proc which # displays the dialog, e.g. tk_chooseColor self.tkCommand = widgetName self.master = master # Defer the actual widget initialization. I want instances # of T to persist even when they are not visible. For # example, this allows a file selection dialog to remember # what file extension it last selected. But in order to allow # persistence, the creation of the underlying Tk dialog must # be delayed. # Make a copy of the configuration, so it can persist. This # is where self remembers things such as the last file type # selected, last color used, etc. self.kw = copy.copy(kw) ################################################################## # Show the dialog. ################################################################## def show(self, **kw): # Use the configuration options to override the current config -- # and remember them for later. for k, v in kw.items(): self.kw[k] = v Tk.Widget._setup(self, self.master, self.kw) resultStr = apply(self.tk.call, (self.tkCommand,) + self._options(self.kw)) try: # Don't leak Python widget descriptors? Tk.Widget.destroy(self) except Tk.TclError: pass return resultStr ################################################################## # Explicitly destroy a widget instance. Nothing to do, here, # because the widget is gone as soon as show() returns. ################################################################## def destroy(self): pass ###################################################################### # Abstract wrapper for the Tk 4.2 tk_get\(Open\|Save\)File dialog. # Tries to remember the last-used directory, so it can be used as # the initial directory next time the dialog is displayed. ###################################################################### class OpenSaveFile(T): ################################################################## # Initialize a new instance. whichDlg tells whether this is # a tk_getOpenFile or a tk_getSaveFile, etc. ################################################################## def __init__(self, whichDlg, master=None, **kw): apply(T.__init__, (self, whichDlg, master), kw) self.filename = None ################################################################## # Show the dialog and return either the selected file or "". ################################################################## def show(self, **kw): self.filename = apply(T.show, (self,), kw) # Try to remember a few configuration items for the next time # the dialog is displayed. if self.filename: dirname, basename = os.path.split(self.filename) # Next time, start in the directory we ended in this time. self.kw['initialdir'] = dirname # Try to figure out what extension to use next time. ext = os.path.splitext(basename)[1] self.kw['defaultextension'] = "%s" % ext # Try to specify the default name to use next time. self.kw['initialfile'] = basename # The Tk dialogs are a little stupid: Even if you specify # an initial file with extension ".txt", the dialog will show # only files matching the first set of extensions in the # -filetypes option. # My cheap workaround is to create a new first entry for # -filetypes which matches the extension selected this # time. oldtypes = () if self.kw.has_key('filetypes'): oldtypes = self.kw['filetypes'] # The following works only for Unix (and maybe Windows). # Somebody wanna fix this for handling Mac file types? newtypes = () dfltExt = ext or "*" dfltDescription = "(Last Selected)" for typespec in oldtypes: description, types = typespec if description == dfltDescription: newtypes = ((dfltDescription, dfltExt),) + oldtypes[1:] break # EXIT FOR LOOP else: newtypes = ((dfltDescription, dfltExt),) + oldtypes self.kw['filetypes'] = newtypes # The Tk dialogs are a little smart: If you create both an # open and a save dialog in the same application, they seem # to both slave to the same working directory. Change to dir # "/blah" when opening? Then when you try to save, the # default displayed directory will be "/blah". # This may be a bug, given what the man pages say, but # I like it. return self.filename ###################################################################### # Wrapper for the tk_getOpenFile dialog. ###################################################################### class OpenFile(OpenSaveFile): def __init__(self, master=None, **kw): apply(OpenSaveFile.__init__, (self, "tk_getOpenFile", master), kw) ###################################################################### # Wrapper for the tk_getSaveFile dialog. ###################################################################### class SaveFile(OpenSaveFile): def __init__(self, master=None, **kw): apply(OpenSaveFile.__init__, (self, "tk_getSaveFile", master), kw) ###################################################################### # Wrapper for the tk_chooseColor dialog. ###################################################################### class ChooseColor(T): ################################################################## # Initialize a new instance. ################################################################## def __init__(self, master=None, **kw): apply(T.__init__, (self, "tk_chooseColor", master), kw) ###################################################################### # Wrapper for the tk_messageBox dialog. ###################################################################### class MessageBox(T): ################################################################## # Initialize a new instance. ################################################################## def __init__(self, master=None, defaultCfg=None, **kw): cfg = Tk._cnfmerge((defaultCfg or {}, kw)) apply(T.__init__, (self, "tk_messageBox", master), cfg) ###################################################################### # These are the specific types of MessageBox dialogs provided by Tk. # For each of these classes, the return value of show() is a string # containing the label of the button which was pressed. # # Yeah, I know, these aren't particularly useful when you can roll # your own much more beautiful dialogs in Python. But hey! They're # here and they work, and they're supposed to have native look-n-feel # on every supported platform. ###################################################################### class AbortRetryIgnore(MessageBox): def __init__(self, master=None, **kw): apply(MessageBox.__init__, (self, master, {'icon':'error', 'type':'abortretryignore'}), kw) class OK(MessageBox): def __init__(self, master=None, **kw): apply(MessageBox.__init__, (self, master, {'type':'ok'}), kw) class OKCancel(MessageBox): def __init__(self, master=None, **kw): apply(MessageBox.__init__, (self, master, {'type':'okcancel'}), kw) class RetryCancel(MessageBox): def __init__(self, master=None, **kw): apply(MessageBox.__init__, (self, master, {'icon':'error', 'type':'retrycancel'}), kw) class YesNo(MessageBox): def __init__(self, master=None, **kw): apply(MessageBox.__init__, (self, master, {'icon':'question', 'type':'yesno'}), kw) class YesNoCancel(MessageBox): def __init__(self, master=None, **kw): apply(MessageBox.__init__, (self, master, {'icon':'question', 'type':'yesnocancel'}), kw) ###################################################################### # Don't wrap tk_dialog -- that's already done in Dialog.py. ###################################################################### ###################################################################### # Main function for unit testing. ###################################################################### def main(): # Demo all of the message box dialog classes: for c in [AbortRetryIgnore, OK, OKCancel, RetryCancel, YesNo, YesNoCancel]: d = c(title=c.__name__) print d.show(message="This is an instance of %s." % c.__name__) # Demo the color chooser dialog: d = ChooseColor(title="PickaKuhla", initialcolor="SlateGray") color = d.show() print "Selected color is", `color` # Demo the open file dialog. d = OpenFile(defaultextension=".py", filetypes=(("Python", ".py"), ("Emacs Lisp", ".el"), ("Text", ".txt"), ("All Files", "*"))) # Run multiple times to demonstrate how the dialog remembers the # last working directory, file extension, etc. for i in range(2): filename = d.show() print "Open file", `d.filename` print 72 * "_" print "Current working directory is", os.getcwd() d = SaveFile(defaultextension=".py", filetypes=(("Pithon [er, Python]", ".py"), ("Text", ".txt"), ("All Files", "*"))) for i in range(2): filename = d.show() print "Save File As", `d.filename` if __name__ == "__main__": main()