Tkinter Animation Will Not Work
Solution 1:
You can use the universal Tk widget after()
method to schedule a function to run after a specified delay given in milliseconds. This only happens once, so typically the function itself also calls after()
to perpetuate the process.
In the code below a custom AnimatedGif
container class is defined which loads and holds all the frames of animated sequence separately in a list which allows quick (random) access to them using []
indexing syntax. It reads individual frames from the file using the -index
indexvalue
image format suboption mentioned on the photo Tk manual page.
I got the test image shown below from the Animation Library website.
Here's how things should look when it's initially started.
You should be able use the same technique to animate multiple images or those that are attached to other kinds of widgets, such as Button
and Canvas
instances.
try:
from tkinter import *
except ImportError:
from Tkinter import * # Python 2classAnimatedGif(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.whileTrue:
try:
frame = PhotoImage(file=image_file_path,
format="gif -index {}".format(frame_num))
except TclError:
break
self._frames.append(frame)
frame_num += 1def__len__(self):
returnlen(self._frames)
def__getitem__(self, frame_num):
return self._frames[frame_num]
defupdate_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)
defenable_animation():
global cancel_id
if cancel_id isNone: # 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)
defcancel_animation():
global cancel_id
if cancel_id isnotNone: # 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()
Solution 2:
Here's an alternative version of my previous answer. Although also based on the universal Tk widget after()
method, it uses the PIL
(or the pillow fork of it) module to read the gif image file. With PIL
it's not only easy to extract each frame from the file, but also to get the delay (or "duration") between frames of the animation directly from the gif file — which eliminates guessing what it should be for different files.
try:
from tkinter import *
except ImportError:
from Tkinter import *
from PIL import Image, ImageSequence, ImageTk
classAnimatedGif(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):
returnlen(self._frames)
def__getitem__(self, frame_num):
return self._frames[frame_num]
defupdate_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)
defenable_animation():
""" Start animation of label image. """global cancel_id
if cancel_id isNone: # Animation not started?
cancel_id = root.after(ani_img[0].delay, update_label_image, animation, ani_img, 0)
defcancel_animation():
""" Stop animation of label image. """global cancel_id
if cancel_id isnotNone: # 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()
Post a Comment for "Tkinter Animation Will Not Work"