PyQt中清除小部件的正确方法

8
什么是使用 PyQt4 清理/删除小部件的“正确”或惯用方法?
请考虑以下代码:
choices = ['a', 'b', 'c']
checkboxes = []
layout = QtGui.QVBoxLayout()

dialog = MyDialog()

for c in choices:
    checkboxes.append(QtGui.QCheckBox(c)
    layout.addWidget(chkbox)

dialog.setLayout(layout)

for c in checkboxes:
    c.setParent(None)
    c.deleteLater()
    c = None

上面的代码使用了 setParent(), deleteLater() 和将对象设置为 None。这些都是必要的吗?
另一个可能的情况是,我有一个对话框,上面有很多小部件,我想删除这些小部件并添加新的小部件。我不想 '泄漏'旧的小部件,但我不确定应该如何正确地做这样的事情。
在我看来,也许永远不需要使用 deleteLater()。它只是递减引用计数吗?如果是这样,那么仅将变量设置为 None 是否会产生相同的效果呢?
1个回答

19

你需要记住的第一件事是,要为你的小部件使用父子关系。当你这样做时,它们将归属于Qt,并在父项被删除时自动清理所有的子项。

dialog = MyDialog()

for c in choices:
    checkboxes.append(QtGui.QCheckBox(c, parent=dialog))
    layout.addWidget(chkbox)
在这种情况下,当您删除对话框时,所有复选框都将被正确清除。这解决了您问题的一部分。我意识到当您将它们添加到布局中时,您隐含地设置了父项。但在删除之前,您不应该清除该父项。正是父子关系使得子项能够自动移除。而不是引用计数。引用方面应该是 Python 方面的东西,在没有更多引用时将进行垃圾回收。 deleteLater非常重要,当您希望在控制返回事件循环时发生删除时使用。这也是从布局中删除某些小部件并添加新小部件时的安全方法:
# clear a layout and delete all widgets
# aLayout is some QLayout for instance
while aLayout.count():
    item = aLayout.takeAt(0)
    item.widget().deleteLater()

这些小部件在此方法完成后将被实际删除。 deleteLater 也可用于删除当前正在发生其下的槽或事件的小部件,例如可以在单击时自行删除的 QPushButton。

设置 c = None 也没有太大的必要。一旦父项被删除并触发其所有子项的递归删除,您对该对象的 Python 引用将无效。因此,您只需要不再使用它们即可。如果它们在列表中,请清除该列表。访问它们会引发 RuntimeError: wrapped C/C++ object of %S has been deleted 意味着它们已经被删除。


在创建将添加到布局中的所有对象时,是否需要使用parent参数?我意识到这可能有些明确,但是它是必需的吗? - durden2.0
1
不是真的。如果您直接将它们添加到布局中,则布局将使它们成为布局所有者的子项。 - jdi
虽然我来晚了,但是我想补充一下——虽然使用 parent 参数不是必须的,但这可能是个好主意,因为它可以稍微约束一下你代码的结构,并且让其他人更容易看出预期的包含层次关系。 - Emmet
@jdi你说“访问它们会引发RuntimeError: wrapped C/C++ object错误” - 我该如何检查对象是否无效/已被删除?我需要一种方法来检查c是否已被删除,但我不知道如何。请参见我的问题:https://dev59.com/1YXca4cB1Zd3GeqPP_YV - memyself
2
@memyself - 检查对象是否已被删除的唯一方法,而不是尝试访问方法并捕获RuntimeError,就是使用PyQt的sip模块或PySide的shiboken模块。它们可以让您查询指针是否已被删除。除此之外,您可以使用objectName()方法来捕获错误。 - jdi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接