diff options
author | Lars Wirzenius <liw@liw.fi> | 2019-11-02 10:54:13 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2019-11-02 10:54:13 +0200 |
commit | 2828885db093be86ef5b2c58f5c05ac3c4ed3664 (patch) | |
tree | 8185b82968b220aa543936e45be34fc239a5c8ea /uitools/TkDlgWrapper.py | |
download | slime-2828885db093be86ef5b2c58f5c05ac3c4ed3664.tar.gz |
Diffstat (limited to 'uitools/TkDlgWrapper.py')
-rw-r--r-- | uitools/TkDlgWrapper.py | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/uitools/TkDlgWrapper.py b/uitools/TkDlgWrapper.py new file mode 100644 index 0000000..54b2236 --- /dev/null +++ b/uitools/TkDlgWrapper.py @@ -0,0 +1,268 @@ +#!/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() |