Как получить доступ к переменным из разных классов в tkinter?

Я много искал и до сих пор не знаю, как получить доступ к переменным из разных классов в Python ... в этом случае я хочу получить доступ к переменной self.v от PageOne класс для PageTwo класс.. пожалуйста, помогите мне

Вот мой код...

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

2 ответа

Решение

По сути, ваш вопрос имеет простой ответ. "Как я могу получить значение от объекта X?" Ответ одинаков для любого объекта: вы получаете его, запрашивая объект X. Все, что вам нужно для этого, это получить ссылку на объект и затем напрямую получить доступ к атрибуту.

Доступ к данным с других страниц

В вашем случае код в PageTwo нужна ссылка на PageOne так что вы можете получить v переменная.

Итак, как вы получаете ссылку? Код (который вы скопировали либо из учебного пособия, либо из ответа на стековый поток, из которого скопировано учебное пособие) был разработан, чтобы упростить это. Каждая страница имеет ссылку на контроллер, а этот контроллер имеет ссылку на каждую страницу. Поэтому вы можете попросить диспетчера дать вам ссылку на страницу.

Первый шаг - сохранить ссылку на контроллер в каждом классе. Интересно, что вы уже делаете это в PageOne, но вы должны сделать это на всех страницах. Убедитесь, что вы добавили self.controller = controller в каждом __init__ метод, вот так:

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

Далее нам нужно добавить метод в класс контроллера, который будет возвращать ссылку на страницу. Добавьте следующую функцию SampleApp:

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

Теперь из любой "страницы" вы можете получить доступ к объекту для любой другой "страницы". Например, в PageTwo Вы можете получить доступ к v переменная от PageOne как это:

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

Использование общих данных

Еще лучшее решение для вас SampleApp класс для создания единого набора переменных, которые разделяют все страницы. Вы можете создать словарь в этом классе, а затем использовать контроллер для предоставления доступа к каждой странице. Например:

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

Затем из любого класса вы можете получить доступ к данным следующим образом:

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

Причина, по которой это лучшее решение, заключается в том, что ваши страницы не должны знать, как реализованы другие страницы. Когда страница зависит от точной реализации другой страницы, это называется тесной связью. Если страницам не нужно знать, как реализованы другие страницы, это называется слабой связью.

Слабое сцепление дает вам больше гибкости. Вместо того, чтобы каждая страница была тесно связана с каждой другой страницей, они все тесно связаны с одним объектом: контроллером. Поскольку каждая страница знает только о контроллере, каждая страница может быть изменена в любое время, не затрагивая остальную часть программы.

Конечно, если вы меняете контроллер, вам нужно изменить все страницы, но если вы хорошо поработали над дизайном контроллера, это менее вероятно, и его будет легче контролировать, когда это произойдет.

Это связано с глобальной рамкой.

Если вы создадите переменную внутри класса, она будет существовать только внутри этой функции. Если вы хотите "передать" переменную внутри класса (или, если на то пошло, функцию) в глобальный фрейм, вы используете 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

Для визуализации памяти вы можете использовать pythontutor, так как он покажет вам шаг за шагом, как создается память.

Я надеюсь, что смогу вам помочь!

РЕДАКТИРОВАТЬ

Я думаю, я должен добавить, что если вы определяете переменную внутри класса / функции с тем же именем, что и переменная в глобальном фрейме, она не удалит глобальную переменную. Вместо этого он создаст новый (с тем же именем) в своем собственном фрейме. Класс или функция всегда будут использовать переменную в своем собственном фрейме, если он доступен.

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

# OUTPUT:
# 5
# 3
Другие вопросы по тегам