¿Cómo acceder a variables de diferentes clases en tkinter?
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.v
de PageOne
clase en PageTwo
clase.
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()
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 PageTwo
necesita una referencia PageOne
para poder obtener la v
variable.
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 = controller
todos __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, PageTwo
puede acceder a la v
variable de PageOne
esta manera:
page1 = self.controller.get_page(PageOne)
page1.v.set("Hello, world")
Usando datos compartidos
Una solución aún mejor es que su SampleApp
clase 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.