35
35
from base64 import encodestring as encodebytes
36
36
import contextlib
37
37
import tempfile
38
+ import warnings
38
39
from matplotlib .cbook import iterable , is_string_like
39
40
from matplotlib .compat import subprocess
40
41
from matplotlib import verbose
41
- from matplotlib import rcParams , rcParamsDefault
42
+ from matplotlib import rcParams , rcParamsDefault , rc_context
42
43
43
44
# Process creation flag for subprocess to prevent it raising a terminal
44
45
# window. See for example:
@@ -109,6 +110,11 @@ class MovieWriter(object):
109
110
frame_format: string
110
111
The format used in writing frame data, defaults to 'rgba'
111
112
'''
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
+
112
118
def __init__ (self , fps = 5 , codec = None , bitrate = None , extra_args = None ,
113
119
metadata = None ):
114
120
'''
@@ -127,8 +133,8 @@ def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
127
133
automatically by the underlying utility.
128
134
extra_args: list of strings or None
129
135
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.
132
138
metadata: dict of string:string or None
133
139
A dictionary of keys and values for metadata to include in the
134
140
output file. Some keys that may be of use include:
@@ -283,6 +289,11 @@ def isAvailable(cls):
283
289
284
290
class FileMovieWriter (MovieWriter ):
285
291
'`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
+
286
297
def __init__ (self , * args , ** kwargs ):
287
298
MovieWriter .__init__ (self , * args , ** kwargs )
288
299
self .frame_format = rcParams ['animation.frame_format' ]
@@ -409,15 +420,15 @@ class FFMpegBase(object):
409
420
410
421
@property
411
422
def output_args (self ):
412
- # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
413
- # kbps
414
423
args = ['-vcodec' , self .codec ]
415
424
# For h264, the default format is yuv444p, which is not compatible
416
425
# with quicktime (and others). Specifying yuv420p fixes playback on
417
426
# iOS,as well as HTML5 video in firefox and safari (on both Win and
418
427
# OSX). Also fixes internet explorer. This is as of 2015/10/29.
419
428
if self .codec == 'h264' and '-pix_fmt' not in self .extra_args :
420
429
args .extend (['-pix_fmt' , 'yuv420p' ])
430
+ # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
431
+ # kbps
421
432
if self .bitrate > 0 :
422
433
args .extend (['-b' , '%dk' % self .bitrate ])
423
434
if self .extra_args :
@@ -691,8 +702,8 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
691
702
`animation.bitrate`.
692
703
693
704
*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.
696
707
697
708
*metadata* is a dictionary of keys and values for metadata to include
698
709
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,
712
723
if savefig_kwargs is None :
713
724
savefig_kwargs = {}
714
725
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
-
738
726
# Need to disconnect the first draw callback, since we'll be doing
739
727
# draws. Otherwise, we'll end up starting the animation.
740
728
if self ._first_draw_id is not None :
@@ -778,7 +766,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
778
766
extra_args = extra_args ,
779
767
metadata = metadata )
780
768
else :
781
- import warnings
782
769
warnings .warn ("MovieWriter %s unavailable" % writer )
783
770
784
771
try :
@@ -792,22 +779,48 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
792
779
793
780
verbose .report ('Animation.save using %s' % type (writer ),
794
781
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
+
795
799
# Create a new sequence of frames for saved data. This is different
796
800
# from new_frame_seq() to give the ability to save 'live' generated
797
801
# frame information to be saved later.
798
802
# TODO: Right now, after closing the figure, saving a movie won't work
799
803
# 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 )
811
824
812
825
# Reconnect signal for first draw if necessary
813
826
if reconnect_first_draw :
@@ -934,7 +947,7 @@ def to_html5_video(self):
934
947
directly into the HTML5 video tag. This respects the rc parameters
935
948
for the writer as well as the bitrate. This also makes use of the
936
949
``interval`` to control the speed, and uses the ``repeat``
937
- paramter to decide whether to loop.
950
+ parameter to decide whether to loop.
938
951
'''
939
952
VIDEO_TAG = r'''<video {size} {options}>
940
953
<source type="video/mp4" src="data:video/mp4;base64,{video}">
0 commit comments