To learn about the Object-Deletion Features of metaclass QObject in PyQT5, I wrote the following code:
from PyQt5.Qt import *
class Window(QWidget):
def __init__(self):
super().__init__()
self.setup_UI()
self.setup_subUI()
self.hook = None
def setup_UI(self):
self.setWindowTitle('MyWindowTitle')
self.resize(600, 400)
def setup_subUI(self):
obj1 = QObject()
obj2 = QObject(obj1)
obj3 = QObject(obj2)
self.hook = obj1 # This is line 19.
obj1.setObjectName('obj1')
obj2.setObjectName('obj2')
obj3.setObjectName('obj3')
obj1.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
obj2.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
obj3.destroyed.connect(lambda obj: print(f'Object {obj.objectName()} has been released'))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
There are several obvious facts in the code above:
Window
class has an non-essential attribute, hook
, that was originally used to bind to obj1
;obj1
is the parent object of obj2
, obj2
is the parent object of obj3
. If no objects are involved in Reference Counting, all three of them will be quickly recovered to activate the destroyed
signal receiver below as well as print this message further.To avoid GC, I added line 19 to try to keep them, self.hook = obj1
, but it backfired.
If line 19 is changed to self.other_attribute_not_schedule = obj1
, the program will not release these 3 objects before the Window UI entity is closed.
More clearly, could you please tell me more details about the objects get deleted with the above code, and why they don't when I use the other line?
Garbage collection activates when the number of references of an object reaches 0. In that case, python tries to delete it, if Qt allows it.
In your original code you first of all call setup_subUI
, which creates a reference:
self.hook = obj1
Then, after setup_subUI
returns, you do the following:
self.hook = None
This results in "overwriting" self.hook
as a reference to None
, so the previous reference (obj1
) is removed. Since that was the only reference, it gets deleted, along with its children.
Then you do the following instead, again in setup_subUI
:
self.other_attribute_not_schedule = obj1
When setup_subUI
returns, the next line will be evaluated as above:
self.hook = None
The difference here is that you're not overwriting self.hook
since it didn't exist yet, and the obj1
is not deleted, because its reference (self.other_attribute_not_schedule
) still exists.
Note there are different types of references (for instance, adding an object to a persistent list) and they might not always be explicit, especially with complex modules like Qt.
For instance, adding the parent to the constructor of the first object will prevent deletion, even if you do self.hook = None
:
obj1 = QObject(self)
This is because the ownership of the object is then managed by Qt itself.
There are many cases for which Qt takes ownership (by reparenting it if necessary) of an object, for instance when adding a widget to a layout and then setting the layout to another widget.