python matplotlib: unable to call FuncAnimation fr

2019-01-14 17:56发布

问题:

I'm trying to implement a function which outputs an animated plot.

If I take simple_anim.py (from matplotlib examples) as a base:

"""
 A simple example of an animated plot
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig, ax = plt.subplots()

x = np.arange(0, 2*np.pi, 0.01)        # x-array
line, = ax.plot(x, np.sin(x))

def animate(i):
    line.set_ydata(np.sin(x+i/10.0))  # update the data
    return line,

#Init only required for blitting to give a clean slate.
def init():
    line.set_ydata(np.ma.array(x, mask=True))
    return line,

ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,
    interval=25, blit=True)
plt.show()

Effectively it works.

BUT, if I close this code inside a function (in order to provide changing parameters, and avoid doing an explicit file for each possible parameter value):

"""
 A simple example of an animated plot
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def a():
    fig, ax = plt.subplots()

    x = np.arange(0, 2*np.pi, 0.01)        # x-array
    line, = ax.plot(x, np.sin(x))

    def animate(i):
        line.set_ydata(np.sin(x+i/10.0))  # update the data
        return line,

    #Init only required for blitting to give a clean slate.
    def init():
        line.set_ydata(np.ma.array(x, mask=True))
        return line,

    ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,
        interval=25, blit=True)
    plt.show()

and then call the function, the figure plot remains white. In fact, it never arrives to enter into the animate function.

I know that I'm missing some information, and that's why it does not work. Does anybody can give me some hints?

Thank you very much,

Andrés

回答1:

The reason that this happens is that the timers and call backs which update the window are attributes of the object ani. If you do not keep a reference to it around, then ani in garbage collected and your timers/callbacks go away.

The solution is to have your function return ani and keep a reference to it in your code:

def a(...):
    # stuff
    ani = animation.FuncAnimation(...)
    # more stuff
    return ani

outer_ani = a(...)

This issue (see github #1656) has been discussed, but not resolved.



回答2:

As tcaswell's good answer implies, the behavior of the problem code is undefined because it relies on an object which has been deleted and is available for garbage collection.

In practice, this undefined behavior manifests differently with different GUI backends. For some users (e.g. this "duplicate" question), using the Wx backend in IDLE or in the default Pylab shortcut on Windows, the undefined code seems to work (I say "seems to" because it's really not working, but rather producing the desired results by luck). When running in the Canopy GUI with its default Qt backend, the code does not work. Qt and Wx have very different architectures and garbage collection. (In Canopy, the GUI backend can be changed in the Python tab of the Preferences menu; if it is changed to Wx, then the undefined code also seems to work, but again, that doesn't make it correct.)