Los marcos no se astackn uno encima del otro en tkinter

Tengo un problema al astackr ‘Páginas’ una encima de la otra en tkinter.

Tengo un Frame principal que contiene dos sub frames que contienen información diferente. El primer sub marco contiene un Listbox y un par de botones y se empaqueta a la izquierda en el marco principal. Se supone que el segundo fotogtwig contiene diferentes ‘Páginas’ (dos por ahora) y hace que llenen todo el fotogtwig. Mi problema es que ambas ‘Páginas’ se muestran una al lado de la otra en lugar de una encima de la otra.

 import tkinter as tk class Settings(tk.Tk): def __init__(self, master=None): tk.Tk.__init__(self, master) self.focus_force() self.grab_set() # set focus to settings window # Main window title self.title("Settings") # set up grid containers container_main = tk.Frame(self, width=500, height=700) container_main.pack(side='top', fill='both', expand=True) container_main.grid_rowconfigure(0, weight=1) container_main.grid_columnconfigure(0, weight=1) container_listbox = tk.Frame(container_main, bg='blue', width=200, height=700) container_listbox.pack(side='left', fill='both', expand=True) container_listbox.grid_rowconfigure(0, weight=1) container_listbox.grid_columnconfigure(0, weight=1) container_settings = tk.Frame(container_main, bg='red', width=300, height=700) container_settings.pack(side='right', fill='both', expand=True) container_settings.grid_rowconfigure(0, weight=1) container_settings.grid_columnconfigure(0, weight=1) # build settings pages self.frames = {} self.frames["Options"] = Options(parent=container_listbox, controller=self) self.frames["General"] = General(parent=container_settings, controller=self) self.frames["Future"] = Future(parent=container_settings, controller=self) 

Si no me acuerdo estas dos líneas. Recibo un error que dice que no puedo usar la cuadrícula del administrador de geometría dentro.

  # self.frames["General"].grid(row=0, column=0, sticky='nsew') # self.frames["Future"].grid(row=0, column=0, sticky='nsew') 

