我有一个我不确定的功能问题。我没有被代码实现困住,所以没有必要为这个问题共享代码。我使用
layout.removeWidget
widget.deleteLater()
几次/几次在我正在建造的项目中。我曾经在这里阅读过,这样deleteLater()
就不会像删除带有子小部件的小部件时所描述的那样起作用。评论说,子部件没有被正确删除,并且将留在内存中,这可能导致内存膨胀/泄漏。简单地说(我的问题),这是真的吗?
医生们没有提到我能找到的。我相信,这只是几年前的一条评论,实际上是为PyQt5 (或4)写的。你们有做过什么测试吗?这是已修复的PyQt旧版本中的一个bug,还是评注或完全错误?
从我的问题中可以看出,我的问题不是如何写东西,而是关于deleteLater()
的幕后功能。
发布于 2022-08-06 18:00:23
首先,重要的是要记住,PyQt (作为PySide)是与Qt的绑定,Qt是用C++编写的。
所有Qt对象和函数都是使用包装器从python访问的。
当从Python创建Qt对象时,实际上有两个对象:
这两个物体的寿命可能并不总是一致的。事实上,C++对象可以在python引用保持不变时被销毁,或者相反。
C++对象破坏
deleteLater()
保证销毁C++对象以及它们拥有所有权的任何对象(包括grandgrand,...children的间接所有权)。请注意,对象不会立即被销毁,而只有当控件返回到事件循环时才会立即销毁:然后该对象发出destroyed()
信号,然后它的所有子对象都会被递归地销毁。
这不会删除python引用,如果它存在的话。例如:
class MyLabel(QLabel):
def __init__(self, value, parent=None):
super().__init__(value, parent)
self.value = value
如果您保持对上面类的实例的python引用,那么即使在用self.value
销毁对象之后,仍然能够访问它的deleteLater()
。如果您尝试访问任何Qt函数或属性,则会得到一个异常:
>>> print(mylabel.value)
'some value'
>>> print(mylabel.text())
RuntimeError: wrapped C/C++ object of type MyLabel has been deleted
因此,python对象显然会一直使用内存资源,直到垃圾回收为止(它的引用计数变为0)。如果在python端保留对具有较大内存占用空间的对象(即大型numpy数组)的引用,则可能会出现问题。
Python对象销毁
删除python引用并不能保证销毁Qt对象,因为它只删除python对象。例如:
>>> parent = QWidget()
>>> label = MyLabel('hello', parent)
>>> parent.show()
>>> del label
>>> print(parent.findChild(QLabel).text())
'hello'
这是因为当对象被添加到父对象时,父对象获得子对象的所有权。
注意,当所有python引用也被销毁时,没有父对象的Qt对象也会被销毁。
这就是为什么这里最常见的问题之一,当有人试图使用本地引用创建另一个窗口而没有任何父对象时,但是窗口没有显示,因为它会立即被垃圾收集。
请注意,这对于Qt6是有效的,就像对Qt5一样,对于Qt4也是一样,对于它们的相对python绑定也是如此。
考虑到第一部分所写的内容:
>>> parent = QWidget()
>>> label = MyLabel('hello', parent)
>>> parent.show()
>>> del parent
>>> print(label.value)
'hello'
>>> print(label.text())
RuntimeError: wrapped C/C++ object of type MyLabel has been deleted
因此,必须始终记住的是对象的位置,包括对象的C++和python属性和属性。如果您担心标准(不是python )小部件的内存使用,那么deleteLater()
总是可以保证释放这些小部件的内存资源,您只需要确保不再存在剩余的python引用(但对于任何python程序来说都是这样)。
最后,一些考虑因素:
https://stackoverflow.com/questions/73258934
复制相似问题