Search code examples
pythonpython-3.xtkintertkinter-canvastkinter-entry

AttributeError: 'Event' object has no attribute 'show_frame' in tkinter?


This is the full traceback message

Exception in Tkinter callback Traceback (most recent call last): File "C:\Python\Python37\lib\tkinter\__init__.py", line 1702, in __call__ return self.func(*args) File "C:\test.py", line 61, in <lambda> canvas.tag_bind(obj1, '<1>', lambda event=None : controller.show_frame(Page1)) AttributeError: 'Event' object has no attribute 'show_frame

WHAT I WANT ?

When I should click on the image, it should redirect to another frame that is Page1.

WHAT I AM GETTING ?

when I click on the image in order to move on the Page1 (next frame) from the StartPage (default frame), it is showing error "AttributeError: 'Event' object has no attribute 'show_frame'"

Following is my code that is giving error.

from tkinter import *
from PIL import Image, ImageTk
   
class tkinterApp(Tk): 
      
    # __init__ function for class tkinterApp  
    def __init__(self, *args, **kwargs):  
          
        # __init__ function for class Tk 
        Tk.__init__(self, *args, **kwargs) 
          
        # creating a container 
        container = Frame(self)   
        container.pack(side = "top", fill = "both", expand = True)  
   
        # initializing frames to an empty array 
        self.frames = {}
        
        # iterating through a tuple consisting 
        # of the different page layouts 
        for F in (StartPage, Page1): 
   
            frame = F(container, self) 
   
            # initializing frame of that object from 
            # startpage, page1, page2 respectively with  
            # for loop 
            self.frames[F] = frame  
   
            frame.grid(row = 0, column = 0, sticky ="nsew")
        self.update()
        self.show_frame(StartPage)
   
    # to display the current frame passed as 
    # parameter 
    def show_frame(self, cont): # cont = page_name
        if cont not in self.frames:
            self.frames[cont] = cont(container, self)
        frame = self.frames[cont]
        frame.tkraise()
        frame.event_generate("<<ShowFrame>>")
   
# first window frame startpage 
   
class StartPage(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.bind("<<ShowFrame>>", self.myStartPage)

    def printcehck(self,event):
        print("hack")

    def myStartPage(self,controller):
        super(StartPage).__init__()
        print(controller)
        canvas = Canvas(self,width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
        canvas.pack()

        self.background = PhotoImage(file="background.png")
        canvas.create_image(300,300,image=self.background, tags="B")

        self.character1 = PhotoImage(file="bg-e-butd.png")
        obj1 = canvas.create_image(50,50,image=self.character1, anchor=NW)
        canvas.tag_bind(obj1, '<1>', lambda event=None : controller.show_frame(self,Page1)) #error occurs here.
           
      
# second window frame page1  
class Page1(Frame): 
      
    def __init__(self, parent, controller): 
          
        Frame.__init__(self, parent)

        canvas = Canvas(self,width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
        canvas.pack()

        self.background = PhotoImage(file="background.png")
        canvas.create_image(300,300,image=self.background, tags="B")

        self.character1 = PhotoImage(file="bg-e-butd.png")
        self.after(1000, lambda: (canvas.create_image(50,50,image=self.character1, anchor=NW)))

   
# Driver Code 
app = tkinterApp()
app.title("Title")
app.mainloop() 

Can anyone let me know where I'm doing the mistake ? I'm new in GUI development using Tkinter.


Solution

  • You need to define controller as a class attribute. The error is telling you that show_frame does not exist because to that attribute controller does not exist.

    In order to pass your frame to the other class properly you need to also define container as a class attribute in the main class.

    Change container to self.container

    In your StartPage class and Page1 class you will need to add self.controller = controller to your init so that the rest of your class will be able to work with this.

    I have updated your code (also removed the image portions as I dont have your images to work with) so I used some labels instead.

    Let me know if you have any questions.

    import tkinter as tk
    from PIL import Image, ImageTk
    
    
    class tkinterApp(tk.Tk):
        def __init__(self, *args, **kwargs):
            tk.Tk.__init__(self, *args, **kwargs)
            self.title("Title")
            self.container = tk.Frame(self)
            self.container.pack(side="top", fill="both", expand=True)
            self.frames = {}
    
            for F in (StartPage, Page1):
                frame = F(self.container, self)
                self.frames[F] = frame
    
                frame.grid(row=0, column=0, sticky="nsew")
            self.update()
            self.show_frame(StartPage)
    
        def show_frame(self, cont):  # cont = page_name
            if cont not in self.frames:
                self.frames[cont] = cont(self.container, self)
            frame = self.frames[cont]
            frame.tkraise()
            frame.event_generate("<<ShowFrame>>")
    
    
    class StartPage(tk.Frame):
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            tk.Label(self, text='StartPage').pack()
            self.bind("<<ShowFrame>>", self.myStartPage)
            self.controller = controller
    
        def printcehck(self, event):
            print("hack")
    
        def myStartPage(self, controller):
            super(StartPage).__init__()
            print(controller)
            self.controller.show_frame(Page1)
    
    
    class Page1(tk.Frame):
        def __init__(self, parent, controller):
            self.controller = controller
            tk.Frame.__init__(self, parent)
            tk.Label(self, text='Page1').pack()
    
    
    if __name__ == '__main__':
        tkinterApp().mainloop()