From 9a48efd733f73f772c5bb1d557913ccdb588dae3 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Wed, 31 Oct 2012 22:18:12 +0000 Subject: [PATCH 1/3] Allow Animation.save() to accept keyword arguments which are passed on to savefig() when saving individual frames. This can be used to set tight bounding boxes etc. --- lib/matplotlib/animation.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 1a2ebe07382a..bff607e6fc78 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -184,9 +184,11 @@ def finish(self): 'Finish any processing for writing the movie.' self.cleanup() - def grab_frame(self): + def grab_frame(self, **savefig_kwargs): ''' Grab the image information from the figure and save as a movie frame. + All keyword arguments in savefig_kwargs are passed on to the 'savefig' + command that saves the figure. ''' verbose.report('MovieWriter.grab_frame: Grabbing frame.', level='debug') @@ -194,7 +196,7 @@ def grab_frame(self): # Tell the figure to save its data to the sink, using the # frame format and dpi. self.fig.savefig(self._frame_sink(), format=self.frame_format, - dpi=self.dpi) + dpi=self.dpi, **savefig_kwargs) except RuntimeError: out, err = self._proc.communicate() verbose.report('MovieWriter -- Error running proc:\n%s\n%s' % (out, @@ -545,7 +547,8 @@ def _stop(self, *args): self.event_source = None def save(self, filename, writer=None, fps=None, dpi=None, codec=None, - bitrate=None, extra_args=None, metadata=None, extra_anim=None): + bitrate=None, extra_args=None, metadata=None, extra_anim=None, + savefig_kwargs={}): ''' Saves a movie file by drawing every frame. @@ -586,6 +589,11 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, `matplotlib.Figure` instance. Also, animation frames will just be simply combined, so there should be a 1:1 correspondence between the frames from the different animations. + + *savefig_kwargs* is a dictionary containing keyword arguments to be + passed on to the 'savefig' command which is called repeatedly to save + the individual frames. This can be used to set tight bounding boxes, + for example. ''' # Need to disconnect the first draw callback, since we'll be doing # draws. Otherwise, we'll end up starting the animation. @@ -645,7 +653,7 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, for anim, d in zip(all_anim, data): #TODO: Need to see if turning off blit is really necessary anim._draw_next_frame(d, blit=False) - writer.grab_frame() + writer.grab_frame(**savefig_kwargs) # Reconnect signal for first draw if necessary if reconnect_first_draw: From 5f42166d49fbf3137087fef8799342937269c803 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Thu, 1 Nov 2012 09:24:44 +0000 Subject: [PATCH 2/3] Don't use a mutable type (here, a dict) as default argument because it only gets evaluated once when the function is defined, not during each function call (i.e. changes will persist across function calls). --- lib/matplotlib/animation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index bff607e6fc78..c31f939b44e1 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -548,7 +548,7 @@ def _stop(self, *args): def save(self, filename, writer=None, fps=None, dpi=None, codec=None, bitrate=None, extra_args=None, metadata=None, extra_anim=None, - savefig_kwargs={}): + savefig_kwargs=None): ''' Saves a movie file by drawing every frame. @@ -595,6 +595,9 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, the individual frames. This can be used to set tight bounding boxes, for example. ''' + if savefig_kwargs is None: + savefig_kwargs = {} + # Need to disconnect the first draw callback, since we'll be doing # draws. Otherwise, we'll end up starting the animation. if self._first_draw_id is not None: From fe44357d04fd708c616e88e386bb06100c12aaca Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Mon, 5 Nov 2012 11:52:50 +0000 Subject: [PATCH 3/3] Drop 'bbox_inches' argument if using a writer that is not temp file-based. --- lib/matplotlib/animation.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index c31f939b44e1..d47a1c72f845 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -598,6 +598,23 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, if savefig_kwargs is None: savefig_kwargs = {} + # FIXME: Using 'bbox_inches' doesn't currently work with writers that pipe + # the data to the command because this requires a fixed frame size (see + # Ryan May's reply in this thread: [1]). Thus we drop the 'bbox_inches' + # argument if it exists in savefig_kwargs. + # + # [1] http://matplotlib.1069221.n5.nabble.com/Animation-class-let-save-accept-kwargs-which-are-passed-on-to-savefig-td39627.html + # + if savefig_kwargs.has_key('bbox_inches'): + if not (writer in ['ffmpeg_file', 'mencoder_file'] or + isinstance(writer, (FFMpegFileWriter, MencoderFileWriter))): + print("Warning: discarding the 'bbox_inches' argument in " \ + "'savefig_kwargs' as it is only currently supported " \ + "with the writers 'ffmpeg_file' and 'mencoder_file' " \ + "(writer used: '{}').".format(writer if isinstance(writer, str) + else writer.__class__.__name__)) + savefig_kwargs.pop('bbox_inches') + # Need to disconnect the first draw callback, since we'll be doing # draws. Otherwise, we'll end up starting the animation. if self._first_draw_id is not None: