Cambiar entre dos cuadros en tkinter

Construí mis primeros scripts con una GUI pequeña y bonita en ellos, como me mostraron los tutoriales, pero ninguno de ellos aborda qué hacer para un progtwig más complejo.

Si tiene algo con un ‘menú de inicio’, para su pantalla inicial, y al seleccionar el usuario, se mueve a una sección diferente del progtwig y vuelve a dibujar la pantalla de manera apropiada, ¿cuál es la forma elegante de hacerlo?

¿Se acaba de .destroy() El marco del ‘menú de inicio’ y luego se crea uno nuevo con los widgets para otra parte? ¿Y revertir este proceso cuando presionan el botón Atrás?

Una forma es astackr los marcos uno encima del otro, luego simplemente puede levantar uno encima del otro en el orden de astackmiento. El de arriba será el que se vea. Esto funciona mejor si todos los marcos son del mismo tamaño, pero con un poco de trabajo puede hacer 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).

Aquí hay un poco de un ejemplo artificial para mostrarle el concepto general:

 import tkinter as tk # python 3 from tkinter import font as tkfont # python 3 #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() 

página de inicio Página 1 página 2

Si encuentra confuso el concepto de crear una instancia en una clase, o si diferentes páginas necesitan argumentos diferentes durante la construcción, puede llamar explícitamente a cada clase por separado. El bucle sirve principalmente para ilustrar el punto en que cada clase es idéntica.

Por ejemplo, para crear las clases individualmente, puede 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, las personas han hecho otras preguntas utilizando este código (o un tutorial en línea que copió este código) como punto de partida. Es posible que desee leer las respuestas a estas preguntas:

  • Entendiendo el padre y el controlador en Tkinter __init__
  • Tkinter! Entender cómo cambiar marcos
  • Cómo obtener datos variables de una clase
  • Llamando funciones desde un Tkinter Frame a otro
  • ¿Cómo acceder a variables de diferentes clases en tkinter?
  • ¿Cómo puedo hacer un método que se ejecuta cada vez que se muestra un marco en tkinter
  • Tkinter Frame Resize
  • Tkinter tiene código para páginas en archivos separados.
  • Actualizar un marco 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() 

Para cambiar los marcos en tkinter , destruya el marco anterior y luego reemplácelo con su nuevo marco.

Si bien el astackmiento de cuadros de Bryan Oakley es una solución inteligente, mantiene todos los cuadros activos a la vez. Esto tiene un efecto secundario no deseado que permite a los usuarios seleccionar widgets de otros marcos presionando la tecla Tab .

He modificado la respuesta de Bryan para destruir el marco anterior antes de reemplazarlo. Como beneficio adicional, esto elimina la necesidad de un objeto container y le permite usar cualquier clase de Frame genérico.

 # Multi-frame tkinter application v2.3 import tkinter as tk class SampleApp(tk.Tk): def __init__(self): tk.Tk.__init__(self) self._frame = None self.switch_frame(StartPage) def switch_frame(self, frame_class): """Destroys current frame and replaces it with a new one.""" new_frame = frame_class(self) if self._frame is not None: self._frame.destroy() self._frame = new_frame self._frame.pack() class StartPage(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10) tk.Button(self, text="Open page one", command=lambda: master.switch_frame(PageOne)).pack() tk.Button(self, text="Open page two", command=lambda: master.switch_frame(PageTwo)).pack() class PageOne(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10) tk.Button(self, text="Return to start page", command=lambda: master.switch_frame(StartPage)).pack() class PageTwo(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10) tk.Button(self, text="Return to start page", command=lambda: master.switch_frame(StartPage)).pack() if __name__ == "__main__": app = SampleApp() app.mainloop() 

Página de inicio Página uno Página dos

Explicación

switch_frame() funciona aceptando cualquier objeto de clase que implementa Frame . La función crea un nuevo marco para reemplazar el anterior.

  • Borra el antiguo _frame si existe, luego lo reemplaza con el nuevo frame.
  • Otros marcos agregados con .pack() , como las barras de menú, no se verán afectados.
  • Se puede utilizar con cualquier clase que implemente tkinter.Frame .
  • La ventana cambia automáticamente de tamaño para adaptarse al nuevo contenido

Historial de versiones

 v2.3 - Pack buttons and labels as they are initialized v2.2 - Initialize `_frame` as `None`. - Check if `_frame` is `None` before calling `.destroy()`. v2.1.1 - Remove type-hinting for backwards compatibility with Python 3.4. v2.1 - Add type-hinting for `frame_class`. v2.0 - Remove extraneous `container` frame. - Application now works with any generic `tkinter.frame` instance. - Remove `controller` argument from frame classes. - Frame switching is now done with `master.switch_frame()`. v1.6 - Check if frame attribute exists before destroying it. - Use `switch_frame()` to set first frame. v1.5 - Revert 'Initialize new `_frame` after old `_frame` is destroyed'. - Initializing the frame before calling `.destroy()` results in a smoother visual transition. v1.4 - Pack frames in `switch_frame()`. - Initialize new `_frame` after old `_frame` is destroyed. - Remove `new_frame` variable. v1.3 - Rename `parent` to `master` for consistency with base `Frame` class. v1.2 - Remove `main()` function. v1.1 - Rename `frame` to `_frame`. - Naming implies variable should be private. - Create new frame before destroying old frame. v1.0 - Initial version.