Intercept Tkinter "Exit" command?
Asked Answered
D

5

7

I'm writing a client-server program in Python with Tkinter. I need the server to keep track of the connected clients. For this, I would like to have the client send an automated message to the server after the exit button(the standard "X" in the corner) is clicked. How can I know when the user is exiting the program?

Disjunction answered 10/1, 2011 at 1:27 Comment(1)
possible duplicate of How do I handle the window close event in Tkinter?Snowy
L
21

You want to use the wm_protocol method of the toplevel window. Specifically, you are interested in the WM_DELETE_WINDOW protocol. If you use that method, it allows you to register a callback which is called when the window is being destroyed.

Usage:

root.protocol("WM_DELETE_WINDOW", app.on_delete)
Lightheaded answered 10/1, 2011 at 1:45 Comment(5)
So I added this line: root.protocol("WM_DELETE_WINDOW", app.on_delete()) right before my call to root.mainloop() but on_delete() is getting called when the window is opened and not when it is closed. Am I doing something wrong?Disjunction
@John: the protocol method takes a reference to a function. By adding the trailing parenthesis you are calling app.on_delete and passing the result of that method to the protocol handler. The correct usage is root.protocol("WM_DELETE_WINDOW", app.on_delete)Lightheaded
Setting the protocol like seems to only work with respect to the main window's OS close button, but not, for example, with a Quit button in a Frame subclass whose command=self.quit). That said, it does answer this question which asks only about the standard exit button.Fascine
@martineau: correct, the protocol only comes into play when the window manager is asked to destroy the window. When you call quit, you're just instructing mainloop to exit, and the window is destroyed gracefully.Lightheaded
Bryan: Thanks for the confirmation. I think not understanding the distinction is why more than one of the other answers claim that setting the protocol doesn't work.Fascine
C
7

You can use python atexit module.

For example:

import atexit

def doSomethingOnExit():
    pass

atexit.register(doSomethingOnExit)
Coruscate answered 22/2, 2015 at 22:54 Comment(1)
This worked perfectly for me. The other ones never got triggered.Laggard
A
4

In my case, the following code didn't work:

root.protocol("WM_DELETE_WINDOW", app.on_delete)  # doesn't work

However, it worked using this form:

root.wm_protocol ("WM_DELETE_WINDOW", app.on_delete)  # does work
Atalee answered 23/3, 2012 at 14:11 Comment(3)
There's no answer saying to use .protocol, though... the accepted answer already said to use .wm_protocol.Laggard
@ArtOfWarfare: the accepted answer mentions wm_protocol, but its code snippet uses .protocol, not .wm_protocol. In any case, both options behave in the exact same way on my machine, I assume piertoni's version differs from mine (unsurprisingly so since the answer dates back to 2012).Currin
protocol and wm_protocol are exactly the same function.Lightheaded
O
2

FWIW: It is also possible to assign a widget-specific behavior.

If you want an action to occur when a specific widget is destroyed, you may consider overriding the destroy() method. See the following example:

class MyButton(Tkinter.Button):
    def destroy(self):
        print "Yo!"
        Tkinter.Button.destroy(self)

root = Tkinter.Tk()

f = Tkinter.Frame(root)
b1 = MyButton(f, text="Do nothing")
b1.pack()
f.pack()

b2 = Tkinter.Button(root, text="f.destroy", command=f.destroy)          
b2.pack()

root.mainloop()

When the button 'b2' is pressed, the frame 'f' is destroyed, with the child 'b1' and "Yo!" is printed.

I posted the same answer on this topic.

Offertory answered 9/2, 2011 at 19:33 Comment(0)
U
1

Don't forget to use a lambda like this:

class App:
    def run(self):
        self.root.protocol("WM_DELETE_WINDOW", lambda: self.quit())
    def quit(self):
        self.root.destroy()
Unceremonious answered 21/4, 2022 at 7:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.