是否可以在图像上提供动画? -Tkinter

Is it possible to provide animation on image ? - Tkinter

我正在 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()

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

我不是 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 变量设置动画速率。要减慢速率,只需增加持续时间。

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

# 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)

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