无法删除 matplotlib.animation.FuncAnimation 个对象
Cannot delete matplotlib.animation.FuncAnimation objects
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 - 来自 Class Viewer
的源代码(封装 FuncAnimation
)
这是我的方法中最相关的部分 Viewer.show(...)
,它实例化了 FuncAnimation
2.a - 首先,我试过:
animation.FuncAnimation(..., blit=True)
当然,实例立即被垃圾回收了
2.b - 然后,我将其存储在 class 变量中:
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>
----------
因此,它确认 none 的 FuncAnimation 被垃圾回收了
2.e - 最后一次尝试,弱引用:
# 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 不再能够执行 "update"。只有 "init" 部分。我的猜测是 FuncAnimation 会在方法 Viewer.show(...)
returns 后立即被垃圾回收,因为 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
仍然持有参考。有什么方法可以删除它吗?
要弄清楚这里发生了什么,需要深入了解动画模块和两个回调注册表的工作原理。
当您创建 Animation
对象时,它会在 draw_event
上的 mpl 回调注册表中注册一个回调,以便在第一次 canvas 之后绘制 Animation
对象被创建,定时动画设置它自己(通过将回调注册到定时器对象)和回调到 close_event
上的 mpl 回调注册表以关闭定时器。
mpl 回调注册表对传入的可调用对象进行大量自省,并将绑定方法重构为对象和相关函数的弱引用。因此,如果您创建了一个动画对象但不保留对它的引用,它的引用计数将变为零,mpl 回调注册表中对它的弱引用将失败,动画将永远不会开始。
定时器的工作方式 Qt
是你注册了一个可调用对象,它被添加到一个列表中(我从你底部的图表中得到这个)所以它持有一个硬引用Animation
对象,因此删除您在对象中持有的引用不足以将引用计数驱动为零。在计时器回调的情况下,这可能是一个功能,而不是错误。
我现在理解的工件是指您正在创建第二个 Animation
对象,并且您得到的是它们 运行 并行(我不确定我期望发生什么那里)。
要停止 运行 Animation
并将其从计时器的回调列表中删除,请使用私有方法(应该是 public) _stop
这是负责拆解(并且是在 close_event
上注册的方法)。
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 - 来自 Class Viewer
的源代码(封装 FuncAnimation
)
这是我的方法中最相关的部分 Viewer.show(...)
,它实例化了 FuncAnimation
2.a - 首先,我试过:
animation.FuncAnimation(..., blit=True)
当然,实例立即被垃圾回收了
2.b - 然后,我将其存储在 class 变量中:
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>
----------
因此,它确认 none 的 FuncAnimation 被垃圾回收了
2.e - 最后一次尝试,弱引用:
# 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 不再能够执行 "update"。只有 "init" 部分。我的猜测是 FuncAnimation 会在方法 Viewer.show(...)
returns 后立即被垃圾回收,因为 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
仍然持有参考。有什么方法可以删除它吗?
要弄清楚这里发生了什么,需要深入了解动画模块和两个回调注册表的工作原理。
当您创建 Animation
对象时,它会在 draw_event
上的 mpl 回调注册表中注册一个回调,以便在第一次 canvas 之后绘制 Animation
对象被创建,定时动画设置它自己(通过将回调注册到定时器对象)和回调到 close_event
上的 mpl 回调注册表以关闭定时器。
mpl 回调注册表对传入的可调用对象进行大量自省,并将绑定方法重构为对象和相关函数的弱引用。因此,如果您创建了一个动画对象但不保留对它的引用,它的引用计数将变为零,mpl 回调注册表中对它的弱引用将失败,动画将永远不会开始。
定时器的工作方式 Qt
是你注册了一个可调用对象,它被添加到一个列表中(我从你底部的图表中得到这个)所以它持有一个硬引用Animation
对象,因此删除您在对象中持有的引用不足以将引用计数驱动为零。在计时器回调的情况下,这可能是一个功能,而不是错误。
我现在理解的工件是指您正在创建第二个 Animation
对象,并且您得到的是它们 运行 并行(我不确定我期望发生什么那里)。
要停止 运行 Animation
并将其从计时器的回调列表中删除,请使用私有方法(应该是 public) _stop
这是负责拆解(并且是在 close_event
上注册的方法)。