File: animation_api.rst

package info (click to toggle)
matplotlib 3.10.1%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 78,352 kB
  • sloc: python: 147,118; cpp: 62,988; objc: 1,679; ansic: 1,426; javascript: 786; makefile: 104; sh: 53
file content (327 lines) | stat: -rw-r--r-- 8,655 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
************************
``matplotlib.animation``
************************

.. automodule:: matplotlib.animation
   :no-members:
   :no-undoc-members:

.. contents:: Table of Contents
   :depth: 1
   :local:
   :backlinks: entry


Animation
=========

The easiest way to make a live animation in Matplotlib is to use one of the
`Animation` classes.

.. seealso::
   - :ref:`animations`

.. inheritance-diagram:: matplotlib.animation.FuncAnimation matplotlib.animation.ArtistAnimation
   :parts: 1

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   Animation
   FuncAnimation
   ArtistAnimation

In both cases it is critical to keep a reference to the instance
object.  The animation is advanced by a timer (typically from the host
GUI framework) which the `Animation` object holds the only reference
to.  If you do not hold a reference to the `Animation` object, it (and
hence the timers) will be garbage collected which will stop the
animation.

To save an animation use `Animation.save`, `Animation.to_html5_video`,
or `Animation.to_jshtml`.

See :ref:`ani_writer_classes` below for details about what movie formats are
supported.


.. _func-animation:

``FuncAnimation``
-----------------

The inner workings of `FuncAnimation` is more-or-less::

  for d in frames:
      artists = func(d, *fargs)
      fig.canvas.draw_idle()
      fig.canvas.start_event_loop(interval)

with details to handle 'blitting' (to dramatically improve the live
performance), to be non-blocking, not repeatedly start/stop the GUI
event loop, handle repeats, multiple animated axes, and easily save
the animation to a movie file.

'Blitting' is a `standard technique
<https://en.wikipedia.org/wiki/Bit_blit>`__ in computer graphics.  The
general gist is to take an existing bit map (in our case a mostly
rasterized figure) and then 'blit' one more artist on top.  Thus, by
managing a saved 'clean' bitmap, we can only re-draw the few artists
that are changing at each frame and possibly save significant amounts of
time.  When we use blitting (by passing ``blit=True``), the core loop of
`FuncAnimation` gets a bit more complicated::

   ax = fig.gca()

   def update_blit(artists):
       fig.canvas.restore_region(bg_cache)
       for a in artists:
           a.axes.draw_artist(a)

       ax.figure.canvas.blit(ax.bbox)

   artists = init_func()

   for a in artists:
      a.set_animated(True)

   fig.canvas.draw()
   bg_cache = fig.canvas.copy_from_bbox(ax.bbox)

   for f in frames:
       artists = func(f, *fargs)
       update_blit(artists)
       fig.canvas.start_event_loop(interval)

This is of course leaving out many details (such as updating the
background when the figure is resized or fully re-drawn).  However,
this hopefully minimalist example gives a sense of how ``init_func``
and ``func`` are used inside of `FuncAnimation` and the theory of how
'blitting' works.

.. note::

    The zorder of artists is not taken into account when 'blitting'
    because the 'blitted' artists are always drawn on top.

The expected signature on ``func`` and ``init_func`` is very simple to
keep `FuncAnimation` out of your book keeping and plotting logic, but
this means that the callable objects you pass in must know what
artists they should be working on.  There are several approaches to
handling this, of varying complexity and encapsulation.  The simplest
approach, which works quite well in the case of a script, is to define the
artist at a global scope and let Python sort things out.  For example::

   import numpy as np
   import matplotlib.pyplot as plt
   from matplotlib.animation import FuncAnimation

   fig, ax = plt.subplots()
   xdata, ydata = [], []
   ln, = ax.plot([], [], 'ro')

   def init():
       ax.set_xlim(0, 2*np.pi)
       ax.set_ylim(-1, 1)
       return ln,

   def update(frame):
       xdata.append(frame)
       ydata.append(np.sin(frame))
       ln.set_data(xdata, ydata)
       return ln,

   ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                       init_func=init, blit=True)
   plt.show()

