Trying to learn more about PyQt I wrote a small script that:
- Creates an
QObjecta - Creates an
QObjectbwithaas its parent - Deletes
a
At this point I expect the object to which the name b is point to have been deleted as well. The documentation of Qt (not PyQt!) says:
The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor.
But b still points to an existing object.
I also tried an explicit garbage collection without any change.
Trying to access a via b's parent() method fails though, as expected.
Why is the QObject referenced by b not deleted when a, which "owns" b, is deleted?
I have added the print outputs as comments below:
import gc
from PyQt5.QtCore import QObject
def tracked_qobjects():
return [id(o) for o in gc.get_objects() if isinstance(o, QObject)]
def children(qobject: QObject):
return [id(c) for c in qobject.findChildren(QObject)]
a = QObject()
b = QObject(parent=a)
print(f"QObjects tracked by gc: {tracked_qobjects()}")
# QObjects tracked by gc: [140325587978704, 140325587978848]
print(f"Children of a: {children(a)}")
# Children of a: [140325587978848]
del a
print(f"QObjects tracked by gc: {tracked_qobjects()}")
# QObjects tracked by gc: [140325587978848]
gc.collect() # not guaranteed to clean up but should not hurt
print(f"QObjects tracked by gc: {tracked_qobjects()}")
# QObjects tracked by gc: [140325587978848]
# Since https://doc.qt.io/qt-5/qobject.html#details says:
# "The parent takes ownership of the object; i.e., it will automatically delete
# its children in its destructor."
# I expect that b now points to a non-existent object.
# But no, this still works! Maybe because we are in PyQt5 and
# not a C++ application?
print(id(b))
# 140325587978848
print(b)
# <PyQt5.QtCore.QObject object at 0x7fa018d30a60>
# The parent is truly gone though and trying to access it from its child raises the "wanted" RuntimeError
print(b.parent())
# RuntimeError: wrapped C/C++ object of type QObject has been deleted
This was a red herring. Python can still provide the memory address (id) that the name
bis point to and the type of the object expected to be there, but the actual object has been destroyed:results in
The error message is about
b. Its (wrapped) C++ object has been deleted so the Python object is errorneous.