Programmatically press the `X` button on the toobar? Programmatically press the `X` button on the toobar? tkinter tkinter

Programmatically press the `X` button on the toobar?


I have not been able to find a post or some documentation that tells me how I can programmatically active the "WM_DELETE_WINDOW" protocol.

You can't. By definition, the WM_DELETE_WINDOW protocol comes from the window manager.

Catching the protocol handler is designed to give you an opportunity to override its behavior. It is not designed to be a way to trigger some code no matter how the application is destroyed. If you want to run some code when the window is destroyed, whether that is by the user clicking the control on the window frame or through some other way, the correct way to do that is to bind to the <Destroy> event on the root window.

You have to be careful, in that any binding on the root window will be triggered for every widget. Therefore, your binding should only run when event.widget is the same as the root window.

The following example illustrates the technique. There is a method handle_close which is called whenever the window is destroyed. Whether you close the window by clicking on the control on the window frame, or whether you click on the "Close me!" button, the code still runs.

import tkinter as tkclass Example(tk.Tk):    def __init__(self):        super().__init__()        self.bind("<Destroy>", self.handle_close)        button = tk.Button(self, text="Close me!", command=self.destroy)        button.pack()    def handle_close(self, event):        if event.widget == self:            print("Closing")        self.quit()example = Example()example.mainloop()


I don't believe there's a method that invokes a specific protocol, since protocol seems to be a specific event watch. Here's a snippet from the module's class Tk:

class Tk(Misc, Wm):    """Toplevel widget of Tk which represents mostly the main window    of an application. It has an associated Tcl interpreter."""    def _loadtk(self):    ...    self.protocol("WM_DELETE_WINDOW", self.destroy)

As you can see, by default the module itself sets the protocol to destroy(). The protocol() method only seeks to replace the default function (at the absence of a function, it just removes the function):

def wm_protocol(self, name=None, func=None):    """Bind function FUNC to command NAME for this widget.    Return the function bound to NAME if None is given. NAME could be    e.g. "WM_SAVE_YOURSELF" or "WM_DELETE_WINDOW"."""    if callable(func):        command = self._register(func)    else:        command = func    return self.tk.call(        'wm', 'protocol', self._w, name, command)protocol = wm_protocol

but to achieve what you want you should be able to reference back to the same handling method with this:

def handle_exit(self):    self.controller.handle_close()

Of course, this is not as versatile since you must explicitly know the handler in your main window.


Thought I have accepted Bryan's answer I did manage to come to a workaround I think is fine here.

If I pass the method that is being used to deal with window closing to my menu class and then check if something has been passed I can then decide on weather or not to use the exit method I made or self.controller.destroy() with an if statement.

Here is my solution.

Main file:

import tkinter as tkfrom tkinter import messageboximport PIP_MENUclass PIP(tk.Tk):    def __init__(self):        super().__init__()        PIP_MENU.start(self, self.handle_close)        self.protocol("WM_DELETE_WINDOW", self.handle_close)    def handle_close(self):        x = messagebox.askquestion("DERP", "Do you want to close without saving?")        if x == "yes":            self.destroy()if __name__ == '__main__':    PIP().mainloop()

Menu file:

import tkinter as tkclass Menu(tk.Menu):    def __init__(self, controller, exit_handler=None):        super().__init__()        self.controller = controller        self.exit_handler = exit_handler        controller.config(menu=self)        file_menu = tk.Menu(self, tearoff=0)        self.add_cascade(label="File", menu=file_menu)        file_menu.add_command(label="Exit", command=self.handle_exit)    def handle_exit(self):        if self.exit_handler != None:            self.exit_handler()        else:            self.controller.quit()def start(controller, exit_handler=None):    return Menu(controller, exit_handler)