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?

John
  • 15,418
  • 12
  • 44
  • 65
  • possible duplicate of [How do I handle the window close event in Tkinter?](http://stackoverflow.com/questions/111155/how-do-i-handle-the-window-close-event-in-tkinter) – nbro May 06 '15 at 23:37

5 Answers5

20

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)
martineau
  • 119,623
  • 25
  • 170
  • 301
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • 1
    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? – John Jan 10 '11 at 17:58
  • 7
    @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)` – Bryan Oakley Jan 11 '11 at 11:58
  • 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. – martineau Dec 06 '21 at 19:19
  • @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. – Bryan Oakley Dec 06 '21 at 19:24
  • 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. – martineau Dec 06 '21 at 19:42
7

You can use python atexit module.

For example:

import atexit

def doSomethingOnExit():
    pass

atexit.register(doSomethingOnExit)
Maksym Ganenko
  • 1,288
  • 15
  • 11
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
gary
  • 4,227
  • 3
  • 31
  • 58
piertoni
  • 1,933
  • 1
  • 18
  • 30
  • There's no answer saying to use `.protocol`, though... the accepted answer already said to use `.wm_protocol`. – ArtOfWarfare Apr 28 '15 at 12:06
  • @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). – Anthony Labarre May 31 '20 at 16:00
  • `protocol` and `wm_protocol` are exactly the same function. – Bryan Oakley Dec 06 '21 at 20:00
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.

Community
  • 1
  • 1
O.C.
  • 71
  • 1
  • 2
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()
Gesugao-san
  • 59
  • 1
  • 7