Search code examples
pythontkintermethodsnested-function

Unable to call sub-function when it is used with self but works fine without self


I have a function, start_round() that initializes the round and creates temporary round variables. Then I have a nested function, rd_animate() that does the animation. Now when I make this rd_animate() function a method, the code does not work. When I do not include self, it works fine. Can anyone explain why this happens?

Code

Without self

def start_round(self):
    def rd_animate():
        #--::Animation::--#
        self.PF_ani_lbl.place(relx = 0.5, rely = 0.5, anchor = "center")
        self.PF_ani_lbl["text"] = "ROUND {}".format(self.round_no)
        self.after(500)
        self.PF_ani_lbl.place_forget()

    print("start_round")
    #--::Creation of round dictionary::--#
    self.round = {}

    #--::Animation::--#
    threading.Timer(0.05, rd_animate).start()

With self

def start_round(self):
    def rd_animate(self):
        #--::Animation::--#
        self.PF_ani_lbl.place(relx = 0.5, rely = 0.5, anchor = "center")
        self.PF_ani_lbl["text"] = "ROUND {}".format(self.round_no)
        self.after(500)
        self.PF_ani_lbl.place_forget()

    print("start_round")
    #--::Creation of round dictionary::--#
    self.round = {}

    #--::Animation::--#
    threading.Timer(0.05, self.rd_animate).start()

Error

It gives an error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\raghavendra\anaconda3\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "C:\Users\raghavendra\Desktop\Pratham\My Coding\MY APPS\RPS Multiplayer Game\root-recreated.py", line 348, in <lambda>
    command = lambda: self.start_match(retrieve()))
  File "C:\Users\raghavendra\Desktop\Pratham\My Coding\MY APPS\RPS Multiplayer Game\root-recreated.py", line 613, in start_match
    super().start_match(username, "Computer", match_settings)
  File "C:\Users\raghavendra\Desktop\Pratham\My Coding\MY APPS\RPS Multiplayer Game\root-recreated.py", line 533, in start_match
    self.start_round()
  File "C:\Users\raghavendra\Desktop\Pratham\My Coding\MY APPS\RPS Multiplayer Game\root-recreated.py", line 616, in start_round
    super().start_round()
  File "C:\Users\raghavendra\Desktop\Pratham\My Coding\MY APPS\RPS Multiplayer Game\root-recreated.py", line 445, in start_round
    self.rd_animate()
  File "C:\Users\raghavendra\anaconda3\lib\tkinter\__init__.py", line 2354, in __getattr__
    return getattr(self.tk, attr)
AttributeError: '_tkinter.tkapp' object has no attribute 'rd_animate'

I want to have it with self since it would be nice


Solution

  • Functions are just objects. As the error says, self is an instance of the tkinter app. It won't have your rd_animate function unless you set it.

    self.rd_animate = rd_animate
    threading.Timer(0.05, self.rd_animate).start()
    

    You'll also want to create a partial function to actually pass the app instance into the parameter of the animate function. That way, the lines within the function won't throw an error saying 1 parameter is expected, but got zero


    Alternatively, not really clear why you're nesting things since the above code would be similar to this since self refers to the enclosing class, only for functions definitely directly in the class body

    class App(tk.Frame):
      def __init__(self, master):
        super().__init__(master) 
        self.round_no = 0
        self.PF_ani_lbl =... 
    
      def rd_animate(self):
        #--::Animation::--#
        self.PF_ani_lbl.place(relx = 0.5, rely = 0.5, anchor = "center")
        self.PF_ani_lbl["text"] = "ROUND {}".format(self.round_no)
        self.after(500)
        self.PF_ani_lbl.place_forget()
    
      def start_round(self):
        threading.Timer(0.05, self.rd_animate).start()
    

    You can make rd_animate "private" function by adding two underscores in front like def __rd_animate