I'm trying to fill a QML list model from python and on a button press, I'd like to get the item I set previously.
I ran into type conversion problems, where I get QObject instead of a python dictionary.
Create a class which gets pushed to the QML root context. The pySignal is invoked by QML javascript code
import sys
import os
from PyQt5.QtCore import QObject, QUrl
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlComponent, QJSValue
from PyQt5.QtQml import QQmlEngine, QQmlComponent
class PythonContext(QObject):
pySignal = pyqtSignal(QJSValue)
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.pySignal.connect(self.onPySignal)
@pyqtSlot(QJSValue)
def onPySignal(self, param):
obj = param.toVariant()
print('onPySignal', obj)
print(dir(obj))
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlEngine()
pyContext = PythonContext()
ctxt = engine.rootContext()
ctxt.setContextProperty('py', pyContext)
component = QQmlComponent(engine)
component.loadUrl(QUrl('qml_signals.qml'))
obj = component.create()
obj.addListItem({'pk': 1, 'name': 'One'})
obj.addListItem({'pk': 2, 'name': 'Two'})
obj.addListItem({'pk': 3, 'name': 'Three'})
engine.quit.connect(app.quit)
sys.exit(app.exec_())
The QML file itself only contains an application window with a ComboBox and a button. A button press invokes the pySignal emission and should send the item currently selected by the ComboBox to the Python context.
QML:
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.12
import QtQuick.Dialogs 1.3
ApplicationWindow {
title: "PyQT5 QML Signals dictionary"
visible: true
width: 400
height: 400
function addListItem(param) {
lm.append(param)
if(lmSelector.count == 1) {
lmSelector.currentIndex = 0
}
}
ColumnLayout {
anchors.fill: parent
ComboBox {
id: lmSelector
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
textRole: "name"
model: ListModel {
id: lm
}
}
Button {
text: "button"
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
onClicked: {
var selectedObject = lm.get(lmSelector.currentIndex)
console.log("button clicked" + selectedObject.name)
/* this does not work */
py.pySignal(selectedObject)
/* but this does and gives the desired output */
py.pySignal({ name: selectedObject.name, pk: selectedObject.pk})
}
}
}
}
Output:
onPySignal <PyQt5.QtCore.QObject object at 0x03CA4710>
What I would expect is:
onPySignal {'name': 'One', 'pk': 1.0}