1

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?

Community
  • 1
  • 1
Michael Craig
  • 23
  • 1
  • 4
  • 2
    You did `container = tk.Frame(self)`, so `container` is a `Frame`, and then you pass that as `parent` in the subsequent loop. That means `self.parent` is a reference to a basic `Frame`, which doesn't have your additional `msg` method, rather than your expanded subclasses. – TigerhawkT3 Sep 20 '16 at 17:59
  • I dont understand. So what should i do? – Michael Craig Sep 21 '16 at 15:06

3 Answers3

2

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
TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
  • Nice it works. Thank you! But is it hard to add references between StartPage and PageOne classes? sorry for being a pain but im curious since im new at python. Anyway thanks for the help! – Michael Craig Sep 21 '16 at 16:43
  • @MichaelCraig - If you have an object of one class that creates instances of another, and the latter need references to the former, you just pass a reference (e.g. `class A: def __init__(self): b = B(self)`). Here, though, you create all those objects at the same level, and one of them just happens to need a reference to another. Since you can't refer to things that don't exist yet, the solution is to create everything first, and add the reference afterward. – TigerhawkT3 Sep 21 '16 at 17:27
1

I got stuck with this also, but after some tweaking and grasping the whole width of the code:

self.controller.frames['YourFrameName'].method()

You see everything has been instantiated and stored in a dictionary in the sampleapp class. And every other frame will have reference to the base 'sampleapp' class. So its as simple as referencing the base app and calling the frame that has the method that we want.

Ardan
  • 11
  • 3
0

Try this:

    #code to get a page and use it inside any page
    #1. create a variable name ex my_pageTwo
    #2. show and get frame(page)
    #3. now you can use a function from page


    my_pageTwo = self.show_frame(PageTwo)
    #
    my_pageTwo = self.get_page(PageTwo)

#4. example
   my_pageTwo.myfunction()  #without self        

#another example from my tkinter app: #this function is inside main page:

    def open_page8_and_ch_list(self):
       if Pages.EEG_raw != '':
            page_8 = self.show_frame(PageEight)
            page_8 = self.get_page(PageEight)
            self.show_frame(PageEight)
            page_8.list_initial_ch_names()
            
        else:
            self.show_frame(PageEight)
            message1_pg8(self)
            
    def message1_pg8(self):
        lines = ['Hey! Open a file to edit.']
        tkinter.messagebox.showinfo('Myapp', "\n".join(lines))
        
# On the other hand "open_page8_and_ch_list
#" goes to container1 to open something in page8:

c1_button7 = tk.Button(container1,
text='Edit inicial EEG',                                       
command=lambda: open_page8_and_ch_list(self))    
c1_button7.grid(row=0, column=7))
pkanda
  • 151
  • 3
  • 10