Play Animations in GIF with Tkinter
You have to drive the animation yourself in Tk. An animated gif consists of a number of frames in a single file. Tk loads the first frame but you can specify different frames by passing an index parameter when creating the image. For example:
frame2 = PhotoImage(file=imagefilename, format="gif -index 2")
If you load up all the frames into separate PhotoImages and then use timer events to switch the frame being shown (label.configure(image=nextframe)
). The delay on the timer lets you control the animation speed. There is nothing provided to give you the number of frames in the image other than it failing to create a frame once you exceed the frame count.
See the photo Tk manual page for the official word.
Play an Animated GIF in python with tkinter
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()
Play an animated GIF in python with tkinter while loading
First using while loop and time.sleep()
in the main thread of a tkinter application is not recommended because it will block tkinter mainloop()
and then cause the GUI not responding.
Suggest to:
- use
.after()
to show the animated GIF because it is not safe to update tkinter widgets in a thread - use thread on
loadingAnimation()
instead
import threading
import tkinter
import sys
import time
root = tkinter.Tk()
frames = [tkinter.PhotoImage(file='./myScripts/M-95.gif', format='gif -index %i'%(i)) for i in range(10)]
def center_window(win):
win.wait_visibility() # make sure the window is ready
x = (win.winfo_screenwidth() - win.winfo_width()) // 2
y = (win.winfo_screenheight() - win.winfo_height()) // 2
win.geometry(f'+{x}+{y}')
def M_95(n=0, top=None, lbl=None):
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
global process_is_alive # used in loadingAnimation()
delay = 4000 // len(frames) # make one cycle of animation around 4 secs
if n == 0:
root.withdraw()
top = tkinter.Toplevel()
lbl = tkinter.Label(top, image=frames[0])
lbl.pack()
center_window(top)
process_is_alive = True
if n < len(frames)-1:
lbl.config(image=frames[n])
lbl.after(delay, M_95, n+1, top, lbl)
else:
top.destroy()
root.deiconify()
process_is_alive = False
def loadingAnimation():
animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
i = 0
while process_is_alive:
sys.stdout.write("\r | Loading..." + animation[i % len(animation)])
sys.stdout.flush()
time.sleep(0.4)
i += 1
M_95() # start GIF animation
threading.Thread(target=loadingAnimation).start()
root.mainloop()
Update: animate GIF more than one cycle in around 4 secs:
def M_95(n=0, top=None, lbl=None):
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
global process_is_alive
num_cycles = 2
count = len(frames) * num_cycles
delay = 4000 // count # make required cycles of animation in around 4 secs
if n == 0:
root.withdraw()
top = tkinter.Toplevel()
lbl = tkinter.Label(top, image=frames[0])
lbl.pack()
center_window(top)
process_is_alive = True
lbl.after(delay, M_95, n+1, top, lbl)
elif n < count-1:
lbl.config(image=frames[n%len(frames)])
lbl.after(delay, M_95, n+1, top, lbl)
else:
top.destroy()
root.destroy()
process_is_alive = False
Question on how to animate gif in tkinter
An animated GIF contains a number of frames and you have to read each frame into a PhotoImage. Then you have to play them back explicitly. Tkinter can't read the delay time between frames so you'll have to test different values. See example below:
import tkinter as tk
root = tk.Tk()
framelist = [] # List to hold all the frames
frame_index = 0 # Frame index
while True:
try:
# Read a frame from GIF file
part = 'gif -index {}'.format(frame_index)
frame = tk.PhotoImage(file='images/animated.gif', format=part)
except:
last_frame = frame_index - 1 # Save index for last frame
break # Will break when GIF index is reached
framelist.append(frame)
frame_index += 1 # Next frame index
def animate(frame_number):
if frame_number > last_frame:
frame_number = 0
label.config(image=framelist[frame_number])
root.after(50, animate, frame_number+1)
label = tk.Label(root, bg='#202020')
label.pack()
animate(0) # Start animation
root.mainloop()
Displaying animated GIFs in Tkinter using PIL
For one, you are creating a new canvas object for every frame. Eventually you will have thousands of images stacked on top of one another. This is highly inefficient; the canvas widget has performance issues when you start to have thousands of objects.
Instead of creating new image objects on the canvas, just reconfigure the existing object with the itemconfig method of the canvas.
Second, you don't need the complexities of threading for such a simple task. There is a well known pattern in tkinter for doing animations: draw a frame, then have that function use after
to call itself in the future.
Something like this:
def animate(self):
if self._image_id is None:
self._image_id = self.display.create_image(...)
else:
self.itemconfig(self._image_id, image= the_new_image)
self.display.after(self.gif["delay"], self.animate)
Finally, unless there's a strict reason to use a canvas, you can lower the complexity a little more by using a Label widget.
Tkinter GIF not display
Tkinter doesn't automatically show animated gifs. You will have to do the animation yourself by loading each frame in a loop.
See Play Animations in GIF with Tkinter
If your problem is that you're not seeing any image at all, it's probably due to the image being garbage-collected.
See Cannot Display an Image in Tkinter
Python Showing a gif as background using Tkinter and PIL
I got your code working.
Note that you haven't set everything on it. You must to "Image.open" in your file before.
"Image" is imported as PIM from PIL to prevent conflict.
import os, tkinter as tk
from PIL import Image as PIM, ImageTk
from tkinter import*
root= tk.Tk()
root.title("SCP-079")
root.resizable(0,0)
root.iconbitmap("079.ico")
root.geometry("540x360")
im = PIM.open("C:\\...\\CeEOy.gif")
ph = ImageTk.PhotoImage(im)
gif = Label(root, image = ph, bg="black", bd=3)
gif.image = ph
framnr = 79
frames = [PhotoImage(file="C:\\...\\CeEOy.gif",
format = 'gif -index %i' %(i)) for i in range(framnr)]
def update(ind):
frame = frames[ind]
ind += 1
if ind>78:
ind = 0
gif.configure(image = frame)
root.after(100, update, ind)
gif = Label(root)
gif.pack()
root.after(0, update, 0)
root.mainloop()
Related Topics
List of Dicts To/From Dict of Lists
Error: 'Int' Object Is Not Subscriptable - Python
Naming Conflict with Built-In Function
How to Write Inline If Statement for Print
How to Hide the Browser in Selenium Rc
Sort Tuples Based on Second Parameter
Thread Safety in Python's Dictionary
Selenium: Firefoxprofile Exception Can't Load the Profile
Intercepting Stdout of a Subprocess While It Is Running
Assign Operator to Variable in Python
How to Config Nltk Data Directory from Code
What's the Best Way to Find the Inverse of Datetime.Isocalendar()
Dropping Infinite Values from Dataframes in Pandas
How to Trigger Function on Value Change
How to Decorate All Functions of a Class Without Typing It Over and Over for Each Method
Time Complexity of Python Set Operations
Cannot Pass an Argument to Python with "#!/Usr/Bin/Env Python"