How to access variables from different classes in tkinter?

18,133

At its core, your question has a simple answer. "How do I get a value from object X?" The answer is the same for any object: you get it by asking object X. All you need in order to do that is get a reference to the object and then access the attribute directly.

Accessing data from other pages

In your case, the code in PageTwo needs a reference to PageOne so you can get the v variable.

So, how do you get a reference? The code (which you copied either from a tutorial, or from the stackoverflow answer that the tutorial copied from) was designed to make this easy. Each page is given a reference to a controller, and this controller has a reference to each page. You can therefore ask the controller to give you a reference to a page.

The first step is to save the reference to the controller in each class. Interestingly, you're already doing this in PageOne, but you should do it in all the pages. Make sure you add self.controller = controller in every __init__ method, like so:

class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        ...
        self.controller=controller
        ...

Next, we need to add a method in the controller class that will return a reference to the page. Add the following function to SampleApp:

class SampleApp(tk.Tk):
    ...
    def get_page(self, page_class):
        return self.frames[page_class]
    ...

Now, from within any "page" you can get access to the object for any other "page". For example, in PageTwo you can access the v variable from PageOne like this:

page1 = self.controller.get_page(PageOne)
page1.v.set("Hello, world")

Using shared data

An even better solution is for your SampleApp class to create a single set of variables that all of the pages share. You can create a dictionary in that class, and then use the controller to give every page access. For example:

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.shared_data = {
            "username": tk.StringVar(),
            "password": tk.StringVar(),
            ...
        )

Then, from within any class you can access the data like this:

entry1 = tk.Entry(self, textvariable=self.controller.shared_data["username"])
...
username = self.controller.shared_data["username"].get()

The reason this is the better solution is that your pages don't have to know how the other pages are implemented. When a page relies on the exact implementation of another page this is called tight coupling. If the pages don't need to know how the other pages are implemented, this is called loose coupling.

Loose coupling gives you more flexibility. Instead of having every page tightly coupled to every other page, they are all tightly coupled to a single object: the controller. As long as every page knows only about the controller, each page is free to be changed at any time without affecting the rest of the program.

Of course, if you change the controller you have to change all of the pages, but if you do a good job designing the controller that's less likely to occur and easier to manage when it does occur.

Share:
18,133
Supachai Abusali
Author by

Supachai Abusali

Updated on July 27, 2022

