这不是我第一次遇到 RuntimeError: underlying C/C++ object has been deleted
错误。我以前曾多次通过随意但直观地修改代码来解决它,但现在我又面临着这个问题,只是不明白为什么会发生这种情况......
我不会在这里发布代码示例,因为我的项目太复杂了,我只是无法找出错误所在。而且我要求通用的解决方案,而不仅仅是针对这种情况。
为什么“底层的C/C++”对象会被删除?
如何避免这种情况发生?
如何测试底层对象是否存在?
这不是我第一次遇到 RuntimeError: underlying C/C++ object has been deleted
错误。我以前曾多次通过随意但直观地修改代码来解决它,但现在我又面临着这个问题,只是不明白为什么会发生这种情况......
我不会在这里发布代码示例,因为我的项目太复杂了,我只是无法找出错误所在。而且我要求通用的解决方案,而不仅仅是针对这种情况。
为什么“底层的C/C++”对象会被删除?
如何避免这种情况发生?
如何测试底层对象是否存在?
MyFreeAgent
构造函数可以在MyGarbageCollector
中注册自己,MyFreeAgent
析构函数会从MyGarbageCollector
中注销自己。因此,MyFreeAgent
实例可以由MyGarbageCollector
进行“清理”,或者MyFreeAgent
甚至可以使用"delete this;
"自己清除,并且你会知道它已经被清除了(因为其析构函数会从MyGarbageCollector
中注销自己)。在这种设计中,单个MyGarbagageCollector
将始终“知道”存在哪些MyFreeAgent
实例。当前接受的答案声称:
您无法测试底层对象是否存在(例如,它是否已被“删除”)。
这是错误的。一定有一种方法可以做到这一点,否则 PyQt 就无法引发异常。以下是一些示例输出,演示了如何明确地测试删除:
>>> import sip
>>> from PyQt4 import QtCore, QtGui
>>> app = QtGui.QApplication([''])
>>> w = QtGui.QWidget()
>>> w.setAttribute(QtCore.Qt.WA_DeleteOnClose)
>>> sip.isdeleted(w)
False
>>> w.close()
True
>>> sip.isdeleted(w)
True
>>> w.objectName()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: wrapped C/C++ object of type QWidget has been deleted
PyQt窗口小部件由Python部分和C++部分组成。如果C++部分被Qt删除,那么将会留下一个空的Python封装对象。如果你试图通过处于这种状态的对象调用任何Qt方法,就会引发一个RuntimeError
(当然,这比可能的段错误更可取)。
一般来说,Qt不倾向于隐式删除对象,这就是为什么我在上面的示例中必须显式标记该小部件进行删除的原因。这个一般规则的例外情况总是清楚地记录在文档中 - 例如指定是Qt还是调用者拥有对象的所有权。 (这就是为什么即使你没有C ++知识,熟悉Qt文档也可能真正有所回报的原因之一)。
在大多数PyQt代码中,避免问题的最简单方法是确保对象具有父级或显式保留对对象的引用(通常作为主窗口的实例属性)。
show()
打开对话框,则不会发生这种情况,因为对话框不会被删除(其父级将使其保持活动状态)。 - ekhumoro@charley是完全正确的,他很好地解释了理论。尽管在实践中,这种所有权问题可能会在许多情况下发生,但其中最常见的一种是在子类化QT类时忘记调用基类构造函数 - 有时当我从头开始编码时,我总是会卡在这个问题上。
以非常常见的子类化QAbstractTableModel为例:
from PyQt4.QtCore import *
class SomeTableModel(QAbstractTableModel):
def __init__(self, filename):
super(SomeTableModel, self).__init__() # If you forget this, you'll get the
# "underlying C/C++ object has been
# deleted" error when you instantiate
# SomeTableModel.
init
来明显地出现。 - eric从Python 3+开始,你甚至可以进一步简化代码。
from PyQt4.QtCore import *
class SomeTableModel(QAbstractTableModel):
def __init__(self, filename):
super().__init__() # No longer need to specify class name, or self
此外,Super 还提供了一些额外的保护措施,具体请参见了解 Python super() 和 __init__() 方法
try: someObject.objectName()
except RuntimeError: return False
.objectName()
测试是一个任意的查询,所有有效的QObjects都应该轻松通过。
如果它们失败了,它们将抛出RuntimeError
,这表明它们无效。
在这个简单的例子中,我们吸收了RuntimeError,而是返回false。