Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

exit from Tkinter mainloop Python 2.7

89 views
Skip to first unread message

kevin...@gmail.com

unread,
Feb 23, 2016, 4:39:52 PM2/23/16
to
Hello:



Newbie here.

Spent the a good part of the day tinkering and reading tutorials,
I was able to create a sample that is very close to my requirement.

When I execute the code below the Dialog displayed as expected and
I can enter data into the textboxes. All good.

When I click on butGo I get the error below.
So is this some sort of scope error?
why does entryName not exist for me to grab it's value?

Your kind assistance is requested.


Traceback (most recent call last):
File "C:\Users\kduffy12\workspace\testPythonA\testA\simpleDialog.py", line 25, in <module>
print entryName.get("1.0", "end-1c" )
AttributeError: 'NoneType' object has no attribute 'get'




from Tkinter import *


def butContinue():
root1.destroy()

root1 = Tk()
root1.geometry("500x250")


lblTop = Label(root1, text= ' Enter Values Below', font="Helvetica 14").grid(row=0, column=0, columnspan=2 , pady=5)
##lblTop.pack(side = TOP)

lblDB = Label(root1, text= ' Weight').grid(row=1, column=0 )
lblPWord = Label(root1, text= ' Height').grid(row=2,column=0)
entryName = Entry(root1).grid(row=1, column=1, pady=5)

entryPWord = Entry(root1).grid(row=2, column=1, pady = 5)

butGo = Button(root1, text=" Continue " , command=butContinue ).grid(row=3, column=1, sticky=W, pady=10)


root1.mainloop()

print entryName.get("1.0", "end-1c" )
print entryPWord.get("1.0", "end-1c" )

Christian Gollwitzer

unread,
Feb 23, 2016, 5:29:51 PM2/23/16
to
Am 23.02.16 um 22:39 schrieb kevin...@gmail.com:
> from Tkinter import *
>
> def butContinue():
> root1.destroy()
> [...]
> entryName = Entry(root1).grid(row=1, column=1, pady=5)
> [...]
> butGo = Button(root1, text=" Continue " , command=butContinue ).grid(row=3, column=1, sticky=W, pady=10)
>
> root1.mainloop()
>
> print entryName.get("1.0", "end-1c" )

> When I click on butGo I get the error below.
> So is this some sort of scope error?
> why does entryName not exist for me to grab it's value?

You call destroy() on the root window of Tk. If you destroy a window,
that will destroy all of it's children. Therefore by deleting the root
window, also the entryName widget was deleted. You need to export the
values before you close the window, i.e. in the butContinue() function.

There is another pitfall with Tk: if you delete the main window, you can
get problems if you try to recreate it. If you want to show several
pages in sequence or the like, you should withdraw the main window

root1.withdraw()

and popup a fresh toplevel by

t=Toplevel()
and do everything in t. Once you are finished, destroying t will keep
your application alive (because the real root window is just hidden)

Another comment:

> root1.geometry("500x250")

Do not do this. The grid and pack geometry managers compute the needed
space automatically. A fixed size in pixels can easily break, when you
go to another computer with a different font size, screen resolution, or
a different OS. If you are not satisfied with the whitespace, use the
padding options of grid or pack.

Christian

Christian Gollwitzer

unread,
Feb 23, 2016, 5:33:28 PM2/23/16
to
Am 23.02.16 um 22:39 schrieb kevin...@gmail.com:
> lblTop = Label(root1, text= ' Enter Values Below', font="Helvetica 14").grid(row=0, column=0, columnspan=2 , pady=5)
> ##lblTop.pack(side = TOP)
>
> lblDB = Label(root1, text= ' Weight').grid(row=1, column=0 )
> lblPWord = Label(root1, text= ' Height').grid(row=2,column=0)

Also here, the labels look odd. Have you tried to do some alignment of
the text with spaces? Remove the sapces and look at the "justify" option
of the label widget and the "sticky" option for grid.

Christian

Peter Otten

unread,
Feb 23, 2016, 5:45:36 PM2/23/16
to
Christian Gollwitzer wrote:

> Am 23.02.16 um 22:39 schrieb kevin...@gmail.com:
>> from Tkinter import *
>>
>> def butContinue():
>> root1.destroy()
>> [...]
>> entryName = Entry(root1).grid(row=1, column=1, pady=5)
>> [...]
>> butGo = Button(root1, text=" Continue " , command=butContinue
>> ).grid(row=3, column=1, sticky=W, pady=10)
>>
>> root1.mainloop()
>>
>> print entryName.get("1.0", "end-1c" )
>
>> When I click on butGo I get the error below.
>> So is this some sort of scope error?
>> why does entryName not exist for me to grab it's value?
>
> You call destroy() on the root window of Tk. If you destroy a window,
> that will destroy all of it's children. Therefore by deleting the root
> window, also the entryName widget was deleted. You need to export the
> values before you close the window, i.e. in the butContinue() function.

