Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a4aebe3

Browse filesBrowse files
committed
Merge pull request #5454 from WarrenWeckesser/abstract-movie-writer
ENH: Create an abstract base class for movie writers.
2 parents 723d015 + 6bae745 commit a4aebe3
Copy full SHA for a4aebe3

File tree

Expand file treeCollapse file tree

3 files changed

+154
-20
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+154
-20
lines changed
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Abstract base class for movie writers
2+
-------------------------------------
3+
4+
The new :class:`~matplotlib.animation.AbstractMovieWriter` class defines
5+
the API required by a class that is to be used as the `writer` in the
6+
`save` method of the :class:`~matplotlib.animation.Animation` class.
7+
The existing :class:`~matplotlib.animation.MovieWriter` class now derives
8+
from the new abstract base class.

‎lib/matplotlib/animation.py

Copy file name to clipboardExpand all lines: lib/matplotlib/animation.py
+67-19Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
except ImportError:
3434
# python2
3535
from base64 import encodestring as encodebytes
36+
import abc
3637
import contextlib
3738
import tempfile
3839
from matplotlib.cbook import iterable, is_string_like
@@ -91,17 +92,76 @@ def __getitem__(self, name):
9192
writers = MovieWriterRegistry()
9293

9394

94-
class MovieWriter(object):
95+
class AbstractMovieWriter(six.with_metaclass(abc.ABCMeta)):
96+
'''
97+
Abstract base class for writing movies. Fundamentally, what a MovieWriter
98+
does is provide is a way to grab frames by calling grab_frame().
99+
100+
setup() is called to start the process and finish() is called afterwards.
101+
102+
This class is set up to provide for writing movie frame data to a pipe.
103+
saving() is provided as a context manager to facilitate this process as::
104+
105+
with moviewriter.saving(fig, outfile='myfile.mp4', dpi=100):
106+
# Iterate over frames
107+
moviewriter.grab_frame(**savefig_kwargs)
108+
109+
The use of the context manager ensures that setup() and finish() are
110+
performed as necessary.
111+
112+
An instance of a concrete subclass of this class can be given as the
113+
`writer` argument of `Animation.save()`.
114+
'''
115+
116+
@abc.abstractmethod
117+
def setup(self, fig, outfile, dpi, *args):
118+
'''
119+
Perform setup for writing the movie file.
120+
121+
fig: `matplotlib.Figure` instance
122+
The figure object that contains the information for frames
123+
outfile: string
124+
The filename of the resulting movie file
125+
dpi: int
126+
The DPI (or resolution) for the file. This controls the size
127+
in pixels of the resulting movie file.
128+
'''
129+
130+
@abc.abstractmethod
131+
def grab_frame(self, **savefig_kwargs):
132+
'''
133+
Grab the image information from the figure and save as a movie frame.
134+
All keyword arguments in savefig_kwargs are passed on to the 'savefig'
135+
command that saves the figure.
136+
'''
137+
138+
@abc.abstractmethod
139+
def finish(self):
140+
'Finish any processing for writing the movie.'
141+
142+
@contextlib.contextmanager
143+
def saving(self, fig, outfile, dpi, *args):
144+
'''
145+
Context manager to facilitate writing the movie file.
146+
147+
All arguments are passed on to `setup`.
148+
'''
149+
self.setup(fig, outfile, dpi, *args)
150+
yield
151+
self.finish()
152+
153+
154+
class MovieWriter(AbstractMovieWriter):
95155
'''
96156
Base class for writing movies. Fundamentally, what a MovieWriter does
97157
is provide is a way to grab frames by calling grab_frame(). setup()
98158
is called to start the process and finish() is called afterwards.
99159
This class is set up to provide for writing movie frame data to a pipe.
100160
saving() is provided as a context manager to facilitate this process as::
101161
102-
with moviewriter.saving('myfile.mp4'):
162+
with moviewriter.saving(fig, outfile='myfile.mp4', dpi=100):
103163
# Iterate over frames
104-
moviewriter.grab_frame()
164+
moviewriter.grab_frame(**savefig_kwargs)
105165
106166
The use of the context manager ensures that setup and cleanup are
107167
performed as necessary.
@@ -183,18 +243,6 @@ def setup(self, fig, outfile, dpi, *args):
183243
# eliminates the need for temp files.
184244
self._run()
185245

186-
@contextlib.contextmanager
187-
def saving(self, *args):
188-
'''
189-
Context manager to facilitate writing the movie file.
190-
191-
``*args`` are any parameters that should be passed to `setup`.
192-
'''
193-
# This particular sequence is what contextlib.contextmanager wants
194-
self.setup(*args)
195-
yield
196-
self.finish()
197-
198246
def _run(self):
199247
# Uses subprocess to call the program for assembling frames into a
200248
# movie file. *args* returns the sequence of command line arguments
@@ -669,10 +717,10 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
669717
670718
*filename* is the output filename, e.g., :file:`mymovie.mp4`
671719
672-
*writer* is either an instance of :class:`MovieWriter` or a string
673-
key that identifies a class to use, such as 'ffmpeg' or 'mencoder'.
674-
If nothing is passed, the value of the rcparam `animation.writer` is
675-
used.
720+
*writer* is either an instance of :class:`AbstractMovieWriter` or
721+
a string key that identifies a class to use, such as 'ffmpeg' or
722+
'mencoder'. If nothing is passed, the value of the rcparam
723+
`animation.writer` is used.
676724
677725
*fps* is the frames per second in the movie. Defaults to None,
678726
which will use the animation's specified interval to set the frames

‎lib/matplotlib/tests/test_animation.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_animation.py
+79-1Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import tempfile
88
import numpy as np
9+
from numpy.testing import assert_equal
910
from nose import with_setup
1011
from matplotlib import pyplot as plt
1112
from matplotlib import animation
@@ -14,10 +15,87 @@
1415
from matplotlib.testing.decorators import CleanupTest
1516

1617

18+
class NullMovieWriter(animation.AbstractMovieWriter):
19+
"""
20+
A minimal MovieWriter. It doesn't actually write anything.
21+
It just saves the arguments that were given to the setup() and
22+
grab_frame() methods as attributes, and counts how many times
23+
grab_frame() is called.
24+
25+
This class doesn't have an __init__ method with the appropriate
26+
signature, and it doesn't define an isAvailable() method, so
27+
it cannot be added to the 'writers' registry.
28+
"""
29+
30+
def setup(self, fig, outfile, dpi, *args):
31+
self.fig = fig
32+
self.outfile = outfile
33+
self.dpi = dpi
34+
self.args = args
35+
self._count = 0
36+
37+
def grab_frame(self, **savefig_kwargs):
38+
self.savefig_kwargs = savefig_kwargs
39+
self._count += 1
40+
41+
def finish(self):
42+
pass
43+
44+
45+
def test_null_movie_writer():
46+
# Test running an animation with NullMovieWriter.
47+
48+
fig = plt.figure()
49+
50+
def init():
51+
pass
52+
53+
def animate(i):
54+
pass
55+
56+
num_frames = 5
57+
filename = "unused.null"
58+
fps = 30
59+
dpi = 50
60+
savefig_kwargs = dict(foo=0)
61+
62+
anim = animation.FuncAnimation(fig, animate, init_func=init,
63+
frames=num_frames)
64+
writer = NullMovieWriter()
65+
anim.save(filename, fps=fps, dpi=dpi, writer=writer,
66+
savefig_kwargs=savefig_kwargs)
67+
68+
assert_equal(writer.fig, fig)
69+
assert_equal(writer.outfile, filename)
70+
assert_equal(writer.dpi, dpi)
71+
assert_equal(writer.args, ())
72+
assert_equal(writer.savefig_kwargs, savefig_kwargs)
73+
assert_equal(writer._count, num_frames)
74+
75+
76+
@animation.writers.register('null')
77+
class RegisteredNullMovieWriter(NullMovieWriter):
78+
79+
# To be able to add NullMovieWriter to the 'writers' registry,
80+
# we must define an __init__ method with a specific signature,
81+
# and we must define the class method isAvailable().
82+
# (These methods are not actually required to use an instance
83+
# of this class as the 'writer' argument of Animation.save().)
84+
85+
def __init__(self, fps=None, codec=None, bitrate=None,
86+
extra_args=None, metadata=None):
87+
pass
88+
89+
@classmethod
90+
def isAvailable(self):
91+
return True
92+
93+
1794
WRITER_OUTPUT = dict(ffmpeg='mp4', ffmpeg_file='mp4',
1895
mencoder='mp4', mencoder_file='mp4',
1996
avconv='mp4', avconv_file='mp4',
20-
imagemagick='gif', imagemagick_file='gif')
97+
imagemagick='gif', imagemagick_file='gif',
98+
null='null')
2199

22100

23101
# Smoke test for saving animations. In the future, we should probably

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.