matplotlib  1.4.3
About: matplotlib is a python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms.
  Fossies Dox: matplotlib-1.4.3.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

animation.py
Go to the documentation of this file.
1 # TODO:
2 # * Loop Delay is broken on GTKAgg. This is because source_remove() is not
3 # working as we want. PyGTK bug?
4 # * Documentation -- this will need a new section of the User's Guide.
5 # Both for Animations and just timers.
6 # - Also need to update http://www.scipy.org/Cookbook/Matplotlib/Animations
7 # * Blit
8 # * Currently broken with Qt4 for widgets that don't start on screen
9 # * Still a few edge cases that aren't working correctly
10 # * Can this integrate better with existing matplotlib animation artist flag?
11 # - If animated removes from default draw(), perhaps we could use this to
12 # simplify initial draw.
13 # * Example
14 # * Frameless animation - pure procedural with no loop
15 # * Need example that uses something like inotify or subprocess
16 # * Complex syncing examples
17 # * Movies
18 # * Can blit be enabled for movies?
19 # * Need to consider event sources to allow clicking through multiple figures
20 from __future__ import (absolute_import, division, print_function,
21  unicode_literals)
22 
23 import six
24 from six.moves import xrange, zip
25 
26 import platform
27 import sys
28 import itertools
29 import contextlib
30 from matplotlib.cbook import iterable, is_string_like
31 from matplotlib.compat import subprocess
32 from matplotlib import verbose
33 from matplotlib import rcParams
34 
35 # Process creation flag for subprocess to prevent it raising a terminal
36 # window. See for example:
37 # https://stackoverflow.com/questions/24130623/using-python-subprocess-popen-cant-prevent-exe-stopped-working-prompt
38 if platform.system() == 'Windows':
39  CREATE_NO_WINDOW = 0x08000000
40  subprocess_creation_flags = CREATE_NO_WINDOW
41 else:
42  # Apparently None won't work here
43  subprocess_creation_flags = 0
44 
45 # Other potential writing methods:
46 # * http://pymedia.org/
47 # * libmng (produces swf) python wrappers: https://github.com/libming/libming
48 # * Wrap x264 API:
49 
50 # (http://stackoverflow.com/questions/2940671/
51 # how-to-encode-series-of-images-into-h264-using-x264-api-c-c )
52 
53 
54 # A registry for available MovieWriter classes
55 class MovieWriterRegistry(object):
56  def __init__(self):
57  self.avail = dict()
58 
59  # Returns a decorator that can be used on classes to register them under
60  # a name. As in:
61  # @register('foo')
62  # class Foo:
63  # pass
64  def register(self, name):
65  def wrapper(writerClass):
66  if writerClass.isAvailable():
67  self.avail[name] = writerClass
68  return writerClass
69  return wrapper
70 
71  def list(self):
72  ''' Get a list of available MovieWriters.'''
73  return list(self.avail.keys())
74 
75  def is_available(self, name):
76  return name in self.avail
77 
78  def __getitem__(self, name):
79  if not self.avail:
80  raise RuntimeError("No MovieWriters available!")
81  return self.avail[name]
82 
84 
85 
86 class MovieWriter(object):
87  '''
88  Base class for writing movies. Fundamentally, what a MovieWriter does
89  is provide is a way to grab frames by calling grab_frame(). setup()
90  is called to start the process and finish() is called afterwards.
91  This class is set up to provide for writing movie frame data to a pipe.
92  saving() is provided as a context manager to facilitate this process as::
93 
94  with moviewriter.saving('myfile.mp4'):
95  # Iterate over frames
96  moviewriter.grab_frame()
97 
98  The use of the context manager ensures that setup and cleanup are
99  performed as necessary.
100 
101  frame_format: string
102  The format used in writing frame data, defaults to 'rgba'
103  '''
104  def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
105  metadata=None):
106  '''
107  Construct a new MovieWriter object.
108 
109  fps: int
110  Framerate for movie.
111  codec: string or None, optional
112  The codec to use. If None (the default) the setting in the
113  rcParam `animation.codec` is used.
114  bitrate: int or None, optional
115  The bitrate for the saved movie file, which is one way to control
116  the output file size and quality. The default value is None,
117  which uses the value stored in the rcParam `animation.bitrate`.
118  A value of -1 implies that the bitrate should be determined
119  automatically by the underlying utility.
120  extra_args: list of strings or None
121  A list of extra string arguments to be passed to the underlying
122  movie utiltiy. The default is None, which passes the additional
123  argurments in the 'animation.extra_args' rcParam.
124  metadata: dict of string:string or None
125  A dictionary of keys and values for metadata to include in the
126  output file. Some keys that may be of use include:
127  title, artist, genre, subject, copyright, srcform, comment.
128  '''
129  self.fps = fps
130  self.frame_format = 'rgba'
131 
132  if codec is None:
133  self.codec = rcParams['animation.codec']
134  else:
135  self.codec = codec
136 
137  if bitrate is None:
138  self.bitrate = rcParams['animation.bitrate']
139  else:
140  self.bitrate = bitrate
141 
142  if extra_args is None:
143  self.extra_args = list(rcParams[self.args_key])
144  else:
145  self.extra_args = extra_args
146 
147  if metadata is None:
148  self.metadata = dict()
149  else:
150  self.metadata = metadata
151 
152  @property
153  def frame_size(self):
154  'A tuple (width,height) in pixels of a movie frame.'
155  width_inches, height_inches = self.fig.get_size_inches()
156  return width_inches * self.dpi, height_inches * self.dpi
157 
158  def setup(self, fig, outfile, dpi, *args):
159  '''
160  Perform setup for writing the movie file.
161 
162  fig: `matplotlib.Figure` instance
163  The figure object that contains the information for frames
164  outfile: string
165  The filename of the resulting movie file
166  dpi: int
167  The DPI (or resolution) for the file. This controls the size
168  in pixels of the resulting movie file.
169  '''
170  self.outfile = outfile
171  self.fig = fig
172  self.dpi = dpi
173 
174  # Run here so that grab_frame() can write the data to a pipe. This
175  # eliminates the need for temp files.
176  self._run()
177 
178  @contextlib.contextmanager
179  def saving(self, *args):
180  '''
181  Context manager to facilitate writing the movie file.
182 
183  ``*args`` are any parameters that should be passed to `setup`.
184  '''
185  # This particular sequence is what contextlib.contextmanager wants
186  self.setup(*args)
187  yield
188  self.finish()
189 
190  def _run(self):
191  # Uses subprocess to call the program for assembling frames into a
192  # movie file. *args* returns the sequence of command line arguments
193  # from a few configuration options.
194  command = self._args()
195  if verbose.ge('debug'):
196  output = sys.stdout
197  else:
198  output = subprocess.PIPE
199  verbose.report('MovieWriter.run: running command: %s' %
200  ' '.join(command))
201  self._proc = subprocess.Popen(command, shell=False,
202  stdout=output, stderr=output,
203  stdin=subprocess.PIPE,
204  creationflags=subprocess_creation_flags)
205 
206  def finish(self):
207  'Finish any processing for writing the movie.'
208  self.cleanup()
209 
210  def grab_frame(self, **savefig_kwargs):
211  '''
212  Grab the image information from the figure and save as a movie frame.
213  All keyword arguments in savefig_kwargs are passed on to the 'savefig'
214  command that saves the figure.
215  '''
216  verbose.report('MovieWriter.grab_frame: Grabbing frame.',
217  level='debug')
218  try:
219  # Tell the figure to save its data to the sink, using the
220  # frame format and dpi.
221  self.fig.savefig(self._frame_sink(), format=self.frame_format,
222  dpi=self.dpi, **savefig_kwargs)
223  except RuntimeError:
224  out, err = self._proc.communicate()
225  verbose.report('MovieWriter -- Error '
226  'running proc:\n%s\n%s' % (out,
227  err), level='helpful')
228  raise
229 
230  def _frame_sink(self):
231  'Returns the place to which frames should be written.'
232  return self._proc.stdin
233 
234  def _args(self):
235  'Assemble list of utility-specific command-line arguments.'
236  return NotImplementedError("args needs to be implemented by subclass.")
237 
238  def cleanup(self):
239  'Clean-up and collect the process used to write the movie file.'
240  out, err = self._proc.communicate()
241  verbose.report('MovieWriter -- '
242  'Command stdout:\n%s' % out, level='debug')
243  verbose.report('MovieWriter -- '
244  'Command stderr:\n%s' % err, level='debug')
245 
246  @classmethod
247  def bin_path(cls):
248  '''
249  Returns the binary path to the commandline tool used by a specific
250  subclass. This is a class method so that the tool can be looked for
251  before making a particular MovieWriter subclass available.
252  '''
253  return rcParams[cls.exec_key]
254 
255  @classmethod
256  def isAvailable(cls):
257  '''
258  Check to see if a MovieWriter subclass is actually available by
259  running the commandline tool.
260  '''
261  try:
262  p = subprocess.Popen(cls.bin_path(),
263  shell=False,
264  stdout=subprocess.PIPE,
265  stderr=subprocess.PIPE,
266  creationflags=subprocess_creation_flags)
267  p.communicate()
268  return True
269  except OSError:
270  return False
271 
272 
274  '`MovieWriter` subclass that handles writing to a file.'
275  def __init__(self, *args, **kwargs):
276  MovieWriter.__init__(self, *args, **kwargs)
277  self.frame_format = rcParams['animation.frame_format']
278 
279  def setup(self, fig, outfile, dpi, frame_prefix='_tmp', clear_temp=True):
280  '''
281  Perform setup for writing the movie file.
282 
283  fig: `matplotlib.Figure` instance
284  The figure object that contains the information for frames
285  outfile: string
286  The filename of the resulting movie file
287  dpi: int
288  The DPI (or resolution) for the file. This controls the size
289  in pixels of the resulting movie file.
290  frame_prefix: string, optional
291  The filename prefix to use for the temporary files. Defaults
292  to '_tmp'
293  clear_temp: bool
294  Specifies whether the temporary files should be deleted after
295  the movie is written. (Useful for debugging.) Defaults to True.
296  '''
297  self.fig = fig
298  self.outfile = outfile
299  self.dpi = dpi
300  self.clear_temp = clear_temp
301  self.temp_prefix = frame_prefix
302  self._frame_counter = 0 # used for generating sequential file names
303  self._temp_names = list()
304  self.fname_format_str = '%s%%07d.%s'
305 
306  @property
307  def frame_format(self):
308  '''
309  Format (png, jpeg, etc.) to use for saving the frames, which can be
310  decided by the individual subclasses.
311  '''
312  return self._frame_format
313 
314  @frame_format.setter
315  def frame_format(self, frame_format):
316  if frame_format in self.supported_formats:
317  self._frame_format = frame_format
318  else:
319  self._frame_format = self.supported_formats[0]
320 
321  def _base_temp_name(self):
322  # Generates a template name (without number) given the frame format
323  # for extension and the prefix.
324  return self.fname_format_str % (self.temp_prefix, self.frame_format)
325 
326  def _frame_sink(self):
327  # Creates a filename for saving using the basename and the current
328  # counter.
329  fname = self._base_temp_name() % self._frame_counter
330 
331  # Save the filename so we can delete it later if necessary
332  self._temp_names.append(fname)
333  verbose.report(
334  'FileMovieWriter.frame_sink: saving frame %d to fname=%s' %
335  (self._frame_counter, fname),
336  level='debug')
337  self._frame_counter += 1 # Ensures each created name is 'unique'
338 
339  # This file returned here will be closed once it's used by savefig()
340  # because it will no longer be referenced and will be gc-ed.
341  return open(fname, 'wb')
342 
343  def grab_frame(self, **savefig_kwargs):
344  '''
345  Grab the image information from the figure and save as a movie frame.
346  All keyword arguments in savefig_kwargs are passed on to the 'savefig'
347  command that saves the figure.
348  '''
349  # Overloaded to explicitly close temp file.
350  verbose.report('MovieWriter.grab_frame: Grabbing frame.',
351  level='debug')
352  try:
353  # Tell the figure to save its data to the sink, using the
354  # frame format and dpi.
355  myframesink = self._frame_sink()
356  self.fig.savefig(myframesink, format=self.frame_format,
357  dpi=self.dpi, **savefig_kwargs)
358  myframesink.close()
359 
360  except RuntimeError:
361  out, err = self._proc.communicate()
362  verbose.report('MovieWriter -- Error '
363  'running proc:\n%s\n%s' % (out,
364  err), level='helpful')
365  raise
366 
367  def finish(self):
368  # Call run here now that all frame grabbing is done. All temp files
369  # are available to be assembled.
370  self._run()
371  MovieWriter.finish(self) # Will call clean-up
372 
373  # Check error code for creating file here, since we just run
374  # the process here, rather than having an open pipe.
375  if self._proc.returncode:
376  raise RuntimeError('Error creating movie, return code: '
377  + str(self._proc.returncode)
378  + ' Try running with --verbose-debug')
379 
380  def cleanup(self):
381  MovieWriter.cleanup(self)
382 
383  # Delete temporary files
384  if self.clear_temp:
385  import os
386  verbose.report(
387  'MovieWriter: clearing temporary fnames=%s' %
388  str(self._temp_names),
389  level='debug')
390  for fname in self._temp_names:
391  os.remove(fname)
392 
393 
394 # Base class of ffmpeg information. Has the config keys and the common set
395 # of arguments that controls the *output* side of things.
397  exec_key = 'animation.ffmpeg_path'
398  args_key = 'animation.ffmpeg_args'
399 
400  @property
401  def output_args(self):
402  # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in
403  # kbps
404  args = ['-vcodec', self.codec]
405  if self.bitrate > 0:
406  args.extend(['-b', '%dk' % self.bitrate])
407  if self.extra_args:
408  args.extend(self.extra_args)
409  for k, v in six.iteritems(self.metadata):
410  args.extend(['-metadata', '%s=%s' % (k, v)])
411 
412  return args + ['-y', self.outfile]
413 
414 
415 # Combine FFMpeg options with pipe-based writing
416 @writers.register('ffmpeg')
418  def _args(self):
419  # Returns the command line parameters for subprocess to use
420  # ffmpeg to create a movie using a pipe.
421  args = [self.bin_path(), '-f', 'rawvideo', '-vcodec', 'rawvideo',
422  '-s', '%dx%d' % self.frame_size, '-pix_fmt', self.frame_format,
423  '-r', str(self.fps)]
424  # Logging is quieted because subprocess.PIPE has limited buffer size.
425  if not verbose.ge('debug'):
426  args += ['-loglevel', 'quiet']
427  args += ['-i', 'pipe:'] + self.output_args
428  return args
429 
430 
431 # Combine FFMpeg options with temp file-based writing
432 @writers.register('ffmpeg_file')
434  supported_formats = ['png', 'jpeg', 'ppm', 'tiff', 'sgi', 'bmp',
435  'pbm', 'raw', 'rgba']
436 
437  def _args(self):
438  # Returns the command line parameters for subprocess to use
439  # ffmpeg to create a movie using a collection of temp images
440  return [self.bin_path(), '-i', self._base_temp_name(),
441  '-vframes', str(self._frame_counter),
442  '-r', str(self.fps)] + self.output_args
443 
444 
445 # Base class of avconv information. AVConv has identical arguments to
446 # FFMpeg
448  exec_key = 'animation.avconv_path'
449  args_key = 'animation.avconv_args'
450 
451 
452 # Combine AVConv options with pipe-based writing
453 @writers.register('avconv')
455  pass
456 
457 
458 # Combine AVConv options with file-based writing
459 @writers.register('avconv_file')
461  pass
462 
463 
464 # Base class of mencoder information. Contains configuration key information
465 # as well as arguments for controlling *output*
466 class MencoderBase:
467  exec_key = 'animation.mencoder_path'
468  args_key = 'animation.mencoder_args'
469 
470  # Mencoder only allows certain keys, other ones cause the program
471  # to fail.
472  allowed_metadata = ['name', 'artist', 'genre', 'subject', 'copyright',
473  'srcform', 'comment']
474 
475  # Mencoder mandates using name, but 'title' works better with ffmpeg.
476  # If we find it, just put it's value into name
477  def _remap_metadata(self):
478  if 'title' in self.metadata:
479  self.metadata['name'] = self.metadata['title']
480 
481  @property
482  def output_args(self):
483  self._remap_metadata()
484  lavcopts = {'vcodec': self.codec}
485  if self.bitrate > 0:
486  lavcopts.update(vbitrate=self.bitrate)
487  args = ['-o', self.outfile, '-ovc', 'lavc', '-lavcopts',
488  ':'.join(itertools.starmap('{0}={1}'.format,
489  lavcopts.items()))]
490  if self.extra_args:
491  args.extend(self.extra_args)
492  if self.metadata:
493  args.extend(['-info', ':'.join('%s=%s' % (k, v)
494  for k, v in six.iteritems(self.metadata)
495  if k in self.allowed_metadata)])
496  return args
497 
498 
499 # Combine Mencoder options with pipe-based writing
500 @writers.register('mencoder')
502  def _args(self):
503  # Returns the command line parameters for subprocess to use
504  # mencoder to create a movie
505  return [self.bin_path(), '-', '-demuxer', 'rawvideo', '-rawvideo',
506  ('w=%i:h=%i:' % self.frame_size +
507  'fps=%i:format=%s' % (self.fps,
508  self.frame_format))] + self.output_args
509 
510 
511 # Combine Mencoder options with temp file-based writing
512 @writers.register('mencoder_file')
514  supported_formats = ['png', 'jpeg', 'tga', 'sgi']
515 
516  def _args(self):
517  # Returns the command line parameters for subprocess to use
518  # mencoder to create a movie
519  return [self.bin_path(),
520  'mf://%s*.%s' % (self.temp_prefix, self.frame_format),
521  '-frames', str(self._frame_counter), '-mf',
522  'type=%s:fps=%d' % (self.frame_format,
523  self.fps)] + self.output_args
524 
525 
526 # Base class for animated GIFs with convert utility
528  exec_key = 'animation.convert_path'
529  args_key = 'animation.convert_args'
530 
531  @property
532  def delay(self):
533  return 100. / self.fps
534 
535  @property
536  def output_args(self):
537  return [self.outfile]
538 
539 
540 @writers.register('imagemagick')
542  def _args(self):
543  return ([self.bin_path(),
544  '-size', '%ix%i' % self.frame_size, '-depth', '8',
545  '-delay', str(self.delay), '-loop', '0',
546  '%s:-' % self.frame_format]
547  + self.output_args)
548 
549 
550 @writers.register('imagemagick_file')
552  supported_formats = ['png', 'jpeg', 'ppm', 'tiff', 'sgi', 'bmp',
553  'pbm', 'raw', 'rgba']
554 
555  def _args(self):
556  return ([self.bin_path(), '-delay', str(self.delay), '-loop', '0',
557  '%s*.%s' % (self.temp_prefix, self.frame_format)]
558  + self.output_args)
559 
560 
561 class Animation(object):
562  '''
563  This class wraps the creation of an animation using matplotlib. It is
564  only a base class which should be subclassed to provide needed behavior.
565 
566  *fig* is the figure object that is used to get draw, resize, and any
567  other needed events.
568 
569  *event_source* is a class that can run a callback when desired events
570  are generated, as well as be stopped and started. Examples include timers
571  (see :class:`TimedAnimation`) and file system notifications.
572 
573  *blit* is a boolean that controls whether blitting is used to optimize
574  drawing.
575  '''
576  def __init__(self, fig, event_source=None, blit=False):
577  self._fig = fig
578  # Disables blitting for backends that don't support it. This
579  # allows users to request it if available, but still have a
580  # fallback that works if it is not.
581  self._blit = blit and fig.canvas.supports_blit
582 
583  # These are the basics of the animation. The frame sequence represents
584  # information for each frame of the animation and depends on how the
585  # drawing is handled by the subclasses. The event source fires events
586  # that cause the frame sequence to be iterated.
587  self.frame_seq = self.new_frame_seq()
588  self.event_source = event_source
589 
590  # Clear the initial frame
591  self._init_draw()
592 
593  # Instead of starting the event source now, we connect to the figure's
594  # draw_event, so that we only start once the figure has been drawn.
595  self._first_draw_id = fig.canvas.mpl_connect('draw_event', self._start)
596 
597  # Connect to the figure's close_event so that we don't continue to
598  # fire events and try to draw to a deleted figure.
599  self._close_id = self._fig.canvas.mpl_connect('close_event',
600  self._stop)
601  if self._blit:
602  self._setup_blit()
603 
604  def _start(self, *args):
605  '''
606  Starts interactive animation. Adds the draw frame command to the GUI
607  handler, calls show to start the event loop.
608  '''
609  # On start, we add our callback for stepping the animation and
610  # actually start the event_source. We also disconnect _start
611  # from the draw_events
612  self.event_source.add_callback(self._step)
613  self.event_source.start()
614  self._fig.canvas.mpl_disconnect(self._first_draw_id)
615  self._first_draw_id = None # So we can check on save
616 
617  def _stop(self, *args):
618  # On stop we disconnect all of our events.
619  if self._blit:
620  self._fig.canvas.mpl_disconnect(self._resize_id)
621  self._fig.canvas.mpl_disconnect(self._close_id)
622  self.event_source.remove_callback(self._step)
623  self.event_source = None
624 
625  def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
626  bitrate=None, extra_args=None, metadata=None, extra_anim=None,
627  savefig_kwargs=None):
628  '''
629  Saves a movie file by drawing every frame.
630 
631  *filename* is the output filename, e.g., :file:`mymovie.mp4`
632 
633  *writer* is either an instance of :class:`MovieWriter` or a string
634  key that identifies a class to use, such as 'ffmpeg' or 'mencoder'.
635  If nothing is passed, the value of the rcparam `animation.writer` is
636  used.
637 
638  *fps* is the frames per second in the movie. Defaults to None,
639  which will use the animation's specified interval to set the frames
640  per second.
641 
642  *dpi* controls the dots per inch for the movie frames. This combined
643  with the figure's size in inches controls the size of the movie.
644 
645  *codec* is the video codec to be used. Not all codecs are supported
646  by a given :class:`MovieWriter`. If none is given, this defaults to the
647  value specified by the rcparam `animation.codec`.
648 
649  *bitrate* specifies the amount of bits used per second in the
650  compressed movie, in kilobits per second. A higher number means a
651  higher quality movie, but at the cost of increased file size. If no
652  value is given, this defaults to the value given by the rcparam
653  `animation.bitrate`.
654 
655  *extra_args* is a list of extra string arguments to be passed to the
656  underlying movie utiltiy. The default is None, which passes the
657  additional argurments in the 'animation.extra_args' rcParam.
658 
659  *metadata* is a dictionary of keys and values for metadata to include
660  in the output file. Some keys that may be of use include:
661  title, artist, genre, subject, copyright, srcform, comment.
662 
663  *extra_anim* is a list of additional `Animation` objects that should
664  be included in the saved movie file. These need to be from the same
665  `matplotlib.Figure` instance. Also, animation frames will just be
666  simply combined, so there should be a 1:1 correspondence between
667  the frames from the different animations.
668 
669  *savefig_kwargs* is a dictionary containing keyword arguments to be
670  passed on to the 'savefig' command which is called repeatedly to save
671  the individual frames. This can be used to set tight bounding boxes,
672  for example.
673  '''
674  if savefig_kwargs is None:
675  savefig_kwargs = {}
676 
677  # FIXME: Using 'bbox_inches' doesn't currently work with
678  # writers that pipe the data to the command because this
679  # requires a fixed frame size (see Ryan May's reply in this
680  # thread: [1]). Thus we drop the 'bbox_inches' argument if it
681  # exists in savefig_kwargs.
682  #
683  # [1] (http://matplotlib.1069221.n5.nabble.com/
684  # Animation-class-let-save-accept-kwargs-which-
685  # are-passed-on-to-savefig-td39627.html)
686  #
687  if 'bbox_inches' in savefig_kwargs:
688  if not (writer in ['ffmpeg_file', 'mencoder_file'] or
689  isinstance(writer,
690  (FFMpegFileWriter, MencoderFileWriter))):
691  print("Warning: discarding the 'bbox_inches' argument in "
692  "'savefig_kwargs' as it is only currently supported "
693  "with the writers 'ffmpeg_file' and 'mencoder_file' "
694  "(writer used: "
695  "'{}').".format(
696  writer if isinstance(writer, six.string_types)
697  else writer.__class__.__name__))
698  savefig_kwargs.pop('bbox_inches')
699 
700  # Need to disconnect the first draw callback, since we'll be doing
701  # draws. Otherwise, we'll end up starting the animation.
702  if self._first_draw_id is not None:
703  self._fig.canvas.mpl_disconnect(self._first_draw_id)
704  reconnect_first_draw = True
705  else:
706  reconnect_first_draw = False
707 
708  if fps is None and hasattr(self, '_interval'):
709  # Convert interval in ms to frames per second
710  fps = 1000. / self._interval
711 
712  # If the writer is None, use the rc param to find the name of the one
713  # to use
714  if writer is None:
715  writer = rcParams['animation.writer']
716 
717  # Re-use the savefig DPI for ours if none is given
718  if dpi is None:
719  dpi = rcParams['savefig.dpi']
720 
721  if codec is None:
722  codec = rcParams['animation.codec']
723 
724  if bitrate is None:
725  bitrate = rcParams['animation.bitrate']
726 
727  all_anim = [self]
728  if extra_anim is not None:
729  all_anim.extend(anim
730  for anim
731  in extra_anim if anim._fig is self._fig)
732 
733  # If we have the name of a writer, instantiate an instance of the
734  # registered class.
735  if is_string_like(writer):
736  if writer in writers.avail:
737  writer = writers[writer](fps, codec, bitrate,
738  extra_args=extra_args,
739  metadata=metadata)
740  else:
741  import warnings
742  warnings.warn("MovieWriter %s unavailable" % writer)
743 
744  try:
745  writer = writers[writers.list()[0]](fps, codec, bitrate,
746  extra_args=extra_args,
747  metadata=metadata)
748  except IndexError:
749  raise ValueError("Cannot save animation: no writers are "
750  "available. Please install mencoder or "
751  "ffmpeg to save animations.")
752 
753  verbose.report('Animation.save using %s' % type(writer),
754  level='helpful')
755  # Create a new sequence of frames for saved data. This is different
756  # from new_frame_seq() to give the ability to save 'live' generated
757  # frame information to be saved later.
758  # TODO: Right now, after closing the figure, saving a movie won't work
759  # since GUI widgets are gone. Either need to remove extra code to
760  # allow for this non-existant use case or find a way to make it work.
761  with writer.saving(self._fig, filename, dpi):
762  for data in zip(*[a.new_saved_frame_seq()
763  for a in all_anim]):
764  for anim, d in zip(all_anim, data):
765  # TODO: Need to see if turning off blit is really necessary
766  anim._draw_next_frame(d, blit=False)
767  writer.grab_frame(**savefig_kwargs)
768 
769  # Reconnect signal for first draw if necessary
770  if reconnect_first_draw:
771  self._first_draw_id = self._fig.canvas.mpl_connect('draw_event',
772  self._start)
773 
774  def _step(self, *args):
775  '''
776  Handler for getting events. By default, gets the next frame in the
777  sequence and hands the data off to be drawn.
778  '''
779  # Returns True to indicate that the event source should continue to
780  # call _step, until the frame sequence reaches the end of iteration,
781  # at which point False will be returned.
782  try:
783  framedata = next(self.frame_seq)
784  self._draw_next_frame(framedata, self._blit)
785  return True
786  except StopIteration:
787  return False
788 
789  def new_frame_seq(self):
790  'Creates a new sequence of frame information.'
791  # Default implementation is just an iterator over self._framedata
792  return iter(self._framedata)
793 
795  'Creates a new sequence of saved/cached frame information.'
796  # Default is the same as the regular frame sequence
797  return self.new_frame_seq()
798 
799  def _draw_next_frame(self, framedata, blit):
800  # Breaks down the drawing of the next frame into steps of pre- and
801  # post- draw, as well as the drawing of the frame itself.
802  self._pre_draw(framedata, blit)
803  self._draw_frame(framedata)
804  self._post_draw(framedata, blit)
805 
806  def _init_draw(self):
807  # Initial draw to clear the frame. Also used by the blitting code
808  # when a clean base is required.
809  pass
810 
811  def _pre_draw(self, framedata, blit):
812  # Perform any cleaning or whatnot before the drawing of the frame.
813  # This default implementation allows blit to clear the frame.
814  if blit:
815  self._blit_clear(self._drawn_artists, self._blit_cache)
816 
817  def _draw_frame(self, framedata):
818  # Performs actual drawing of the frame.
819  raise NotImplementedError('Needs to be implemented by subclasses to'
820  ' actually make an animation.')
821 
822  def _post_draw(self, framedata, blit):
823  # After the frame is rendered, this handles the actual flushing of
824  # the draw, which can be a direct draw_idle() or make use of the
825  # blitting.
826  if blit and self._drawn_artists:
827  self._blit_draw(self._drawn_artists, self._blit_cache)
828  else:
829  self._fig.canvas.draw_idle()
830 
831  # The rest of the code in this class is to facilitate easy blitting
832  def _blit_draw(self, artists, bg_cache):
833  # Handles blitted drawing, which renders only the artists given instead
834  # of the entire figure.
835  updated_ax = []
836  for a in artists:
837  # If we haven't cached the background for this axes object, do
838  # so now. This might not always be reliable, but it's an attempt
839  # to automate the process.
840  if a.axes not in bg_cache:
841  bg_cache[a.axes] = a.figure.canvas.copy_from_bbox(a.axes.bbox)
842  a.axes.draw_artist(a)
843  updated_ax.append(a.axes)
844 
845  # After rendering all the needed artists, blit each axes individually.
846  for ax in set(updated_ax):
847  ax.figure.canvas.blit(ax.bbox)
848 
849  def _blit_clear(self, artists, bg_cache):
850  # Get a list of the axes that need clearing from the artists that
851  # have been drawn. Grab the appropriate saved background from the
852  # cache and restore.
853  axes = set(a.axes for a in artists)
854  for a in axes:
855  a.figure.canvas.restore_region(bg_cache[a])
856 
857  def _setup_blit(self):
858  # Setting up the blit requires: a cache of the background for the
859  # axes
860  self._blit_cache = dict()
861  self._drawn_artists = []
862  self._resize_id = self._fig.canvas.mpl_connect('resize_event',
863  self._handle_resize)
864  self._post_draw(None, self._blit)
865 
866  def _handle_resize(self, *args):
867  # On resize, we need to disable the resize event handling so we don't
868  # get too many events. Also stop the animation events, so that
869  # we're paused. Reset the cache and re-init. Set up an event handler
870  # to catch once the draw has actually taken place.
871  self._fig.canvas.mpl_disconnect(self._resize_id)
872  self.event_source.stop()
873  self._blit_cache.clear()
874  self._init_draw()
875  self._resize_id = self._fig.canvas.mpl_connect('draw_event',
876  self._end_redraw)
877 
878  def _end_redraw(self, evt):
879  # Now that the redraw has happened, do the post draw flushing and
880  # blit handling. Then re-enable all of the original events.
881  self._post_draw(None, self._blit)
882  self.event_source.start()
883  self._fig.canvas.mpl_disconnect(self._resize_id)
884  self._resize_id = self._fig.canvas.mpl_connect('resize_event',
885  self._handle_resize)
886 
887 
889  '''
890  :class:`Animation` subclass that supports time-based animation, drawing
891  a new frame every *interval* milliseconds.
892 
893  *repeat* controls whether the animation should repeat when the sequence
894  of frames is completed.
895 
896  *repeat_delay* optionally adds a delay in milliseconds before repeating
897  the animation.
898  '''
899  def __init__(self, fig, interval=200, repeat_delay=None, repeat=True,
900  event_source=None, *args, **kwargs):
901  # Store the timing information
902  self._interval = interval
903  self._repeat_delay = repeat_delay
904  self.repeat = repeat
905 
906  # If we're not given an event source, create a new timer. This permits
907  # sharing timers between animation objects for syncing animations.
908  if event_source is None:
909  event_source = fig.canvas.new_timer()
910  event_source.interval = self._interval
911 
912  Animation.__init__(self, fig, event_source=event_source,
913  *args, **kwargs)
914 
915  def _step(self, *args):
916  '''
917  Handler for getting events.
918  '''
919  # Extends the _step() method for the Animation class. If
920  # Animation._step signals that it reached the end and we want to
921  # repeat, we refresh the frame sequence and return True. If
922  # _repeat_delay is set, change the event_source's interval to our loop
923  # delay and set the callback to one which will then set the interval
924  # back.
925  still_going = Animation._step(self, *args)
926  if not still_going and self.repeat:
927  self.frame_seq = self.new_frame_seq()
928  if self._repeat_delay:
929  self.event_source.remove_callback(self._step)
930  self.event_source.add_callback(self._loop_delay)
931  self.event_source.interval = self._repeat_delay
932  return True
933  else:
934  return Animation._step(self, *args)
935  else:
936  return still_going
937 
938  def _stop(self, *args):
939  # If we stop in the middle of a loop delay (which is relatively likely
940  # given the potential pause here, remove the loop_delay callback as
941  # well.
942  self.event_source.remove_callback(self._loop_delay)
943  Animation._stop(self)
944 
945  def _loop_delay(self, *args):
946  # Reset the interval and change callbacks after the delay.
947  self.event_source.remove_callback(self._loop_delay)
948  self.event_source.interval = self._interval
949  self.event_source.add_callback(self._step)
950  Animation._step(self)
951 
952 
954  '''
955  Before calling this function, all plotting should have taken place
956  and the relevant artists saved.
957 
958  frame_info is a list, with each list entry a collection of artists that
959  represent what needs to be enabled on each frame. These will be disabled
960  for other frames.
961  '''
962  def __init__(self, fig, artists, *args, **kwargs):
963  # Internal list of artists drawn in the most recent frame.
964  self._drawn_artists = []
965 
966  # Use the list of artists as the framedata, which will be iterated
967  # over by the machinery.
968  self._framedata = artists
969  TimedAnimation.__init__(self, fig, *args, **kwargs)
970 
971  def _init_draw(self):
972  # Make all the artists involved in *any* frame invisible
973  axes = []
974  for f in self.new_frame_seq():
975  for artist in f:
976  artist.set_visible(False)
977  # Assemble a list of unique axes that need flushing
978  if artist.axes not in axes:
979  axes.append(artist.axes)
980 
981  # Flush the needed axes
982  for ax in axes:
983  ax.figure.canvas.draw()
984 
985  def _pre_draw(self, framedata, blit):
986  '''
987  Clears artists from the last frame.
988  '''
989  if blit:
990  # Let blit handle clearing
991  self._blit_clear(self._drawn_artists, self._blit_cache)
992  else:
993  # Otherwise, make all the artists from the previous frame invisible
994  for artist in self._drawn_artists:
995  artist.set_visible(False)
996 
997  def _draw_frame(self, artists):
998  # Save the artists that were passed in as framedata for the other
999  # steps (esp. blitting) to use.
1000  self._drawn_artists = artists
1001 
1002  # Make all the artists from the current frame visible
1003  for artist in artists:
1004  artist.set_visible(True)
1005 
1006 
1008  '''
1009  Makes an animation by repeatedly calling a function *func*, passing in
1010  (optional) arguments in *fargs*.
1011 
1012  *frames* can be a generator, an iterable, or a number of frames.
1013 
1014  *init_func* is a function used to draw a clear frame. If not given, the
1015  results of drawing from the first item in the frames sequence will be
1016  used. This function will be called once before the first frame.
1017 
1018  If blit=True, *func* and *init_func* should return an iterable of
1019  drawables to clear.
1020 
1021  *kwargs* include *repeat*, *repeat_delay*, and *interval*:
1022  *interval* draws a new frame every *interval* milliseconds.
1023  *repeat* controls whether the animation should repeat when the sequence
1024  of frames is completed.
1025  *repeat_delay* optionally adds a delay in milliseconds before repeating
1026  the animation.
1027  '''
1028  def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
1029  save_count=None, **kwargs):
1030  if fargs:
1031  self._args = fargs
1032  else:
1033  self._args = ()
1034  self._func = func
1035 
1036  # Amount of framedata to keep around for saving movies. This is only
1037  # used if we don't know how many frames there will be: in the case
1038  # of no generator or in the case of a callable.
1039  self.save_count = save_count
1040 
1041  # Set up a function that creates a new iterable when needed. If nothing
1042  # is passed in for frames, just use itertools.count, which will just
1043  # keep counting from 0. A callable passed in for frames is assumed to
1044  # be a generator. An iterable will be used as is, and anything else
1045  # will be treated as a number of frames.
1046  if frames is None:
1047  self._iter_gen = itertools.count
1048  elif six.callable(frames):
1049  self._iter_gen = frames
1050  elif iterable(frames):
1051  self._iter_gen = lambda: iter(frames)
1052  if hasattr(frames, '__len__'):
1053  self.save_count = len(frames)
1054  else:
1055  self._iter_gen = lambda: xrange(frames).__iter__()
1056  self.save_count = frames
1057 
1058  # If we're passed in and using the default, set it to 100.
1059  if self.save_count is None:
1060  self.save_count = 100
1061 
1062  self._init_func = init_func
1063 
1064  # Needs to be initialized so the draw functions work without checking
1065  self._save_seq = []
1066 
1067  TimedAnimation.__init__(self, fig, **kwargs)
1068 
1069  # Need to reset the saved seq, since right now it will contain data
1070  # for a single frame from init, which is not what we want.
1071  self._save_seq = []
1072 
1073  def new_frame_seq(self):
1074  # Use the generating function to generate a new frame sequence
1075  return self._iter_gen()
1076 
1078  # Generate an iterator for the sequence of saved data. If there are
1079  # no saved frames, generate a new frame sequence and take the first
1080  # save_count entries in it.
1081  if self._save_seq:
1082  return iter(self._save_seq)
1083  else:
1084  return itertools.islice(self.new_frame_seq(), self.save_count)
1085 
1086  def _init_draw(self):
1087  # Initialize the drawing either using the given init_func or by
1088  # calling the draw function with the first item of the frame sequence.
1089  # For blitting, the init_func should return a sequence of modified
1090  # artists.
1091  if self._init_func is None:
1092  self._draw_frame(next(self.new_frame_seq()))
1093  else:
1094  self._drawn_artists = self._init_func()
1095 
1096  def _draw_frame(self, framedata):
1097  # Save the data for potential saving of movies.
1098  self._save_seq.append(framedata)
1099 
1100  # Make sure to respect save_count (keep only the last save_count
1101  # around)
1102  self._save_seq = self._save_seq[-self.save_count:]
1103 
1104  # Call the func with framedata and args. If blitting is desired,
1105  # func needs to return a sequence of any artists that were modified.
1106  self._drawn_artists = self._func(framedata, *self._args)
Object type(const Exception &)
Definition: Objects.hxx:3423
def setup(self, fig, outfile, dpi, args)
Definition: animation.py:158
def _pre_draw(self, framedata, blit)
Definition: animation.py:811
def __init__(self, args, kwargs)
Definition: animation.py:275
def __init__(self, fig, interval=200, repeat_delay=None, repeat=True, event_source=None, args, kwargs)
Definition: animation.py:900
def _post_draw(self, framedata, blit)
Definition: animation.py:822
def _draw_frame(self, framedata)
Definition: animation.py:817
def __init__(self, fig, artists, args, kwargs)
Definition: animation.py:962
def grab_frame(self, savefig_kwargs)
Definition: animation.py:210
def grab_frame(self, savefig_kwargs)
Definition: animation.py:343
def __init__(self, fig, func, frames=None, init_func=None, fargs=None, save_count=None, kwargs)
Definition: animation.py:1029
def _draw_next_frame(self, framedata, blit)
Definition: animation.py:799
def _blit_clear(self, artists, bg_cache)
Definition: animation.py:849