Я разрабатываю графический интерфейс в Tkinter и хочу применить анимацию в приведенном ниже 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 секунду, как эта анимация, и оставаться на экране. (без возможности закрытия).
Я не уверен на 100%, что понял проблему, но я опишу, как анимировать изображение.
Tkinter не содержит функций для анимации изображений, поэтому вам придется писать их самостоятельно. Вам нужно будет извлечь все части изображения, продолжительность части изображения, а затем создать секвенсор для замены частей изображения на вашем дисплее.
Pillow может извлекать последовательности изображений. Изображения WEBP, по-видимому, поддерживают только одну продолжительность кадра, тогда как изображения GIF могут иметь разную продолжительность кадра для каждого фрагмента изображения. Я буду использовать только первую продолжительность для изображений GIF, даже если их много. Насколько я видел, Pillow не поддерживает получение длительности кадра из изображений WEBP, но вы можете прочитать ее из файла, см. Спецификацию контейнера WebP.
Пример реализации:
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)
Тогда вам не придется ничего менять в программе.
Также можете ли вы сказать мне, как мы можем реализовать это с помощью холста?
Как насчет этого?
Я изменил значение продолжительности на after_id = root.after(100000, run_animation_once)
, но скорость такая же. :(
Извините, если я был недостаточно ясен. Перед запуском анимации установите для переменной duration
переменную большее значение, например duration = 10000
.
Спасибо, это работает! Но изображение, которое отображается, слишком быстрое. Можно ли сделать его немного медленным?