matplotlib  1.4.2
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.2.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

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