summaryrefslogtreecommitdiff
path: root/uitools/Shells.py
diff options
context:
space:
mode:
Diffstat (limited to 'uitools/Shells.py')
-rw-r--r--uitools/Shells.py315
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()