Home > Software design >  How to transfer data between Toplevel widgets (OOP):
How to transfer data between Toplevel widgets (OOP):

Time:08-11

I am beginner to Python language !

I designed three windows by three classes. First window is the main window and it is on the first class. And the others defined as toplevel widgets by another two classes. I need to send data which input to the second window (first toplevel widget) and get (view) it from the third window (second toplevel widget). So, to do that I used the - getattr - function.

But it gaven some (AttributeError) error :


d = self.var1.get()

File "C:\Python\Python310\lib\tkinter_init_.py", line 2383, in getattr

return getattr(self.tk, attr)

AttributeError: '_tkinter.tkapp' object has no attribute 'var1'


from tkinter import *
import tkinter as tk


class obj_1(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title("TK WINDOW 01")
        self.geometry("300x150 10 10")

        btn_next = tk.Button(self, text="Second Window", width=15, height=1, command=lambda: obj_2())
        btn_next.pack(pady=20)

class obj_2(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)
       
        self.title("TK WINDOW 02")
        self.geometry("400x100 300 300")

        self.var1 = tk.StringVar()     

        btn_next = tk.Button(self, text="-->", width=15, height=1, command=lambda: obj_3())
        btn_next.pack()

        self.lbl_ent_1 = tk.Entry(self, textvariable=self.var1, width=15)
        self.lbl_ent_1.pack(pady=20)
        
        self.lbl_ent_1.focus()

    def get_from_second(self):
        d = self.var1.get()        
        return d


class obj_3(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)

        self.title("TK WINDOW 03")
        self.geometry("400x100 800 300")

        self.var2 = tk.StringVar()

        btn_view = tk.Button(self, text="View", width=15, height=1, command=lambda: self.get_from_third())
        btn_view.pack()

        self.lbl_vw = tk.Label(self, textvariable=self.var2, width=15, height=1, pady=20)
        self.lbl_vw.pack()

    def get_from_third(self):
        p = getattr(obj_2, 'get_from_second')(self)

        self.var2.set(p)
        print(p)

if __name__ == '__main__':
    app = obj_1()
    app.mainloop()

CodePudding user response:

I commented the changes where they were made, but in short: you had the wrong super for both of your toplevel windows, with the proper super you get a master to refer to. Too much juggling (C from A from B). My example is direct (B from A) then (C from A). You don't even need the get_from_second because it has been obsoleted by a virtual get_directly_from_root. In other words, store your var on the root and then refer to it via master.

from tkinter import *
import tkinter as tk


class obj_1(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title("TK WINDOW 01")
        self.geometry("300x150 10 10")

        btn_next = tk.Button(self, text="Second Window", width=15, height=1, command=lambda: obj_2(self))
        btn_next.pack(pady=20)
        
        self.var1 = tk.StringVar() #store the var on the master


class obj_2(tk.Toplevel): #Toplevel not Tk
    def __init__(self, master, *args, **kwargs): #master will be your main Tk window
        tk.Toplevel.__init__(self, master, *args, **kwargs)
       
        self.title("TK WINDOW 02")
        self.geometry("400x100 300 300")

        btn_next = tk.Button(self, text="-->", width=15, height=1, command=lambda: obj_3(master))
        btn_next.pack()

        self.lbl_ent_1 = tk.Entry(self, textvariable=master.var1, width=15) #<--var from master
        self.lbl_ent_1.pack(pady=20)
        
        self.lbl_ent_1.focus()



class obj_3(tk.Toplevel): #Toplevel not Tk
    def __init__(self, master, *args, **kwargs): #master will be your main Tk window
        tk.Toplevel.__init__(self, master, *args, **kwargs)

        self.title("TK WINDOW 03")
        self.geometry("400x100 800 300")

        self.var2 = tk.StringVar()

        btn_view = tk.Button(self, text="View", width=15, height=1, command=lambda: self.get_from_third())
        btn_view.pack()

        self.lbl_vw = tk.Label(self, textvariable=self.var2, width=15, height=1, pady=20)
        self.lbl_vw.pack()

    def get_from_third(self):
        self.var2.set(self.master.var1.get())

if __name__ == '__main__':
    app = obj_1()
    app.mainloop()

I left your code mostly in tact but consider the below. Your button is entirely unnecessary, you can just automatically have the data by sharing var1 from master.


class obj_3(tk.Toplevel): #Toplevel not Tk
    def __init__(self, master, *args, **kwargs): #master will be your main Tk window
        tk.Toplevel.__init__(self, master, *args, **kwargs)

        self.title("TK WINDOW 03")
        self.geometry("400x100 800 300")

        self.lbl_vw = tk.Label(self, textvariable=master.var1, width=15, height=1, pady=20)
        self.lbl_vw.pack()

Aside: All this obj_x naming is very poor practice. Give your classes meaningful names that give an indication of what they do. The same goes for varx, and any other name you make which is entirely arbitrary. Imagine if tkinter named everything Widget1, Widget2, etc...How could you possibly work with that? The same goes for your code. You keep naming everything arbitrarily and you wont be able to work with your own code.

What is vw in self.lbl_vw? VariableWindow, perhaps? That sounds like a great name for that class. While you're at it, stop naming your classes with definition syntax. Even if you kept the poor obj_x naming it should still, at least, be: Obj_x.

Not really a question:

Why are you opening a 3rd window to display a variable that was just typed, and why would you have to click a button to see it? Again, I'm not really asking this. I want you to ask yourself that. Now that I've boiled your code down to one var, your app doesn't make any sense in design. Your app is currently 3 entire windows built around 1 variable. It's probably time to reconsider what it is you are actually trying to do.

Imagine if you opened your browser, had to click a search button which opens up a query window, and then click another button which opens ANOTHER window with the page in it. It looks like you are essentially building that. It's a bad design.

  • Related