Search code examples
pythoneventstkinterdestroytoplevel

Tkinter TopLevel Destroy not being detected


So I've given myself a little project and I'm trying to make a little tool to connect to the OKEX exchange. Now I'm currently working on the GUI and I've decided to use Tkinter. After lots of research and what-not, I've come up with the following, but now I've become a bit stuck.

I've got 2 classes, one for the main window and one for the login window. However, some of the functions of the main window rely on what happens after I've submitted the login details. Now I know that Toplevel is for creating additional windows in Tkinter, and you normally close these windows with .destroy() , and if I want to pick up on this event in the main window class then I need to use the Toplevel.protocol("WM_DELETE_WINDOW", function_name) call ...but this isn't working for me.

It works as expected if I close using the cross in the top right, but not if I close with my function that calls .destroy() , can anyone explain to me why this isn't working as intended? Perhaps I have missed something ?

I want to change the text in the first frame to "Logged In" after the user (me) enters in their details, but I need to login first and pass through a user object to contain those details.

Anyways, here's the code, please help me out! The code in question is the myquit function in the LoginBox class , and the goToLogin function in the MainBox class :)

from tkinter import *
from tkinter.ttk import *


class LoginBox:
    def __init__(self, master):  # Master is the frame that is passed in.
        # Create Frame
        self.master = master
        self.master.title('~ Please Login ~')


    def login_function(self):
        user = "xxx"
        secret_key = "yyy"

        print("User - API Key: " + user)
        print("User - Secret Key: " + secret_key)

        # Perhaps check the login ...

        # if it's a success then quit the function
        self.myquit()

    def myquit(self):
        self.master.destroy()


class MainBox:
    def __init__(self, master):

        # Set the root window
        self.master = master
        self.master.geometry("500x500")
        self.master.title("OkBot v0.1")
        self.master.resizable(False, False)

        # Initialize the frames
        self.uiFrame1 = Frame(self.master)  # The Top Layer -- Login Text + Login Button 

        # uiFrame1 Initialize --Login Text + Login Button
        self.ui1_button = Button(self.uiFrame1, text="Login", command=self.goToLogin).grid(row=0, column=3, sticky=E, padx=1)

    # Create Topview for popup , pass in User Object to LoginBox
    def goToLogin(self):
        loginMaster = Toplevel(self.master)
        loginMaster.protocol("WM_DELETE_WINDOW", self.checkLogin)  # This is if they close via X
        loginGUI = LoginBox(loginMaster)

    def checkLogin(self):

        print("This function was called -- The Protocol for destroyed windows works")


# Initialize the objects and start the program
mainWindow = Tk()
myProgram = MainBox(mainWindow)
mainWindow.mainloop()

Solution

  • It works as expected if I close using the cross in the top right, but not if I close with my function that calls .destroy() , can anyone explain to me why this isn't working as intended? Perhaps I have missed something ?

    loginMaster.protocol("WM_DELETE_WINDOW", self.checkLogin) only tells tkinter what to do when the window manager destroys the window. When you call some function which calls destroy(), that doesn't involve the window manager and therefore your callback is not called.

    If you want something to happen when the window is destroyed, you should bind to the <Destroy> event.