Skip to content

add cache_frame_data kwarg into FuncAnimation. fixes #8528. #11894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Add ``cache_frame_data`` keyword-only argument into ``matplotlib.animation.FuncAnimation``
------------------------------------------------------------------------------------------

| ``matplotlib.animation.FuncAnimation`` has been caching frame data by default.

| However, this caching is not ideal in certain cases.
| e.g. When ``FuncAnimation`` needs to be only drawn(not saved) interactively and memory required by frame data is quite large.

| By adding ``cache_frame_data`` keyword-only argument, users can disable this caching now if necessary.
| Thereby, this new argument provides a fix for issue #8528.
13 changes: 10 additions & 3 deletions lib/matplotlib/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1598,10 +1598,14 @@ def init_func() -> iterable_of_artists
blitting any animated artists will be drawn according to their zorder.
However, they will be drawn on top of any previous artists, regardless
of their zorder. Defaults to *False*.

cache_frame_data : bool, optional
Controls whether frame data is cached. Defaults to *True*.
Disabling cache might be helpful when frames contain large objects.
"""

def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
save_count=None, **kwargs):
save_count=None, *, cache_frame_data=True, **kwargs):
if fargs:
self._args = fargs
else:
Expand Down Expand Up @@ -1649,6 +1653,8 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
# for a single frame from init, which is not what we want.
self._save_seq = []

self._cache_frame_data = cache_frame_data

def new_frame_seq(self):
# Use the generating function to generate a new frame sequence
return self._iter_gen()
Expand Down Expand Up @@ -1703,8 +1709,9 @@ def _init_draw(self):
self._save_seq = []

def _draw_frame(self, framedata):
# Save the data for potential saving of movies.
self._save_seq.append(framedata)
if self._cache_frame_data:
# Save the data for potential saving of movies.
self._save_seq.append(framedata)

# Make sure to respect save_count (keep only the last save_count
# around)
Expand Down
49 changes: 49 additions & 0 deletions lib/matplotlib/tests/test_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path
import subprocess
import sys
import weakref

import numpy as np
from pathlib import Path
Expand Down Expand Up @@ -258,3 +259,51 @@ def test_failing_ffmpeg(tmpdir, monkeypatch):
make_animation().save("test.mpeg")
finally:
animation.writers.reset_available_writers()


@pytest.mark.parametrize("cache_frame_data, weakref_assertion_fn", [
pytest.param(
False, lambda ref: ref is None, id='cache_frame_data_is_disabled'),
pytest.param(
True, lambda ref: ref is not None, id='cache_frame_data_is_enabled'),
])
def test_funcanimation_holding_frames(cache_frame_data, weakref_assertion_fn):
fig, ax = plt.subplots()
line, = ax.plot([], [])

class Frame(dict):
# this subclassing enables to use weakref.ref()
pass

def init():
line.set_data([], [])
return line,

def animate(frame):
line.set_data(frame['x'], frame['y'])
return line,

frames_generated = []

def frames_generator():
for _ in range(5):
x = np.linspace(0, 10, 100)
y = np.random.rand(100)

frame = Frame(x=x, y=y)

# collect weak references to frames
# to validate their references later
frames_generated.append(weakref.ref(frame))

yield frame

anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=frames_generator,
cache_frame_data=cache_frame_data)

writer = NullMovieWriter()
anim.save('unused.null', writer=writer)
assert len(frames_generated) == 5
for f in frames_generated:
assert weakref_assertion_fn(f())