Even when you follow this advice, entryName was set to None by the line

>> entryName = Entry(root1).grid(row=1, column=1, pady=5)

as grid() always returns None. You need two steps

entryName = Entry(root1)
entryName.grid(row=1, column=1, pady=5)

Also,

>> print entryName.get("1.0", "end-1c" )

I believe that the Entry widget's get() method doesn't take any arguments.

Dave Farrance

unread,
Feb 24, 2016, 4:34:59 AM2/24/16
to
kevin...@gmail.com wrote:

>from Tkinter import *
>
>def butContinue():
> root1.destroy()

As Christian said, you're destroying the root window and its children,
so instead use root1.quit() here.

> ...
>
>root1.mainloop()
>
>print entryName.get("1.0", "end-1c" )
>print entryPWord.get("1.0", "end-1c" )

And your root1.destroy() goes here instead. (The root window would
normally be destroyed on the script exit, but some IDE debuggers will
leave it open.)

kevin...@gmail.com

unread,
Mar 4, 2016, 4:50:43 PM3/4/16
to


Christian & Others:

Thanks for your attention to this matter.
My code now look like this:

from Tkinter import *


def butContinue():
dbUser = entryName.get()
pWord = entryPWord.get()
print dbUser
print pWord
root1.quit()


dbUser = ""
pWord = ""
root1 = Tk()
##root1.geometry("500x250")


lblTop = Label(root1, text= ' Enter Values Below', font="Helvetica 14").grid(row=0, column=0, columnspan=2 , pady=5)
##lblTop.pack(side = TOP)

lblDB = Label(root1, text= 'Weight').grid(row=1, column=0 )
lblPWord = Label(root1, text= 'Height').grid(row=2,column=0)
entryName = Entry(root1)
entryName.grid(row=1, column=1, pady=5)

entryPWord = Entry(root1)
entryPWord.grid(row=2, column=1, pady = 5)

butGo = Button(root1, text=" Continue " , command=butContinue ).grid(row=3, column=1, sticky=W, pady=10)


root1.mainloop()

print "After the MainLoop"
print dbUser
print pWord

After I type in the text Weight and Height
No error are reported and output looks like this:

Weight
Height
After the MainLoop


Question:
Why did this code not cause Weight and Height to print again.

print "After the MainLoop"
print dbUser
print pWord

Thanks in advance.


KBD


Peter Otten

unread,
Mar 4, 2016, 5:28:33 PM3/4/16
to
kevin...@gmail.com wrote:

>
>
> Christian & Others:
>
> Thanks for your attention to this matter.
> My code now look like this:
>
> from Tkinter import *
>
>
> def butContinue():
> dbUser = entryName.get()

Here you set the local variable dbUser (every name you rebind inside a
function is local to that function by default)

> pWord = entryPWord.get()
> print dbUser

and here you print it.

> print pWord
> root1.quit()
>
>
> dbUser = ""

Here you set the global variable dbUser

> pWord = ""
> root1 = Tk()
> ##root1.geometry("500x250")
>
>
> lblTop = Label(root1, text= ' Enter Values Below', font="Helvetica
> 14").grid(row=0, column=0, columnspan=2 , pady=5)
> ##lblTop.pack(side = TOP)
>
> lblDB = Label(root1, text= 'Weight').grid(row=1, column=0 )
> lblPWord = Label(root1, text= 'Height').grid(row=2,column=0)
> entryName = Entry(root1)
> entryName.grid(row=1, column=1, pady=5)
>
> entryPWord = Entry(root1)
> entryPWord.grid(row=2, column=1, pady = 5)
>
> butGo = Button(root1, text=" Continue " , command=butContinue
> ).grid(row=3, column=1, sticky=W, pady=10)
>
>
> root1.mainloop()
>
> print "After the MainLoop"
> print dbUser

and here you print that global variable.

> print pWord
>
> After I type in the text Weight and Height
> No error are reported and output looks like this:
>
> Weight
> Height
> After the MainLoop
>
>
> Question:
> Why did this code not cause Weight and Height to print again.
>
> print "After the MainLoop"
> print dbUser
> print pWord
>
> Thanks in advance.

If you want

> def butContinue():
> dbUser = entryName.get()
> pWord = entryPWord.get()

to change the global dbUser and pWord you need to declare them as global
explicitly:

> def butContinue():
global dbUser, pWord

Rick Johnson

unread,
Mar 9, 2016, 2:17:48 PM3/9/16
to
On Friday, March 4, 2016 at 3:50:43 PM UTC-6, kevin...@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.
0 new messages