Search code examples
pythoneventstkintertoplevel

Python Tkinter communicate through multiple windows and return values after destroy


I want to make a program with multiple tkinter windows, but i encounter some problems. I have a main window containing one button. After pressing that button, a toplevel will open which contain widgets through user input some data. This toplevel also contain a button that, once pressed, the toplevel is destroyed and the data introduced is returned to the function called by the event from the first button. The main window is destroyed and the data is passed as an argument to a third window who will use it.

from tkinter import *


def third_window(data):
    root = Tk()

    lbl = Label(root, text=data)
    lbl.place(x=20,y=20)

    root.mainloop()

def second_window():
    def event_btn(event):
        e = entry.get()
        if len(e) != 0:
            root.destroy()
            print(e)
            return e
    root = Toplevel()
    root.geometry("400x400+200+200")

    entry = Entry(root, width=15)
    entry.place(x=30,y=30)
    btn = Button(root, text="Send")
    btn.bind("<Button-1>", event_btn)
    btn.place(x=80, y=80)
    root.wait_window()

    root.mainloop()

def main():
    def event_btn(event):
        data = second_window()
        print(data)
        root.destroy()
        third_window(data)
    root = Tk()
    root.geometry("200x200+100+100")

    btn = Button(root, text="Test button")
    btn.bind("<Button-1>", event_btn)   
    btn.place(x=50, y=50)

    root.mainloop()

if __name__ == "__main__":
    main()

I encounter 2 problems: the main window isn't closing after the toplevel is destroyed and the data from toplevel is not returned.


Solution

  • One way to get it to work is simply withdraw the root window instead of destroying it, and use a StringVar to pass the data instead.

    You could also simply rewrite the root with your new layout, if you no longer need what is on it, I would look around at other examples.

    One of the main problems you're facing right now is destroying the TopLevel, before the data is passed to the root, but destroying after the return call will ignore the destroy, and it doesn't like passing the TopLevel to the root, for me anyway.

    I don't really understand why you're preferring <bind> over the command attribute for the buttons.

    from tkinter import *
    
    def third_window(data):
        top = Toplevel()
        lbl = Label(top, text=data)
        lbl.place(x=20,y=20)
        top.wait_window()
    
    def second_window(root, v):
        def event_btn():
            if len(v.get()) != 0:
                top.destroy()
        top = Toplevel()
        top.geometry("400x400+200+200")
    
        entry = Entry(top, textvariable = v, width=15)
        entry.place(x=30,y=30)
        btn = Button(top, text="Send", command = event_btn)
        btn.place(x=80, y=80)
        root.wait_window(top)
    
    def main():
        def event_btn():
            second_window(root, v)
            print(v.get())
            root.withdraw()
            third_window(v.get())
        root = Tk()
        root.geometry("200x200+100+100")
    
        btn = Button(root, text="Test button", command = event_btn) 
        btn.place(x=50, y=50)
    
        v = StringVar()
        root.mainloop()
    
    if __name__ == "__main__":
        main()