Comments

  • Supachai Abusali
    Supachai Abusali almost 2 years

    I have been searching a lot and I still don't know how you access variables from different classes in python. In this case I want to access the variable self.v from PageOne class to PageTwo class.

    Here is my code.

    import tkinter as tk
    import smtplib
    
    TITLE_FONT = ("Helvetica", 18, "bold")
    
    class SampleApp(tk.Tk):
    
        def __init__(self):
            tk.Tk.__init__(self)
    
            container = tk.Frame(self)
            container.pack(side="top", fill="both", expand=True)
            container.grid_rowconfigure(0, weight=1)
            container.grid_columnconfigure(0, weight=1)
    
            self.frames = {}
            for F in (StartPage, PageOne, PageTwo):
                frame = F(container, self)
                self.frames[F] = frame
    
                frame.grid(row=0, column=0, sticky="nsew")
    
            self.show_frame(StartPage)
    
        def show_frame(self, c):
            frame = self.frames[c]
            frame.tkraise()
    
    class StartPage(tk.Frame):
    
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            label = tk.Label(self, text="PyMail",foreground = "Red", font=("Courier", 30, "bold"))
            label.pack(side="top")
            sublabel = tk.Label(self, text="Bringing you the\n the easiest way of communication",
                                font=("Courier", 15))
            sublabel.pack()
    
            wallpaper = tk.PhotoImage(file='Python-logo-notext.gif')
            img = tk.Label(self, image=wallpaper)
            img.image = wallpaper
            img.pack(side="top", expand = True)
    
            button1 = tk.Button(self, text="Click Here to Login to your account",fg="red",
                                command=lambda: controller.show_frame(PageOne))
            button2 = tk.Button(self, text="Go to Page Two",
                                command=lambda: controller.show_frame(PageTwo))
            button2.pack(side="bottom")
            button1.pack(side="bottom")
    
    class PageOne(tk.Frame):
    
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            self.controller=controller
            label = tk.Label(self, text="Personal Information", font=TITLE_FONT, foreground="blue")
            label.pack(side="top", fill="x", pady=10)
            global optionv
            self.optionv = tk.StringVar()
            self.optionv.set("---Select One---")
            optionm = tk.OptionMenu(self, self.optionv, "---Select One---", "@gmail.com", "@yahoo.com", "@hotmail.com")
    
            t1 = tk.Label(self, text="Email Account: ")
            self.v = tk.StringVar()
            self.v.set("")
            entry1 = tk.Entry(self, textvariable=self.v)
            t2 = tk.Label(self,text="\nPassword: ")
            self.pwd = tk.StringVar()
            self.pwd.set("")
            entry2 = tk.Entry(self, textvariable=self.pwd)
            entry2.config(show="*")
            lgbutton=tk.Button(self, text="Log In", command=self.login) 
            button = tk.Button(self, text="Go to the start page",
                               command=lambda: controller.show_frame(StartPage))
            #final = tk.Label(self, textvariable=self.v)
            #finalpwd = tk.Label(self, textvariable=self.pwd)
    
            t1.pack()
            entry1.pack()
            optionm.pack()
            t2.pack()
            entry2.pack()
            #final.pack()
            #finalpwd.pack()
            lgbutton.pack()
            button.pack(side="bottom")
    
        def login(self):
            value = tk.Label(self, text="Invalid username / password", font=("Courier", 15, "bold"), foreground="red")
            success = tk.Label(self, text="Login was Successful \n (Click ""Continue"" to compose email)", font=("Courier", 15, "bold"), foreground="blue")
            cbutton = tk.Button(self, text="Continue", command=lambda: self.controller.show_frame(PageTwo))
            status = tk.Label(self, text="Please select your email domain", foreground="red")
            if self.optionv.get() == "@gmail.com":
                try:
                    global server
                    server = smtplib.SMTP("smtp.gmail.com", 587)
                    server.ehlo()
                    server.starttls()
                    server.login(self.v.get()+self.optionv.get(), self.pwd.get())
                    success.pack()
                    cbutton.pack(side="bottom")
                except:
                    value.pack() 
            elif self.optionv.get() == "@yahoo.com":
                try:
                    server = smtplib.SMTP("smtp.yahoo.com", 587)
                    server.ehlo()
                    server.starttls()
                    server.login(self.v.get()+self.optionv.get(), self.pwd.get())
                    success.pack()
                    cbutton.pack(side="bottom")
                except:
                    value.pack()
    
            elif self.optionv.get() == "@hotmail.com":
                try:
                    server = smtplib.SMTP("smtp.live.com", 587)
                    server.ehlo()
                    server.starttls()
                    server.login(self.v.get()+self.optionv.get(), self.pwd.get())
                    success.pack()
                    cbutton.pack(side="bottom")
                except:
                    value.pack()
            else:
                status.pack()
    
    class PageTwo(tk.Frame): 
    
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            label = tk.Label(self, text="Compose Mail", font=TITLE_FONT, foreground="green") 
            label.pack(side="top", fill="x", pady=10)
    
            self.reciever = tk.StringVar()
            self.reciever.set("")
            senderl = tk.Label(self, text="Send to: ")
            rmail = tk.Entry(self, textvariable=self.reciever)
    
            self.senderoption = tk.StringVar()
            self.senderoption.set("---Select One---")
            senderdomain = tk.OptionMenu(self, self.senderoption, "---Select One---", "@gmail.com", "@hotmail.com", "@yahoo.com")
    
            self.mail = tk.StringVar()
            self.mail.set("")
            self.textw = tk.Entry(self, textvariable=self.mail)
    
            button = tk.Button(self, text="Go to the start page",
                               command=lambda: controller.show_frame(StartPage))
    
            sendbutton = tk.Button(self, text = "Send Mail", command=self.sendmail)
    
            senderl.pack(side="top", anchor="w")
            rmail.pack(side="top", anchor="nw")
            senderdomain.pack(side="top", anchor="nw")
            self.textw.pack(fill="both")
            button.pack(side="bottom")
            sendbutton.pack(side="bottom")
    
        def sendmail(self):
            sent = tk.Label(self, text="Email has been sent")
            if self.senderoption.get() == "@gmail.com":
                try: 
                    server.sendmail(self.v.get()+self.optionv.get(), self.reciever.get()+self.senderoption.get(), "YES")
                    print("Success")
                    sent.pack()
                except:
                    print("Unsuccesful")
                    print(PageOne.self.v.get())
    
    if __name__ == "__main__":
        app = SampleApp()
        app.title("PyMail")
        app.geometry("400x400")
        app.mainloop()
    
  • Russell Smith
    Russell Smith over 8 years
    The question as written has nothing to do with the global frame. Using globals is a workaround, but it's not a proper fix.
  • Martijn Luyckx
    Martijn Luyckx over 8 years
    @BryanOakley After reading your solution, I get what you're saying. I may have misinterpreted the question it seems. Thanks for your feedback!
  • Supachai Abusali
    Supachai Abusali over 8 years
    @MartijnLuyckx thank you for your advice but I already tried that before posting the question and it didnt seem to work
  • Martijn Luyckx
    Martijn Luyckx over 8 years
    @SupachaiAbusali That is indeed quite weird! I'm sorry that I could not help.
  • Supachai Abusali
    Supachai Abusali over 8 years
    @MartijnLuyckx no worriess... your contribution was very much appreciated
  • Martijn Luyckx
    Martijn Luyckx over 8 years
    @SupachaiAbusali That pleases me, I'm glad I could contribute :)
  • Supachai Abusali
    Supachai Abusali over 8 years
    Thank you so much for your help, I have always wondered what the "controller" was and you did a great job in explaining it.. Thank You so much
  • Russell Smith
    Russell Smith over 8 years
    @SupachaiAbusali: if you find this answer, and answers to other questions that you've asked, please consider voting for them and/or selecting one answer. See stackoverflow.com/help/someone-answers
  • Yunus
    Yunus over 5 years
    Hi would you please answer this question regarding tkinter ? I have tried for hours but no luck. stackoverflow.com/questions/53942023/…
  • martineau
    martineau over 4 years
    Martijn: Global variables are generally considered a poor programming practice (see Why are global variables evil? or Why is Global State so Evil?) and their use should be discouraged IMO. Don't they teach Computer Science students this at your university?
  • Martijn Luyckx
    Martijn Luyckx over 4 years
    @martineau When I posted this answer (4 years ago), I had only been enrolled in university for two months. They do absolutely teach about programming practices, but I had to get the basics down first (which I was still doing back then). I was definitely not in a position to answer this question correctly and thoroughly, but I didn't know that yet back then due to a lack of knowing what I didn't know yet.