8000 Merge pull request #6421 from dopplershift/anim-cleanups · matplotlib/matplotlib@40b01b6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 40b01b6

Browse files
committed
Merge pull request #6421 from dopplershift/anim-cleanups
FIX: animation confusion
2 parents f7445bb + 229ddf6 commit 40b01b6

File tree

1 file changed

+56
-43
lines changed

1 file changed

+56
-43
lines changed

lib/matplotlib/animation.py

Lines changed: 56 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@
3535
from base64 import encodestring as encodebytes
3636
import contextlib
3737
import tempfile
38+
import warnings
3839
from matplotlib.cbook import iterable, is_string_like
3940
from matplotlib.compat import subprocess
4041
from 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

284290
class 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

Comments
 (0)
0