Exiting Tkinter when using IDLE

Kurt B. Kaiser kbk at shore.net
Sun Mar 14 03:23:38 EST 2004


Jason Harper <JasonHarper at pobox.com> writes:

> Running a Tkinter app in the subprocess works, you just can't debug it
> if it runs long enough to get into its mainloop.

Slightly modified from OP:

=================
from Tkinter import *

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        self.button = Button(frame, text="QUIT", fg="red",
                             command=self.quit)
        self.button.pack(side=LEFT)

        self.hi_there = Button(frame, text="Hello",
                               command=self.say_hi)
        self.hi_there.pack(side=LEFT)

    def say_hi(self):
        print "hi there, everyone!"

    def quit(self):
        print "quitting"
        root.quit()
        #root.destroy()

root = Tk()
app = App(root)
root.mainloop()

====================

Using IDLE w/subprocess, set a breakpoint (right click) on 'print "quitting"',
Turn on the debugger, then Run/F5 this program. Click on 'Go', then
the apps' QUIT button.  The debugger will break on the print
statement.  You can then Step into Tkinter. The last line in the
debugger window is

> 'Tkinter".quit(), line 968: self.tk.quit()

So this is working as I would expect.  The debugger only traces Python.

Maybe I don't understand what you mean by "debug".

>> When IDLE doesn't use a subprocess, the user's Tk gets mixed with
>> IDLE's, and it's not surprising things fail.
>> 
>> For me, on OpenBSD, Linux, and W2K, using the subprocess: if I use
>> self.quit, the Dialog becomes unresponsive because the mainloop has
>> been exited.  But the advertised action on mainloop exit is to destroy
>> any remaining widgets, and that's not happening.  If I restart the
>> shell, the Tk widget is destroyed correctly.
>
> Where did you see that "advertised"?  

line 968 in Tkinter.py:

    def quit(self):
        """Quit the Tcl interpreter. All widgets will be destroyed."""
        self.tk.quit()

> quit() does nothing but cause the innermost mainloop() to exit, and
> exiting a mainloop() does nothing other than no longer processing
> events.  mainloops()s can be nested, 

What would be the application?  I've never tried that.

> they CAN'T have side-effects like that!

Looking at the code for .../Modules/_tkinkter.c:Tkapp_Quit(), it 
merely sets the quitMainLoop global to 1.  This causes the 'while'
in Tkapp_MainLoop() to terminate.  It looks like the doc string
is incorrect.

...

If the above code is run using IDLE (no debugger), once the QUIT is
pressed the widget becomes inactive and IDLE returns to its prompt,
as OP indicated.

Here's where it gets interesting.  Run the above code as follows:

python -i batista3.py

When you click on QUIT, Python prints 'quitting' and returns to its
prompt.  However, the buttons are still active, you can print the
messages as often as you like.  It appears that the mainloop is
still active.  I haven't figured that one out yet.  It's the 
same on Linux, and with Python2.2.2.

> If you exit without somehow calling destroy() on all your toplevels,
> it's the OS's process termination cleanup that kills the rest of
> them, as far as I can tell.  And of course IDLE's subprocess doesn't
> terminate just because your Python code finished.

You are correct about that.  The best way is to use root.destroy(),
as commented out in the above code.  Most Tkinter code calls 
sys.exit or runs off the end when you quit() the mainloop, so the
residual widgets aren't noticed.  

It's unfortuate that the sample Tkinter program uses quit():

http://www.python.org/doc/current/lib/node633.html,

IMHO it's an example that gets people off to a bad start.

If you destroy() the root widget, Tkapp_MainLoop()'s call to
Tk_GetNumMainWindows() returns 0 and the MainLoop exits.  destroy() is
a good method which works both with IDLE and the interactive
interpreter.

Tkinter's quit() doesn't seem to map to Tk.  When using Tcl/Tk, the
usual way to exit Tk_MainLoop is to destroy the root window.

>From the Tk8.3 manual:
"Tk_MainLoop is a procedure that loops repeatedly calling
Tcl_DoOneEvent. It returns only when there are no applications left in
this process (i.e. no main windows exist anymore)."

> It would appear that IDLE needs to detect that the user program is
> using Tkinter (by importing Tkinter itself and overriding
> mainloop?), and in that case switch to a different strategy for
> handling communication with the main IDLE process - a routine that
> constantly reschedules itself via after(), most likely.  The problem
> is the initial RPC request that runs the user program: I'm pretty
> sure that the shell window is going to be non-responsive until it
> receives the reply, which isn't going to happen until the program
> exits.

I'm not sure what your objective is for this complication.  If you
run the above code, you'll see it has no difficulty sending output
to the IDLE GUI's Shell window.  You can also add a raw_input() to
the quit() method, and it works fine.

-- 
KBK



More information about the Python-list mailing list