I'm building a python app using tkinter(python 3.9.7) and I'm struggling to make a functionnal sidebar navigation. As you'll see in the code below i use a class inheriting from tk.Tk for the main windows, inside of that I instantiate an instance of a specific class inheriting from tk.Frame for each page i need. Inside thoses objects I instantiate an instance of the SideBar class inheriting from tk.Frame too.
Since I switched to a specific class for the sidebar (before that, i duplicated the sidebar code inside each page class and it worked but it was not clean and a lot of code repetition.) the navigation is broken, every button raises the last pages.
The correct Page is Raised at startup but every button raises the last page (SyncPage).
I think the lambda for my buttons is wrong, but i can't figure why and how to correct it.
class Interface(tk.Tk):
def __init__(self):
super().__init__(className="Serbitar")
self.attributes("-type", "utility")
self.width = 600
self.height = 800
self.geometry(str(self.width) + "x" + str(self.height))
self.title = "Serbitar" + str(self.version)
self.yt_data = Yt_Data()
# Top frame pour accueillir la barre de titre
self.top_frame = tk.Frame(self, relief="groove", bd=2)
# Content Frame est un container pour acceuillir
# les différentes frames en fonction du contenu
self.content_frame = tk.Frame(self, relief="flat", bd=2)
self.content_frame.grid_rowconfigure(0, weight=1)
self.content_frame.grid_columnconfigure(0, weight=1)
self.content_pages = {}
for P in (StartPage, YTPage, CalibrePage, TaskPage, ExportPage, SyncPage):
page = P(self.content_frame, self)
self.content_pages[P] = page
page.grid(row=0, column=0, sticky="nsew")
# Elements de la Barre de titre
self.l_titre = tk.Label(
self.top_frame, text="Welcome to Serbitar v" + self.version, padx=30
)
self.b_exit = tk.Button(self.top_frame, text="X", command=self.quit)
# Placement des éléments de la barre de titre
self.l_titre.pack(side="left", fill="both", expand=True)
self.b_exit.pack(side="right", fill="y")
# Placement des frames globales
self.top_frame.pack(side=tk.TOP, expand=True, fill="x")
self.content_frame.pack(side=tk.BOTTOM, expand=True, fill="both")
self.show_page(StartPage)
def show_page(self, cont):
page = self.content_pages[cont]
print(self.content_pages[cont])
page.tkraise()
self.update()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.ctl = controller
self.left_fsp = SideBar(self, self.ctl, 0)
self.main_f = tk.Frame(self, relief="flat", bd=2)
self.txt = tk.Text(self.main_f, width=55, height=20)
wlcme_txt = "Welcome to serbitar v" + str(__version__) + "\n\n"
self.txt.insert(tk.END, wlcme_txt)
self.spacer_m = tk.Frame(
self.main_f, relief="flat", width=470, height=400, bd=0
)
self.left_fsp.pack(side="left")
self.main_f.pack(side="right", expand=True, fill="both")
self.txt.grid(column=0, row=0)
self.spacer_m.grid(column=0, row=1, sticky="sew")
class SideBar(tk.Frame):
def __init__(self, parent, ctl, fid):
tk.Frame.__init__(self, parent)
captions = ["Home", "Youtube", "Calibre", "Taskwarrior", "Export", "Sync"]
page_name = [StartPage, YTPage, CalibrePage, TaskPage, ExportPage, SyncPage]
self.sb_btton = {}
for ids in range(6):
if fid == ids:
self.page_lbl = tk.Label(self, text=captions[ids], pady=10)
if ids == 0:
self.page_lbl.grid(column=0, row=ids, sticky="new")
else:
self.page_lbl.grid(column=0, row=ids, sticky="ew")
else:
print(page_name[ids])
self.sb_btton[ids] = tk.Button(
self,
text=captions[ids],
command=lambda: ctl.show_page(page_name[ids]),
)
if ids == 0:
self.sb_btton[ids].grid(column=0, row=ids, sticky="new")
else:
self.sb_btton[ids].grid(column=0, row=ids, sticky="ew")
self.spacer = tk.Frame(self, relief="flat", width=130, height=600, bd=0)
self.spacer.grid(column=0, row=6, sticky="sew")
edit: solutions is here, with great explaination thank you : tkinter creating buttons in for loop passing command arguments
command=lambda ids=ids: ctl.show_page(page_name[ids])