【问题标题】:python matplotlib: unable to call FuncAnimation from inside a functionpython matplotlib:无法从函数内部调用 FuncAnimation
【发布时间】:2014-01-13 18:56:31
【问题描述】:

我正在尝试实现一个输出动画情节的函数。

如果我以 simple_anim.py(来自 matplotlib 示例)为基础:

"""
 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()

它确实有效。

但是,如果我在函数内关闭此代码(以提供更改参数,并避免为每个可能的参数值创建显式文件):

"""
 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()

然后调用该函数,图形绘图保持白色。事实上,它永远不会进入动画功能。

我知道我遗漏了一些信息,这就是它不起作用的原因。 有人可以给我一些提示吗?

非常感谢,

安德烈斯

【问题讨论】:

  • 这两个代码对我来说都很好。也请张贴代码调用a

标签: python animation matplotlib


【解决方案1】:

发生这种情况的原因是更新窗口的计时器和回调是对象ani 的属性。如果您不保留对它的引用,那么垃圾收集中的ani 并且您的计时器/回调消失。

解决方案是让您的函数返回 ani 并在您的代码中保留对它的引用:

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

outer_ani = a(...)

这个问题(见github #1656)已经讨论过,但没有解决。

【讨论】:

  • 但是plt.show() 在窗口关闭之前不会阻塞吗?函数a() 仅在plt.show() 返回后留下。在 ani 的垃圾回收之前无法发生。
  • plt.show() 可以是非阻塞的,例如,如果您在 shell 中以交互方式运行代码
  • 使用 matplotlib 1.3.0 和 python 3.2.5 plt.show() 在任何情况下对我来说都是阻塞的,除非我提供 block=False 作为参数,但如果我这样做,情节确实只显示在交互模式,并且仅在 shell 空闲等待用户输入时才保持动画。即使在这种情况下,问题中的两个代码块对我来说仍然可以正常工作。这个问题可能存在版本依赖性,还是我错过了一些明显的东西?
  • 它可能也依赖于后端。我通常使用 2.7,所以我也可以相信 gc 逻辑会有所不同(而且我认为对于 when gc 运行无论如何都没有任何保证)。让交互式后端的事件循环工作对我来说仍然是一个黑匣子(特别是任何不是 QT 的东西)。此外,我通常在ipython 工作,它在启动时会产生一些魔力,我认为这包括默认使show 非阻塞(不过我必须深入研究代码以理清机制)。
  • 非常感谢,返还ani时有效。我没有指定用于运行它的代码是我的错。实际上,它是从 iPython 调用的,并且第一个代码在 vanilla Python 上运行良好
【解决方案2】:

正如 tcaswell 的好答案所暗示的那样,问题代码的行为是未定义的,因为它依赖于一个已被删除且可用于垃圾回收的对象。

在实践中,这种未定义的行为在不同的 GUI 后端表现出不同的表现。对于某些用户(例如this "duplicate" question),在 IDLE 中使用 Wx 后端或在 Windows 上的默认 Pylab 快捷方式中,未定义的代码似乎可以工作(我说“似乎”是因为它真的不起作用,而是产生了预期的结果侥幸)。在具有默认 Qt 后端的 Canopy GUI 中运行时,代码不起作用。 Qt 和 Wx 有非常不同的架构和垃圾收集。 (在 Canopy 中,可以在 Preferences 菜单的 Python 选项卡中更改 GUI 后端;如果将其更改为 Wx,那么未定义的代码似乎也可以工作,但同样,这并不正确。)

【讨论】:

    【解决方案3】:

    FuncAnimation 不应该被当作垃圾刮掉,所以只需将它分配给一个全局变量

        global anim
        
        def callfuncanimation():
            global anim
            anim = FuncAnimation(....)
    

    由于 anim 是一个全局变量,所以它是持久存在的,不会被刮掉

    【讨论】:

      【解决方案4】:

      正如Fixed 1656: Animation is Garbage Collected if not explicitly stored #8214 所说,如果动画没有显式存储,Python 的垃圾收集将删除您的动画。
      如果你想在 main 函数中获取它,你可以像 @tacaswell 那样做。但是,当你想在另一个函数中生成动画时,这会失败。
      我们可以定义一个全局变量来存储 FuncAnimation 的返回值。

      global anim
      
      def return_anim():
        anim = animation.FuncAnimation(fig, animate, blit=True)
        return anim
      
      def use_ani():
        global anim
        anim = return_anim
      
      use_ani()
      

      请务必记住设置 blit=True,否则 Python 不会重绘您的动画。

      【讨论】:

        猜你喜欢
        • 2020-08-19
        • 1970-01-01
        • 2018-08-14
        • 1970-01-01
        • 2021-10-27
        • 1970-01-01
        • 1970-01-01
        • 2015-05-18
        相关资源
        最近更新 更多