Search code examples
pythonpython-3.xtkinteranimated-gif

Play an Animated GIF in python with tkinter


I am wanting to create a virtual pet style game using python3 and tkinter. So far I have the main window and have started putting labels in, but the issue I am having is playing an animated gif. I have searched on here and have found some answers, but they keep throwing errors. The result I found has the index position of the gif using PhotoImage continue through a certain range.

    # Loop through the index of the animated gif
frame2 = [PhotoImage(file='images/ball-1.gif', format = 'gif -index %i' %i) for i in range(100)]

def update(ind):

    frame = frame2[ind]
    ind += 1
    img.configure(image=frame)
    ms.after(100, update, ind)

img = Label(ms)
img.place(x=250, y=250, anchor="center")

ms.after(0, update, 0)
ms.mainloop()

When I run this in terminal with "pyhton3 main.py" I get the following error:

_tkinter.TclError: no image data for this index

What am I overlooking or completely leaving out?

Here is the link to the GitHub repository to see the full project:VirtPet_Python

Thanks in advance!


Solution

  • The error means that you tried to load 100 frames, but the gif has less than that.

    Animated gifs in tkinter are notoriously bad. I wrote this code an age ago that you can steal from, but will get laggy with anything but small gifs:

    import tkinter as tk
    from PIL import Image, ImageTk
    from itertools import count
    
    class ImageLabel(tk.Label):
        """a label that displays images, and plays them if they are gifs"""
        def load(self, im):
            if isinstance(im, str):
                im = Image.open(im)
            self.loc = 0
            self.frames = []
    
            try:
                for i in count(1):
                    self.frames.append(ImageTk.PhotoImage(im.copy()))
                    im.seek(i)
            except EOFError:
                pass
    
            try:
                self.delay = im.info['duration']
            except:
                self.delay = 100
    
            if len(self.frames) == 1:
                self.config(image=self.frames[0])
            else:
                self.next_frame()
    
        def unload(self):
            self.config(image="")
            self.frames = None
    
        def next_frame(self):
            if self.frames:
                self.loc += 1
                self.loc %= len(self.frames)
                self.config(image=self.frames[self.loc])
                self.after(self.delay, self.next_frame)
    
    root = tk.Tk()
    lbl = ImageLabel(root)
    lbl.pack()
    lbl.load('ball-1.gif')
    root.mainloop()