QML не становится владельцем объекта, полученного из слота PyQt
Как правильно вызвать функцию Python из QML, которая будет создавать экземпляр объекта и возвращать его в QML, а сторона QML будет отвечать за время жизни объекта? Проблема, с которой я сталкиваюсь, заключается в том, что объект собирает мусор еще до того, как он достигает QML.
Обратите внимание, что я не хочу сохранять явные ссылки на объект на стороне Python, и в соответствии со следующей цитатой издокументация то, что я ищу, должно даже быть поведением по умолчанию:
Когда данные переносятся из C ++ в QML, право собственности на данные всегда остается за C ++.Исключением из этого правила является случай, когда QObject возвращается из явного вызова метода C ++: в этом случае механизм QML предполагает владение объектом...
Я создал минимальный исполняемый пример, который демонстрирует проблему (суть ссылки):
main.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import signal
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtCore import QUrl, QObject, QVariant, pyqtSlot
from PyQt5.QtQml import QQmlEngine, qmlRegisterType
class Dummy(QObject):
def __del__(self):
print("Deleted")
class GUIEntryPoint(QObject):
@pyqtSlot(result=QVariant)
def get_foo(self):
foo = Dummy()
print("Created {}".format(foo))
print("Ownership after instantiation: {}".format(QQmlEngine.objectOwnership(foo)))
#QQmlEngine.setObjectOwnership(foo, 1) # has no effect
return foo
@pyqtSlot(QVariant)
def print_foo(self, foo):
print("{}, ownership: {}".format(foo, QQmlEngine.objectOwnership(foo)))
def run():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QGuiApplication(sys.argv)
# these don't seem to make a difference
qmlRegisterType(GUIEntryPoint, 'GUIEntryPoint', 1, 0, 'GUIEntryPoint')
qmlRegisterType(Dummy, 'Dummy', 1, 0, 'Dummy')
qml_url = QUrl.fromLocalFile(os.path.join('zzz.qml'))
view = QQuickView()
gep = GUIEntryPoint()
view.rootContext().setContextProperty('BE', gep)
view.setSource(qml_url)
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
run()
zzz.qml:
import QtQuick 2.2
Rectangle {
id: rootItem
width: 640
height: 400
property var pleaseKeepMe: BE.get_foo()
Component.onCompleted: {
console.log("Completed");
BE.print_foo(pleaseKeepMe); // prints None, it has been deleted
console.log("creating another");
var x = BE.get_foo();
// at this point x has already been deleted on the Python side
console.log("created another");
BE.print_foo(x); // prints None
console.log("\n\nPress CTRL-C to exit");
}
}