我有一个 QMainWidnow 会生成许多不同的对话框,在删除对话框之前我已经手动断开了所有信号。这是必要的吗,还是它们被垃圾收集器或 PyQt 后端清理掉了?
class AlignmentStationInterface(QMainWindow):
def __init__(self)
super().__init__()
add_button = QPushButton("Add Position")
add_button.clicked.connect(self.add_position)
self.position_builder_dialog = None
def add_position(self):
print('write new location')
if self.new_position_builder_popup is not None:
return
self.position_builder_dialog = PositionBuilderDialog()
self.position_builder_dialog.new_position_return.connect(self.save_position)
self.position_builder_dialog.finished.connect(self.cleanup_dialog)
def cleanup_dialog(self):
# Are these disconnect lines necessary?
self.position_builder_dialog.new_position_return.disconnect(self.save_position)
self.position_builder_dialog.finished.disconnect(self.cleanup_dialog)
self.position_builder_dialog.deleteLater()
def save_position(self):
pass
我希望后端能处理好事情,但我担心内存泄漏。
理论上没有必要,但也要看情况。
信号通常会在其某一部分被破坏时自动断开:只要信号连接到被破坏的 QObject 的函数,它们通常会在删除时自动断开。
但是您需要记住,使用 PyQt(和 PySide)等 Python 绑定最终总会得到两个对象:“Python 对象”和“Qt 对象”(存在于“C++ 世界”中的对象) .
这意味着Python对象和Qt对象可以有不同的生命周期:
您的代码是示例性的:您正在创建对 Qt 包装对象的 python 引用,但是
deleteLater()
只会销毁 C++ 对应项,因为 self.position_builder_dialog.deleteLater()
不会删除 Python 解释器中的 self.position_builder_dialog
对象。事实上,如果在调用
cleanup_dialog()
之后再次点击按钮(依次调用add_position()
),不会发生任何事情,因为
self.new_position_builder_popup is not None
。虽然 C++ 对象已被销毁,但 Python 引用仍然存在。
理论上,即使对话框实际被销毁(使用
deleteLater()
),您仍然可以调用 PositionBuilderDialog
的方法,只要它们不依赖于与该对话框严格相关的 Qt 函数。那是因为这些方法存在于 Python 命名空间中,并且它们仍然“有意义”。
让我们假设以下基本示例:
class PositionBuilderDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.value = ''
self.lineEdit = QLineEdit()
layout = QVBoxLayout(self)
layout.addWidget(self.lineEdit)
self.lineEdit.textChanged.connect(self.edited)
def edited(self, text):
self.value = text
然后考虑以下情况:
class AlignmentStationInterface(QMainWindow):
...
def add_position(self):
print('write new location')
if self.new_position_builder_popup is not None:
print(self.new_position_builder_popup.value)
print(self.new_position_builder_popup.lineEdit)
print(self.new_position_builder_popup.lineEdit.text())
return
...
单击按钮
一次并且对话框已关闭(然后在
cleanup_dialog()
中删除)后,单击按钮将执行以下操作:
if
new_position_builder_popup
不是
None
;
打印对话框的内容
value
打印对
lineEdit
text()
会尝试调用已不存在的包装C++对象的相关函数;
在您的情况下,您不需要断开信号,但您
def cleanup_dialog(self):
self.position_builder_dialog.deleteLater()
self.position_builder_dialog = None
deleteLater()
,但通常建议确保所有 Python 引用也被删除。前者实际上会删除real
Qt对象(这将释放程序正在使用的大部分内存),而后者通常主要用于reliable代码:在担心内存泄漏之前,您应该担心对象引用的使用。 请注意,在某些情况下(尤其是使用 lambda 时),信号连接可能会创建进一步的闭包,这意味着即使理论上 Python 对象已被销毁,它们仍然可能存在。请记住,在处理绑定中的包装对象时,您永远不完全依赖垃圾收集,并且对象的生命周期可能完全不一致。
根据上面的内容,采取以下代码:
def add_position(self):
dialog = PositionBuilderDialog(self) # note the "self" argument
dialog.show()
在 Python 世界中,这会自动进行垃圾收集
dialog
。尽管如此,该对话框仍会毫无问题地显示。这是因为该对话框是使用父级创建的,这确保了只要父级对话框存在,它就始终保持活动状态。 Qt 对 Python 一无所知,它只知道对话框有一个父级,然后它不会销毁它,直到该父级出现为止。