Michael Craig Michael Craig - 2 months ago 26
Python Question

Tkinter call function in another class

Hi i got some code from an answer on Switch between two frames in tkinter which im trying to modify so a button in the StartPage class can call a function called msg in PageOne class.

But im getting this error:


AttributeError: 'Frame' object has no attribute 'msg'


Here is the code so far i marked out the modifications i made to the code.

import tkinter as tk # python3
#import Tkinter as tk # python

TITLE_FONT = ("Helvetica", 18, "bold")

class SampleApp(tk.Tk):

def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)

# 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, PageTwo):
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()


class StartPage(tk.Frame):

def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller

self.parent = parent #<-- my mod

label = tk.Label(self, text="This is the start page", font=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"))
self.button3 = tk.Button(text='Calling msg in another class', command=self.parent.msg)#<-- my mod
button1.pack()
button2.pack()
button3.pack()#<-- my mod

class PageOne(tk.Frame):

def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller

self.parent = parent #<-- my mod

label = tk.Label(self, text="This is page 1", font=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()

def msg(self): #<-- my mod
print("IT WORKS!!")

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=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()


Could anyone help me out?

Answer

Since you're instantiating all your classes in a loop, the easiest way to add references between them is probably to do it after that loop. Remove those references from inside the class, as they won't be valid until after instantiation. Note how button3 is no longer created or packed in StartPage, but rather in SampleApp.

for F in (StartPage, PageOne, PageTwo):
    ...

self.frames['StartPage'].pageone = self.frames['PageOne']
self.frames['StartPage'].button3 = tk.Button(
                                text='Calling msg in another class',
                                command=self.frames['StartPage'].pageone.msg) #<-- my mod
self.frames['StartPage'].button3.pack() #<-- my mod
Comments