I am following the MVC model that Brian made on the answer to “Switch between two frames in tkinter“. He stacks the frames on top of each other (all are made at the very beginning) and then we just show them at our will.
Is it possible to add another frame once the program has begun to run or only with this model you can view the frames than are made at the start? (Thanks to the answers given I was able to figure how to do it)
But there is still an issue with Page Two, as it is not 100 % independent. Everytime it call the frame it does not start from strach.
The following is the modificactions I made to the code.
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):
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()
def add_PageTwo (self):
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
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["PageTwo"] = PageTwo(parent=container, controller=self)
self.frames["PageTwo"].grid(row=0, column=0, sticky="nsew")
self.show_frame("PageTwo")
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)
button2 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
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)
button1 = tk.Button(self, text="Go to the page 2",
command=lambda: controller.add_PageTwo())
button1.pack()
button2 = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button2.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. GREAT", 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()
The error lies at the PageNew
class definition:
class PageNew(tk.Frame, parent, controller):
...
The names in the parentheses are the classes of which PageNew
will inherit, while you probably meant to pass them as parameters of your __init__
method.
At this point of the script, parent
would refer to a module-level variable named parent
.
However, such a variable does not exist, resulting in a NameError
.
You need to remove these, and keep only tk.Frame
.
class PageNew(tk.Frame):
...
As for your question, yes, it is possible to create frames at runtime, and then show them. They don't need to be all created at initialization.