无法删除 matplotlib.animation.FuncAnimation 对象

2023-12-10

编辑/TL;博士:看起来好像有一个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 不再能够执行“更新”。只有“init”部分。我的猜测是 FuncAnimation 一旦方法被垃圾收集Viewer.show(...)回归、场景animid 已经被回收了。

3 - Help

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

EDIT:我安装了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还是有参考价值的。有什么办法去掉吗?


要弄清楚这里发生了什么,需要深入了解动画模块和两个回调注册表的工作原理。

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

mpl 回调注册表对传入的可调用对象进行一系列内省,并将绑定方法重建为对象和相关函数的弱引用。因此,如果您创建一个动画对象但不保留对其的引用,则它的引用计数将为零,mpl 回调注册表中对其的弱引用将失败,并且动画将永远不会开始。

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

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

停止运行Animation并使用私有方法(应该是公共的)将其从计时器的回调列表中删除_stop这就是负责拆卸的原因(并且是在close_event).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法删除 matplotlib.animation.FuncAnimation 对象 的相关文章

随机推荐