My qml doesn't find my model from python



  • Hello here is a simple test to fill a ListView from ma python main file, I have the following error:
    file:/// ..... /main.qml:26: ReferenceError: elements is not defined

    here is the qml file

    import QtQuick 2.9
    import QtQuick.Window 2.2
    import QtQuick.Controls 1.4
    import PyModels 1.0
    
    Window {
        id: window
        visible: true
        width: 640
        height: 460
        title: qsTr("Test")
        
        Button {
            id: btn
            text: qsTr("fill model")
            onClicked: {
                py_mainapp.fillModel()
            }
        }
    
        ListView {
                anchors.top: parent.top
                anchors.left: btn.right
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                model: elements.items
                delegate: Item {
                    anchors.left: parent.left
                    anchors.right: parent.right
                    height: 24
                    Row {
                        anchors.fill: parent
                        Label {
                            text: model.name
                        }
                        Label {
                            text: model.title
                    }
                }
            }
        }     
        
    }
    

    Here is the py file

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import sys
    import os
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtQml import QQmlApplicationEngine, QQmlListProperty, qmlRegisterType
    from PyQt5.QtCore import pyqtSlot, QObject, QVariant,pyqtProperty
    
    
    class ListElementPy(QObject):
    
        def __init__(self, name, title):
            super(ListElementPy, self).__init__()
            self._title = title
            self._name = name
            
        @pyqtProperty('QString')
        def title(self):
            return self._title
    
        @title.setter
        def title(self, avalue):
            if avalue != self._title:
                self._title = avalue
                    
        @pyqtProperty('QString')
        def name(self):
            return self._name
            
        @name.setter
        def name(self, avalue):
            if avalue != self._name:
                self._name = avalue
                    
    
    class ElementsPy(QObject):
        
        def __init__(self):
            super(ElementsPy, self).__init__()
            self._items = []
            
        @pyqtProperty('QString')
        def items(self):
            return QQmlListProperty(ListElementPy, self, self._items)
           
        def append(self, avalue):
            self._items.append(avalue)
                    
    
    class MainApp(QObject):
    
        def __init__(self, context, parent):
            super(MainApp, self).__init__(parent)
            self.win = parent
            self.ctx = context
            self.elements = ElementsPy()
            
    
        @pyqtSlot()
        def fillModel(self):
            for x in range (1,50):
                self.elements.append(ListElementPy("name %.0f" % (x), "title %.0f" % (x)))
                
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        qmlRegisterType(ListElementPy, "PyModels", 1, 0, "ListElementPy")
        qmlRegisterType(ElementsPy, "PyModels", 1, 0, "ElementsPy")
        engine = QQmlApplicationEngine()
        ctx = engine.rootContext()
        engine.load("main.qml")
        win = engine.rootObjects()[0]
        py_mainapp = MainApp(ctx, win)
        ctx.setContextProperty("py_mainapp", py_mainapp)
        ctx.setContextProperty("elements", py_mainapp.elements)
        win.show()
        sys.exit(app.exec_())
    
    

    What am I doing wrong?
    py_mainapp is found in the qml, but not elements



  • I added the following lines in the qml (which seems to me quite strange as I already defined elements in the python file)

    ElementsPy {
        id: elements
    }
    ListElementPy {
        property string name
        property string title
    }
    

    Now I have the following error:
    TypeError: init() takes 1 positional argument but 2 were given



  • ok, I've found on internet (https://riverbankcomputing.com/pipermail/pyqt/2017-January/038497.html) a solution working, so I'm gonna start with modifying the following code which works:
    qml:

    import QtQuick 2.9
    import QtQuick.Window 2.2
    import QtQuick.Controls 1.4
    
    import Example 1.0
    
    Window {
        width: 500
        height: 500
        visible: true
    
        ListView {
            anchors.fill: parent
            model: store.channels
            delegate: Item {
                anchors.left: parent.left
                anchors.right: parent.right
                height: 24
                Row {
                    anchors.fill: parent
                    Label {
                        text: name
                    }
                    Label {
                        text: title
                    }
                }
            }
        }
    }
    

    python:

    from sys import argv
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
    from PyQt5.QtQml import QQmlListProperty, QQmlApplicationEngine, qmlRegisterType
                    
    class Channel(QObject):
    
        nameChanged = pyqtSignal()
        titleChanged = pyqtSignal()
    
        def __init__(self, name='', title='', *args, **kwargs):
            super().__init__(*args, **kwargs)
            self._name = name
            self._title = title
    
        @pyqtProperty('QString', notify=nameChanged)
        def name(self):
            return self._name
    
        @name.setter
        def name(self, name):
            if name != self._name:
                self._name = name
                self.nameChanged.emit()
                
        @pyqtProperty('QString', notify=nameChanged)
        def title(self):
            return self._title
    
        @title.setter
        def title(self, title):
            if title != self._title:
                self._title = title
                self.titleChanged.emit()
    
    class Store(QObject):
    
        channelsChanged = pyqtSignal()
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self._channels = [
                Channel('aa', 'AAA'),
                Channel('bb', 'BBB')
            ]
    
        @pyqtProperty(QQmlListProperty, notify=channelsChanged)
        def channels(self):
            return QQmlListProperty(Channel, self, self._channels)
    
        @channels.setter
        def channels(self, channels):
            if channels != self._channels:
                self._channels = channels
                self.channelsChanged.emit()
    
        def appendChannel(self, channel):
            self._channels.append(channel)
            self.channelsChanged.emit()
    
    
    def main():
        app = QApplication(argv)
    
        qmlRegisterType(Channel, 'Example', 1, 0, 'Channel')
        qmlRegisterType(Store, 'Example', 1, 0, 'Store')
    
        store = Store()
    
        engine = QQmlApplicationEngine()
        engine.rootContext().setContextProperty('store', store)
        engine.load('main.qml')
    
        # After 3 seconds, we append a new Channel
        QTimer.singleShot(3000, lambda: store.appendChannel(Channel('cc', 'CCC')))
    
        exit(app.exec_())
    
    
    if __name__ == '__main__':
        main()
    


  • The problem is in your delegate. The syntax is role.property not modelName.property. See this wiki post (the code is C++ but there's no real difference in python)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.