Как передать метод класса в root.protocol("WM_DELETE_WINDOW", def_is_in_class)?

Искал в интернете, как стыковать GUI со всяким остальным кодом, пришел к некоторой конструкции (код ниже). Единственно, что у меня с этой конструкцией не может сработать root.protocol("WM_DELETE_WINDOW, on_closing") потому, что on_closing у меня располагается в классе, а root.protocol вне.

Можно ли как то переделать код, чтобы состыковать root.protocol и описанную в классе on_closing?

Код программы:

# Описываю класс main_app:
class main_app(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        self.active_way = ttk.Label(textvariable=self.shared_state.active_path, anchor="center")
        self.active_way.grid(row=1, column=0, columnspan=3, sticky=E + W + N)
        # тут пошли всякие button'ы, label'ы, def'ы описывающие нажатие, и.т.п.

    def on_closing(self):
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            self.shared_state.should_stop.set(True) # <- это описано в классе, потому не могу вынести on_closing за пределы класса.
            root.destroy()


# Описываю запускающуюся часть:
if __name__ == "__main__":
    root = tk.Tk()
    root.tk.call('source', 'azure-dark.tcl')
    ttk.Style().theme_use('azure-dark')
    main_app(root)
    root.geometry('655x800+1257+100')
    root.title("GUI WebM->MP4 Convertor")
    # root.protocol("WM_DELETE_WINDOW", on_closing) # <- вот оно не работает =(
    root.mainloop()

Собственно, сам пробовал перетаскивать инициализацию рута внутрь класса, но оно отказалось запускаться.

Update 1:

Перетащил таки все в __init__:

  1. Если я делаю вот так:
class main_app(Frame):
    def __init__(self, parent, *args, **kwargs):
        root = Tk()
        Frame.__init__(self, parent, *args, **kwargs)
        self.shared_state = SharedState()
        self.parent = parent

        # тут все остальное

        root.protocol("WM_DELETE_WINDOW", self.on_closing(root))
        root.mainloop()


if __name__ == "__main__":
    main_app()

То у меня выдается вот это:

TypeError: __init__() missing 1 required positional argument: 'parent'
  1. Если я делаю вот так:
class main_app(Frame):
    def __init__(self):
        root = Tk()
        Frame.__init__(self)
        self.shared_state = SharedState()

        # тут все остальное

        root.protocol("WM_DELETE_WINDOW", self.on_closing(root))
        root.mainloop()


if __name__ == "__main__":
    main_app()

То при запуске программы у меня открывается GUI, и сразу же запускается on_closing.


Ответы (1 шт):

Автор решения: insolor

Вариант с переносом внутрь __init__:

import tkinter as tk
import tkinter.messagebox as messagebox


class main_app(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        root.protocol("WM_DELETE_WINDOW", self.on_closing)
 
    def on_closing(self):
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            root.destroy()


if __name__ == "__main__":
    root = tk.Tk()

    main_app(root)
    
    root.mainloop()

Вариант с привязкой "снаружи" после создания объекта:

import tkinter as tk
import tkinter.messagebox as messagebox


class main_app(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        
 
    def on_closing(self):
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            root.destroy()


if __name__ == "__main__":
    root = tk.Tk()

    app = main_app(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    
    root.mainloop()

Варианты с наследованием от класса Tk:

import tkinter as tk
import tkinter.messagebox as messagebox


class MainWindow(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # self.protocol("WM_DELETE_WINDOW", self.on_closing)  # <-- или так
        
 
    def on_closing(self):
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            self.destroy()


if __name__ == "__main__":
    root = MainWindow()
    # root.protocol("WM_DELETE_WINDOW", root.on_closing)  # <-- или так
    root.mainloop()
→ Ссылка