3535 from base64 import encodestring as encodebytes
3636import contextlib
3737import tempfile
38+ import warnings
3839from matplotlib .cbook import iterable , is_string_like
3940from matplotlib .compat import subprocess
4041from matplotlib import verbose
41- from matplotlib import rcParams , rcParamsDefault
42+ from matplotlib import rcParams , rcParamsDefault , rc_context
4243
4344# Process creation flag for subprocess to prevent it raising a terminal
4445# window. See for example:
@@ -109,6 +110,11 @@ class MovieWriter(object):
109110 frame_format: string
110111 The format used in writing frame data, defaults to 'rgba'
111112 '''
113+
114+ # Specifies whether the size of all frames need to be identical
115+ # i.e. whether we can use savefig.bbox = 'tight'
116+ frame_size_can_vary = False
117+
112118 def __init__ (self , fps = 5 , codec = None , bitrate = None , extra_args = None ,
113119 metadata = None ):
114120 '''
@@ -127,8 +133,8 @@ def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
127133 automatically by the underlying utility.
128134 extra_args: list of strings or None
129135 A list of extra string arguments to be passed to the underlying
130- movie utiltiy . The default is None, which passes the additional
131- argurments in the 'animation.extra_args' rcParam.
136+ movie utility . The default is None, which passes the additional
137+ arguments in the 'animation.extra_args' rcParam.
132138 metadata: dict of string:string or None
133139 A dictionary of keys and values for metadata to include in the
134140 output file. Some keys that may be of use include:
@@ -283,6 +289,11 @@ def isAvailable(cls):
283289
284290class FileMovieWriter (MovieWriter ):
285291 '`MovieWriter` subclass that handles writing to a file.'
292+
293+ # In general, if frames are writen to files on disk, it's not important
294+ # that they all be identically sized
295+ frame_size_can_vary = True
296+
286297 def __init__ (self , * args , ** kwargs ):
287298 MovieWriter .__init__ (self , * args , ** kwargs )
288299 self .frame_format = rcParams ['animation.frame_format' ]
@@ -409,15 +420,15 @@ class FFMpegBase(object):
409420
410421 @property
411422 def output_args (self ):
412- # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
413- # kbps
414423 args = ['-vcodec' , self .codec ]
415424 # For h264, the default format is yuv444p, which is not compatible
416425 # with quicktime (and others). Specifying yuv420p fixes playback on
417426 # iOS,as well as HTML5 video in firefox and safari (on both Win and
418427 # OSX). Also fixes internet explorer. This is as of 2015/10/29.
419428 if self .codec == 'h264' and '-pix_fmt' not in self .extra_args :
420429 args .extend (['-pix_fmt' , 'yuv420p' ])
430+ # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
431+ # kbps
421432 if self .bitrate > 0 :
422433 args .extend (['-b' , '%dk' % self .bitrate ])
423434 if self .extra_args :
@@ -691,8 +702,8 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
691702 `animation.bitrate`.
692703
693704 *extra_args* is a list of extra string arguments to be passed to the
694- underlying movie utiltiy . The default is None, which passes the
695- additional argurments in the 'animation.extra_args' rcParam.
705+ underlying movie utility . The default is None, which passes the
706+ additional arguments in the 'animation.extra_args' rcParam.
696707
697708 *metadata* is a dictionary of keys and values for metadata to include
698709 in the output file. Some keys that may be of use include:
@@ -712,29 +723,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
712723 if savefig_kwargs is None :
713724 savefig_kwargs = {}
714725
715- # FIXME: Using 'bbox_inches' doesn't currently work with
716- # writers that pipe the data to the command because this
717- # requires a fixed frame size (see Ryan May's reply in this
718- # thread: [1]). Thus we drop the 'bbox_inches' argument if it
719- # exists in savefig_kwargs.
720- #
721- # [1] (http://matplotlib.1069221.n5.nabble.com/
722- # Animation-class-let-save-accept-kwargs-which-
723- # are-passed-on-to-savefig-td39627.html)
724- #
725- if 'bbox_inches' in savefig_kwargs :
726- if not (writer in ['ffmpeg_file' , 'mencoder_file' ] or
727- isinstance (writer ,
728- (FFMpegFileWriter , MencoderFileWriter ))):
729- print ("Warning: discarding the 'bbox_inches' argument in "
730- "'savefig_kwargs' as it is only currently supported "
731- "with the writers 'ffmpeg_file' and 'mencoder_file' "
732- "(writer used: "
733- "'{0}')." .format (
734- writer if isinstance (writer , six .string_types )
735- else writer .__class__ .__name__ ))
736- savefig_kwargs .pop ('bbox_inches' )
737-
738726 # Need to disconnect the first draw callback, since we'll be doing
739727 # draws. Otherwise, we'll end up starting the animation.
740728 if self ._first_draw_id is not None :
@@ -778,7 +766,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
778766 extra_args = extra_args ,
779767 metadata = metadata )
780768 else :
781- import warnings
782769 warnings .warn ("MovieWriter %s unavailable" % writer )
783770
784771 try :
@@ -792,22 +779,48 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
792779
793780 verbose .report ('Animation.save using %s' % type (writer ),
794781 level = 'helpful' )
782+
783+ # FIXME: Using 'bbox_inches' doesn't currently work with
784+ # writers that pipe the data to the command because this
785+ # requires a fixed frame size (see Ryan May's reply in this
786+ # thread: [1]). Thus we drop the 'bbox_inches' argument if it
787+ # exists in savefig_kwargs.
788+ #
789+ # [1] (http://matplotlib.1069221.n5.nabble.com/
790+ # Animation-class-let-save-accept-kwargs-which-
791+ # are-passed-on-to-savefig-td39627.html)
792+ #
793+ if 'bbox_inches' in savefig_kwargs and not writer .frame_size_can_vary :
794+ warnings .warn ("Warning: discarding the 'bbox_inches' argument in "
795+ "'savefig_kwargs' as it not supported by "
796+ "{0})." .format (writer .__class__ .__name__ ))
797+ savefig_kwargs .pop ('bbox_inches' )
798+
795799 # Create a new sequence of frames for saved data. This is different
796800 # from new_frame_seq() to give the ability to save 'live' generated
797801 # frame information to be saved later.
798802 # TODO: Right now, after closing the figure, saving a movie won't work
799803 # since GUI widgets are gone. Either need to remove extra code to
800- # allow for this non-existant use case or find a way to make it work.
801- with writer .saving (self ._fig , filename , dpi ):
802- for anim in all_anim :
803- # Clear the initial frame
804- anim ._init_draw ()
805- for data in zip (* [a .new_saved_frame_seq ()
806- for a in all_anim ]):
807- for anim , d in zip (all_anim , data ):
808- # TODO: Need to see if turning off blit is really necessary
809- anim ._draw_next_frame (d , blit = False )
810- writer .grab_frame (** savefig_kwargs )
804+ # allow for this non-existent use case or find a way to make it work.
805+ with rc_context ():
806+ # See above about bbox_inches savefig kwarg
807+ if (not writer .frame_size_can_vary and
808+ rcParams ['savefig.bbox' ] == 'tight' ):
809+ verbose .report ("Disabling savefig.bbox = 'tight', as it is "
810+ "not supported by "
811+ "{0}." .format (writer .__class__ .__name__ ),
812+ level = 'helpful' )
813+ rcParams ['savefig.bbox' ] = None
814+ with writer .saving (self ._fig , filename , dpi ):
815+ for anim in all_anim :
816+ # Clear the initial frame
817+ anim ._init_draw ()
818+ for data in zip (* [a .new_saved_frame_seq ()
819+ for a in all_anim ]):
820+ for anim , d in zip (all_anim , data ):
821+ # TODO: See if turning off blit is really necessary
822+ anim ._draw_next_frame (d , blit = False )
823+ writer .grab_frame (** savefig_kwargs )
811824
812825 # Reconnect signal for first draw if necessary
813826 if reconnect_first_draw :
@@ -934,7 +947,7 @@ def to_html5_video(self):
934947 directly into the HTML5 video tag. This respects the rc parameters
935948 for the writer as well as the bitrate. This also makes use of the
936949 ``interval`` to control the speed, and uses the ``repeat``
937- paramter to decide whether to loop.
950+ parameter to decide whether to loop.
938951 '''
939952 VIDEO_TAG = r'''<video {size} {options}>
940953 <source type="video/mp4" src="data:video/mp4;base64,{video}">
0 commit comments