Description
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
-
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(). -
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%) -
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.