Is there any way to display an animated GIF in Tkinter using Python Image Library?
I thought the ImageSequence module would be the way to do it, but I don't know how to use it and if it's possible.
The first question is if there is any easy way. For example: load a GIF using PIL and the ImageSequence and just draw it on a Tkinter window using ImageTk.PhotoImage and it will be animated.
Or do I have to set up a function myself, using the after method or something like time.sleep to loop through the GIF frames and draw them on a tkinter window?
The second question: even if I have to make a function to loop through the GIF frames, is the ImageSequence module supposed to do this or PIL has another module for it?
I'm using Python 3.1 and a private port of PIL, indicated in this topic.
Newsgroups: comp.lang.python
From: "Fredrik Lundh"
Date: Mon, 1 May 2006
Daniel Nogradi wrote:
'The source distribution of the 1.1.4 version comes with a Scripts
directory where you can find player.py, gifmaker.py and explode.py
which all deal with animated gif.'
they're still shipped with 1.1.5 (and 1.1.6), and they should work.
if all you're missing is a few files from the script directory, you can get
them here:
http://svn.effbot.org/public/pil/Scripts/
player.py is run from the command line
see if this one works for you:
from Tkinter import *
from PIL import Image, ImageTk
class MyLabel(Label):
def __init__(self, master, filename):
im = Image.open(filename)
seq = []
try:
while 1:
seq.append(im.copy())
im.seek(len(seq)) # skip to next frame
except EOFError:
pass # we're done
try:
self.delay = im.info['duration']
except KeyError:
self.delay = 100
first = seq[0].convert('RGBA')
self.frames = [ImageTk.PhotoImage(first)]
Label.__init__(self, master, image=self.frames[0])
temp = seq[0]
for image in seq[1:]:
temp.paste(image)
frame = temp.convert('RGBA')
self.frames.append(ImageTk.PhotoImage(frame))
self.idx = 0
self.cancel = self.after(self.delay, self.play)
def play(self):
self.config(image=self.frames[self.idx])
self.idx += 1
if self.idx == len(self.frames):
self.idx = 0
self.cancel = self.after(self.delay, self.play)
root = Tk()
anim = MyLabel(root, 'animated.gif')
anim.pack()
def stop_it():
anim.after_cancel(anim.cancel)
Button(root, text='stop', command=stop_it).pack()
root.mainloop()
Simple PIL version:
canvas = Image.new("RGB",(Width,Height),"white")
gif = Image.open('text.gif', 'r')
frames = []
try:
while 1:
frames.append(gif.copy())
gif.seek(len(frames))
except EOFError:
pass
for frame in frames:
canvas.paste(frame)
canvas.show()
Related
I searched all around the web and the StackOverflow website, but didn't find any suitable answer to my problem.
I have a Python class used to create gifs objects:
import tkinter as tk
from PIL import ImageTk, Image
from itertools import count
class ImageLabel(tk.Label):
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)
Which can be used as follows (supposing we defined a frame before):
gif = ImageLabel( frame )
gif.load( "path/to/spinner.gif" )
gif.place( anchor="center", relx= 0.7, rely=0.5 )
I would like to be able to run this created gif in parallel with another command, in the same frame. For example: let's say I am clicking a button which performs a long operation, in this case I would like to have a gif displayed among it, which runs parallely and be destroyed after the process finishes.
Can you help me? Thanks.
I solved by simply placing a gif and sending in parallel a function to execute the job:
self.spinner_gif.place( anchor = "center", relx = 0.7, rely = 0.55 )
threading.Thread( target = self.Function ).start()
and then at the end of the Function function:
self.spinner_gif.place_forget()
I am dealing with tkinter and opencv to display frames of video in tkinter canvas. my code is as following :
import tkinter as tk
from PIL import ImageTk as itk
from PIL import Image
from tkinter import filedialog as fd
import cv2
window = tk.Tk()
class window_tk():
def __init__(self,main):
self.canvas = tk.Canvas(main, bg='white' )
self.img = itk.PhotoImage(file=self.init_img_route)
self.bg= self.canvas.create_image(0,0,anchor = tk.NW,image=self.img)
self.vid = None
def load_video(self):
self.foldername = fd.askopenfilename(parent=window,initialdir="C:/",title='Select a video file to load.',filetypes=[('video files','*.wmv *.mp4 *.mov *.avi')])
self.label_foldername.config(text='Video Load : '+self.foldername)
self.current_pic_num=0
try:
self.vid = cv2.VideoCapture(self.foldername)
frame_number =0
print(self.vid,self.vid.isOpened())
self.frame_count = 0
if self.vid.isOpened():
vid_w = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
vid_h = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
vid_f = self.vid.get(cv2.CAP_PROP_FPS)
ret,frame = self.vid.read()
#cv2.imshow('frame',frame)
frame_convert = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
#print(self.frame_count, vid_w,vid_h,vid_f,frame.shape)
imgg = itk.PhotoImage(Image.fromarray(frame_convert))
self.canvas.itemconfig(self.bg, image = imgg)
self.canvas.pack(fill='both',expand=1)
# frame_number+=1
except IndexError:
pass
I confirmed that frame has successfully loaded by checking it as cv2.imshow(), but The canvas only shows the white empty image. Is there anything I missed ?
I found answer and leave the solution for my study.
I changed imgg to self.img and let it globally used in class.
I don't know why it solved the problem so if anyone can explain the reason thank you very much.
I have been trying to put a GIF in tkinter.
[GIF THAT I NEED HELP WITH]: https://i.stack.imgur.com/64SG8.gif
But for some reason, it is not coming properly and some pixel at a time only. Could someone help me with it?
from tkinter import *
import tkinter as tk
from PIL import Image
root=Tk()
file="preview.gif"
info = Image.open(file)
frames = info.n_frames # gives total number of frames that gif contains
# creating list of PhotoImage objects for each frames
im = [tk.PhotoImage(file=file,format=f"gif -index {i}") for i in range(frames)]
count = 0
anim = None
def animation(count):
global anim
im2 = im[count]
gif_label.configure(image=im2)
count += 1
if count == frames:
count = 0
anim = root.after(50,lambda :animation(count))
def stop_animation():
root.after_cancel(anim)
gif_label = tk.Label(root,image="")
gif_label.pack()
start = tk.Button(root,text="start",command=lambda :animation(count))
start.pack()
stop = tk.Button(root,text="stop",command=stop_animation)
stop.pack()
root.mainloop()
When I try to run this program it hangs on the list comprehension creating the frame list im.
Pillow has a neat function for getting frames of an animated gif. Example below, using slightly different object names but nonetheless:
from PIL import Image, ImageTk, ImageSequence
...
file = "preview.gif"
animated_gif = Image.open(file)
image_frame_list = []
for frame in ImageSequence.Iterator(animated_gif):
image_frame_list.append(ImageTk.PhotoImage(image=frame))
im = image_frame_list
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!
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()
First of all, you need to know what is the last range of your GIF file. so by changing the different value of i, you will get it.For my condition is 31.
then just need to put the condition.So it will play gif infinitely.
from tkinter import *
import time
import os
root = Tk()
frames = [PhotoImage(file='./images/play.gif',format = 'gif -index %i' %(i)) for i in range(31)]
def update(ind):
frame = frames[ind]
ind += 1
print(ind)
if ind>30: #With this condition it will play gif infinitely
ind = 0
label.configure(image=frame)
root.after(100, update, ind)
label = Label(root)
label.pack()
root.after(0, update, 0)
root.mainloop()
A very simple approach would be to use multithreading.
To run the GIF infinitely in a Tkinter window you should follow the following:
Create a function to run the GIF.
Put your code to run the GIF inside while True inside the function.
Create a thread to run the function.
Run root.mainloop() in the primary flow of the program.
Use time.sleep() to control the speed of your animation.
Refer to my code below:
i=0
ph = ImageTk.PhotoImage(Image.fromarray(imageframes[i]))
imglabel=Label(f2,image=ph)
imglabel.grid(row=0,column=0)
def runthegif(root,i):
while True:
i = i + 7
i= i % 150
ph=ImageTk.PhotoImage(PhotoImage(file='images/ball.gif',format='gif -index %i' %i))
imagelabel=Label(f2,image=ph)
imagelabel.grid(row=0,column=0)
time.sleep(0.1)
t1=threading.Thread(target=runthegif,args=(root,i))
t1.start()
root.mainloop()
Is there any way to display an animated GIF in Tkinter using Python Image Library?
I thought the ImageSequence module would be the way to do it, but I don't know how to use it and if it's possible.
The first question is if there is any easy way. For example: load a GIF using PIL and the ImageSequence and just draw it on a Tkinter window using ImageTk.PhotoImage and it will be animated.
Or do I have to set up a function myself, using the after method or something like time.sleep to loop through the GIF frames and draw them on a tkinter window?
The second question: even if I have to make a function to loop through the GIF frames, is the ImageSequence module supposed to do this or PIL has another module for it?
I'm using Python 3.1 and a private port of PIL, indicated in this topic.
Newsgroups: comp.lang.python
From: "Fredrik Lundh"
Date: Mon, 1 May 2006
Daniel Nogradi wrote:
'The source distribution of the 1.1.4 version comes with a Scripts
directory where you can find player.py, gifmaker.py and explode.py
which all deal with animated gif.'
they're still shipped with 1.1.5 (and 1.1.6), and they should work.
if all you're missing is a few files from the script directory, you can get
them here:
http://svn.effbot.org/public/pil/Scripts/
player.py is run from the command line
see if this one works for you:
from Tkinter import *
from PIL import Image, ImageTk
class MyLabel(Label):
def __init__(self, master, filename):
im = Image.open(filename)
seq = []
try:
while 1:
seq.append(im.copy())
im.seek(len(seq)) # skip to next frame
except EOFError:
pass # we're done
try:
self.delay = im.info['duration']
except KeyError:
self.delay = 100
first = seq[0].convert('RGBA')
self.frames = [ImageTk.PhotoImage(first)]
Label.__init__(self, master, image=self.frames[0])
temp = seq[0]
for image in seq[1:]:
temp.paste(image)
frame = temp.convert('RGBA')
self.frames.append(ImageTk.PhotoImage(frame))
self.idx = 0
self.cancel = self.after(self.delay, self.play)
def play(self):
self.config(image=self.frames[self.idx])
self.idx += 1
if self.idx == len(self.frames):
self.idx = 0
self.cancel = self.after(self.delay, self.play)
root = Tk()
anim = MyLabel(root, 'animated.gif')
anim.pack()
def stop_it():
anim.after_cancel(anim.cancel)
Button(root, text='stop', command=stop_it).pack()
root.mainloop()
Simple PIL version:
canvas = Image.new("RGB",(Width,Height),"white")
gif = Image.open('text.gif', 'r')
frames = []
try:
while 1:
frames.append(gif.copy())
gif.seek(len(frames))
except EOFError:
pass
for frame in frames:
canvas.paste(frame)
canvas.show()