Insert into ListView with Loader



  • Hi!
    I'm a newbie to QML and have encountered some strange behaviour (at least to me).

    I have a ListView with a Loader as a delegate. The Loader loads component using a switch statement based on a name role in the model (the model is a sub-class of QAbstractListModel). So far so good. I can insert new items at the end of the list and they show up using the correct component definition. I can also remove elements and everything works fine. However if I try to insert a element before the last element the component gets created using the wrong component definition, it becomes the same as the last element (except when inserting the first elements, which is very strange).

    Minimal example that always inserts before the last element. The rows that were inserted before app start work, but the rows inserted with the timer all become blue.
    test.qml

    import QtQuick 2.11
    import QtQuick.Window 2.2
    import QtQuick.Controls 2.2
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        ListView {
            model: provider.model
            anchors.fill: parent
            delegate: Loader {
                width: parent.width
                sourceComponent: switch(NameRole){
                    case 'red':
                        return red
                        break;
                    case 'blue':
                        return blue
                        break;
                    case 'green':
                        return green
                        break;
                }
            }
        }
        Component {
            id:red
            Rectangle {
                width: 100
                height: 100
                color: 'red'
            }
        }
        Component {
            id:blue
            Rectangle {
                width: 100
                height: 100
                color: 'blue'
            }
        }
        Component {
            id:green
            Rectangle {
                width: 100
                height: 100
                color: 'green'
            }
        }
    }
    

    test.py

    
    import sys
    
    from functools import partial
    
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtCore import Slot, QObject, Property, QAbstractListModel
    from PySide2 import QtCore
    from PySide2.QtQml import QQmlApplicationEngine
    
    
    class RuleModel(QAbstractListModel):
        NameRole = QtCore.Qt.UserRole + 1001
    
        def __init__(self, parent=None):
            super(RuleModel, self).__init__(parent)
            self._entries = []
    
        def rowCount(self, parent=QtCore.QModelIndex()):
            if parent.isValid(): return 0
            return len(self._entries)
    
        def data(self, index, role=QtCore.Qt.DisplayRole):
            if 0 <= index.row() < self.rowCount() and index.isValid():
                item = self._entries[index.row()]
                if role == RuleModel.NameRole:
                    return item['NameRole']
    
        def roleNames(self):
            roles = dict()
            roles[RuleModel.NameRole] = b'NameRole'
            return roles
    
        def appendRow(self, name):
            self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
            self._entries.insert(self.rowCount()-1, dict(NameRole=name))
            self.endInsertRows()
    
    
    class ModelProvider(QObject):
        def __init__(self, parent=None):
            super(ModelProvider, self).__init__(parent)
            self._model = RuleModel()
    
        @Property(QObject, constant=False)
        def model(self):
            return self._model
    
    
        @Slot()
        def append(self, name):
            self.model.appendRow('a')
    
    
    def insert_brg(model):
        model.appendRow('blue')
        model.appendRow('red')
        model.appendRow('green')
        model.appendRow('green')
    
    def green(model):
        model.appendRow('green')
    
    if __name__ == '__main__':
        app = QGuiApplication(sys.argv)
    
        provider = ModelProvider()
        engine = QQmlApplicationEngine()
    
        engine.rootContext().setContextProperty("provider", provider)
        engine.load('test.qml')
        if not engine.rootObjects():
            sys.exit(-1)
    
        insert_brg(provider.model)
        timer = QtCore.QTimer(interval=500)
        timer.timeout.connect(partial(green, provider.model))
        timer.start()
    
        sys.exit(app.exec_())
    


  • Ah, I found the problem. I was telling beginInserRows I was going to insert at the end and then not inserting at the end. Telling it the right place solved the problem.



  • Ah, I found the problem. I was telling beginInserRows I was going to insert at the end and then not inserting at the end. Telling it the right place solved the problem.