'Unable to exit tkinter app when using "wait_variable()"

I have a python code that includes tkinter window and other running tasks.

I've been trying to bind "WM_DELETE_WINDOW" event to a function that exits my python code when I close the window but can't achieve that.

This is what I try:

def on_exit():
    root.destroy()
    sys.exit()
root.protocol('WM_DELETE_WINDOW', on_exit)

The window is destroyed successfully but the python code doesn't exit. Any possible reason for sys.exit() not to work?

What am I doing wrong? any alternative approach should I try?

Doing some testing I figured out what can be the problem.

Here's a small code that summarizes my code which is much bigger.

import tkinter as tk
import sys

root = tk.Tk()
submitted = tk.IntVar()

def on_exit():
    root.destroy()
    sys.exit()
root.protocol('WM_DELETE_WINDOW', on_exit)

def submit():
    submitted.set(1)
    print("submitted")

button= tk.Button(root, text="Submit",command=submit)
button.pack()
button.wait_variable(submitted)

root.mainloop()

I believe now that wait_variable is the source of the problem.

And the code actually exits when I added submitted.set(1) to on_exit() ( or if I clicked the button first before closing the window ) but if I tried closing the window without pressing the button, the code won't exit.

So does this mean that wait_variable not only makes tkinter app wait, but also prevents python code exiting?!

I tried os._exit(1) and it worked, but I think it's not clean.



Solution 1:[1]

As your updated question points out the problem is wait_variable(). Going off the documentation for this method wait_variable() enters a local event loop that wont interrupt the mainloop however it appears that until that local event loop is terminated (the variable is updated in some way) it will prevent the python instance from terminating as there is still an active loop. So in order to prevent this you have also correctly pointed out you need to update this variable right before you terminate the tk instance.

This might seam odd but it is the behavior I would expect. It is my understanding that an active loop needs to be terminated before a python instance can exit.

As Bryan has pointed out in the comments the wait_variable() method is "a function which calls the vwait command inside the embedded tcl interpreter. This tcl interpreter knows nothing about python exceptions which is likely why it doesn't recognize the python exception raised by sys.exit()"

Link to relevant documentation:

wait_variable()

Relevant text from link:

wait_variable(name)

Waits for the given Tkinter variable to change. This method enters a local event loop, so other parts of the application will still be responsive. The local event loop is terminated when the variable is updated (setting it to it’s current value also counts).

You can also set the variable to whatever it is currently set as to terminate this event loop.

This line should work for you:

submitted.set(submitted.get())

That said you do not actually need sys.exit(). You can simply use root.destroy().

You new function should look like this:

def on_exit():
    submitted.set(submitted.get())
    root.destroy()

The python instance will automatically close if there is no more code after the mainloop.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 martineau