.

  def show_frame(self, page_name): frame = self.frames[page_name] frame.tkraise() class Options(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(parent, text='List Box') label.grid(row=0, column=0, sticky='nsew', padx=1, pady=1) button1 = tk.Button(parent, text='General', command=lambda: controller.show_frame('General')) button2 = tk.Button(parent, text='Future', command=lambda: controller.show_frame('Future')) button1.grid(row=1, column=0, sticky='ew') button2.grid(row=2, column=0, sticky='ew') class General(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(parent, text='General') label.pack(side='left', fill='both', expand=True, ) print("Hi I'm General") class Future(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(parent, text='Future') label.pack(side='left', fill='both', expand=True) print("Hi I'm Future") app = Settings() app.mainloop() 

Ambas ‘Páginas’ se inicializan y se muestran al mismo tiempo, lo que tiene sentido. Simplemente no sé cómo hacer que una suba sobre la otra ya que frame.tkraise() supone que debe hacer esto, pero no es así. También me gustaría poder hacer grid_forget() en la página o las páginas que no están en la parte superior para evitar potencialmente ingresar valores en un cuadro de entrada oculto en el futuro.

EDITAR: Si comento la página ‘Futuro’, la página ‘General’ ocupará todo el espacio del marco, por lo que con grid_forget() obtendría el mismo resultado. Simplemente no sé dónde lo haría pero grid_forget() y luego, ¿dónde podría volver a configurar o hacer una llamada a grid() ?

Mi problema es que ambas ‘Páginas’ se muestran una al lado de la otra en lugar de una encima de la otra.

Si usa pack() para colocar un marco en la ventana raíz y luego usa grid() dentro de ese marco, entonces funcionará, pero si intenta usar pack() dentro de un marco y luego intenta usar grid() dentro de ese mismo cuadro fallará.

Lo mismo ocurre con la ventana raíz y los marcos. Si pack() un marco en la ventana raíz, entonces no puede usar grid() para colocar nada en esa misma ventana raíz.

El problema con el problema grid() vs pack() se debió a que la ubicación donde la clase General y la clase Future configuraban los widgets de tags era el marco principal donde se estaba usando pack() . Esto impidió el uso de grid() en ese mismo marco principal para colocar los marcos General y Futuro.

Para arreglar esto cambiamos:

 label = tk.Label(parent, text='General') and label = tk.Label(parent, text='Future') 

a:

 label = tk.Label(self, text='General') and label = tk.Label(self, text='Future') 

Lo anterior fue la única solución necesaria para que esto funcione correctamente.

 import tkinter as tk class Settings(tk.Tk): def __init__(self, master=None): tk.Tk.__init__(self, master) self.focus_force() self.grab_set() # set focus to settings window # Main window title self.title("Settings") container_main = tk.Frame(self, width=500, height=700) container_main.pack(side='top', fill='both', expand=True) container_main.grid_rowconfigure(0, weight=1) container_main.grid_columnconfigure(0, weight=1) container_listbox = tk.Frame(container_main, bg='blue', width=200, height=700) container_listbox.pack(side='left', fill='both', expand=True) container_listbox.grid_rowconfigure(0, weight=1) container_listbox.grid_columnconfigure(0, weight=1) container_settings = tk.Frame(container_main, bg='red', width=300, height=700) container_settings.pack(side='right', fill='both', expand=True) container_settings.grid_rowconfigure(0, weight=1) container_settings.grid_columnconfigure(0, weight=1) self.frames = {} self.frames["Options"] = Options(parent=container_listbox, controller=self) self.frames["General"] = General(parent=container_settings, controller=self) self.frames["Future"] = Future(parent=container_settings, controller=self) self.frames["General"].grid(row=0, column=0, sticky='nsew') self.frames["Future"].grid(row=0, column=0, sticky='nsew') def show_frame(self, page_name): frame = self.frames[page_name] frame.tkraise() class Options(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(parent, text='List Box') label.grid(row=0, column=0, sticky='nsew', padx=1, pady=1) button1 = tk.Button(parent, text='General', command=lambda: controller.show_frame('General')) button2 = tk.Button(parent, text='Future', command=lambda: controller.show_frame('Future')) button1.grid(row=1, column=0, sticky='ew') button2.grid(row=2, column=0, sticky='ew') class General(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(self, text='General') label.pack(side='left', fill='both', expand=True) print("Hi I'm General") class Future(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller label = tk.Label(self, text='Future') label.pack(side='left', fill='both', expand=True) print("Hi I'm Future") app = Settings() app.mainloop() 

Me he tomado la libertad de reestructurar la lógica de su aplicación para transmitirle cómo podría ser más fácil resolver su problema; también he incluido información adicional.

El problema con su implementación actual en su capacidad para hide y show sus páginas, está en la asignación de palabras clave de su command para sus botones. En la implementación que proporcioné, la clase de Options es donde ocurre toda la magia, específicamente, los métodos show_future y show_future . Simplemente, cuando se hace clic en el botón general, invoco pack_forget() en el widget future_page , pack(...) en el widget general_page , y viceversa cuando quiero mostrar la página futura. (Esta implementación se puede escalar fácilmente, pero dejaré eso a tu disposición).

Además, estructuré la lógica de la aplicación de manera que modularizase el código, de modo que en cada paso pudiera probarlo y ver dónde estaba, por ejemplo, comencé en main() creando cada contenedor y asegurándome de que todo estuviera correctamente diseñado. , (Me gusta dar a cada contenedor un color diferente, facilitando la visualización del espaciado, y así) antes de pasar a los marcos de Settings y Options donde ubicaría los widgets, y escribiría el código necesario para que la GUI funcione como es debido .

No explicaré todas las líneas, pero estoy seguro de que en este punto puedes ver a qué me refiero. Recomiendo leer el código (comenzando con main() ) y descubrir cómo y por qué lo escribí de la manera que lo hice. (Escribí esto para funcionar como creo que pretendías, así como para ofrecer algunas sugerencias aquí y allá, no pretende ser perfecto).

 import tkinter as tk class Options(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#FFFFFF', relief='ridge', bd=1) # Grab the 'Settings' tk.Frame object settings_frame = self.master.master.winfo_children()[1].winfo_children()[0] self.settings = settings_frame self.options() def options(self): self.label = tk.Label(self, text='List Box') self.button1 = tk.Button(self, text='General', command=self.show_general) self.button2 = tk.Button(self, text='Future', command=self.show_future) self.label.grid(row=0, sticky='nsew', padx=1, pady=1) # column is set to 0 by default self.button1.grid(row=1, sticky='nsew', padx=1, pady=1) self.button2.grid(row=2, sticky='nsew', padx=1, pady=1) def show_general(self): self.settings.future_page.pack_forget() self.settings.general_page.pack(fill='both', expand=True) def show_future(self): self.settings.general_page.pack_forget() self.settings.future_page.pack(fill='both', expand=True) class Settings(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master, bg='#FFFFFF', relief='ridge', bd=1) self.pages() def pages(self): self.general_page = tk.Label(self, fg='#FFFFFF', bg='#FF0000', relief='ridge', bd=1, text="Hi, I'm General.") self.future_page = tk.Label(self, fg='#FFFFFF', bg='#0000FF', relief='ridge', bd=1, text="Hi, I'm Future.") self.general_page.pack(fill='both', expand=True) def main(): root = tk.Tk() root.title('Settings') root.configure(bg='#DDDDDD', width=500, height=500) root.resizable(width=False, height=False) main_container = tk.Frame(root, bg='#FFFFFF', width=500, height=500) main_container.pack(fill='both', padx=20, pady=20) # Add some padding to see container difference main_container.pack_propagate(False) # Avoid sizing based on widget contents listbox_left = tk.Frame(main_container, bg='#4285F4', width=235) # Take 15 from both sides for padding settings_right = tk.Frame(main_container, bg='#272727', width=235) listbox_left.pack(side='left', fill='y', padx=(10, 0), pady=10) listbox_left.pack_propagate(False) settings_right.pack(side='right', fill='y', padx=(0, 10), pady=10) settings_right.pack_propagate(False) settings = Settings(settings_right) # Must be instantiated before Options options = Options(listbox_left) settings.pack(fill='both', expand=True, padx=2, pady=2) options.pack(fill='both', expand=True, padx=2, pady=2) root.mainloop() if __name__ == '__main__': main() 

¡Espero que esto ayude!