RFC: tkSimpleDialog IMPROVED AGAIN!.

rantingrick rantingrick at gmail.com
Sun Jul 3 19:08:35 EDT 2011


Sorry folks. I found a few bugs in that pasted code of the new and
improved tkSimpleDialog. I believe most here could debug it however
just in case any newbies are watching i want the code to execute
without error.


## START CODE ##
import Tkinter as tk

MESSAGE = """
You are following bad design patterns.
NO SOUP FOR YOU!
"""

class Dialog(tk.Toplevel):
    def __init__(self, parent, title='Dialog'):
        if not isinstance(parent, (tk.Tk, tk.Toplevel)):
            raise Exception(MESSAGE)
        """
        Initialize a dialog;
            parent:
                A Tkinter.Toplevel instance.
            title:
                The dialog's title as a string.
        """
        tk.Toplevel.__init__(self, parent)
        self.withdraw()
        if parent.winfo_viewable():
            # If the parent window is not viewable, don't
            # make the child transient, or else it would be
            # opened withdrawn!
            self.transient(parent)
        if title:
            self.title(title)
        self.parent = parent
        # self.result is where we store any values for return to
        # the caller.
        self.result = None
        # We save the dialog's main widget frame just incase a
        # caller needs to access it later.
        self.frame = tk.Frame(self)
        # Next we call self.body to create the body of the dialog.
        # We save the return value as "self.initial_focus" so we
        # give it focus later.
        self.initial_focus = self.body(self.frame)
        self.frame.pack(padx=5, pady=5)
        self.buttonbox()

    def show(self):
        # XXX: Need "show" and "show_modal" methods!
        self.deiconify()
        # The window needs to be visible for the grab so we
        # call wait_visibility and let Tkinter do the waiting
        # for us.
        self.wait_visibility()
        self.grab_set()
        if not self.initial_focus:
            self.initial_focus = self
        self.protocol("WM_DELETE_WINDOW", self.cancel)
        if self.parent is not None:
            self.geometry(
                "+%d+%d" %(
                    self.parent.winfo_rootx()+50,
                    self.parent.winfo_rooty()+50)
                )
        self.initial_focus.focus_set()
        self.wait_window(self)
        return self.result

    def destroy(self):
        """Destroy the window."""
        self.initial_focus = None
        tk.Toplevel.destroy(self)
    #
    # Construction Hooks.
    #

    def body(self, master):
        # XXX: create_body
        """
        OVERIDE: Create dialog body and return widget that
        should have initial focus.
        """
        pass

    def buttonbox(self):
        # XXX: create_buttonbox
        # XXX: Overiding this method can break funcionality if
        #      you do not properly bind the ok and cancel buttons
        #      correctly!
        # RFC: Should we create a generic button creation method
        #      or a mixin?
        """
        Add a standard button box;
        Override if you do not want differnt buttons.
        """
        box = tk.Frame(self)
        w = tk.Button(
            box,
            text="OK",
            width=10,
            command=self.ok,
            default=tk.ACTIVE
            )
        w.pack(side=tk.LEFT, padx=5, pady=5)
        w = tk.Button(
            box,
            text="Cancel",
            width=10,
            command=self.cancel
            )
        w.pack(side=tk.LEFT, padx=5, pady=5)
        self.bind("<Return>", self.ok)
        self.bind("<Escape>", self.cancel)
        box.pack()

    #
    # Standard Button Callbacks.
    #

    def ok(self, event=None):
        # XXX: okButtonCallback | callbackOkButton | (cb*|*cb)
        """ Proces the data automatically when user presses ok
button."""
        if not self.validate():
            self.initial_focus.focus_set() # put focus back
            return
        self.withdraw()
        self.update_idletasks()
        try:
            self.apply()
        finally:
            self.cancel()

    def cancel(self, event=None):
        # XXX: cancelButtonCallback | callbackCancelButton | (cb*|*cb)
        """ Return focus to the parent window and destroy self"""
        if self.parent is not None:
            self.parent.focus_set()
        self.destroy()

    #
    # Command Hooks.
    #

    def validate(self): # XXX: data_validate
        """
        Validate the data;
        This method is called automatically to validate the data
        before the dialog is destroyed. By default, it always
validates
        True. Overide to change behavoir to something more suitabe for
        your derived dialog.
        """
        return True

    def apply(self): # XXX: data_process
        """
        Process the data;
        This method is called automatically to process the data
        but only *after* the dialog is destroyed. By default, it does
        nothing. Overide to change the behavoir to something more
suitabe
        for your derived dialog
        """
        pass


if __name__ == "__main__":
    root = tk.Tk()
    import tkMessageBox
    ERROR_STR = "We don't have any \"{0}\"!"

    class MyDialog(Dialog):

        def body(self, master):
            self.strvar = tk.StringVar(master, "Eggs")
            self.label = tk.Label(self, text='Default')
            self.label.pack()
            self.options = tk.OptionMenu(
                self,
                self.strvar,
                "Jam",
                "Ham",
                "Spam",
                )
            self.options.pack()

        def validate(self):
            text = self.strvar.get()
            if text and text.lower() != 'spam':
                tkMessageBox.showerror(
                    'Order Error',
                    ERROR_STR.format(text),
                    parent=self
                    )
                return False
            return True

        def apply(self):
            self.result = self.strvar.get()

    m = MyDialog(root, title='Menu = spam')
    # Test changing widget values after initialization.
    m.label['text'] = 'What would you like to order?'
    # Show the modal dialog.
    result = m.show()
    print 'User Choose: {0}'.format(result)

    root.mainloop()
## END CODE ##



More information about the Python-list mailing list