使用 PIL 在 Tkinter 中显示动画 GIF [英] Displaying animated GIFs in Tkinter using PIL
问题描述
我正在尝试制作一个使用 Tkinter 显示动画 GIF 的程序.这是我最初使用的代码:
from __future__ import Division # 只是因为在 2.7.4 中除法不起作用从 Tkinter 导入 *从 PIL 导入 Image,ImageTk进口螺纹从时间导入睡眠def anim_gif(名称):## 返回 { 'frames', 'delay', 'loc', 'len' }im = Image.open(名称)gif = {'帧':[],'延迟':100,'位置':0,'连':0 }图片 = []尝试:为真:pics.append(im.copy())im.seek(len(图片))除了EOFError:通过temp = pics[0].convert('RGBA')gif['frames'] = [ImageTk.PhotoImage(temp)]温度 = 图片 [0]对于图片中的项目[1:]:临时粘贴(项目)gif['frames'].append(ImageTk.PhotoImage(temp.convert('RGBA')))尝试:gif['延迟'] = im.info['持续时间']除了:通过gif['len'] = len(gif['frames'])返回gif定义比率(a,b):如果 b 方法.其次,对于如此简单的任务,您不需要线程的复杂性.tkinter 中有一个众所周知的动画模式:绘制一个框架,然后让该函数在将来使用 after
调用自身.
像这样:
def animate(self):如果 self._image_id 是 None:self._image_id = self.display.create_image(...)别的:self.itemconfig(self._image_id, image= the_new_image)self.display.after(self.gif["delay"], self.animate)
最后,除非有严格的理由使用画布,否则您可以通过使用标签小部件来稍微降低复杂性.
I'm trying to make a program to display an animated GIF using Tkinter. Here is the code that I originally used:
from __future__ import division # Just because division doesn't work right in 2.7.4
from Tkinter import *
from PIL import Image,ImageTk
import threading
from time import sleep
def anim_gif(name):
## Returns { 'frames', 'delay', 'loc', 'len' }
im = Image.open(name)
gif = { 'frames': [],
'delay': 100,
'loc' : 0,
'len' : 0 }
pics = []
try:
while True:
pics.append(im.copy())
im.seek(len(pics))
except EOFError: pass
temp = pics[0].convert('RGBA')
gif['frames'] = [ImageTk.PhotoImage(temp)]
temp = pics[0]
for item in pics[1:]:
temp.paste(item)
gif['frames'].append(ImageTk.PhotoImage(temp.convert('RGBA')))
try: gif['delay'] = im.info['duration']
except: pass
gif['len'] = len(gif['frames'])
return gif
def ratio(a,b):
if b < a: d,c = a,b
else: c,d = a,b
if b == a: return 1,1
for i in reversed(xrange(2,int(round(a / 2)))):
if a % i == 0 and b % i == 0:
a /= i
b /= i
return (int(a),int(b))
class App(Frame):
def show(self,image=None,event=None):
self.display.create_image((0,0),anchor=NW,image=image)
def animate(self,event=None):
self.show(image=self.gif['frames'][self.gif['loc']])
self.gif['loc'] += 1
if self.gif['loc'] == self.gif['len']:
self.gif['loc'] = 0
if self.cont:
threading.Timer((self.gif['delay'] / 1000),self.animate).start()
def kill(self,event=None):
self.cont = False
sleep(0.1)
self.quit()
def __init__(self,master):
Frame.__init__(self,master)
self.grid(row=0,sticky=N+E+S+W)
self.rowconfigure(1,weight=2)
self.rowconfigure(3,weight=1)
self.columnconfigure(0,weight=1)
self.title = Label(self,text='No title')
self.title.grid(row=0,sticky=E+W)
self.display = Canvas(self)
self.display.grid(row=1,sticky=N+E+S+W)
self.user = Label(self,text='Posted by No Username')
self.user.grid(row=2,sticky=E+W)
self.comment = Text(self,height=4,width=40,state=DISABLED)
self.comment.grid(row=3,sticky=N+E+S+W)
self.cont = True
self.gif = anim_gif('test.gif')
self.animate()
root.protocol("WM_DELETE_WINDOW",self.kill)
root = Tk()
root.rowconfigure(0,weight=1)
root.columnconfigure(0,weight=1)
app = App(root)
app.mainloop()
try: root.destroy()
except: pass
test.gif is the following GIF:
This works fine, but the GIF quality is terrible. I tried changing it to what follows:
def anim_gif(name):
## Returns { 'frames', 'delay', 'loc', 'len' }
im = Image.open(name)
gif = { 'frames': [],
'delay': 100,
'loc' : 0,
'len' : 0 }
pics = []
try:
while True:
gif['frames'].append(im.copy())
im.seek(len(gif['frames']))
except EOFError: pass
try: gif['delay'] = im.info['duration']
except: pass
gif['len'] = len(gif['frames'])
return gif
class App(Frame):
def show(self,image=None,event=None):
can_w = self.display['width']
can_h = self.display['height']
pic_w,pic_h = image.size
rat_w,rat_h = ratio(pic_w,pic_h)
while pic_w > int(can_w) or pic_h > int(can_h):
pic_w -= rat_w
pic_h -= rat_h
resized = image.resize((pic_w,pic_h))
resized = ImageTk.PhotoImage(resized)
self.display.create_image((0,0),anchor=NW,image=resized)
However, this will occasionally flash a picture. While the picture looks good, it's pretty useless as a program. What am I doing wrong?
解决方案 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.
这篇关于使用 PIL 在 Tkinter 中显示动画 GIF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!