Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c39fb57

Browse files
committed
Improve comments and docs.
1 parent f0801e9 commit c39fb57

File tree

1 file changed

+144
-16
lines changed

1 file changed

+144
-16
lines changed

lib/matplotlib/animation.py

Lines changed: 144 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@
3333

3434
#Needs:
3535
# - Need comments, docstrings
36-
# - Need to look at codecs
37-
# - Is there a common way to add metadata?
38-
# - Should refactor the way we get frames to save to simplify saving from multiple figures
36+
# - Is there a common way to add metadata? Yes. Could probably just pass
37+
# in a dict and assemble the string from there.
38+
# - Examples showing:
39+
# - Passing in a MovieWriter instance
40+
# - Using a movie writer's context manager to make a movie using only Agg,
41+
# no GUI toolkit.
42+
3943

4044
# A registry for available MovieWriter classes
4145
class MovieWriterRegistry(object):
@@ -55,6 +59,7 @@ def wrapper(writerClass):
5559
return wrapper
5660

5761
def list(self):
62+
''' Get a list of available MovieWriters.'''
5863
return self.avail.keys()
5964

6065
def __getitem__(self, name):
@@ -65,7 +70,47 @@ def __getitem__(self, name):
6570
writers = MovieWriterRegistry()
6671

6772
class MovieWriter(object):
73+
'''
74+
Base class for writing movies. Fundamentally, what a MovieWriter does
75+
is provide is a way to grab frames by calling grab_frame(). setup()
76+
is called to start the process and finish() is called afterwards.
77+
This class is set up to provide for writing movie frame data to a pipe.
78+
saving() is provided as a context manager to facilitate this process as::
79+
80+
`` with moviewriter.saving('myfile.mp4'):
81+
# Iterate over frames
82+
moviewriter.grab_frame()``
83+
84+
The use of the context manager ensures that setup and cleanup are
85+
performed as necessary.
86+
87+
Attributes
88+
----------
89+
frame_format: string
90+
The format used in writing frame data, defaults to 'rgba'
91+
'''
6892
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None):
93+
'''
94+
Construct a new MovieWriter object.
95+
96+
Parameters
97+
----------
98+
fps: int
99+
Framerate for movie.
100+
codec: string or None, optional
101+
The codec to use. If None (the default) the setting in the
102+
rcParam `animation.codec` is used.
103+
bitrate: int or None, optional
104+
The bitrate for the saved movie file, which is one way to control
105+
the output file size and quality. The default value is None,
106+
which uses the value stored in the rcParam `animation.bitrate`.
107+
A value of -1 implies that the bitrate should be determined
108+
automatically by the underlying utility.
109+
extra_args: list of strings or None
110+
A list of extra string arguments to be passed to the underlying
111+
movie utiltiy. The default is None, which passes the additional
112+
argurments in the 'animation.extra_args' rcParam.
113+
'''
69114
self.fps = fps
70115
self.frame_format = 'rgba'
71116

@@ -86,17 +131,41 @@ def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None):
86131

87132
@property
88133
def frame_size(self):
134+
'A tuple (width,height) in pixels of a movie frame.'
89135
width_inches, height_inches = self.fig.get_size_inches()
90136
return width_inches * self.dpi, height_inches * self.dpi
91137

92138
def setup(self, fig, outfile, dpi, *args):
139+
'''
140+
Perform setup for writing the movie file.
141+
142+
Parameters
143+
----------
144+
145+
fig: `matplotlib.Figure` instance
146+
The figure object that contains the information for frames
147+
outfile: string
148+
The filename of the resulting movie file
149+
dpi: int
150+
The DPI (or resolution) for the file. This controls the size
151+
in pixels of the resulting movie file.
152+
'''
93153
self.outfile = outfile
94154
self.fig = fig
95155
self.dpi = dpi
156+
157+
# Run here so that grab_frame() can write the data to a pipe. This
158+
# eliminates the need for temp files.
96159
self._run()
97160

98161
@contextlib.contextmanager
99162
def saving(self, *args):
163+
'''
164+
Context manager to facilitate writing the movie file.
165+
166+
*args are any parameters that should be passed to setup()
167+
'''
168+
# This particular sequence is what contextlib.contextmanager wants
100169
self.setup(*args)
101170
yield
102171
self.finish()
@@ -105,18 +174,24 @@ def _run(self):
105174
# Uses subprocess to call the program for assembling frames into a
106175
# movie file. *args* returns the sequence of command line arguments
107176
# from a few configuration options.
108-
command = self.args()
177+
command = self._args()
109178
verbose.report('MovieWriter.run: running command: %s'%' '.join(command))
110179
self._proc = subprocess.Popen(command, shell=False,
111180
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
112181
stdin=subprocess.PIPE)
113182

