3636import abc
3737import contextlib
3838import tempfile
39+ import warnings
3940from matplotlib .cbook import iterable , is_string_like
4041from matplotlib .compat import subprocess
4142from matplotlib import verbose
42- from matplotlib import rcParams , rcParamsDefault
43+ from matplotlib import rcParams , rcParamsDefault , rc_context
4344
4445# Process creation flag for subprocess to prevent it raising a terminal
4546# window. See for example:
@@ -192,6 +193,11 @@ class MovieWriter(AbstractMovieWriter):
192193 frame_format: string
193194 The format used in writing frame data, defaults to 'rgba'
194195 '''
196+
197+ # Specifies whether the size of all frames need to be identical
198+ # i.e. whether we can use savefig.bbox = 'tight'
199+ frame_size_can_vary = False
200+
195201 def __init__ (self , fps = 5 , codec = None , bitrate = None , extra_args = None ,
196202 metadata = None ):
197203 '''
@@ -210,8 +216,8 @@ def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
210216 automatically by the underlying utility.
211217 extra_args: list of strings or None
212218 A list of extra string arguments to be passed to the underlying
213- movie utiltiy . The default is None, which passes the additional
214- argurments in the 'animation.extra_args' rcParam.
219+ movie utility . The default is None, which passes the additional
220+ arguments in the 'animation.extra_args' rcParam.
215221 metadata: dict of string:string or None
216222 A dictionary of keys and values for metadata to include in the
217223 output file. Some keys that may be of use include:
@@ -355,6 +361,11 @@ def isAvailable(cls):
355361
356362class FileMovieWriter (MovieWriter ):
357363 '`MovieWriter` subclass that handles writing to a file.'
364+
365+ # In general, if frames are writen to files on disk, it's not important
366+ # that they all be identically sized
367+ frame_size_can_vary = True
368+
358369 def __init__ (self , * args , ** kwargs ):
359370 MovieWriter .__init__ (self , * args , ** kwargs )
360371 self .frame_format = rcParams ['animation.frame_format' ]
@@ -491,15 +502,15 @@ class FFMpegBase(object):
491502
492503 @property
493504 def output_args (self ):
494- # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
495- # kbps
496505 args = ['-vcodec' , self .codec ]
497506 # For h264, the default format is yuv444p, which is not compatible
498507 # with quicktime (and others). Specifying yuv420p fixes playback on
499508 # iOS,as well as HTML5 video in firefox and safari (on both Win and
500509 # OSX). Also fixes internet explorer. This is as of 2015/10/29.
501510 if self .codec == 'h264' and '-pix_fmt' not in self .extra_args :
502511 args .extend (['-pix_fmt' , 'yuv420p' ])
512+ # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
513+ # kbps
503514 if self .bitrate > 0 :
504515 args .extend (['-b' , '%dk' % self .bitrate ])
505516 if self .extra_args :
@@ -794,8 +805,8 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
794805 `animation.bitrate`.
795806
796807 *extra_args* is a list of extra string arguments to be passed to the
797- underlying movie utiltiy . The default is None, which passes the
798- additional argurments in the 'animation.extra_args' rcParam.
808+ underlying movie utility . The default is None, which passes the
809+ additional arguments in the 'animation.extra_args' rcParam.
799810
800811 *metadata* is a dictionary of keys and values for metadata to include
801812 in the output file. Some keys that may be of use include:
@@ -815,29 +826,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
815826 if savefig_kwargs is None :
816827 savefig_kwargs = {}
817828
818- # FIXME: Using 'bbox_inches' doesn't currently work with
819- # writers that pipe the data to the command because this
820- # requires a fixed frame size (see Ryan May's reply in this
821- # thread: [1]). Thus we drop the 'bbox_inches' argument if it
822- # exists in savefig_kwargs.
823- #
824- # [1] (http://matplotlib.1069221.n5.nabble.com/
825- # Animation-class-let-save-accept-kwargs-which-
826- # are-passed-on-to-savefig-td39627.html)
827- #
828- if 'bbox_inches' in savefig_kwargs :
829- if not (writer in ['ffmpeg_file' , 'mencoder_file' ] or
830- isinstance (writer ,
831- (FFMpegFileWriter , MencoderFileWriter ))):
832- print ("Warning: discarding the 'bbox_inches' argument in "
833- "'savefig_kwargs' as it is only currently supported "
834- "with the writers 'ffmpeg_file' and 'mencoder_file' "
835- "(writer used: "
836- "'{0}')." .format (
837- writer if isinstance (writer , six .string_types )
838- else writer .__class__ .__name__ ))
839- savefig_kwargs .pop ('bbox_inches' )
840-
841829 # Need to disconnect the first draw callback, since we'll be doing
842830 # draws. Otherwise, we'll end up starting the animation.
843831 if self ._first_draw_id is not None :
@@ -881,7 +869,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
881869 extra_args = extra_args ,
882870 metadata = metadata )
883871 else :
884- import warnings
885872 warnings .warn ("MovieWriter %s unavailable" % writer )
886873
887874 try :
@@ -895,22 +882,48 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
895882
896883 verbose .report ('Animation.save using %s' % type (writer ),
897884 level = 'helpful' )
885+
886+ # FIXME: Using 'bbox_inches' doesn't currently work with
887+ # writers that pipe the data to the command because this
888+ # requires a fixed frame size (see Ryan May's reply in this
889+ # thread: [1]). Thus we drop the 'bbox_inches' argument if it
890+ # exists in savefig_kwargs.
891+ #
892+ # [1] (http://matplotlib.1069221.n5.nabble.com/
893+ # Animation-class-let-save-accept-kwargs-which-
894+ # are-passed-on-to-savefig-td39627.html)
895+ #
896+ if 'bbox_inches' in savefig_kwargs and not writer .frame_size_can_vary :
897+ warnings .warn ("Warning: discarding the 'bbox_inches' argument in "
898+ "'savefig_kwargs' as it not supported by "
899+ "{0})." .format (writer .__class__ .__name__ ))
900+ savefig_kwargs .pop ('bbox_inches' )
901+
898902 # Create a new sequence of frames for saved data. This is different
899903 # from new_frame_seq() to give the ability to save 'live' generated
900904 # frame information to be saved later.
901905 # TODO: Right now, after closing the figure, saving a movie won't work
902906 # since GUI widgets are gone. Either need to remove extra code to
903- # allow for this non-existant use case or find a way to make it work.
904- with writer .saving (self ._fig , filename , dpi ):
905- for anim in all_anim :
906- # Clear the initial frame
907- anim ._init_draw ()
908- for data in zip (* [a .new_saved_frame_seq ()
909- for a in all_anim ]):
910- for anim , d in zip (all_anim , data ):
911- # TODO: Need to see if turning off blit is really necessary
912- anim ._draw_next_frame (d , blit = False )
913- writer .grab_frame (** savefig_kwargs )
907+ # allow for this non-existent use case or find a way to make it work.
908+ with rc_context ():
909+ # See above about bbox_inches savefig kwarg
910+ if (not writer .frame_size_can_vary and
911+ rcParams ['savefig.bbox' ] == 'tight' ):
912+ verbose .report ("Disabling savefig.bbox = 'tight', as it is "
913+ "not supported by "
914+ "{0}." .format (writer .__class__ .__name__ ),
915+ level = 'helpful' )
916+ rcParams ['savefig.bbox' ] = None
917+ with writer .saving (self ._fig , filename , dpi ):
918+ for anim in all_anim :
919+ # Clear the initial frame
920+ anim ._init_draw ()
921+ for data in zip (* [a .new_saved_frame_seq ()
922+ for a in all_anim ]):
923+ for anim , d in zip (all_anim , data ):
924+ # TODO: See if turning off blit is really necessary
925+ anim ._draw_next_frame (d , blit = False )
926+ writer .grab_frame (** savefig_kwargs )
914927
915928 # Reconnect signal for first draw if necessary
916929 if reconnect_first_draw :
@@ -1038,7 +1051,7 @@ def to_html5_video(self):
10381051 directly into the HTML5 video tag. This respects the rc parameters
10391052 for the writer as well as the bitrate. This also makes use of the
10401053 ``interval`` to control the speed, and uses the ``repeat``
1041- paramter to decide whether to loop.
1054+ parameter to decide whether to loop.
10421055 '''
10431056 VIDEO_TAG = r'''<video {size} {options}>
10441057 <source type="video/mp4" src="data:video/mp4;base64,{video}">
0 commit comments