Is it necessary to call Tk() when writing a GUI app with Tkinter?

Rick Johnson rantingrickjohnson at gmail.com
Thu Mar 1 04:22:16 CET 2012


On Feb 29, 6:17 pm, Terry Reedy <tjre... at udel.edu> wrote:
> On 2/29/2012 9:24 AM, Rick Johnson wrote:

> > On Feb 28, 11:06 pm, John Salerno<johnj... at gmail.com>  wrote:
> >> However, in the Python documentation, I see this:
>
> >> root = Tk()
> >> app = Application(master=root)
> >> app.mainloop()
> >> root.destroy()
> >> I tried the above and I got the following error:
>
> >> Traceback (most recent call last):
> >>    File "C:\Users\John\Desktop\gui.py", line 12, in<module>
> >>      root.destroy()
> >>    File "C:\Python32\lib\tkinter\__init__.py", line 1714, in destroy
> >>      self.tk.call('destroy', self._w)
> >> _tkinter.TclError: can't invoke "destroy" command:  application has been destroyed
>
> >> So apparently closing the window with the X button (on Windows)
>
>  >> implicitly calls the destroy() method of the root frame.
>  >> If that's the case, why does the documentation explicitly call it?
>
> I do not know if tk has changed since the example was written or if it
> was buggy from the beginning. I opened an issue to fix it.
>
> http://bugs.python.org/issue14163

"protocol" is ONLY a method of Tkinter.Tk and Tkinter.Toplevel.
Actually Toplevel and Tk are exactly the same object but Tk has an TCL
interpretor attached. Tkinter.Tk is meant to be the parent of ALL
widgets within your Tkinter GUI. Tkinter.Frame is nothing more than a
box to stuff widgets into. Tkinter.Frame IS NOT a window and therefor
it DOES NOT have window methods.

HOWEVER!

Most people start falsely believing that a Tkinter.Frame AND
Tkinter.Toplevel are the same thing; since Tkinter will "auto-
magically" pack your frame into a default Tkinter.Tk window (psst:
that's just a fancy Toplevel widget!) if you don't explicitly create
the Tk instance yourself.

>>>Slightly tangential meanderings
This inconsistency also rears it's ugly head in the dialog modules:
tkFileDialog and tkMessageBox. Both of which do not require a parent
argument to their convenience functions. Instead they allow the parent
to be OPTIONALLY passed.

Inquisitive Ivan mused: """ What's wrong with that Rick, the dialog
will still display whether a parent argument is passed or not. Heck,
even if no viable parent exists, Tkinter will create one! Tkinter is a
smart module!"""

True Ivan. However, if you dig a little deeper you will see that the
dialog created WITHOUT a parent does not function in a manner
consistent to modal dialogs; that is, owning the focus and becoming a
transient of another Toplevel window.

Also, about your second point, noobs get confused when that default
root window pops up. Then they come here and ask the same question
over and over. Can't you see the design flaw that is directly in front
of your face NOR smell the pungent odors that reeking from this
module!
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Some might argue that this implicit root window creation is beneficial
for toy GUIs -- and i agree! HOWEVER, you will due enormous damage to
a neophyte's learning process. I say just force the extra line of code
and be consistent. Actually i believe SO strongly in "explicit root
window creation" that i edited the source code of my Tkinter version
to NOT allow ANY widget's master to be None.

I had written a more exhaustive "expo-say"  some time back but i
cannot remember the title.

> > Most applications will have both: user destroying, and program
> > destroying.
> > from tkMessageBox import askyesnocancel
>
> from tkinter.messagebox in 3.x

Yes, Tkinter has changed a bit in Python>=3.0, thanks for pointing
this out.

> [...snip code...]
> This works as adjusted for 3.x. I presume that a quit button or menu
> entry should also call onDestroyWindow so the effect is the same as
> clicking the outer [X] button.

Yes, but i think the REAL problem is faulty code logic. Remove the
last line "root.destroy()" and the problem is solved. Obviously the
author does not have an in-depth knowledge of Tkinter.

> I tried the same approach to fix the doc example, but unlike your class
> App(Tk), class App(Frame) does not a .protocol attribute. See the
> tracker issue for all my comments on the example.

see above comments about Tkinter.Frame, Tkinter.Toplevel, and
Tkinter.Tk ^^^

> I considered removing both the quit button and 'root.destroy' to get a
> beginning example that works properly, but as you said, having both is
> common so I would like both if the solution is not too esoteric.

If you want to keep things simple, i would:

 1. Create the root window explicitly!
 2. Bind the command of the button to root.destroy
(command=root.destroy)

I would offer better advice if i could but i have no idea where this
"book the OP is learning" is located? No one ever provided a link to
the code in question?

PS: I would highly suggest against using the "from Tkinter import *".
Instead, use "import Tkinter as tk" and prefix all module contents
with "tk.". Also, use "from Tkconstants import X, Y, X"



More information about the Python-list mailing list