0
# Animation and Interactivity
1
2
Creating animated plots and interactive visualizations. Matplotlib provides comprehensive animation capabilities through function-based and artist-based approaches, with support for various export formats and interactive backends.
3
4
## Capabilities
5
6
### Animation Base Classes
7
8
Core animation framework for creating animated visualizations.
9
10
```python { .api }
11
import matplotlib.animation as manimation
12
13
class Animation:
14
def __init__(self, fig, event_source=None, blit=False):
15
"""Base class for all animations."""
16
17
def save(self, filename, writer=None, fps=None, dpi=None, codec=None,
18
bitrate=None, extra_args=None, metadata=None, extra_anim=None,
19
savefig_kwargs=None, *, progress_callback=None) -> None:
20
"""Save the animation to a file."""
21
22
def to_html5_video(self, embed_limit=None) -> str:
23
"""Convert animation to HTML5 video."""
24
25
def to_jshtml(self, fps=None, embed_frames=True, default_mode=None) -> str:
26
"""Generate HTML representation with JavaScript controls."""
27
28
def pause(self) -> None:
29
"""Pause the animation."""
30
31
def resume(self) -> None:
32
"""Resume the animation."""
33
34
class TimedAnimation(Animation):
35
def __init__(self, fig, interval=200, repeat_delay=0, repeat=True,
36
event_source=None, *args, **kwargs):
37
"""Base class for timed animations."""
38
39
def _draw_frame(self, framedata) -> None:
40
"""Draw a single frame of the animation."""
41
```
42
43
### Function-Based Animation
44
45
Animation using a function that is called repeatedly to update the plot.
46
47
```python { .api }
48
class FuncAnimation(TimedAnimation):
49
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
50
save_count=None, *, cache_frame_data=True, **kwargs):
51
"""Animation by repeatedly calling a function."""
52
53
def new_frame_seq(self):
54
"""Return a new sequence of frame information."""
55
56
def new_saved_frame_seq(self):
57
"""Return a new sequence of saved frame information."""
58
59
# Convenience function
60
def FuncAnimation(fig, func, frames=None, init_func=None, fargs=None,
61
interval=200, repeat_delay=0, repeat=True, blit=False,
62
save_count=None, cache_frame_data=True) -> FuncAnimation:
63
"""Animate by repeatedly calling function func."""
64
```
65
66
### Artist-Based Animation
67
68
Animation using a list of artists that are shown sequentially.
69
70
```python { .api }
71
class ArtistAnimation(TimedAnimation):
72
def __init__(self, fig, artists, interval=200, repeat_delay=0, repeat=True,
73
blit=False):
74
"""Animation using a list of artists."""
75
76
def new_frame_seq(self):
77
"""Return a new sequence of artists."""
78
79
def new_saved_frame_seq(self):
80
"""Return a new sequence of saved artists."""
81
82
# Convenience function
83
def ArtistAnimation(fig, artists, interval=200, repeat_delay=0, repeat=True,
84
blit=False) -> ArtistAnimation:
85
"""Animate by sequentially displaying a list of artists."""
86
```
87
88
### Writer Classes
89
90
Classes for saving animations to various file formats.
91
92
```python { .api }
93
class Writer:
94
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
95
metadata=None):
96
"""Base class for animation writers."""
97
98
def setup(self, fig, outfile, dpi=None) -> None:
99
"""Setup for writing the animation."""
100
101
def grab_frame(self, **kwargs) -> None:
102
"""Grab the current frame."""
103
104
def finish(self) -> None:
105
"""Finish writing the animation."""
106
107
class MovieWriter(Writer):
108
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
109
metadata=None):
110
"""Base class for movie writers using external tools."""
111
112
class FFMpegWriter(MovieWriter):
113
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
114
metadata=None):
115
"""Writer using FFmpeg for video encoding."""
116
117
class ImageMagickWriter(MovieWriter):
118
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
119
metadata=None):
120
"""Writer using ImageMagick for GIF creation."""
121
122
class PillowWriter(Writer):
123
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
124
metadata=None):
125
"""Writer using Pillow for GIF creation."""
126
127
class HTMLWriter(Writer):
128
def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None,
129
metadata=None, embed_frames=False):
130
"""Writer for HTML with JavaScript controls."""
131
```
132
133
### Interactive Widgets
134
135
Interactive controls and widgets for plot manipulation.
136
137
```python { .api }
138
from matplotlib.widgets import *
139
140
class Button:
141
def __init__(self, ax, label, image=None, color='0.85', hovercolor='0.95'):
142
"""Clickable button widget."""
143
144
def on_clicked(self, func) -> None:
145
"""Connect function to button click event."""
146
147
class Slider:
148
def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
149
closedmin=True, closedmax=True, slidermin=None, slidermax=None,
150
dragging=True, valstep=None, orientation='horizontal',
151
initcolor='r', track_color='lightgrey', handle_style='round'):
152
"""Slider widget for continuous value selection."""
153
154
def on_changed(self, func) -> None:
155
"""Connect function to slider value change event."""
156
157
def set_val(self, val) -> None:
158
"""Set slider value."""
159
160
class CheckButtons:
161
def __init__(self, ax, labels, actives=None):
162
"""Check button widget for multiple boolean selections."""
163
164
def on_clicked(self, func) -> None:
165
"""Connect function to check button click event."""
166
167
class RadioButtons:
168
def __init__(self, ax, labels, active=0, activecolor='blue'):
169
"""Radio button widget for single selection from multiple options."""
170
171
def on_clicked(self, func) -> None:
172
"""Connect function to radio button selection event."""
173
174
class TextBox:
175
def __init__(self, ax, label, initial='', color='.95', hovercolor='1',
176
label_pad=.01, textalignment='left'):
177
"""Text input widget."""
178
179
def on_text_change(self, func) -> None:
180
"""Connect function to text change event."""
181
182
def on_submit(self, func) -> None:
183
"""Connect function to text submission event."""
184
```
185
186
## Usage Examples
187
188
### Basic Function Animation
189
190
```python
191
import matplotlib.pyplot as plt
192
import matplotlib.animation as animation
193
import numpy as np
194
195
# Set up the figure and axis
196
fig, ax = plt.subplots()
197
x = np.linspace(0, 2*np.pi, 100)
198
line, = ax.plot(x, np.sin(x))
199
200
ax.set_ylim(-2, 2)
201
ax.set_xlabel('X')
202
ax.set_ylabel('Y')
203
ax.set_title('Animated Sine Wave')
204
205
def animate(frame):
206
"""Animation function called sequentially."""
207
# Update the y data with a phase shift
208
y = np.sin(x + frame * 0.1)
209
line.set_ydata(y)
210
return line,
211
212
def init():
213
"""Initialize animation - return the artists to be animated."""
214
line.set_ydata(np.sin(x))
215
return line,
216
217
# Create animation
218
anim = animation.FuncAnimation(fig, animate, init_func=init,
219
frames=200, interval=50, blit=True, repeat=True)
220
221
# Display the animation
222
plt.show()
223
224
# Save as GIF (optional)
225
# anim.save('sine_wave.gif', writer='pillow', fps=20)
226
```
227
228
### Multiple Plot Animation
229
230
```python
231
import matplotlib.pyplot as plt
232
import matplotlib.animation as animation
233
import numpy as np
234
235
# Create subplots
236
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
237
238
# Data setup
239
x = np.linspace(0, 2*np.pi, 100)
240
line1, = ax1.plot(x, np.sin(x), 'b-', label='sin(x)')
241
line2, = ax1.plot(x, np.cos(x), 'r-', label='cos(x)')
242
ax1.set_ylim(-2, 2)
243
ax1.set_title('Trigonometric Functions')
244
ax1.legend()
245
ax1.grid(True)
246
247
# Scatter plot setup
248
scat = ax2.scatter([], [], s=100, alpha=0.7)
249
ax2.set_xlim(-2, 2)
250
ax2.set_ylim(-2, 2)
251
ax2.set_title('Moving Points')
252
ax2.grid(True)
253
254
def animate(frame):
255
"""Animate both subplots."""
256
# Update trigonometric plots
257
phase = frame * 0.1
258
line1.set_ydata(np.sin(x + phase))
259
line2.set_ydata(np.cos(x + phase))
260
261
# Update scatter plot
262
n_points = 20
263
theta = np.linspace(0, 2*np.pi, n_points) + phase
264
x_scat = 1.5 * np.cos(theta)
265
y_scat = 1.5 * np.sin(theta)
266
267
# Update scatter plot data
268
scat.set_offsets(np.column_stack((x_scat, y_scat)))
269
270
return line1, line2, scat
271
272
# Create and run animation
273
anim = animation.FuncAnimation(fig, animate, frames=200, interval=100, blit=True)
274
plt.tight_layout()
275
plt.show()
276
```
277
278
### Artist Animation Example
279
280
```python
281
import matplotlib.pyplot as plt
282
import matplotlib.animation as animation
283
import numpy as np
284
285
# Generate data for multiple frames
286
fig, ax = plt.subplots()
287
288
# Create list of artists for each frame
289
artists = []
290
for i in range(50):
291
# Generate data for this frame
292
x = np.linspace(0, 10, 100)
293
y = np.sin(x + i * 0.2) * np.exp(-x/10)
294
295
# Create line artist
296
line, = ax.plot(x, y, 'b-')
297
title = ax.text(0.5, 0.9, f'Frame {i+1}', transform=ax.transAxes,
298
ha='center', fontsize=14)
299
300
# Add both line and title to this frame's artists
301
artists.append([line, title])
302
303
ax.set_xlim(0, 10)
304
ax.set_ylim(-1.5, 1.5)
305
ax.set_xlabel('X')
306
ax.set_ylabel('Y')
307
ax.grid(True)
308
309
# Create artist animation
310
anim = animation.ArtistAnimation(fig, artists, interval=100, blit=True, repeat=True)
311
plt.show()
312
```
313
314
### Interactive Animation with Widgets
315
316
```python
317
import matplotlib.pyplot as plt
318
import matplotlib.animation as animation
319
from matplotlib.widgets import Slider, Button
320
import numpy as np
321
322
# Create figure and subplots
323
fig = plt.figure(figsize=(12, 8))
324
ax_plot = plt.subplot2grid((4, 4), (0, 0), colspan=4, rowspan=3)
325
ax_slider1 = plt.subplot2grid((4, 4), (3, 0), colspan=2)
326
ax_slider2 = plt.subplot2grid((4, 4), (3, 2), colspan=2)
327
328
# Initial parameters
329
x = np.linspace(0, 4*np.pi, 200)
330
freq_init = 1.0
331
amp_init = 1.0
332
333
# Create initial plot
334
line, = ax_plot.plot(x, amp_init * np.sin(freq_init * x), 'b-', linewidth=2)
335
ax_plot.set_xlim(0, 4*np.pi)
336
ax_plot.set_ylim(-3, 3)
337
ax_plot.set_xlabel('X')
338
ax_plot.set_ylabel('Amplitude')
339
ax_plot.set_title('Interactive Animated Sine Wave')
340
ax_plot.grid(True)
341
342
# Animation variables
343
anim_running = True
344
phase = 0
345
346
# Create sliders
347
slider_freq = Slider(ax_slider1, 'Frequency', 0.1, 5.0, valinit=freq_init)
348
slider_amp = Slider(ax_slider2, 'Amplitude', 0.1, 3.0, valinit=amp_init)
349
350
def animate(frame):
351
"""Animation function."""
352
global phase
353
if anim_running:
354
phase += 0.1
355
freq = slider_freq.val
356
amp = slider_amp.val
357
y = amp * np.sin(freq * x + phase)
358
line.set_ydata(y)
359
return line,
360
361
def update_freq(val):
362
"""Update frequency from slider."""
363
pass # Animation function will read slider value
364
365
def update_amp(val):
366
"""Update amplitude from slider."""
367
pass # Animation function will read slider value
368
369
# Connect sliders
370
slider_freq.on_changed(update_freq)
371
slider_amp.on_changed(update_amp)
372
373
# Create animation
374
anim = animation.FuncAnimation(fig, animate, frames=1000, interval=50,
375
blit=True, repeat=True)
376
377
plt.tight_layout()
378
plt.show()
379
```
380
381
### 3D Animation
382
383
```python
384
import matplotlib.pyplot as plt
385
import matplotlib.animation as animation
386
from mpl_toolkits.mplot3d import Axes3D
387
import numpy as np
388
389
# Create 3D plot
390
fig = plt.figure(figsize=(10, 8))
391
ax = fig.add_subplot(111, projection='3d')
392
393
# Generate 3D data
394
def generate_data(frame):
395
"""Generate 3D surface data for given frame."""
396
x = np.linspace(-5, 5, 50)
397
y = np.linspace(-5, 5, 50)
398
X, Y = np.meshgrid(x, y)
399
400
# Create animated wave
401
t = frame * 0.1
402
Z = np.sin(np.sqrt(X**2 + Y**2) - t) * np.exp(-0.1 * np.sqrt(X**2 + Y**2))
403
404
return X, Y, Z
405
406
# Initial plot
407
X, Y, Z = generate_data(0)
408
surface = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
409
410
ax.set_xlim(-5, 5)
411
ax.set_ylim(-5, 5)
412
ax.set_zlim(-1, 1)
413
ax.set_xlabel('X')
414
ax.set_ylabel('Y')
415
ax.set_zlabel('Z')
416
ax.set_title('3D Animated Wave')
417
418
def animate(frame):
419
"""Animate 3D surface."""
420
ax.clear()
421
422
X, Y, Z = generate_data(frame)
423
surface = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
424
425
ax.set_xlim(-5, 5)
426
ax.set_ylim(-5, 5)
427
ax.set_zlim(-1, 1)
428
ax.set_xlabel('X')
429
ax.set_ylabel('Y')
430
ax.set_zlabel('Z')
431
ax.set_title(f'3D Animated Wave - Frame {frame}')
432
433
return surface,
434
435
# Create animation
436
anim = animation.FuncAnimation(fig, animate, frames=100, interval=100, blit=False)
437
plt.show()
438
```
439
440
### Saving Animations
441
442
```python
443
import matplotlib.pyplot as plt
444
import matplotlib.animation as animation
445
import numpy as np
446
447
# Create simple animation
448
fig, ax = plt.subplots()
449
x = np.linspace(0, 2*np.pi, 100)
450
line, = ax.plot(x, np.sin(x))
451
ax.set_ylim(-2, 2)
452
453
def animate(frame):
454
y = np.sin(x + frame * 0.1)
455
line.set_ydata(y)
456
return line,
457
458
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=True)
459
460
# Save as MP4 (requires ffmpeg)
461
try:
462
anim.save('animation.mp4', writer='ffmpeg', fps=20, bitrate=1800)
463
print("Saved as MP4")
464
except:
465
print("FFmpeg not available")
466
467
# Save as GIF (requires Pillow or ImageMagick)
468
try:
469
anim.save('animation.gif', writer='pillow', fps=20)
470
print("Saved as GIF")
471
except:
472
print("Pillow not available")
473
474
# Save as HTML with JavaScript controls
475
anim.save('animation.html', writer='html', fps=20, embed_frames=True)
476
print("Saved as HTML")
477
478
plt.show()
479
```