¿Cambiar entre dos fotogramas en tkinter?
He creado mis primeros scripts con una pequeña GUI, como me han mostrado los tutoriales, pero ninguno de ellos aborda qué hacer para un programa más complejo.
Si tiene algo con un 'menú de inicio', para su pantalla de inicio, y luego de la selección del usuario, se mueve a una sección diferente del programa y vuelve a dibujar la pantalla de manera apropiada, ¿cuál es la forma elegante de hacer esto?
¿Se debe simplemente crear .destroy()
el marco del 'menú de inicio' y luego crear uno nuevo lleno de widgets para otra parte? ¿Y revertir este proceso cuando presionan el botón Atrás?
Una forma es apilar los marcos uno encima del otro, luego simplemente puede elevar uno sobre el otro en el orden de apilamiento. El de arriba será el que quede visible. Esto funciona mejor si todos los marcos son del mismo tamaño, pero con un poco de trabajo puedes conseguir que funcione con marcos de cualquier tamaño.
Nota : para que esto funcione, todos los widgets de una página deben tener esa página (es decir:) self
o un descendiente como padre (o maestro, según la terminología que prefiera).
A continuación se muestra un ejemplo artificial para mostrarle el concepto general:
try:
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
except ImportError:
import Tkinter as tk # python 2
import tkFont as tkfont # python 2
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Si encuentra confuso el concepto de crear una instancia en una clase, o si diferentes páginas necesitan diferentes argumentos durante la construcción, puede llamar explícitamente a cada clase por separado. El bucle sirve principalmente para ilustrar el hecho de que cada clase es idéntica.
Por ejemplo, para crear las clases individualmente puedes eliminar el bucle ( for F in (StartPage, ...)
con esto:
self.frames["StartPage"] = StartPage(parent=container, controller=self)
self.frames["PageOne"] = PageOne(parent=container, controller=self)
self.frames["PageTwo"] = PageTwo(parent=container, controller=self)
self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
self.frames["PageOne"].grid(row=0, column=0, sticky="nsew")
self.frames["PageTwo"].grid(row=0, column=0, sticky="nsew")
Con el tiempo, la gente ha hecho otras preguntas utilizando este código (o un tutorial en línea que copió este código) como punto de partida. Quizás quieras leer las respuestas a estas preguntas:
- Comprender el padre y el controlador en Tkinter __init__
- ¡Tkinter! Comprender cómo cambiar de marco
- Cómo obtener datos variables de una clase
- Llamar funciones de un marco Tkinter a otro
- ¿Cómo acceder a variables de diferentes clases en tkinter?
- ¿Cómo haría un método que se ejecute cada vez que se muestra un fotograma en tkinter?
- Cambiar el tamaño del marco de Tkinter
- Tkinter tiene código para páginas en archivos separados
- Actualizar un marco de tkinter al presionar un botón
Aquí hay otra respuesta simple, pero sin usar clases.
from tkinter import *
def raise_frame(frame):
frame.tkraise()
root = Tk()
f1 = Frame(root)
f2 = Frame(root)
f3 = Frame(root)
f4 = Frame(root)
for frame in (f1, f2, f3, f4):
frame.grid(row=0, column=0, sticky='news')
Button(f1, text='Go to frame 2', command=lambda:raise_frame(f2)).pack()
Label(f1, text='FRAME 1').pack()
Label(f2, text='FRAME 2').pack()
Button(f2, text='Go to frame 3', command=lambda:raise_frame(f3)).pack()
Label(f3, text='FRAME 3').pack(side='left')
Button(f3, text='Go to frame 4', command=lambda:raise_frame(f4)).pack(side='left')
Label(f4, text='FRAME 4').pack()
Button(f4, text='Goto to frame 1', command=lambda:raise_frame(f1)).pack()
raise_frame(f1)
root.mainloop()