How do I handle the window close event in Tkinter?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Tkinter, closing a window is not just about destroying the GUI. It is often the moment to confirm exit, save unsaved data, stop background tasks, or clean up resources. Tkinter exposes the window manager close action through the WM_DELETE_WINDOW protocol, and that is the correct place to intercept the user clicking the title-bar close button.
Registering a Close Handler
The main window object, usually called root, lets you replace the default close behavior with your own callback.
The important line is:
When the user clicks the close button, Tkinter calls on_close instead of destroying the window immediately.
Asking for Confirmation Before Exit
A common use case is confirming that the user really wants to quit.
This is especially helpful when closing the window could discard unsaved work.
Cleaning Up Resources
The close handler is also the right place to stop background activity. For example, if your app has an open file, socket, or scheduled callback, close or cancel it before destroying the root window.
Without that cleanup, callbacks may continue trying to run during shutdown and produce hard-to-debug errors.
Handling Child Windows
If you create Toplevel windows, each one can have its own close protocol. That means a child window can confirm or clean up independently of the main application window.
This is useful when different windows manage different resources.
Saving State Before Destruction
Many desktop apps save size, position, or form data when closing. The close callback is a convenient place to do that.
That pattern keeps shutdown behavior explicit instead of scattered through unrelated parts of the program.
quit() vs destroy()
Tkinter developers sometimes confuse quit() and destroy(). quit() stops the Tkinter main loop, while destroy() actually removes the widgets and closes the window.
For most close handlers, destroy() is the right final step. If you only call quit() without destroying the window, widgets may still exist and cleanup may be incomplete.
Common Pitfalls
A common mistake is forgetting to call destroy() at the end of the close handler. The callback runs, but the window stays open.
Another issue is doing long-running work directly in the close callback. If saving or cleanup takes noticeable time, the app can look frozen during shutdown.
Developers also sometimes attach cleanup logic to unrelated buttons and assume it covers the title-bar close button too. It does not. Use WM_DELETE_WINDOW explicitly.
Finally, remember that each Toplevel window can have its own close behavior. Do not assume the root window handler automatically manages every child window correctly.
Summary
- Use
root.protocol("WM_DELETE_WINDOW", callback)to handle the close button. - Put confirmation, cleanup, and save logic inside that callback.
- Finish by calling
destroy()so the window actually closes. - Register separate handlers for
Toplevelwindows when needed. - Treat shutdown as a real event with explicit resource cleanup, not just a UI action.

