Using the methodology described in this post, I've created a multi-frame application. Without resizing the main window, the transitions from one frame to the next are very smooth, as all of the widgets are pre-loaded upon initializing the application.
However, many of my frames have different sizes, so ideally I would like to be able to set the geometry of the main window according to the requested size of the frame. I've used two methods thus far to achieve this, both working successfully:
- Using
frame.grid_remove()
on the top frame when switching to another, and thenframe.grid()
on the new top frame, as described in this post. - Using the incoming frame's size attributes,
frame.winfo_reqwidth()
andframe.winfo_reqheight()
, and then calling the master window'sgeometry(f'{width}x{height}')
method to set the size.
However, my issue is that this transition, in both cases, is not smooth, and causes visual disruptions when switching frames, including leaving widgets blacked out until the mouse is moved again. I would like to make the resizing process less visually disruptive for my end-users.
import tkinter as tk, tkinter.ttk as ttk
class Application(tk.Tk):
def __init__(self):
super().__init__()
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
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()
The different versions of the Application
method show_frame
I have tried to use are:
1.
class Application(tk.Tk):
def show_frame(self, page_name, frame_removed):
frame = self.frames[page_name]
frame_removed.grid_remove()
frame.grid(sticky="nswe")
class StartPage(ttk.Frame):
def __init__(self, parent, controller):
...
button1 = ttk.Button(self, text="Go to page one",
command=lambda: controller.show_frame(PageOne, self))
button2 = ttk.Button(self, text="Go to page two",
command=lambda: controller.show_frame(PageTwo, self))
# all pages have been modified in the same manner as this
class Application(tk.Tk):
def show_frame(self, page_name):
frame = self.frames[page_name]
width = str(frame.winfo_reqwidth())
height = str(frame.winfo_reqheight())
if width != "1":
self.geometry(f'{width}x{height}')
frame.tkraise()