PyQt4 setParent 与 deleteLater
PyQt4 setParent vs deleteLater
我有一个布局,我用 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
.
的引用发生了什么
以下是我对 C++ 的理解以及 Python 对 PyQt 中 QObject 的引用。
我相信当您将小部件添加到布局时,小部件将成为布局的子项。所以,如果我调用 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 时,是否有 'accepted' 方法来进行此清理?我应该使用 setParent
、deleteLater
还是两者都使用?
查看实际情况的最简单方法可能是在交互式会话中逐步完成:
>>> parent = QtGui.QWidget()
>>> child = QtGui.QWidget()
>>> layout = QtGui.QHBoxLayout(parent)
>>> layout.addWidget(child)
>>> child.parent() is layout
False
>>> child.parent() is parent
True
因此布局不会成为小部件的父级。这是有道理的,因为小部件只能有 other widgets 作为父级,而布局不是小部件。所有添加到布局的小部件最终都会将其父级重置为布局的父级(无论何时)。
>>> item = layout.itemAt(0)
>>> item
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318>
>>> item.widget() is child
True
由于布局与其包含的小部件之间没有 parent/child 关系,因此需要不同的 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)
。
最后一点:上面的解释器会话有点误导,因为每次执行一行时都会处理事件队列。这意味着立即可以看到 deleteLater
的效果,如果代码是 运行 作为脚本,情况就不会如此。要在脚本中立即删除,您需要使用 sip
模块:
>>> import sip
>>> sip.delete(child)
我有一个布局,我用 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
.
以下是我对 C++ 的理解以及 Python 对 PyQt 中 QObject 的引用。
我相信当您将小部件添加到布局时,小部件将成为布局的子项。所以,如果我调用 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 时,是否有 'accepted' 方法来进行此清理?我应该使用 setParent
、deleteLater
还是两者都使用?
查看实际情况的最简单方法可能是在交互式会话中逐步完成:
>>> parent = QtGui.QWidget()
>>> child = QtGui.QWidget()
>>> layout = QtGui.QHBoxLayout(parent)
>>> layout.addWidget(child)
>>> child.parent() is layout
False
>>> child.parent() is parent
True
因此布局不会成为小部件的父级。这是有道理的,因为小部件只能有 other widgets 作为父级,而布局不是小部件。所有添加到布局的小部件最终都会将其父级重置为布局的父级(无论何时)。
>>> item = layout.itemAt(0)
>>> item
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318>
>>> item.widget() is child
True
由于布局与其包含的小部件之间没有 parent/child 关系,因此需要不同的 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)
。
最后一点:上面的解释器会话有点误导,因为每次执行一行时都会处理事件队列。这意味着立即可以看到 deleteLater
的效果,如果代码是 运行 作为脚本,情况就不会如此。要在脚本中立即删除,您需要使用 sip
模块:
>>> import sip
>>> sip.delete(child)