Search code examples
pythontkinterturtle-graphicsanimated-gif

Animated GIF with Python Turtle


I've found a way to display an animated GIF using Python tkinter and I'm trying to make it work with Python Turtle Graphics.

I can get the animated gif to display with the following code, but there are issues.

1) For some reason having .grid(row=0, column=0) puts the image off-screen. 2) The image has a surrounding border which I don't want to display.

I've tried .place() with various arguments, but none those place the image on the screen.

Any suggestions on how to place the image a a specific position without a border please?

# main file

import turtle
import tkinter_gif

screen = turtle.Screen()
canvas = screen.getcanvas()
gif_window = tkinter_gif.ImageLabel(canvas)
gif_window.grid(row=1, column=0)
gif_window.load("giphy.gif")
turtle.done()
# tkinter_gif.py

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=None)
        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)

Solution

  • I suggest to change ImageLabel from tk.Label to image item of Canvas as below:

    class ImageLabel:
        def __init__(self, canvas):
            self.canvas = canvas
    
        def load(self, im, x=0, y=0):
            # create a canvas image item
            self.image = self.canvas.create_image(x, y, image=None)
            self.canvas.tag_lower(self.image)
    
            if isinstance(im, str):
                im = Image.open(im)
    
            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
    
            num_frames = len(self.frames)
            if num_frames == 1:
                self.canvas.itemconfig(self.image, image=self.frames[0])
            else:
                self.next_frame(0, num_frames)
    
        def unload(self):
            self.canvas.delete(self.image)
            self.frames = None
    
        def next_frame(self, loc, total):
            if self.frames:
                self.canvas.itemconfig(self.image, image=self.frames[loc])
                loc = (loc + 1) % total
                self.canvas.after(self.delay, self.next_frame, loc, total)
    

    Then load it at specific position:

    gif_window = tkinter_gif.ImageLabel(canvas)
    gif_window.load("giphy.gif", -200, -200) # 0, 0 is the center of canvas