【问题标题】:Is it possible to provide animation on image ? - Tkinter是否可以在图像上提供动画? - Tkinter
【发布时间】:2020-12-09 18:49:50
【问题描述】:

我正在 Tkinter 中开发一个 GUI,并希望在图像出现时在下面的 GIF 中应用动画。

这是我的代码,

from tkinter import *
from PIL import Image, ImageTk

root = Tk()

frame = Frame(root)
frame.pack()

canvas = Canvas(frame, width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
canvas.pack()

background = PhotoImage(file="background.png")
canvas.create_image(300,300,image=background)

my_pic = PhotoImage(file="start000-befored.png")
frame.after(1000, lambda: (canvas.create_image(50,50,image=my_pic, anchor=NW))) #and on this image, I want to give the effect.
root.mainloop()

不是点击 GIF 中显示的播放按钮,图像应该像这个动画一样在 1 秒后自动出现并停留在屏幕上。 (没有关闭选项)。

【问题讨论】:

    标签: python python-3.x tkinter tk tkinter-canvas


    【解决方案1】:

    我不是 100% 确定我理解了这个问题,但我将描述如何为图像设置动画。

    Tkinter 不包含动画图像的功能,因此您必须自己编写它们。您必须提取所有子图像、子图像持续时间,然后构建一个序列器以在您的显示器上交换子图像。

    Pillow 可以提取图像序列。 WEBP 图像似乎只支持一个帧持续时间,而 GIF 图像可能对每个子图像具有不同的帧持续时间。即使有很多,我也会只对 GIF 图像使用第一个持续时间。据我所知,Pillow 不支持从 WEBP 图像获取帧持续时间,但您可以从文件中读取它,请参阅WebP Container Specification

    示例实现:

    import tkinter as tk
    from PIL import Image, ImageTk, ImageSequence
    import itertools
    
    root = tk.Tk()
    display = tk.Label(root)
    display.pack(padx=10, pady=10)
    
    filename = 'images/animated-nyan-cat.webp'
    pil_image = Image.open(filename)
    no_of_frames = pil_image.n_frames
    
    # Get frame duration, assuming all frame durations are the same
    duration = pil_image.info.get('duration', None)   # None for WEBP
    if duration is None:
        with open(filename, 'rb') as binfile:
            data = binfile.read()
        pos = data.find(b'ANMF')    # Extract duration for WEBP sequences
        duration = int.from_bytes(data[pos+12:pos+15], byteorder='big')
    
    # Create an infinite cycle of PIL ImageTk images for display on label
    frame_list = []
    for frame in ImageSequence.Iterator(pil_image):
        cp = frame.copy()
        frame_list.append(cp)
    tkframe_list = [ImageTk.PhotoImage(image=fr) for fr in frame_list]
    tkframe_sequence = itertools.cycle(tkframe_list)
    tkframe_iterator = iter(tkframe_list)
    
    def show_animation():
        global after_id
        after_id = root.after(duration, show_animation)
        img = next(tkframe_sequence)
        display.config(image=img)
    
    def stop_animation(*event):
        root.after_cancel(after_id)
    
    def run_animation_once():
        global after_id
        after_id = root.after(duration, run_animation_once)
        try:
            img = next(tkframe_iterator)
        except StopIteration:
            stop_animation()
        else:
            display.config(image=img)
        
    root.bind('<space>', stop_animation)
    
    # Now you can run show_animation() or run_animation_once() at your pleasure
    root.after(1000, run_animation_once)
    
    root.mainloop()
    

    有一些库,比如 imgpy,支持 GIF 动画,但我没有使用任何此类库的经验。

    加法

    duration 变量设置动画速率。要减慢速度,只需增加持续时间即可。

    将动画放在画布上的最简单方法就是将标签放在画布上,参见下面的示例:

    # Replace this code
    root = tk.Tk()
    display = tk.Label(root)
    display.pack(padx=10, pady=10)
    
    # with this code
    root = tk.Tk()
    canvas = tk.Canvas(root, width=500, height=500)
    canvas.pack(padx=10, pady=10)
    display = tk.Label(canvas)
    window = canvas.create_window(250, 250, anchor='center', window=display)
    

    那么您不必更改程序中的任何其他内容。

    【讨论】:

    • 谢谢,它正在工作!但是显示的图像太快了,有没有可能让它慢一点?
    • 另外你能告诉我我们如何使用画布实现它吗?
    • 怎么样?
    • 我把duration的值改成after_id = root.after(100000, run_animation_once),但是速度还是一样的。 :(
    • 对不起,如果我不够清楚。在开始动画之前将变量duration 变量设置为更大的值,例如duration = 10000
    猜你喜欢
    • 1970-01-01
    • 2020-06-11
    • 2011-09-29
    • 1970-01-01
    • 2014-08-04
    • 2011-12-31
    • 2015-02-08
    • 1970-01-01
    相关资源
    最近更新 更多