hassan akbar
hassan akbar

Reputation: 61

how to run tkinter gif loop only ONCE

this is the code that i used to load a gif into a label object in tkinter




class ImageLabel(tk.Label):
    """a label that displays images, and plays them if they are gifs"""
    def load(self, im):
        if isinstance(im, str):
            im = Image.open(im)
            print(im.is_animated)
            print(im.n_frames)
        self.loc = 0
        self.frames = []

        try:
            for i in count(1):
                self.frames.append(ImageTk.PhotoImage(im.copy()))
                im.seek(i)
        except EOFError:
            pass

        try:
            self.delay = im.info['duration']
        except:
            self.delay = 900

        if len(self.frames) == 1:
            self.config(image=self.frames[0])
        else:
            self.next_frame()

    def unload(self):
        self.config(image="")
        self.frames = None

    def next_frame(self):
        if self.frames:
            self.loc += 1
            self.loc %= len(self.frames)
            self.config(image=self.frames[self.loc])
            self.after(self.delay, self.next_frame)
            

my aim is to load the gif in only a single loop based on the number of frames like lets say there are 5 frames in an image it only loops through that and stops

can someone help me with this.

if i change the

for i in count(im.n_frames):

it only loads the first frame and stops after that.

Upvotes: 0

Views: 462

Answers (3)

acw1668
acw1668

Reputation: 46786

If you want to play the animation of GIF image only once, you need to modify next_frame() not to call .after() when the last frame has been shown.

Below is the modified ImageLabel class:

class ImageLabel(tk.Label):
    """a label that displays images, and plays them if they are gifs"""
    def load(self, im):
        if isinstance(im, str):
            im = Image.open(im)
        print(im.is_animated)
        print(im.n_frames)
        self.delay = im.info.get('duration', 900)

        # load all the frames inside the image
        self.frames = []
        for i in range(im.n_frames):
            im.seek(i)
            self.frames.append(ImageTk.PhotoImage(im.copy()))

        # start the animation
        self.next_frame()

    def unload(self):
        self.config(image="")
        self.frames = None

    # modified to play the animation only once
    def next_frame(self, loc=0):
        self.config(image=self.frames[loc])
        if loc < len(self.frames)-1:
            self.after(self.delay, self.next_frame, loc+1)

Upvotes: 0

CVerica
CVerica

Reputation: 355

This is line for line an answer provided as to how to get tkinter to loop a gif indefinitely (except that you changed the duration of the delay).

I'm not sure you realize what count is doing here. Count, imported from itertools, is going to infintely count (acting as a "while(true)" but incrementing a number) unless given a barrier. It accepts two parameters (start= , stop= ) but if only given one, it defaults to start. So you have initiated a count at the value of im.n_frames.

What's happening is that you are loading the first frame, and starting the count at the last frame. When it then goes to find the next frame, you're hitting EOF, and starting the whole thing over again.

If the images are indexed starting at 1, try

for i in range(1, im.n_frames+1):

Upvotes: 0

hassan akbar
hassan akbar

Reputation: 61

there are two things that would be required to make this work in this code snippet

Number 1 change the loc intially to -1 secondly change the next_frame function to

 def next_frame(self):
        if self.frames:
            self.loc += 1
            self.config(image=self.frames[self.loc])
            self.after(self.delay, self.next_frame)

Upvotes: 1

Related Questions