114183
def finish(self):
184+
'Finish any processing for writing the movie.'
115185
self.cleanup()
116186

117187
def grab_frame(self):
188+
'''
189+
Grab the image information from the figure and save as a movie frame.
190+
'''
118191
verbose.report('MovieWriter.grab_frame: Grabbing frame.', level='debug')
119192
try:
193+
# Tell the figure to save its data to the sink, using the
194+
# frame format and dpi.
120195
self.fig.savefig(self._frame_sink(), format=self.frame_format,
121196
dpi=self.dpi)
122197
except RuntimeError:
@@ -126,12 +201,15 @@ def grab_frame(self):
126201
raise
127202

128203
def _frame_sink(self):
204+
'Returns the place to which frames should be written.'
129205
return self._proc.stdin
130206

131-
def args(self):
207+
def _args(self):
208+
'Assemble list of utility-specific command-line arguments.'
132209
return NotImplementedError("args needs to be implemented by subclass.")
133210

134211
def cleanup(self):
212+
'Clean-up and collect the process used to write the movie file.'
135213
out,err = self._proc.communicate()
136214
verbose.report('MovieWriter -- Command stdout:\n%s' % out,
137215
level='debug')
@@ -140,10 +218,19 @@ def cleanup(self):
140218

141219
@classmethod
142220
def bin_path(cls):
221+
'''
222+
Returns the binary path to the commandline tool used by a specific
223+
subclass. This is a class method so that the tool can be looked for
224+
before making a particular MovieWriter subclass available.
225+
'''
143226
return rcParams[cls.exec_key]
144227

