我有一个布局,使用layout.addWidget(widget)
之类的东西添加了许多自定义小部件。稍后,我想删除所有这些自定义小部件并添加新的。对于deleteLater
和setParent(None)
,我对执行此操作的最佳方法感到困惑。例如,这是我的清理函数,它为布局中的所有小部件调用:
def _removeFilterWidgetFromLayout(self, widget):
"""Remove filter widget"""
self._collection_layout.removeWidget(widget)
widget.setParent(None)
widget.deleteLater()
我怀疑并非所有这些行都需要正确清理小部件使用的内存。我不确定C++和Python对
widget
的引用是怎么回事。这是我对PyQt中QObjects的C++和Python引用的了解。
我相信,当您将小部件添加到布局时,该小部件将成为该布局的子级。因此,如果我调用
removeWidget
,则父级关系中断,因此我必须自己清理C++和Python引用,因为该小部件没有其他父级。对setParent
的调用是一种删除带有布局的父级关系的显式方法。调用deleteLater
是为了照顾C++引用。Python引用是垃圾回收的,因为
widget
变量超出范围,并且没有其他Python对象指向widget
。我需要调用
setParent
和deleteLater
还是deleteLater
足以正确清理此问题?作为附带说明,我发现在这种情况下,调用
setParent(None)
是一个非常昂贵的函数调用。通过删除此调用,我可以大大加快整个清理过程。我不确定deleteLater
是否足以正确清理所有内容。这是我的line_profiler
的分析输出:Line # Hits Time Per Hit % Time Line Contents
==============================================================
2167 @profile
2168 def _removeFilterWidgetFromLayout(self, widget):
2169 """Remove filter widget"""
2170
2171 233 1528 6.6 1.0 self._collection_layout.removeWidget(widget)
2172 233 143998 618.0 97.9 widget.setParent(None)
2173 233 1307 5.6 0.9 widget.deleteLater()
使用PyQt4时,是否有“可接受的”方式进行清理?我应该同时使用
setParent
,deleteLater
还是同时使用两者? 最佳答案
观察实际情况的最简单方法可能是在交互式 session 中逐步进行操作:
>>> parent = QtGui.QWidget()
>>> child = QtGui.QWidget()
>>> layout = QtGui.QHBoxLayout(parent)
>>> layout.addWidget(child)
>>> child.parent() is layout
False
>>> child.parent() is parent
True
因此,布局不会成为小部件的父级。这是有道理的,因为小部件只能将其他小部件作为父级,而布局不是小部件。最终,所有添加到布局的小部件都将其父级重置为该布局的父级(无论何时获得父级)。
>>> item = layout.itemAt(0)
>>> item
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318>
>>> item.widget() is child
True
由于布局及其包含的小部件之间没有父/子关系,因此需要不同的API来访问基础对象。这些项目归布局所有,但基础对象的所有权保持不变。
>>> layout.removeWidget(child)
>>> child.parent() is parent
True
>>> layout.count()
0
>>> repr(layout.itemAt(0))
'None'
>>> item
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318>
此时,布局已删除其项目(因为它拥有该项目的所有权),因此不再拥有对所包含窗口小部件的任何引用。鉴于此,使用该项目的python包装器执行更多操作已不再安全(如果尝试调用其任何方法,解释器可能会崩溃)。
>>> child.deleteLater()
>>> parent.children()
[<PyQt4.QtGui.QHBoxLayout object at 0x7fa1715fe1f8>]
>>> child.parent()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: wrapped C/C++ object of type QWidget has been deleted
>>>
由于我们仍然拥有子窗口小部件的所有权,因此可以在其上调用
deleteLater
。从回溯中可以看出,这将删除底层的C++对象,但是它将保留其python包装器对象。但是,一旦所有剩余的python引用都消失了,垃圾回收器将最终删除该包装器。请注意,在此过程中永远不需要调用setParent(None)
。最后一点:上面的解释器 session 会产生一些误导,因为每次执行一行时都会处理事件队列。这意味着可以立即看到
deleteLater
的效果,如果代码作为脚本运行则不会出现这种情况。要立即删除脚本,您需要使用sip
模块:>>> import sip
>>> sip.delete(child)
关于PyQt4 setParent vs DeleteLater,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30241684/