如何访问 tkinter 中不同类的变量?

2022-01-31 00:00:00 python python-3.x tkinter user-interface

问题描述

我已经搜索了很多,但我仍然不知道您如何访问 python 中不同类的变量.在这种情况下,我想从 PageOne 类访问变量 self.vPageTwo 类.

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.

这是我的代码.

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
 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="
Password: ")
        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 
 (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()


解决方案

从本质上讲,您的问题有一个简单的答案.我如何从对象 X 中获取值?"任何对象的答案都是相同的:您可以通过询问对象 X 来获得它.为此,您需要获取对该对象的引用,然后直接访问该属性.

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.

在您的情况下,PageTwo 中的代码需要对 PageOne 的引用,因此您可以获得 v 变量.

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

那么,如何获得参考?代码(您从教程或从教程复制的 stackoverflow 答案中复制)旨在使这很容易.每个页面都有一个对控制器的引用,而这个控制器对每个页面都有一个引用.因此,您可以要求控制器为您提供对页面的引用.

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.

第一步是在每个类中保存对控制器的引用.有趣的是,您已经在 PageOne 中执行此操作,但您应该在所有页面中执行此操作.确保在每个 __init__ 方法中添加 self.controller = controller,如下所示:

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
        ...

接下来,我们需要在控制器类中添加一个方法,该方法将返回对页面的引用.将以下函数添加到 SampleApp:

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]
    ...

现在,您可以从任何页面"中访问任何其他页面"的对象.例如,在 PageTwo 中,您可以像这样从 PageOne 访问 v 变量:

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")

使用共享数据

更好的解决方案是让您的 SampleApp 类创建一组所有页面共享的变量.您可以在该类中创建一个字典,然后使用控制器授予每个页面访问权限.例如:

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.

相关文章