145228
@classmethod
146229
def isAvailable(cls):
230+
'''
231+
Check to see if a MovieWriter subclass is actually available by
232+
running the commandline tool.
233+
'''
147234
try:
148235
subprocess.Popen(cls.bin_path(), shell=False,
149236
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -153,23 +240,47 @@ def isAvailable(cls):
153240

154241

155242
class FileMovieWriter(MovieWriter):
243+
'`MovieWriter` subclass that handles writing to a file.'
156244
def __init__(self, *args):
157245
MovieWriter.__init__(self, *args)
158246
self.frame_format = rcParams['animation.frame_format']
159247

160248
def setup(self, fig, outfile, dpi, frame_prefix='_tmp', clear_temp=True):
161-
print fig, outfile, dpi, frame_prefix, clear_temp
249+
'''
250+
Perform setup for writing the movie file.
251+
252+
Parameters
253+
----------
254+
255+
fig: `matplotlib.Figure` instance
256+
The figure object that contains the information for frames
257+
outfile: string
258+
The filename of the resulting movie file
259+
dpi: int
260+
The DPI (or resolution) for the file. This controls the size
261+
in pixels of the resulting movie file.
262+
frame_prefix: string, optional
263+
The filename prefix to use for the temporary files. Defaults
264+
to '_tmp'
265+
clear_temp: bool
266+
Specifies whether the temporary files should be deleted after
267+
the movie is written. (Useful for debugging.) Defaults to True.
268+
'''
162269
self.fig = fig
163270
self.outfile = outfile
164271
self.dpi = dpi
165272
self.clear_temp = clear_temp
166273
self.temp_prefix = frame_prefix
167-
self._frame_counter = 0
274+
self._frame_counter = 0 # used for generating sequential file names
168275
self._temp_names = list()
169276
self.fname_format_str = '%s%%04d.%s'
170277

171278
@property
172279
def frame_format(self):
280+
'''
281+
Format (png, jpeg, etc.) to use for saving the frames, which can be
282+
decided by the individual subclasses.
283+
'''
173284
return self._frame_format
174285

175286
@frame_format.setter
@@ -180,27 +291,36 @@ def frame_format(self, frame_format):
180291
self._frame_format = self.supported_formats[0]
181292

182293
def _base_temp_name(self):
294+
# Generates a template name (without number) given the frame format
295+
# for extension and the prefix.
183296
return self.fname_format_str % (self.temp_prefix, self.frame_format)
184297

185298
def _frame_sink(self):
299+
# Creates a filename for saving using the basename and the current
300+
# counter.
186301
fname = self._base_temp_name() % self._frame_counter
302+
303+
# Save the filename so we can delete it later if necessary
187304
self._temp_names.append(fname)
188305
verbose.report(
189306
'FileMovieWriter.frame_sink: saving frame %d to fname=%s' % (self._frame_counter, fname),
190307
level='debug')
191-
self._frame_counter += 1
308+
self._frame_counter += 1 # Ensures each created name is 'unique'
192309

193310
# This file returned here will be closed once it's used by savefig()
194311
# because it will no longer be referenced and will be gc-ed.
195312
return open(fname, 'wb')
196313

197314
def finish(self):
198-
#Delete temporary files
315+
# Call run here now that all frame grabbing is done. All temp files
316+
# are available to be assembled.
199317
self._run()
200-
MovieWriter.finish(self)
318+
MovieWriter.finish(self) # Will call clean-up
201319

202320
def cleanup(self):
203321
MovieWriter.cleanup(self)
322+
323+
#Delete temporary files
204324
if self.clear_temp:
205325
import os
206326
verbose.report(
@@ -210,6 +330,8 @@ def cleanup(self):
210330
os.remove(fname)
211331

212332

333+
# Base class of ffmpeg information. Has the config keys and the common set
334+
# of arguments that controls the *output* side of things.
213335
class FFMpegBase:
214336
exec_key = 'animation.ffmpeg_path'
215337
args_key = 'animation.ffmpeg_args'
@@ -226,26 +348,30 @@ def output_args(self):
226348
return args + ['-y', self.outfile]
227349

228350

351+
# Combine FFMpeg options with pipe-based writing
229352
@writers.register('ffmpeg')
230353
class FFMpegWriter(MovieWriter, FFMpegBase):
231-
def args(self):
354+
def _args(self):
232355
# Returns the command line parameters for subprocess to use
233-
# ffmpeg to create a movie
356+
# ffmpeg to create a movie using a pipe
234357
return [self.bin_path(), '-f', 'rawvideo', '-vcodec', 'rawvideo',
235358
'-s', '%dx%d' % self.frame_size, '-pix_fmt', self.frame_format,
236359
'-r', str(self.fps), '-i', 'pipe:'] + self.output_args
237360

238361

362+
#Combine FFMpeg options with temp file-based writing
239363
@writers.register('ffmpeg_file')
240364
class FFMpegFileWriter(FileMovieWriter, FFMpegBase):
241365
supported_formats = ['png', 'jpeg', 'ppm', 'tiff', 'sgi', 'bmp', 'pbm', 'raw', 'rgba']
242-
def args(self):
366+
def _args(self):
243367
# Returns the command line parameters for subprocess to use
244-
# ffmpeg to create a movie
368+
# ffmpeg to create a movie using a collection of temp images
245369
return [self.bin_path(), '-r', str(self.fps), '-i',
246370
self._base_temp_name()] + self.output_args
247371

248372

373+
# Base class of mencoder information. Contains configuration key information
374+
# as well as arguments for controlling *output*
249375
class MencoderBase:
250376
exec_key = 'animation.mencoder_path'
251377
args_key = 'animation.mencoder_args'
@@ -260,20 +386,22 @@ def output_args(self):
260386
return args
261387

262388

389+
# Combine Mencoder options with pipe-based writing
263390
@writers.register('mencoder')
264391
class MencoderWriter(MovieWriter, MencoderBase):
265-
def args(self):
392+
def _args(self):
266393
# Returns the command line parameters for subprocess to use
267394
# mencoder to create a movie
268395
return [self.bin_path(), '-', '-demuxer', 'rawvideo', '-rawvideo',
269396
('w=%i:h=%i:' % self.frame_size +
270397
'fps=%i:format=%s' % (self.fps, self.frame_format))] + self.output_args
271398

272399

400+
# Combine Mencoder options with temp file-based writing
273401
@writers.register('mencoder_file')
274402
class MencoderFileWriter(FileMovieWriter, MencoderBase):
275403
supported_formats = ['png', 'jpeg', 'tga', 'sgi']
276-
def args(self):
404+
def _args(self):
277405
# Returns the command line parameters for subprocess to use
278406
# mencoder to create a movie
279407
return [self.bin_path(),

0 commit comments

Comments
 (0)