I am trying to convert an existing PySide2/QtWidgets application to PySide2/QML. I am trying to get a list of custom objects from a Python service call via a QML MouseArea
click.
I currently have a main script (main.py
) that launches a QQuickView
containing my QML (contained in main.qml
). It also registers a custom type for my model (Role
, defined in role.py
) and exposes an instance of my service class (contained in mock_role_service.py
) to the view's root context.
My QML displays correctly and I can click my MouseArea
, but instead of getting back List[Role]
, I seem to be getting a QVariant
tuple. More specifically, QVariant(PySide::PyObjectWrapper, )
.
Relevant files:
mock_role_service.py
:
class MockRoleService(QObject):
def __init__(self):
super().__init__()
self.next_id = 5
self.records = {
1: Role(id_=1,
name='Admin'),
2: Role(id_=2,
name='User')
}
@Slot(result=list)
def find_all(self) -> List[Role]:
return list(self.records.values())
main.py
:
...
app = QGuiApplication(sys.argv)
qmlRegisterType(Role, 'Models', 1, 0, 'Role')
view = QQuickView()
url = QUrl('Views/main.qml')
view.setSource(url)
view.setResizeMode(QQuickView.SizeRootObjectToView)
role_service = MockRoleService()
view.rootContext().setContextProperty("roleService", role_service)
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
sys.exit(app.exec_())
main.qml
:
...
MouseArea {
onClicked: {
console.log(roleService.find_all())
for (role in roleService.find_all()) {
console.log(role.get_id())
}
}
}
...
The result of the first console.log()
call is QVariant(PySide::PyObjectWrapper, )
and the for loop is never entered.
I've only been able to find a couple of similar problems online, and the common solution to them so far (like in this answer) is to set the value to a property of the class and specify it to be of type QVariantList
. This is because PySide2 apparently did away with their QVariant
-like types, so I can't specify the slot's result type correctly.
However, I'm not sure this solution is appropriate for this situation because I am dealing with a service object. Setting a property on a service class to hold a return value feels brittle. Is there no other way of accomplishing this?