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()))
所以,有一个matplotlib.backends.backend_qt4.TimerQT
仍然有一个参考。有什么方法可以删除吗?
答案 0 :(得分:2)
要理清这里发生的事情,请深入了解动画模块和两个回调注册表的工作方式。
当您创建Animation
对象时,它会将回调注册到draw_event
上的mpl回调注册表中,以便在第一次创建Animation
对象后绘制画布之后定时动画将其设置为自我(通过将回调注册到计时器对象中)并回调到close_event
上的mpl回调注册表以将定时器拆除。
mpl回调注册表对进入的可调用对象进行了大量内省,并将绑定方法重构为对象的弱参数和相关函数。因此,如果你创建了一个动画对象但没有对其进行引用,那么它的引用将变为零,mpl回调注册表中的弱引用将失败,并且动画永远不会启动
计时器的工作方式Qt
是你注册一个可调用的,它被添加到一个列表中(我从底部的图中得到这个),所以它持有对{{的硬引用1}} object,因此删除你在对象中保存的引用不足以将引用计数驱动为零。在计时器回调的情况下,这可能是一个功能,而不是一个错误。
通过工件,我现在理解为意味着你正在创建第二个Animation
对象,你得到的是它们两个并行运行(我不确定我期望在那里发生什么)。
要停止正在运行的Animation
并将其从计时器的回调列表中删除,请使用私有方法(应该是公开的)Animation
这是导致拆除的原因(并且是在_stop
)上注册的方法。