8000 PillowWriter GIF duration and loop · Issue #19169 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
PillowWriter GIF duration and loop #19169
Closed
@znstrider

Description

@znstrider

Current PillowWriter functionality does not allow me to specify the duration for each of the gif frames and it is not possible to not have the gif loop.

Additionally, I frequently get a ValueError: buffer is not large enough when animating multi axes figures. (Not sure if this is a common problem).

Animation to help introduce a visual story can be quite beneficial to understanding and for that I would like to be able to not loop the gif and "pause" the gif via different durations for all frames.

Proposed Solution

  1. add duration=None, loop=0 arguments to setup() and to a PillowWriter specific saving context_manager.
    To not loop we can specify loop=None and then we specifically need to not pass the loop argument to save() in finish().

  2. What is the difference between Image.frombuffer and Image.open(buf)? I find that Image.open(buf) solves my problem of the ValueError: buffer is not large enough.
    It seems to take more time for me though. (about +33%)

  3. removed unused variable assignment: renderer = self.fig.canvas.get_renderer() from grab_frame()

class PillowWriter(AbstractMovieWriter):
    @classmethod
    def isAvailable(cls):
        return True

    def setup(self, fig, outfile, dpi=None, duration=None, loop=None):
        super().setup(fig, outfile, dpi=dpi)
        self.duration = duration
        self.loop = loop
        self._frames = []

    def grab_frame(self, **savefig_kwargs):
        buf = BytesIO()
        self.fig.savefig(
            buf, **{**savefig_kwargs, "dpi": self.dpi})
        self._frames.append(Image.open(buf))

    def finish(self):
        duration = self.duration or int(1000 / self.fps)

        # to not loop at all we need to avoid passing the loop argument to save
        if self.loop is None:
            self._frames[0].save(
                self.outfile, save_all=True, append_images=self._frames[1:],
                duration=duration)
        else:
            self._frames[0].save(
                self.outfile, save_all=True, append_images=self._frames[1:],
                duration=duration, loop=self.loop)

    @contextlib.contextmanager
    def saving(self, fig, outfile, dpi, duration=None, loop=0, *args, **kwargs):
        """
        Context manager to facilitate writing the movie file.
        ``*args, **kw`` are any parameters that should be passed to `setup`.
        """
        # This particular sequence is what contextlib.contextmanager wants
        self.setup(fig, outfile, dpi, duration, loop, *args, **kwargs)
        try:
            yield self
        finally:
            self.finish()

Additional context and prior art

endless looping for gifs was introduced here:
#11789

Documentation would have to be updated I suppose.
If this seems like a useful improvement to the PillowWriter I could go ahead and create a PR.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0