PySide2中QQmlPropertyMap插入QObject异常:值丢失或返回None
这两个看似诡异的现象,本质上都是Qt对象所有权规则和Python引用计数机制共同作用的结果,我给你逐个拆解清楚:
1. 直接插入临时QObject实例返回None
先看你遇到的第一个场景:
from PySide2.QtCore import QObject from PySide2.QtQml import QQmlPropertyMap qmap = QQmlPropertyMap() qmap.insert('test_key', QObject()) print(qmap.value('test_key')) # 输出:None
而先把QObject赋值给变量再插入就正常:
qmap = QQmlPropertyMap() obj = QObject() qmap.insert('test_key', obj) print(qmap.value('test_key')) # 输出:<PySide2.QtCore.QObject(...)>
原因解析
当你直接在insert参数里创建QObject()时,这个对象是临时对象——Python没有其他变量引用它。insert执行完毕后,这个对象的引用计数直接降到0,Python的垃圾回收机制会立刻销毁它。
而QQmlPropertyMap对于QObject类型的值,默认不会接管对象的所有权(除非你主动设置)。也就是说,它只是保存了一个对象的指针,并不会负责维持对象的生命周期。当Python端的对象被销毁后,Qt这边的指针就变成了无效的野指针,调用value()自然返回None。
而先把QObject赋值给变量obj,相当于给这个对象多了一个引用。只要obj还在当前作用域内,对象的引用计数就不会降到0,也就不会被回收,QQmlPropertyMap自然能正常获取到它。
2. 复用变量名后第一个键值被清除
再看第二个场景:
qmap = QQmlPropertyMap() obj = QObject() qmap.insert('test_key', obj) obj = QObject() # 复用变量名指向新对象 qmap.insert('test_key2', obj) print(qmap.value('test_key')) # 输出:None print(qmap.value('test_key2')) # 输出:<PySide2.QtCore.QObject(...)>
原因解析
第一次创建的QObject,唯一的Python引用就是变量obj。当你执行obj = QObject()时,obj转而指向了新创建的第二个QObject——此时第一个QObject的引用计数直接降到0,被Python垃圾回收销毁了。
和第一个问题同理,QQmlPropertyMap没有接管第一个QObject的所有权,所以当对象被销毁后,test_key对应的键值就变成了None。
如果用不同变量名,第一个QObject会一直被变量引用着,不会被回收;如果把同一个对象插入多个键,这个对象的引用计数会增加(变量+多个键的引用),自然也不会被销毁。
解决小技巧
如果希望QQmlPropertyMap负责维护QObject的生命周期,可以在插入对象后,调用setOwnership把所有权转移给Qt:
from PySide2.QtCore import Qt, QObject from PySide2.QtQml import QQmlPropertyMap qmap = QQmlPropertyMap() qmap.insert('test_key', QObject()) qmap.setOwnership('test_key', Qt.Ownership.CppOwnership) print(qmap.value('test_key')) # 现在能正常输出对象了
这样即使Python端没有引用这个对象,Qt也会持有它,直到QQmlPropertyMap被销毁。
内容的提问来源于stack exchange,提问作者CrazyChucky

