diff options
Diffstat (limited to 'uitools/Shells.py')
-rw-r--r-- | uitools/Shells.py | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/uitools/Shells.py b/uitools/Shells.py new file mode 100644 index 0000000..623deea --- /dev/null +++ b/uitools/Shells.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +###################################################################### +# This module provides base classes for various shell classes. +# +# Mitch Chapman +#--------------------------------------------------------------------- +# $Log: Shells.py,v $ +# Revision 1.1 1996/12/01 22:58:54 mchapman +# Initial revision +# +###################################################################### + +__version__ = "$Revision: 1.1 $" + +import Tkinter; Tk=Tkinter +import Cursors + +###################################################################### +# Make widget a transient for master. +# widget should be a Toplevel. +###################################################################### +def makeTransientFor(widget, master): + shell = master.winfo_toplevel() + name = shell.group() or "." + widget.group(name) + widget.transient(widget._nametowidget(name)) + +###################################################################### +# This is a Tk Frame which knows how to register itself for cursor +# stacking. It is intended to serve as the main window of an +# application. +###################################################################### +class Main(Tk.Frame, Cursors.Mixin): + def __init__(self, master=None, **kw): + apply(Tk.Frame.__init__, (self, master), kw) + apply(Cursors.Mixin.__init__, (self,)) + + def destroy(self): + Tk.Frame.destroy(self) + Cursors.Mixin.destroy(self) + + +###################################################################### +# This is a Tk Toplevel which knows how to register itself for cursor +# stacking. +###################################################################### +class Toplevel(Tk.Toplevel, Cursors.Mixin): + ################################################################## + # Initialize a new instance. + ################################################################## + def __init__(self, master=None, **kw): + apply(Tk.Toplevel.__init__, (self, master), kw) + apply(Cursors.Mixin.__init__, (self,)) + + ################################################################## + # Destroy an instance. + ################################################################## + def destroy(self): + Tk.Toplevel.destroy(self) + Cursors.Mixin.destroy(self) + + +###################################################################### +# This is a modal dialog class. +###################################################################### +class NonModal: + ################################################################## + # Initialize a new instance. master should be a widget in front + # of which self must appear. + ################################################################## + def __init__(self, master): + self.master = master + self.top = Toplevel(self.master) + self.top.withdraw() + self.isShowing = 0 + self.top.title(self.__class__.__name__) + self.waitVar = `self.top` + 'EndDialogVar' + + # Indicate that the dialog has not been centered in front of + # its master before. + self.isCentered = 0 + + # Modal dialogs may need to block input from other application + # windows by setting a grab on them. oldGrabber is where the + # previous owner of the app grab (if any) is saved. + self.oldGrabber = None + + # In case somebody manages to blow away self by underhanded + # means, stop waiting for waitVar. + self.top.protocol("WM_DELETE_WINDOW", self.deleteWindow) + + # A dialog has a result of arbitrary type. + # The result should be considered valid after the dialog is + # dismissed. + # Modal dialogs can use this to return arbitrary + # descriptions of what the user did. For example, a file- + # selection dialog might return either the pathname specified + # by the user or, if the dialog was cancelled, None. + self.result = None + + ################################################################## + # Default response to an attempt to delete the window: ignore it. + ################################################################## + def deleteWindow(self): + return + + ################################################################## + # Show a dialog. + ################################################################## + def show(self): + if self.isShowing: + # Dialog is already visible. Raise it to the top. + self.top.tkraise() + return + # Give us a chance to initialize contents. Other program + # state may have changed while the dialog was unmapped. + self.initControls() + + self.center() + # Remember the old grab owner, for modal subclasses. + self.oldGrabber = self.top.grab_current() + + self.top.deiconify() + + # Here's where modal subclasses get a chance to set a grab. + self.top.wait_visibility() + self.modalGrab() + + self.top.focus() + # Old results are invalid, now. + self.result = None + self.isShowing = 1 + + ################################################################## + # Initialize the contents of the dialog, e.g. put default values + # into text fields. + ################################################################## + def initControls(self): + return + + ################################################################## + # Center the dialog in front of its master, if possible. + ################################################################## + def center(self): + if self.isCentered: + # Center the dialog only the first time it is displayed. + # After that, just pop it up where the user left it. + return + + # Try to ensure any geometry requests have been processed. + self.top.update_idletasks() + + w = self.master.winfo_width() + h = self.master.winfo_height() + x = self.master.winfo_rootx() + y = self.master.winfo_rooty() + reqw = self.top.winfo_reqwidth() + reqh = self.top.winfo_reqheight() + + centerx = `x + (w - reqw)/2` + centery = `y + (h - reqh)/2` + geomStr = "+" + centerx + "+" + centery + self.top.geometry(geomStr) + self.isCentered = 1 + + ################################################################## + # This is a hook for modal dialog subclasses. It gives them a + # chance to do a grab_set on the dialog's Toplevel, to prevent + # input from being delivered to other application windows. + ################################################################## + def modalGrab(self): + return + + ################################################################## + # This is another hook for modal dialog subclasses. It allows + # for the release of any grab set via modalGrab. + ################################################################## + def modalReleaseGrab(self): + return + + ################################################################## + # Terminate the dialog, returning its result. + ################################################################## + def terminate(self): + # Set the wait variable, so modal dialogs will know they're + # done. + self.top.setvar(self.waitVar, 1) + self.top.withdraw() + self.isShowing = 0 + + # Here's where modal subclasses get a chance to release any + # grab they may have taken. + self.modalReleaseGrab() + + self.master.focus() + return self.result + + ################################################################## + # Destroy the visuals. + ################################################################## + def destroy(self): + self.top.destroy() + + +###################################################################### +# This class represents an application-modal dialog. +###################################################################### +class Modal(NonModal): + ################################################################## + # Initialize a new instance. + ################################################################## + def __init__(self, master): + apply(NonModal.__init__, (self, master)) + + # Make self a transient for its master. This basically forces + # self to remain in front of its master, and not to be + # separately iconifiable. (Depends on the window manager?) + # + # Note this isn't done for non-modal dialogs. Sorry, + # OpenWindows fans, but I can't stand having to slide + # non-modal dialogs out of the way just so I can see the + # master window... + makeTransientFor(self.top, master) + + # If somebody had an application-wide pointer grab, + # remember them here. When the dialog is dismissed, the + # grab will be returned to the previous owner. + self.oldGrabber = None + + ################################################################## + # Show a modal dialog. + ################################################################## + def show(self): + apply(NonModal.show, (self,)) + self.top.waitvar(self.waitVar) + return self.result + + ################################################################## + # Put an application-wide grab into effect, to block input from + # other application windows. + ################################################################## + def modalGrab(self): + # Display a do-not-enter cursor in front of all other toplevels + # in the application. + self.top.pushOtherCursors(Cursors.noEnterCursor) + self.top.grab_set() + + ################################################################## + # Release the grab obtained via modalGrab(). + ################################################################## + def modalReleaseGrab(self): + self.top.grab_release() + # If somebody else owned the grab before we did, give it + # back to them. + if self.oldGrabber and (self.oldGrabber != self.top): + self.oldGrabber.grab_set() + + self.top.popOtherCursors() + + +###################################################################### +# Main function for unit testing. +###################################################################### +def main(): + global f, nonmodal, modal + + # Display a non-modal dialog. + f = Main() + nonmodal = None + modal = None + + def showNonModal(event=None): + global f, nonmodal + + if not nonmodal: + nonmodal = NonModal(f) + label = Tk.Label(nonmodal.top, + text="This is a non-modal dialog.") + label.pack() + + def quitCB(event=None, dlg=nonmodal): + result = dlg.terminate() + print "Dialog terminated with result", `result` + btn = Tk.Button(nonmodal.top, text="OK", command=quitCB) + btn.pack() + + nonmodal.show() + + def showModal(event=None): + global f, modal + + if not modal: + modal = Modal(f) + label = Tk.Label(modal.top, text="This is a modal dialog.") + label.pack() + + def quitCB(event=None, dlg=modal): + result = dlg.terminate() + print "Dialog terminated with result", `result` + btn = Tk.Button(modal.top, text="OK", command=quitCB) + btn.pack() + + modal.show() + + for text, command in [("NonModal", showNonModal), + ("Modal", showModal), + ("Quit", f.quit)]: + b = Tk.Button(f, text=text, width=10, command=command) + b.pack() + + f.pack() + f.mainloop() + +if __name__ == "__main__": + main() |