首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PyQt4 setParent vs deleteLater

PyQt4 setParent vs deleteLater
EN

Stack Overflow用户
提问于 2015-05-14 15:50:19
回答 1查看 4.7K关注 0票数 13

我有一个布局,我添加了很多自定义小部件,比如layout.addWidget(widget)。稍后,我希望删除所有这些自定义小部件并添加新的小部件。当涉及到deleteLatersetParent(None)时,我对如何才能做到这一点感到困惑。例如,下面是为布局中的所有小部件调用的清理函数:

代码语言:javascript
复制
def _removeFilterWidgetFromLayout(self, widget):
    """Remove filter widget"""

    self._collection_layout.removeWidget(widget)
    widget.setParent(None)
    widget.deleteLater()

我怀疑并非所有这些行都需要正确地清理小部件使用的内存。我不知道C++和Python对widget的引用发生了什么。

下面是我对C++和Python对QObjects在PyQt中的引用的理解。

我相信,当您将小部件添加到布局中时,小部件就会成为布局的子部件。因此,如果我调用removeWidget,那么父关系就会中断,所以我必须自己清理C++和Python,因为小部件没有其他父引用。对setParent的调用是消除与布局的父关系的显式方法。对deleteLater的调用是为了处理C++引用。

Python引用是垃圾收集的,因为widget变量超出了作用域,并且没有指向widget的其他Python对象。

我是否需要打电话给setParentdeleteLater,或者deleteLater已经足够清理这个问题了?

另外,我发现在这个场景中调用setParent(None)是一个非常昂贵的函数调用。通过删除这个调用,我可以大大加快我的整个清理过程。我不确定deleteLater是否足以正确地清理所有的东西。下面是我从line_profiler的分析输出

代码语言:javascript
复制
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时,是否有一种“接受”的方法来进行这种清理?我应该使用setParentdeleteLater,还是两者兼用?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-05-14 19:34:10

也许查看实际情况的最简单的方法是在交互会话中逐步完成:

代码语言:javascript
复制
>>> parent = QtGui.QWidget()
>>> child = QtGui.QWidget()
>>> layout = QtGui.QHBoxLayout(parent)
>>> layout.addWidget(child)
>>> child.parent() is layout
False
>>> child.parent() is parent
True

因此,布局不会成为小部件的父级。这是有意义的,因为小部件只能有其他小部件作为父部件,而布局不是小部件。添加到布局中的所有小部件最终将使其父部件重置为布局的父部件(无论何时得到布局)。

代码语言:javascript
复制
>>> item = layout.itemAt(0)
>>> item
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318>
>>> item.widget() is child
True

由于在布局和它们包含的小部件中没有父/子关系,因此访问底层对象需要不同的API。这些项属于布局,但基础对象的所有权保持不变。

代码语言:javascript
复制
>>> layout.removeWidget(child)
>>> child.parent() is parent
True
>>> layout.count()
0
>>> repr(layout.itemAt(0))
'None'
>>> item
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318>

此时,布局已经删除了它的项(因为它拥有它的所有权),因此不再保存对包含的小部件的任何引用。考虑到这一点,对项目的python包装做很多事情已经不再安全了(如果我们试图调用它的任何方法,解释器可能会崩溃)。

代码语言:javascript
复制
>>> 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模块:

代码语言:javascript
复制
>>> import sip
>>> sip.delete(child)
票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30241684

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档