The second method is to use `functools.partial` to pass arguments to the
function::

   import numpy as np
   import matplotlib.pyplot as plt
   from matplotlib.animation import FuncAnimation
   from functools import partial

   fig, ax = plt.subplots()
   line1, = ax.plot([], [], 'ro')

   def init():
       ax.set_xlim(0, 2*np.pi)
       ax.set_ylim(-1, 1)
       return line1,

   def update(frame, ln, x, y):
       x.append(frame)
       y.append(np.sin(frame))
       ln.set_data(x, y)
       return ln,

   ani = FuncAnimation(
       fig, partial(update, ln=line1, x=[], y=[]),
       frames=np.linspace(0, 2*np.pi, 128),
       init_func=init, blit=True)

   plt.show()

A third method is to use closures to build up the required
artists and functions.  A fourth method is to create a class.

Examples
^^^^^^^^

* :doc:`../gallery/animation/animate_decay`
* :doc:`../gallery/animation/bayes_update`
* :doc:`../gallery/animation/double_pendulum`
* :doc:`../gallery/animation/animated_histogram`
* :doc:`../gallery/animation/rain`
* :doc:`../gallery/animation/random_walk`
* :doc:`../gallery/animation/simple_anim`
* :doc:`../gallery/animation/strip_chart`
* :doc:`../gallery/animation/unchained`

``ArtistAnimation``
-------------------

Examples
^^^^^^^^

* :doc:`../gallery/animation/dynamic_image`

Writer Classes
==============

.. inheritance-diagram:: matplotlib.animation.FFMpegFileWriter matplotlib.animation.FFMpegWriter matplotlib.animation.ImageMagickFileWriter matplotlib.animation.ImageMagickWriter matplotlib.animation.PillowWriter matplotlib.animation.HTMLWriter
   :top-classes: matplotlib.animation.AbstractMovieWriter
   :parts: 1

The provided writers fall into a few broad categories.

The Pillow writer relies on the Pillow library to write the animation, keeping
all data in memory.

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   PillowWriter

The HTML writer generates JavaScript-based animations.

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   HTMLWriter

The pipe-based writers stream the captured frames over a pipe to an external
process.  The pipe-based variants tend to be more performant, but may not work
on all systems.

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   FFMpegWriter
   ImageMagickWriter

The file-based writers save temporary files for each frame which are stitched
into a single file at the end.  Although slower, these writers can be easier to
debug.

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   FFMpegFileWriter
   ImageMagickFileWriter

The writer classes provide a way to grab sequential frames from the same
underlying `~matplotlib.figure.Figure`.  They all provide three methods that
must be called in sequence:

- `~.AbstractMovieWriter.setup` prepares the writer (e.g. opening a pipe).
  Pipe-based and file-based writers take different arguments to ``setup()``.
- `~.AbstractMovieWriter.grab_frame` can then be called as often as
  needed to capture a single frame at a time
- `~.AbstractMovieWriter.finish` finalizes the movie and writes the output
  file to disk.

Example::

   moviewriter = MovieWriter(...)
   moviewriter.setup(fig, 'my_movie.ext', dpi=100)
   for j in range(n):
       update_figure(j)
       moviewriter.grab_frame()
   moviewriter.finish()

If using the writer classes directly (not through `Animation.save`), it is
strongly encouraged to use the `~.AbstractMovieWriter.saving` context manager::

  with moviewriter.saving(fig, 'myfile.mp4', dpi=100):
      for j in range(n):
          update_figure(j)
          moviewriter.grab_frame()

to ensure that setup and cleanup are performed as necessary.

Examples
--------

* :doc:`../gallery/animation/frame_grabbing_sgskip`

.. _ani_writer_classes:

Helper Classes
==============

Animation Base Classes
----------------------

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   Animation
   TimedAnimation

Writer Registry
---------------

A module-level registry is provided to map between the name of the
writer and the class to allow a string to be passed to
`Animation.save` instead of a writer instance.

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   MovieWriterRegistry

Writer Base Classes
-------------------

To reduce code duplication base classes

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   AbstractMovieWriter
   MovieWriter
   FileMovieWriter

and mixins

.. autosummary::
   :toctree: _as_gen
   :nosignatures:

   FFMpegBase
   ImageMagickBase

are provided.

See the source code for how to easily implement new `MovieWriter` classes.