Python Tkinter动画将无法工作
我正在尝试显示gif图像中的动画。从上一篇文章中,我发现Tkinter不会自动为图像设置动画。我的Tk界面显示图像的第一帧,当我单击按钮播放其动画时,它什么也不做。这可能与按钮相关的命令有关。代码如下:Python Tkinter动画将无法工作,python,animation,tkinter,gif,Python,Animation,Tkinter,Gif,我正在尝试显示gif图像中的动画。从上一篇文章中,我发现Tkinter不会自动为图像设置动画。我的Tk界面显示图像的第一帧,当我单击按钮播放其动画时,它什么也不做。这可能与按钮相关的命令有关。代码如下: from Tkinter import * import Tkinter root = Tk() photo_path = "/users/zinedine/downloads/091.gif" photo = PhotoImage( file = photo_path )
from Tkinter import *
import Tkinter
root = Tk()
photo_path = "/users/zinedine/downloads/091.gif"
photo = PhotoImage(
file = photo_path
)
def run():
frame = 1
while True:
try:
photo = PhotoImage(
file = photo_path,
format = "gif - {}".format(frame)
)
frame = frame + 1
except Exception: # This because I don't know what exception it would raise
frame = 1
break
picture = Label(image = photo)
picture.pack()
picture.configure(run())
animate = Button(
root,
text = "animate",
command = run()
)
animate.pack()
root.geometry("250x250+100+100")
root.mainloop()
您可以使用universaltk小部件方法来安排函数在指定的延迟(以毫秒为单位)后运行。这只会发生一次,因此通常函数本身也会调用
after()
来永久化进程
在下面的代码中,定义了一个自定义的AnimatedGif
容器类,该类在一个列表中分别加载和保存动画序列的所有帧,该列表允许使用[]
索引语法快速(随机)访问这些帧。它使用上提到的-index
indexvalue
图像格式子选项从文件中读取各个帧
我从网站上得到了如下所示的结果
这里是事情开始时的样子
您应该能够使用相同的技术为多个图像或附加到其他类型小部件的图像设置动画,例如按钮
和画布
实例
try:
from tkinter import *
except ImportError:
from Tkinter import * # Python 2
class AnimatedGif(object):
""" Animated GIF Image Container. """
def __init__(self, image_file_path):
# Read in all the frames of a multi-frame gif image.
self._frames = []
frame_num = 0 # Number of next frame to read.
while True:
try:
frame = PhotoImage(file=image_file_path,
format="gif -index {}".format(frame_num))
except TclError:
break
self._frames.append(frame)
frame_num += 1
def __len__(self):
return len(self._frames)
def __getitem__(self, frame_num):
return self._frames[frame_num]
def update_label_image(label, ani_img, ms_delay, frame_num):
global cancel_id
label.configure(image=ani_img[frame_num])
frame_num = (frame_num+1) % len(ani_img)
cancel_id = root.after(
ms_delay, update_label_image, label, ani_img, ms_delay, frame_num)
def enable_animation():
global cancel_id
if cancel_id is None: # Animation not started?
ms_delay = 1000 // len(ani_img) # Show all frames in 1000 ms.
cancel_id = root.after(
ms_delay, update_label_image, animation, ani_img, ms_delay, 0)
def cancel_animation():
global cancel_id
if cancel_id is not None: # Animation started?
root.after_cancel(cancel_id)
cancel_id = None
root = Tk()
root.title("Animation Demo")
root.geometry("250x125+100+100")
ani_img = AnimatedGif("small_globe.gif")
cancel_id = None
animation = Label(image=ani_img[0]) # Display first frame initially.
animation.pack()
Button(root, text="start animation", command=enable_animation).pack()
Button(root, text="stop animation", command=cancel_animation).pack()
Button(root, text="exit", command=root.quit).pack()
root.mainloop()
这是我的另一个版本。虽然也基于通用Tk小部件方法,但它使用
PIL
(或其枕叉)模块读取gif图像文件。使用PIL
不仅可以轻松地从文件中提取每个帧,还可以直接从gif文件中获取动画帧之间的延迟(或“持续时间”),从而消除了对不同文件的猜测
try:
from tkinter import *
except ImportError:
from Tkinter import *
from PIL import Image, ImageSequence, ImageTk
class AnimatedGif(object):
""" Animated GIF Image Container. """
def __init__(self, image_file_path):
# Read in all the frames of a multi-frame gif image.
self._frames = []
img = Image.open(image_file_path)
for frame in ImageSequence.Iterator(img):
photo = ImageTk.PhotoImage(frame)
photo.delay = frame.info['duration'] * 10 # Add attribute.
self._frames.append(photo)
def __len__(self):
return len(self._frames)
def __getitem__(self, frame_num):
return self._frames[frame_num]
def update_label_image(label, ani_img, frame_num):
""" Change label image to given frame number of AnimatedGif. """
global cancel_id
frame = ani_img[frame_num]
label.configure(image=frame)
frame_num = (frame_num+1) % len(ani_img) # Next frame number.
cancel_id = root.after(frame.delay, update_label_image, label, ani_img, frame_num)
def enable_animation():
""" Start animation of label image. """
global cancel_id
if cancel_id is None: # Animation not started?
cancel_id = root.after(ani_img[0].delay, update_label_image, animation, ani_img, 0)
def cancel_animation():
""" Stop animation of label image. """
global cancel_id
if cancel_id is not None: # Animation started?
root.after_cancel(cancel_id)
cancel_id = None
root = Tk()
root.title("Animation Demo")
root.geometry("250x125+100+100")
ani_img = AnimatedGif("small_globe.gif")
cancel_id = None
animation = Label(image=ani_img[0]) # Display first frame initially.
animation.pack()
Button(root, text="start animation", command=enable_animation).pack()
Button(root, text="stop animation", command=cancel_animation).pack()
Button(root, text="exit", command=root.quit).pack()
root.mainloop()
首先,picture.configure(run())是错误的。小部件
configure()
方法需要格式为w.configure(option=value,…)
的参数。您要做的是在调用configure
本身之前自己调用run()
函数。第二,你对另一个问题的回答是使用“定时器事件切换显示的帧”,你没有这样做。但是,我会使用什么定时器事件呢?类似于time.sleep
?因为我不是答案的作者,所以我不能确切地确定它们的意思。它可能使用了repeatinterval
选项,用于其confgiure()
方法,或者可能是通用小部件方法。不要担心。希望社区中的某个人能够提供一个简单而清晰的答案。我对GUI编程非常陌生,所以如果有什么奇怪的方法,我会非常乐意使用它。但是,我只有一个问题,你在播放动画时必须使用计时器吗?在我看来,加载动画所需的时间已经超过了从一帧到另一帧的等待时间。这很有效!然而,虽然它确实为图像设置了动画,但它的速度非常快。我的图像是44帧,感觉像是以44 fps的速度,而不是正常的自然速度。我希望通过将ms_delay
配置为关键字参数并将其设置为类似0.5
的值来改变这一点,但它不接受浮点数,甚至修改为整数(例如1)似乎也没有任何作用。否则,你就是一个纯粹的天才!可疑的是的,确定要使用的延迟是很棘手的。我的回答中的1000//len(ani_img)
有效地试图计算一个允许在一秒钟内显示所有帧的量-这对于我使用的只有13帧的测试图像来说是正常的。“正确”的方法是在gif文件的每一帧之前使用图形控制扩展(GCE)块中的延迟值。要获得这些信息,您需要使用Tkinter
模块之外的一些东西…可能是PIL
,或者编写您自己的代码来解析文件。这给了我一个AttributeError
。以下是回溯:traceback(最近一次调用):文件“”,第1行,在anigif=AnimatedGif(img_路径)文件“”中,第5行,在uu init_uuuuuself.load()文件“”中,第13行,在load img=Image.open(self.filepath)AttributeError:class Image没有属性“open”
这没有意义,因为Image.open()
是标准的图像
类方法。它的文档是。也许你有一个过时的版本。您可以下载原始模块的Python2.x安装程序。但是,我强烈建议您使用更新的Pillow
版本,该版本同时具有Python2.x和3.x的安装程序(并且仍然受到积极支持)Pillowfork的安装程序当前为。有关Pillow
版本的Image.Open()的文档
is-据说基本上与原版相同,因为枕头
主要与原版PIL
模块向后兼容。