How to access variables from different classes in tkinter?

和自甴很熟 提交于 2019-12-16 20:26:08

问题


I have been searching a lot and I still don't know how yo 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()

回答1:


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.




回答2:


This has to do with the global frame.

If you create a variable inside of a class, it will only exist inside of that function. If you want to 'transfer' a variable inside of a class (or function, for that matter) to the global frame, you use global.

class firstClass():
    global my_var_first
    my_var_first = "first variable"

print(my_var_first) # This will work, because the var is in the global frame

class secondClass():
    my_var_second = "second variable"
    print(my_var_first) # This will work, as the var is in the global frame and not defined in the class

print(my_var_second) # This won't work, because there is no my_var_second in the global frame

To visualise the memory, you can use pythontutor, as it will show you step by step how the memory is created.

I hope I could help you!

EDIT

I think I should add that if you define a variable inside a class/function with the same name as a variable in the global frame, it will not remove the global variable. Instead, it will create a new one (with the same name) in its own frame. A class or function will always use the variable in its own frame if available.

x = 5
def print_variable():
    x = 3
    print(x)
print(x)
print_variable()

# OUTPUT:
# 5
# 3


来源:https://stackoverflow.com/questions/33646605/how-to-access-variables-from-different-classes-in-tkinter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!