Skip to content

Commit 017d88b

Browse files
committed
add cache_frame_data kwarg into FuncAnimation. fixes #8528.
1 parent 7c0e760 commit 017d88b

File tree

2 files changed

+94
-3
lines changed

2 files changed

+94
-3
lines changed

lib/matplotlib/animation.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -1621,9 +1621,13 @@ def init_func() -> iterable_of_artists:
16211621
However, they will be drawn on top of any previous artists, regardless
16221622
of their zorder. Defaults to ``False``.
16231623
1624+
cache_frame_data : bool, optional
1625+
Controls whether frame data is cached. Defaults to ``True``.
1626+
Disabling cache might be helpful when frames contain large objects.
1627+
16241628
'''
16251629
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
1626-
save_count=None, **kwargs):
1630+
save_count=None, cache_frame_data=True, **kwargs):
16271631
if fargs:
16281632
self._args = fargs
16291633
else:
@@ -1671,6 +1675,8 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
16711675
# for a single frame from init, which is not what we want.
16721676
self._save_seq = []
16731677

1678+
self._cache_frame_data = cache_frame_data
1679+
16741680
def new_frame_seq(self):
16751681
# Use the generating function to generate a new frame sequence
16761682
return self._iter_gen()
@@ -1725,8 +1731,9 @@ def _init_draw(self):
17251731
self._save_seq = []
17261732

17271733
def _draw_frame(self, framedata):
1728-
# Save the data for potential saving of movies.
1729-
self._save_seq.append(framedata)
1734+
if self._cache_frame_data:
1735+
# Save the data for potential saving of movies.
1736+
self._save_seq.append(framedata)
17301737

17311738
# Make sure to respect save_count (keep only the last save_count
17321739
# around)

lib/matplotlib/tests/test_animation.py

+84
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from pathlib import Path
33
import sys
4+
import weakref
45

56
import numpy as np
67
import pytest
@@ -260,3 +261,86 @@ def test_failing_ffmpeg(tmpdir, monkeypatch):
260261
make_animation().save("test.mpeg")
261262
finally:
262263
animation.writers.reset_available_writers()
264+
265+
266+
def test_funcanimation_holds_frames_by_default():
267+
fig, ax = plt.subplots()
268+
line, = ax.plot([], [])
269+
270+
class Frame(dict):
271+
# this subclassing enables to use weakref.ref()
272+
pass
273+
274+
def init():
275+
line.set_data([], [])
276+
return line,
277+
278+
def animate(frame):
279+
line.set_data(frame['x'], frame['y'])
280+
return line,
281+
282+
frames_generated = []
283+
284+
def frames_generator():
285+
for _ in range(5):
286+
x = np.linspace(0, 10, 100)
287+
y = np.random.rand(100)
288+
289+
frame = Frame(x=x, y=y)
290+
291+
# collect weak references to frames
292+
# to validate their references later
293+
frames_generated.append(weakref.ref(frame))
294+
295+
yield frame
296+
297+
anim = animation.FuncAnimation(fig, animate, init_func=init,
298+
frames=frames_generator, save_count=5)
299+
300+
writer = NullMovieWriter()
301+
anim.save('unused.null', writer=writer)
302+
303+
for f in frames_generated:
304+
assert f() is not None
305+
306+
307+
def test_funcanimation_does_not_hold_frames_when_cache_frame_data_is_False():
308+
fig, ax = plt.subplots()
309+
line, = ax.plot([], [])
310+
311+
class Frame(dict):
312+
# this subclassing enables to use weakref.ref()
313+
pass
314+
315+
def init():
316+
line.set_data([], [])
317+
return line,
318+
319+
def animate(frame):
320+
line.set_data(frame['x'], frame['y'])
321+
return line,
322+
323+
frames_generated = []
324+
325+
def frames_generator():
326+
for _ in range(5):
327+
x = np.linspace(0, 10, 100)
328+
y = np.random.rand(100)
329+
330+
frame = Frame(x=x, y=y)
331+
332+
# collect weak references to frames
333+
# to validate their references later
334+
frames_generated.append(weakref.ref(frame))
335+
336+
yield frame
337+
338+
anim = animation.FuncAnimation(fig, animate, init_func=init,
339+
frames=frames_generator, save_count=5,
340+
cache_frame_data=False)
341+
342+
writer = NullMovieWriter()
343+
anim.save('unused.null', writer=writer)
344+
345+
for f in frames_generated:
346+
assert f() is None

0 commit comments

Comments
 (0)