¿Cómo acceder a variables de diferentes clases en tkinter?

Resuelto Supachai Abusali asked hace 9 años • 2 respuestas

He estado buscando mucho y todavía no sé cómo se accede a variables de diferentes clases en Python. En este caso quiero acceder a la variable self.vde PageOneclase en PageTwoclase.

Aquí está mi código.

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()
Supachai Abusali avatar Nov 11 '15 15:11 Supachai Abusali
Aceptado

En esencia, su pregunta tiene una respuesta simple. "¿Cómo obtengo un valor del objeto X?" La respuesta es la misma para cualquier objeto: la obtienes preguntándole al objeto X. Todo lo que necesitas para hacerlo es obtener una referencia al objeto y luego acceder al atributo directamente.

Acceder a datos de otras páginas

En su caso, el código PageTwonecesita una referencia PageOnepara poder obtener la vvariable.

Entonces, ¿cómo se obtiene una referencia? El código (que copió de un tutorial o de la respuesta de stackoverflow de la que copió el tutorial) fue diseñado para facilitar esto. A cada página se le asigna una referencia a un controlador, y este controlador tiene una referencia a cada página. Por lo tanto, puede pedirle al controlador que le proporcione una referencia a una página.

El primer paso es guardar la referencia al controlador en cada clase. Curiosamente, ya estás haciendo esto en PageOne, pero deberías hacerlo en todas las páginas. Asegúrate de agregar self.controller = controllertodos __init__los métodos, así:

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

A continuación, debemos agregar un método en la clase del controlador que devolverá una referencia a la página. Agregue la siguiente función a SampleApp:

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

Ahora, desde cualquier "página" puede obtener acceso al objeto de cualquier otra "página". Por ejemplo, PageTwopuede acceder a la vvariable de PageOneesta manera:

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

Usando datos compartidos

Una solución aún mejor es que su SampleAppclase cree un único conjunto de variables que compartan todas las páginas. Puede crear un diccionario en esa clase y luego usar el controlador para otorgar acceso a cada página. Por ejemplo:

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

Luego, desde cualquier clase puedes acceder a datos como este:

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

La razón por la que esta es la mejor solución es que sus páginas no necesitan saber cómo se implementan las otras páginas. Cuando una página depende de la implementación exacta de otra página, esto se denomina acoplamiento estrecho . Si las páginas no necesitan saber cómo se implementan las otras páginas, esto se denomina acoplamiento flexible .

El acoplamiento flojo le brinda más flexibilidad. En lugar de tener cada página estrechamente acoplada entre sí, todas están estrechamente acopladas a un solo objeto: el controlador. Siempre que cada página conozca únicamente el controlador, cada página se puede cambiar en cualquier momento sin afectar el resto del programa.

Por supuesto, si cambia el controlador, tendrá que cambiar todas las páginas, pero si hace un buen trabajo diseñando el controlador, es menos probable que eso ocurra y será más fácil de gestionar cuando ocurra.

Bryan Oakley avatar Nov 11 '2015 12:11 Bryan Oakley