鉴于您的示例目前的结构方式,当对话框关闭时,对话框及其所有子小部件都不会被删除。
您可以通过将示例的结尾更改为以下内容来验证此事实:
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()
)。
附注:当将小部件添加到布局中时,它们将自动重新分配给最终包含布局的顶层小部件。因此,严格来说,在代码中显式设置这些小部件的父级并不是必需的。