首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Python PyQt6 deleteLater()和子部件

Python PyQt6 deleteLater()和子部件
EN

Stack Overflow用户
提问于 2022-08-06 10:23:26
回答 1查看 45关注 0票数 1

我有一个我不确定的功能问题。我没有被代码实现困住,所以没有必要为这个问题共享代码。我使用

代码语言:javascript
运行
复制
layout.removeWidget
widget.deleteLater()

几次/几次在我正在建造的项目中。我曾经在这里阅读过,这样deleteLater()就不会像删除带有子小部件的小部件时所描述的那样起作用。评论说,子部件没有被正确删除,并且将留在内存中,这可能导致内存膨胀/泄漏。简单地说(我的问题),这是真的吗?

医生们没有提到我能找到的。我相信,这只是几年前的一条评论,实际上是为PyQt5 (或4)写的。你们有做过什么测试吗?这是已修复的PyQt旧版本中的一个bug,还是评注或完全错误?

从我的问题中可以看出,我的问题不是如何写东西,而是关于deleteLater()的幕后功能。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-06 18:00:23

首先,重要的是要记住,PyQt (作为PySide)是与Qt的绑定,Qt是用C++编写的。

所有Qt对象和函数都是使用包装器从python访问的。

当从Python创建Qt对象时,实际上有两个对象:

  • C++对象;
  • python对象,允许我们使用上面的Qt对象;

这两个物体的寿命可能并不总是一致的。事实上,C++对象可以在python引用保持不变时被销毁,或者相反。

C++对象破坏

deleteLater()保证销毁C++对象以及它们拥有所有权的任何对象(包括grandgrand,...children的间接所有权)。请注意,对象不会立即被销毁,而只有当控件返回到事件循环时才会立即销毁:然后该对象发出destroyed()信号,然后它的所有子对象都会被递归地销毁。

这不会删除python引用,如果它存在的话。例如:

代码语言:javascript
运行
复制
class MyLabel(QLabel):
    def __init__(self, value, parent=None):
        super().__init__(value, parent)
        self.value = value

如果您保持对上面类的实例的python引用,那么即使在用self.value销毁对象之后,仍然能够访问它的deleteLater()。如果您尝试访问任何Qt函数或属性,则会得到一个异常:

代码语言:javascript
运行
复制
>>> 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对象。例如:

代码语言:javascript
运行
复制
>>> parent = QWidget()
>>> label = MyLabel('hello', parent)
>>> parent.show()
>>> del label
>>> print(parent.findChild(QLabel).text())
'hello'

这是因为当对象被添加到父对象时,父对象获得子对象的所有权。

注意,当所有python引用也被销毁时,没有父对象的Qt对象也会被销毁。

这就是为什么这里最常见的问题之一,当有人试图使用本地引用创建另一个窗口而没有任何父对象时,但是窗口没有显示,因为它会立即被垃圾收集。

请注意,这对于Qt6是有效的,就像对Qt5一样,对于Qt4也是一样,对于它们的相对python绑定也是如此。

考虑到第一部分所写的内容:

代码语言:javascript
运行
复制
>>> 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程序来说都是这样)。

最后,一些考虑因素:

  • 在一些非常复杂的情况下并不总是能够跟踪所有的python引用,所以即使您销毁了一个父对象,对某些子对象的引用仍然存在;
  • 并不是所有的Qt对象都是QObjects,它们的使用、行为或内存管理都取决于Qt和C++实现;这些对象有时可以在它们的python引用被销毁时被销毁,或者当Qt“决定这样做”时;
  • 并不是所有的QObjects在其他QObjects被“添加”时都拥有它们的所有权:一个常见的例子是QAction,它可以在许多对象(QMenu、QMenuBar、QToolBar甚至标准QWidgets)之间共享;总是在文档中寻找有疑问的"...takes所有权“短语;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73258934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档