I get the following error when running the display method defined in the Page1 class from the Display option on the menu bar:
'_tkinter.tkapp' object has no attribute 'body_frm'
The method works correctly when run from the Display button in Page1.
I created an instance of Page1 called self.page1. This is used to call the method from the menu bar:
file_menu.add_command(label='Display', command=lambda: self.page1.display(self))
I think there may be something wrong here as I'm using lambda and display(self)
.
With just command=self.page1.display
, I get the following error: '_tkinter.tkapp' object has no attribute 'page1'
With lambda: self.page1.display
, I don't get an error but the method doesn't work
With lambda: self.page1.display()
, I get TypeError: display() missing 1 required positional argument: 'self'
The call from the page button uses command=self.display
and works fine.
I can get the method to function both on the menu bar and page button by change self.body_frm
to Page1.body_frm
. While this works, I don't believe that's a proper implementation.
Code:
import tkinter as tk
from tkinter import ttk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('Method Test')
self.geometry('500x300')
# Create frame container
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
# Create menu bar
self.create_menu_frame(self)
# Create a dictionary of frames
self.frames = {}
self.page1 = Page1
self.page2 = Page2
# Add the two page frames to the dictionary.
for page_frame in (self.page1, self.page2):
frame = page_frame(container, self)
self.frames[page_frame] = frame
frame.grid(row=0, column=0, sticky=tk.NSEW)
# Switch Order Entry frame
self.show_frame(self.page1)
def create_menu_frame(self, container: ttk.Frame) -> None:
# Create menu bar
menu_bar = tk.Menu(container)
# Create options for File menu
file_menu = tk.Menu(menu_bar, tearoff=0)
file_menu.add_command(
label='Display', command=lambda: self.page1.display(self))
file_menu.add_separator()
file_menu.add_command(
label='Page 1', command=lambda: self.show_frame(self.page1))
file_menu.add_command(
label='Page 2', command=lambda: self.show_frame(self.page2))
file_menu.add_separator()
file_menu.add_command(label='Exit', command=self.quit)
# Assign file menu list to the File option
menu_bar.add_cascade(label="File", menu=file_menu)
# Assign assign menu bar to the window
container.config(menu=menu_bar)
def show_frame(self, cont: tk.Frame) -> None:
frame = self.frames[cont]
# Raises the specified frame to the top
frame.tkraise()
class Page1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Page1.result_str = tk.StringVar(value='')
# Create page frame sections
self.header_frm = tk.Frame(self)
self.header_frm.pack(side=tk.TOP)
self.header_frm.grid_rowconfigure(0, weight=1)
self.body_frm = tk.Frame(self)
self.body_frm.pack()
self.body_frm.grid_rowconfigure(0, weight=1)
self.footer_frm = tk.Frame(self)
self.footer_frm.pack()
self.footer_frm.grid_rowconfigure(0, weight=1)
# Header Section
label = tk.Label(self.header_frm, text='This is the Main Page')
label.pack(pady=10, padx=10)
# footer Section
close_btn = tk.Button(self.footer_frm, text='Close', command=quit)
close_btn.grid(row=1, column=1, padx=5, pady=5)
display_btn = tk.Button(
self.footer_frm, text='Display', command=self.display)
display_btn.grid(row=1, column=2, padx=5, pady=5)
switch_window_btn = tk.Button(
self.footer_frm, text='Page 2',
command=lambda: controller.show_frame(controller.page2)
)
switch_window_btn.grid(row=1, column=0, padx=5, pady=5)
def display(self):
label = tk.Label(
self.body_frm,
text='Display method works')
label.grid(row=2, column=0, columnspan=2, padx=15, pady=5)
class Page2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text='Second Page')
label.pack(padx=10, pady=10)
switch_window_button = tk.Button(
self,
text='Return to Page 1',
command=lambda: controller.show_frame(controller.page1),
)
switch_window_button.pack(side='bottom', fill=tk.X)
def main():
prog_app = App()
prog_app.mainloop()
if __name__ == '__main__':
main()
Correct code
command=lambda: self.frames[self.page1].display()
or
command=lambda: self.frames[Page1].display()
self.page1
is not instance of class but only alias for Page1
.
Real instance is in self.frames[self.page1]
(or self.frames[Page1]
)