无法删除matplotlib.animation.FuncAnimation对象

时间:2015-08-28 21:39:51

标签: python-2.7 matplotlib garbage-collection pyqt4 blit

EDIT / TL; DR:看起来有一个matplotlib.backends.backend_qt4.TimerQT对象包含对我的FuncAnimation对象的引用。如何删除它以释放FuncAnimation对象?

1 - 一点上下文

我试图为使用matplotlib生成的绘图设置动画。我使用matplotlib.animation.FuncAnimation。 这个动画情节包含在一个FigureCanvasQTAgg(matplotlib.backends.backend_qt4agg)中,即。一个PyQt4小部件。

class ViewerWidget(FigureCanvasQTAgg):
    def __init__(self, viewer, parent):
        # viewer is my FuncAnimation, encapsulated in a class
        self._viewer = viewer
        FigureCanvasQTAgg.__init__(self, viewer.figure)

当GUI中发生配置更改时,清除图(figure.clf())并将其子图(轴和线)替换为新图。

2 - 来自班级Viewer的源代码(封装FuncAnimation

这是我的方法Viewer.show(...)中最相关的部分,它实现了FuncAnimation

2.a - 首先,我尝试过:

animation.FuncAnimation(..., blit=True)

当然,实例是立即收集的垃圾

2.b - 然后,我将其存储在类变量中:

self._anim = animation.FuncAnimation(..., blit=True)

它适用于第一个动画,但是一旦配置发生变化,我就会在以前的动画中找到新的动画

2.c - 我手动添加了del

# Delete previous FuncAnimation if any       
if self._anim:
    del self._anim
self._anim = animation.FuncAnimation(..., blit=True)

没有任何改变

2.d - 经过一些调试后,我检查了垃圾收集器:

# DEBUG: check garbage collector
def objects_by_id(id_):
    for obj in gc.get_objects():
        if id(obj) == id_:
            return obj
    self._id.remove(id_)
    return "garbage collected"

# Delete previous FuncAnimation if any       
if self._anim:
    del self._anim

# DEBUG
print "-"*10
for i in self._id.copy():
    print i, objects_by_id(i)
print "-"*10
self._anim = animation.FuncAnimation(self._figure_handler.figure,
                                     update,
                                     init_func=init,
                                     interval=self._update_anim,
                                     blit=True)

# DEBUG: store ids only, to enable object being garbage collected
self._anim_id.add(id(anim))

经过3次配置更改后,显示:

----------
140488264081616 <matplotlib.animation.FuncAnimation object at 0x7fc5f91360d0>
140488264169104 <matplotlib.animation.FuncAnimation object at 0x7fc5f914b690>
140488145151824 <matplotlib.animation.FuncAnimation object at 0x7fc5f1fca750>
140488262315984 <matplotlib.animation.FuncAnimation object at 0x7fc5f8f86fd0>
----------

因此,它确认没有任何FuncAnimation被垃圾收集

2.e - 上次尝试,使用weakref:

# DEBUG: check garbage collector
def objects_by_id(id_):
    for obj in gc.get_objects():
        if id(obj) == id_:
            return obj
    self._id.remove(id_)
    return "garbage collected"

# Delete previous FuncAnimation if any
if self._anim_ref:
    anim = self._anim_ref()
    del anim


# DEBUG
print "-"*10
for i in self._id.copy():
    print i, objects_by_id(i)
print "-"*10
anim = animation.FuncAnimation(self._figure_handler.figure,
                               update,
                               init_func=init,
                               interval=self._update_anim,
                               blit=True)

self._anim_ref = weakref.ref(anim)

# DEBUG: store ids only, to enable object being garbage collected
self._id.add(id(anim))

这一次,日志令人困惑,我不知道发生了什么。

----------
140141921353872 <built-in method alignment>
----------
----------
140141921353872 <built-in method alignment>
140141920643152 Bbox('array([[ 0.,  0.],\n       [ 1.,  1.]])')
----------
----------
140141921353872 <built-in method alignment>
140141920643152 <viewer.FftPlot object at 0x7f755565e850>
140141903645328 Bbox('array([[ 0.,  0.],\n       [ 1.,  1.]])')
----------
(...)

我的<matplotlib.animation.FuncAnimation object at 0x...>在哪里?

以前没有更多的动画制品,到目前为止一直很好,但是...... FuncAnimation不再能够执行&#34;更新&#34;。只有&#34; init&#34;部分。我的猜测是,一旦方法Viewer.show(...)返回,FuncAnimation就会被垃圾收集,因为anim ID已经被回收了。

3 - 帮助

我不知道从这里看哪里。有什么建议吗?

修改 我安装了objgraph来可视化对FuncAnimation的所有后向引用,我得到了这个:

            import objgraph, time
            objgraph.show_backrefs([self._anim],
                                   max_depth=5,
                                   filename="/tmp/debug/func_graph_%d.png"
                                   % int(time.time()))

back references

所以,有一个matplotlib.backends.backend_qt4.TimerQT仍然有一个参考。有什么方法可以删除吗?

1 个答案:

答案 0 :(得分:2)

要理清这里发生的事情,请深入了解动画模块和两个回调注册表的工作方式。

当您创建Animation对象时,它会将回调注册到draw_event上的mpl回调注册表中,以便在第一次创建Animation对象后绘制画布之后定时动画将其设置为自我(通过将回调注册到计时器对象中)并回调到close_event上的mpl回调注册表以将定时器拆除。

mpl回调注册表对进入的可调用对象进行了大量内省,并将绑定方法重构为对象的弱参数和相关函数。因此,如果你创建了一个动画对象但没有对其进行引用,那么它的引用将变为零,mpl回调注册表中的弱引用将失败,并且动画永远不会启动

计时器的工作方式Qt是你注册一个可调用的,它被添加到一个列表中(我从底部的图中得到这个),所以它持有对{{的硬引用1}} object,因此删除你在对象中保存的引用不足以将引用计数驱动为零。在计时器回调的情况下,这可能是一个功能,而不是一个错误。

通过工件,我现在理解为意味着你正在创建第二个Animation对象,你得到的是它们两个并行运行(我不确定我期望在那里发生什么)。

要停止正在运行的Animation并将其从计时器的回调列表中删除,请使用私有方法(应该是公开的)Animation这是导致拆除的原因(并且是在_stop)上注册的方法。