exit from Tkinter mainloop Python 2.7

Rick Johnson rantingrickjohnson at gmail.com
Wed Mar 9 14:17:34 EST 2016


On Friday, March 4, 2016 at 3:50:43 PM UTC-6, kevin... at gmail.com wrote:
> Thanks for your attention to this matter.
> My code now look like this: [...]

All of the help offered in this thread ignores the elephant
in the room. What are you trying to achieve here Kevin? To
me, this looks like some sort of dialog, but if it is a
dialog, the whole design is a miserable failure. And why in
the hell would you call the w.quit() method when the user
presses a "continue" button? Do you realize that the quit
method does not "close the dialog" (if you can call this a
dialog), no, but simply instructs Tkinter to stop processing
events? Once a user clicks your "continue" button, he can no
longer interact with the GUI, and the user is left in a
state of befuddlement.

Those who've attempted to help you have suggested that you
utilize the quit method in your "continue button callback"
so that the widgets will stay alive long enough for you to
fetch the value, but that is horrible advice. Trust me, the
quit method is not something you want to use unless you know
what you're doing. It's should never be used in daily
coding, and only in very specific advanced circumstances.

The destroy method that you utilized initially *IS* the
correct course of action here. You see, your interface is
not a failure because of the destroy method, no, it is a
failure because you failed to (1) fetch the values in the
button callback BEFORE the widgets are destroyed, and (2)
You failed to store those values somewhere that will be
persistent after the function returns. But even if you
followed both of these suggestions, you design is flawed.

If you want to create a proper "dialog interface", the key
is not to use the "quit" method, but to follow these simple
design rules:

  (1) Present the user with a "focused window" that contains
  "form" filled with "input fields" that will facilitate the
  inputting of the required values. Typically, but not
  always, you'll want this window to block program execution
  until the user submits the form (FYI: Dialogs that block
  are commonly referred to as "modal dialogs")
  
  (2) Include at least one button to submit the values. This
  button is typically named "Okay", or "Yes" or "Submit".
  But when the user presses this button, the dialog will
  *NOT* be closed until the values have been validated. If
  the values are found to be inadequate, the dialog will
  inform the user of his mistakes and refocus the input-form
  for editing. A more advanced dialog will utilize fields
  that validate their *OWN* values in "real time". These
  "real time validations" are less likely to confuse a user,
  and they facilitate a more efficient interface since the
  user will be aware of mistakes as they occur.
  
  (3) Include at least one button to cancel the interaction.
  Sometimes, a user will open a dialog accidentally, or for
  some other reason decide that they need a way out. For
  instance: Imagine all the times that you've accidentally
  pressed the X button on a window. If the window closed
  without first asking you "Do you really want to close
  without saving changes", you'd be screwed!
        
A proper "dialog window" will be constructed of three basic
top-level components:

    +--------------------------+
    |DIALOG_TITLE              |
    +--------------------------+
    |                          |
    |       DIALOG_BODY        |
    |                          |
    +--------------------------+
    |      DIALOG_BUTTONS      |
    +--------------------------+
    
The DIALOG_TITLE is simply a string that will be displayed
in the title bar of the dialog. The DIALOG_BODY is a fluid
area in the middle that can be filled with user-editable
fields, and the DIALOG_BUTTONS is an area at the bottom
where appropriate buttons will be displayed.

A proper dialog object will allow the caller to display the
dialog in either "modal" or "non-modal" forms, and it will
expose hooks that will allow the programmer to customize the
validation and submission of the form data. An even more
advanced version will require the programmer to extend a
"form object" and pass it into the constructor.

