Skip to content

Cannot stop/garbage collect an animation #16853

Open

Description

@nbud

Bug report

Bug summary

The animation documentation states that the reference to the Animation object must be held to prevent the animation from stopping due to garbage collection. I cannot reproduce this. Also, I do not find any way to stop the animation. I presume the Animation object is held somewhere, and this is undocumented.

The bigger picture for me: I pass as the frames argument of FuncAnimation a generator which streams data from a device via a socket. When the generator is garbage collected, the socket is deleted and the device is free to take new connections. Because I cannot garbage collect the Animation object, I cannot garbe collec the socket instrument either and the device hangs.

Related #16221

Code for reproduction

Adapted from https://matplotlib.org/3.1.3/api/animation_api.html :

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import gc

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
gc.collect() # force garbage collection
plt.show()

Actual outcome

The animation works.

Expected outcome

The animation should not work, as described in the doc.

Matplotlib version

  • Operating system: Windows 10
  • Matplotlib version: 3.1.3
  • Matplotlib backend (print(matplotlib.get_backend())): issue affects at least Qt5Agg and Tk5Agg
  • Python version: 3.7.6
  • Jupyter version (if applicable):
  • Other libraries: Anaconda 2020.02

Issue was reproducted in a Python console and in Spyder 4

Activity

  1. ImportanceOfBeingErnest commented on Mar 20, 2020

    @ImportanceOfBeingErnest
    Member

    Without having looked at this too much in detail, it seems to come from blitting being used. Would blit=False show the expected behaviour?

  2. nbud commented on Mar 20, 2020

    @nbud
    Author

    @ImportanceOfBeingErnest Nice catch, blit=False shows the expected behaviour.

  3. nbud commented on Mar 20, 2020

    @nbud
    Author

    After more thought on it, I found a way to stop the animation which does not rely on the garbage collection of the Animation object (see code below for the record).

    The behaviour described in the first message of this issue is not anymore an issue for me. Up to you to leave the issue open.

    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    
    fig, ax = plt.subplots()
    xdata, ydata = [], []
    (ln,) = plt.plot([], [], "ro")
    
    
    def init():
        ax.set_xlim(0, 2 * np.pi)
        ax.set_ylim(-1, 1)
        return (ln,)
    
    
    def update(frame):
        xdata.append(frame)
        ydata.append(np.sin(frame))
        ln.set_data(xdata, ydata)
        return (ln,)
    
    
    def generate_data():
        stop_flag = False
        x = 0
        while not stop_flag:
            x += 2 * np.pi / 128
            stop_flag = yield x
        print("stopped")
    
    data_generator = generate_data()
    
    def stop():
        data_generator.send("STOP")
    
    def onClose(event):
        stop()
        
    def onPress(event):
        if event.key == " ":
            stop()
        
    fig.canvas.mpl_connect("close_event", onClose)
    fig.canvas.mpl_connect("key_press_event", onPress)
    ani = FuncAnimation(fig, update, frames=data_generator, init_func=init, blit=False)
    plt.show()
    
    
  4. ImportanceOfBeingErnest commented on Mar 20, 2020

    @ImportanceOfBeingErnest
    Member

    The culprit is indeed this line in _setup_blit:

    self._resize_id = self._fig.canvas.mpl_connect('resize_event',
    self._on_resize)

  5. github-actions commented on Jul 14, 2023

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  6. tacaswell commented on Jul 14, 2023

    @tacaswell
    Member

    mpl_connect should only be holding a weakref. This warrants more investigation.

  7. anntzer commented on Sep 14, 2023

    @anntzer
    Contributor

    This seems to be fixed as of master for me; @tacaswell can you confirm or infirm this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions