I am trying to pass a QStandardItemModel object to QML via a connection with a ContextProperty containing a backend which generates this model based on a button click, but while the model is correctly created in Python, it seems it cannot be loaded by QML. In reality the application I am working on is more complex, but I am including a minimal example which replicates the error I get. Bascially, when it comes time to set the model which is passed to QML via a signal, I get the error: Unable to assign [undefined] to QAbstractItemModel*
I know this kind of question has probably been asked before, and I found other related questions, but their solutions were very similar to what I implemented, so I am failing to see exactly where the error is occuring.
Firstly, the QML application is a simple window with a Button and a TreeView which starts with a "null" model on application start-up. Once the button is clicked, it triggers a slot ("trigger_signal_send") in the backend which creates a model, and emits a signal containing this model. The QML file also has a Connection object which listens for the emitted signal from the "backend" and updates the model in the TreeView. Note I am only including the relevant lines (things like anchors and graphics properties have been omitted).
main.qml
import QtQuick 2.14
import QtQuick.Controls 1.4
import QtQuick.Window 2.14
Window {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property var myModel: null
function setModel(model) {
mainWindow.myModel = model
}
Rectangle {
anchors.fill: parent
anchors.margins: {
top: 10
left: 10
right: 10
bottom: 60
}
clip: true
TreeView {
id: treeView
model: mainWindow.myModel
anchors.fill: parent
}
}
Button {
id: btn
onClicked: {
backend.trigger_signal_send()
}
}
Connections {
id: conn
target: backend
function onSendModel(model) {
mainWindow.setModel(model)
}
}
}
main.py Here is where I define the Backend class, with the relevant Slot that generates and emits the model when the button is clicked, and where I connect my Backend object to QML via the ContextProperty.
# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys
from PySide2.QtCore import QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide2.QtQml import QQmlApplicationEngine
class Backend(QObject):
# Class attributes
model: QStandardItemModel = QStandardItemModel()
# Signals
sendModel: Signal = Signal(QStandardItemModel)
def __init__(self, *args, **kwargs):
super(Backend, self).__init__(*args, **kwargs)
self.sendModel.connect(self.print_model)
@Slot()
def print_model(self, model):
print(model.item(0).text())
return
@Slot(result=None)
def trigger_signal_send(self):
self.model = QStandardItemModel()
self.model.appendRow(QStandardItem('TEST ITEM'))
self.sendModel.emit(self.model)
return
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty('backend', backend)
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
I tried setting the "result" keyword in the @Slot decorator to the QStandardItemModel type and returning the model directly from that slot, but I get an error at the QML level: Unknown method return type: QStandardItemModel*. This was my rationale behind chaning the return value to None and to just emit that model as a signal. I have also tried changing the "setModel" function in QML to just reference the treeView.model attribute directly, but I get the original error.
I know that the model is created correctly, and is of the right type because if I create another Slot directly in the backend class that will print the text of the first item when "sendSignal" is emitted, everything works correctly.
Furthermore, when setting the first line of "onSendSignal" to log the input to that function, it says "undefined", so the issue lies in between when sendSignal.emit(model) gets called in the backend and when onSendSignal(model) is called in the Connections block of my .qml file.
I have spent a lot of time playing around with different solutions, but I cannot seem to figure out what is being lost in between these two function calls. Any help would be greatly appreciated!
What happens is that QML does not recognize the QStandardItemModel type, when a model is exported then it is better to use QObject as a signature:
# Signals
sendModel: Signal = Signal(QObject)
Unlike the QTreeView, in TreeView you have to set the roles that each column will consume:
TreeView {
id: treeView
model: mainWindow.myModel
anchors.fill: parent
TableViewColumn {
title: "Name"
role: "display"
width: 300
}
}