Skip to content
Open
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
14 changes: 14 additions & 0 deletions doc/release/next_whats_new/animation_paused_start.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Animations can now be created in a paused state
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'.Animation' now accepts a 'paused' keyword argument. When set to 'True',
the animation will not automatically start when the figure is first drawn.
This allows starting an animation in a paused state without needing to
access private API. Additionally, '.TimerBase.is_running' was added to
query whether the underlying event source is currently running.

::

ani = FuncAnimation(fig, update, frames=10, paused=True)
ani.event_source.is_running() # False
ani.resume()
ani.event_source.is_running() # True
7 changes: 4 additions & 3 deletions lib/matplotlib/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -883,9 +883,9 @@ class Animation:
FuncAnimation, ArtistAnimation
"""

def __init__(self, fig, event_source, blit=False):
def __init__(self, fig, event_source, blit=False, *, paused=False):
self._draw_was_started = False

self._paused_at_start = paused
self._fig = fig
# Disables blitting for backends that don't support it. This
# allows users to request it if available, but still have a
Expand Down Expand Up @@ -934,7 +934,8 @@ def _start(self, *args):
# Now do any initial draw
self._init_draw()
# Actually start the event_source.
self.event_source.start()
if not self._paused_at_start:
self.event_source.start()

def _stop(self, *args):
# On stop we disconnect all of our events.
Expand Down
7 changes: 6 additions & 1 deletion lib/matplotlib/animation.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,12 @@ class Animation:
frame_seq: Iterable[Artist]
event_source: EventSourceProtocol | None # TODO: We should remove None
def __init__(
self, fig: Figure, event_source: EventSourceProtocol, blit: bool = ...
self,
fig: Figure,
event_source: EventSourceProtocol,
blit: bool = ...,
*,
paused: bool = ...,
) -> None: ...
def __del__(self) -> None: ...
def save(
Expand Down
7 changes: 7 additions & 0 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,7 @@ def __init__(self, interval=None, callbacks=None):
# Set .interval and not ._interval to go through the property setter.
self.interval = 1000 if interval is None else interval
self.single_shot = False
self._running = False

def __del__(self):
"""Need to stop timer and possibly disconnect timer."""
Expand All @@ -1080,10 +1081,16 @@ def __del__(self):
def start(self):
"""Start the timer."""
self._timer_start()
self._running = True

def stop(self):
"""Stop the timer."""
self._timer_stop()
self._running = False

def is_running(self):
"""Return whether the timer is currently running."""
return self._running

def _timer_start(self):
pass
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backend_bases.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class TimerBase:
def __del__(self) -> None: ...
def start(self) -> None: ...
def stop(self) -> None: ...
def is_running(self) -> bool: ...
@property
def interval(self) -> int: ...
@interval.setter
Expand Down
30 changes: 30 additions & 0 deletions lib/matplotlib/tests/test_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,33 @@ 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_paused_start():
"""Test that paused=True prevents the animation from auto-starting."""
fig, ax = plt.subplots()
line, = ax.plot([], [])

def update(frame):
return line,

ani = animation.FuncAnimation(fig, update, frames=5, paused=True)
fig.canvas.draw() # triggers the first draw_event / _start callback

assert not ani.event_source.is_running()
plt.close(fig)


def test_animation_default_starts():
"""Test that the default paused=False still auto-starts."""
fig, ax = plt.subplots()
line, = ax.plot([], [])

def update(frame):
return line,

ani = animation.FuncAnimation(fig, update, frames=5)
fig.canvas.draw()

assert ani.event_source.is_running()
plt.close(fig)
Loading