Fortunately, the tkSimpleDialog has already wrapped *SOME*
of this functionality up for you. All you need to do is
import the class and override a few "virtual methods" to
achieve your desired goal. The tkSimpleDialod.Dialog class
exposes 3 hooks that you can utilize to modify the behavior

  (1) body: In this method you create your widgets.It
  requires one argument which is a tk.Frame instance that
  the widgets can be made a child of.
  
  (2) validate: in this method you validate your widgets and
  return a Boolean flag. If you don't need any validation
  then don't bother to override it.
  
  (3) apply: In this method you can do something with the
  "dialog.result" *AFTER* the dialog closes. Typically
  though, you will handle this action from outside the
  dialog object. But in certain limited circumstances, it
  can simplify your code.

############################################################
# START EXAMPLE CODE                                               
############################################################
import Tkinter as tk
from tkSimpleDialog import Dialog
from tkMessageBox import showerror

class MyDialog(Dialog):
    def body(self, master): # CLOBBERS BASE METHOD!
        # This is called just before the dialog is shown.
        w = tk.Label(master, text= 'Age')
        w.grid(row=0, column=0)
        self.e1 = e1 = tk.Entry(master)
        e1.grid(row=0, column=1)
        w = tk.Label(master, text= 'Weight')
        w.grid(row=1, column=0)
        self.e2 = e2 = tk.Entry(master)
        e2.grid(row=1, column=1)
        return e1 # This widget will get focus!

    def _validateInteger(self, value): 
        # INTERNAL HELPER FUNCTION
        try:
            int(value)
        except ValueError:
            return False
        else:
            return True
        
    def validate(self): # CLOBBERS BASE METHOD!
        # This is called after the okay button is pressed
        a = self.e1.get().strip()
        b = self.e2.get().strip()
        #
        # Validate Weight
        if a=='':
            showerror('', 'Age cannot be empty')
            return False
        elif not self._validateInteger(a):
            showerror('', 'Age must be a number!')
            return False
        #
        # Validate Height
        elif b=='':
            showerror('', 'Weight cannot be empty')
            return False
        elif not self._validateInteger(b):
            showerror('', 'Weight must be a number!')
            return False
        #
        # Let the dialog know everything is A-OKAY!
        else:
            self.result = [int(a), int(b)]
            return True

    def apply(self): # CLOBBERS BASE METHOD!
        # This is called after the dialog closes. 
        # It's not normally needed since you can reliably set the return 
        # value in "validate", but it's important to realize that this 
        # method is one of the provided hooks.
        pass
        

if __name__ == '__main__':
    def goodTimes():
        d = MyDialog(root, title='Good Times Application')
        result = d.result
        print 'result =', `result`
        if result:
            age, weight = result
            if age < 18 or age > 80:
                print "Sorry, but you're either too young or too old"
            elif weight < 100 or weight > 120:
                print "Sorry, but you're too bony or too heavy."
            else:
                print 'Let the good times roll!'
        else:
            print 'USER CANCELED!'
            
    root = tk.Tk()
    w = tk.Button(root, text='For a good time, press me', command=goodTimes)
    w.pack(padx=5, pady=5)
    root.mainloop()

############################################################
# END EXAMPLE CODE
############################################################

Unfortunatley, the tkSimpleDialog is terriby written, uses
poor naming conventions, does not expose enough hooks, and
is therefore a sad excuse for what a modern "dialog library"
should be.

For example: the virtual methods of "body", "validate", and
"apply" would be much more intuitive if they had been named
"create_form", validate_form", and "process_form"
respectively. 

Although, i would go a step further and mark them as "hooks"
by naming them: "hook_createForm", "hook_validateForm", and
"hook_processForm". I hate reading source and not knowing
which methods are clobbering base methods. That REALLY
pisses me off! And IMO, is a giant elephant-sized bowel
movement of a code smell!

But in your case, even with tkSimpleDialog's many flaws, it
is still leaps and bounds better than what you have managed
to do (sorry to be so frank), and it will save you the time
and effort of re-inventing the wheel.

I would suggest you use it only long enough to understand
its flaws, and then re-write it to better suit your needs.
That's what i did long ago, and my sanity has benefited
greatly from discarding such horrible libraries such as this.



More information about the Python-list mailing list