diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 7146dc28fcc9..a2bf6378f229 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -878,12 +878,17 @@ class Animation: Whether blitting is used to optimize drawing. If the backend does not support blitting, then this parameter has no effect. + start_paused : bool, default: False + Whether the animation should start in a paused state. If True, the + animation will draw the initial frame but will not begin animating + until `.resume()` is called. + See Also -------- FuncAnimation, ArtistAnimation """ - def __init__(self, fig, event_source, blit=False): + def __init__(self, fig, event_source, blit=False, start_paused=False): self._draw_was_started = False self._fig = fig @@ -891,6 +896,7 @@ def __init__(self, fig, event_source, blit=False): # allows users to request it if available, but still have a # fallback that works if it is not. self._blit = blit and fig.canvas.supports_blit + self._start_paused = start_paused # These are the basics of the animation. The frame sequence represents # information for each frame of the animation and depends on how the @@ -933,8 +939,10 @@ def _start(self, *args): self._fig.canvas.mpl_disconnect(self._first_draw_id) # Now do any initial draw self._init_draw() - # Actually start the event_source. - self.event_source.start() + # Do not start the timer if the animation was requested to start + # in a paused state; the user must call .resume() manually. + if not self._start_paused: + self.event_source.start() def _stop(self, *args): # On stop we disconnect all of our events. diff --git a/lib/matplotlib/tests/test_animation.py b/lib/matplotlib/tests/test_animation.py index bfddddf6d665..a08c6b3ef2b5 100644 --- a/lib/matplotlib/tests/test_animation.py +++ b/lib/matplotlib/tests/test_animation.py @@ -558,3 +558,51 @@ def test_animation_with_transparency(): # Check that the alpha channel is not 255, so frame has transparency assert frame.getextrema()[3][0] < 255 plt.close(fig) + + +def test_animation_start_paused(): + """Test that Animation(start_paused=True) starts in a paused state.""" + from unittest.mock import patch + + def _make_anim(fig, **kwargs): + line, = fig.axes[0].plot([], []) + + def init(): + line.set_data([], []) + return line, + + def animate(i): + line.set_data([0, 1], [0, i]) + return line, + + return animation.FuncAnimation( + fig, animate, init_func=init, frames=5, interval=100, **kwargs) + + # start_paused=True: event_source.start() should NOT be called in _start + fig1, _ = plt.subplots() + anim1 = _make_anim(fig1, start_paused=True) + with patch.object(anim1.event_source, 'start', + wraps=anim1.event_source.start) as mock_start: + anim1._start() + mock_start.assert_not_called() + plt.close(fig1) + + # start_paused=False (default): event_source.start() SHOULD be called + fig2, _ = plt.subplots() + anim2 = _make_anim(fig2) + with patch.object(anim2.event_source, 'start', + wraps=anim2.event_source.start) as mock_start2: + anim2._start() + mock_start2.assert_called_once() + plt.close(fig2) + + # start_paused=True then resume(): start() should be called + fig3, _ = plt.subplots() + anim3 = _make_anim(fig3, start_paused=True) + with patch.object(anim3.event_source, 'start', + wraps=anim3.event_source.start) as mock_start3: + anim3._start() + mock_start3.assert_not_called() + anim3.resume() + mock_start3.assert_called_once() + plt.close(fig3)