PyQt:具有父级的小部件何时被删除?

17

假设我想创建一个对话框,作为我的主程序的子级:

from PyQt4 import QtGui, QtCore

class WizardJournal(QtGui.QDialog):

    def __init__(self, parent):

        super(WizardJournal, self).__init__(parent)

        self.parent = parent

        self.initUI()


    def initUI(self):

        self.parent.wizard = QtGui.QWidget()

        self.ok_button = QtGui.QPushButton("OK", self)

        self.vbox_global = QtGui.QVBoxLayout(self)

        self.vbox_global.addWidget(self.ok_button)

        self.paret.wizard.setLayout(self.vbox_global)
        self.parent.wizard.show()


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    parent = QtGui.QWidget()
    obj = WizardJournal(parent)
    sys.exit(app.exec_())

此对话框将由我的主程序打开和关闭。关于内存消耗,以下哪种方法更好:

  • self.ok_button = QtGui.QPushButton("OK", self)
  • self.ok_button = QtGui.QPushButton("OK")

基本上,我想知道在创建小部件时是否应该指定其父级窗口部件。如果我没有在创建时指定父级窗口部件并关闭对话框,那么 OK 按钮会被从内存中释放吗?

1个回答

33

鉴于您的示例目前的结构方式,当对话框关闭时,对话框及其所有子小部件都不会被删除。

您可以通过将示例的结尾更改为以下内容来验证此事实:

app.exec_()
print('\n'.join(repr(w) for w in app.allWidgets()))

这将会产生类似于以下输出(一旦对话框关闭):

<__main__.WizardJournal object at 0x7fcd850f65e8>
<PyQt4.QtGui.QPushButton object at 0x7fcd850f6708>
<PyQt4.QtGui.QWidget object at 0x7fcd850f6558>
<PyQt4.QtGui.QDesktopWidget object at 0x7fcd850f6828>
<PyQt4.QtGui.QWidget object at 0x7fcd850f6678>
在PyQt中,你需要注意一个对象可能会有两种引用:一种在Python端(即PyQt的包装对象),另一种在C++端(即底层Qt对象)。因此,要完全删除一个对象,你需要移除所有这些引用。
一般来说,Qt不会删除对象,除非你明确告诉它这样做。当创建带有父级的对话框时,你需要注意这一点,否则很容易产生内存泄漏。通常会看到写成这样的代码:
def openDialog(self):
    dialog = MyDialog(self)
    dialog.show()

这个方法乍一看似乎无害 - 但每次调用都会创建一个新的对话框,因为 C++ 端有 parent 引用,导致 Qt 最终会持有所有这些对话框。避免这种情况的一种方法是重新编写方法,使其只在 Python 端保留引用:

def openDialog(self):
    self.dialog = MyDialog()
    self.dialog.show()

但是对于必须有父级的模态对话框该怎么办呢?在这种情况下,你可以像这样初始化对话框类:

但是对于必须有父级的模态对话框该怎么办呢?在这种情况下,你可以像这样初始化对话框类:

class MyDialog(QtGui.QDialog):
    def __init__(self, parent):
        super(MyDialog, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

现在,当对话框关闭时,Qt将自动删除它,并递归删除其所有子对象。这将仅留下一个空的PyQt包装器对象,它最终将由Python垃圾收集器删除。

因此,针对您的特定示例,我认为我会重写它,使其看起来像这样:

import sys
from PyQt4 import QtGui, QtCore

class WizardJournal(QtGui.QDialog):
    def __init__(self, parent):
        super(WizardJournal, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.initUI()

    def initUI(self):
        self.ok_button = QtGui.QPushButton("OK", self)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.ok_button)
        self.show()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    parent = QtGui.QWidget()
    obj = WizardJournal(parent)
    app.exec_()
    print('\n'.join(repr(w) for w in app.allWidgets()))

对话框类现在完全自包含,只有一个对其实例的外部 Python 引用。(如果您需要从对话框类中访问父窗口小部件,可以使用 self.parent())。

附注:当将小部件添加到布局中时,它们将自动重新分配给最终包含布局的顶层小部件。因此,严格来说,在代码中显式设置这些小部件的父级并不是必需的。


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