2

I used Tkinter with python to play animation and it works perfectly fine, on launch it has about 25-30 fps. But once I click on label to changed a gif it starts increasing fps with every click. I can't even determine what exactly is causing this issue. I tried changing timeout parameter in .after() function and adding time.sleep(). Is it a Tkinter bug?

from tkinter import *
from PIL import Image
import random


root = Tk()

class GifAnimation:
    def __init__(self):
        self.master = root
        self.gif_path = ""
        self.gif_label: Label = None
        self.gif_count = 0
        
        self.gifs_list = [
            "./gifs/gif1.gif",
            "./gifs/gif2.gif",
            "./gifs/gif3.gif",
            "./gifs/gif4.gif",
            "./gifs/gif5.gif"
        ]
    
    def configure(self):
        self.gif_label = Label(self.master)
        self.gif_label.pack()
        self.gif_label.bind("<Button-1>", lambda x: self.change_gif())
        
        self.gif_path = random.choice(self.gifs_list)
        self.gif_info = Image.open(self.gif_path)
        self.gif_frames = self.gif_info.n_frames
        self.gif_im = [PhotoImage(file=self.gif_path,format=f"gif -index {i}") for i in range(self.gif_frames)]
    
    def animation(self):
        self.gif_im2 = self.gif_im[self.gif_count]
        self.gif_label.configure(image=self.gif_im2)
        self.gif_count += 1
        if self.gif_count == self.gif_frames:
            self.gif_count = 0
        self.master.after(100, self.animation)
    
    def change_gif(self):
        self.gif_label.destroy() # avoid infitite label creation
        self.gif_count = 0 # reset gif frames
        self.configure() 
        self.animation()

if __name__ == '__main__':
    gif = GifAnimation()
    gif.configure()
    gif.animation()
    root.mainloop()
Melleo
  • 21
  • 3
  • Each time you press the button, it starts a new `.after` loop that changes the gif while the old one doesn't stop. So the bug is in your code. – TheLizzard Sep 15 '22 at 12:19
  • 1
    Thank you! ```self.master.after_cancel()``` resolved this issue. –  Melleo Sep 15 '22 at 12:31

1 Answers1

0

As explained in the comment, self.master.after_cancel() needs to be added.

after_cancel() function is used to stop the scheduling of the after()function. *

self.master.after_cancel() needs the after() id (cf self.after_id in animations).

Here are the two functions that needs to be modified.

def animation(self):
    self.gif_im2 = self.gif_im[self.gif_count]
    self.gif_label.configure(image=self.gif_im2)
    self.gif_count += 1
    if self.gif_count == self.gif_frames:
        self.gif_count = 0
    self.after_id= self.master.after(100, self. Animation)

def change_gif(self):
    self.master.after_cancel(self.after_id)
    self.gif_label.destroy()  # avoid infitite label creation
    self.gif_count = 0  # reset gif frames
    self.configure()
    self. Animation()

The full working code:

from tkinter import *
from PIL import Image
import random


root = Tk()


class GifAnimation:
    def __init__(self):
        self.master = root
        self.gif_path = ""
        self.gif_label: Label = None
        self.gif_count = 0

        self.gifs_list = [
            r"Z:\TEMP\tabata\gif\0002.gif",
            r"Z:\TEMP\tabata\gif\0003.gif",
            r"Z:\TEMP\tabata\gif\0004.gif",
            r"Z:\TEMP\tabata\gif\0000.gif",
            r"Z:\TEMP\tabata\gif\0001.gif"
        ]

    def configure(self):
        self.gif_label = Label(self.master)
        self.gif_label.pack()
        self.gif_label.bind("<Button-1>", lambda x: self.change_gif())

        self.gif_path = random.choice(self.gifs_list)
        self.gif_info = Image.open(self.gif_path)
        self.gif_frames = self.gif_info.n_frames
        self.gif_im = [PhotoImage(
            file=self.gif_path, format=f"gif -index {i}") for i in range(self.gif_frames)]

    def animation(self):
        self.gif_im2 = self.gif_im[self.gif_count]
        self.gif_label.configure(image=self.gif_im2)
        self.gif_count += 1
        if self.gif_count == self.gif_frames:
            self.gif_count = 0
        self.after_id = self.master.after(100, self.animation)

    def change_gif(self):
        self.master.after_cancel(self.after_id)
        self.gif_label.destroy()  # avoid infitite label creation
        self.gif_count = 0  # reset gif frames
        self.configure()
        self.animation()


if __name__ == '__main__':
    gif = GifAnimation()
    gif.configure()
    gif.animation()
    root.mainloop()
MagTun
  • 5,619
  • 5
  • 63
  • 104