Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Problem with model, or Instantiator



  • I have a QAbstractListModel with some methods that I think are correct:

    void JsonObjectStore::insert(int ind, QJSValue jsondata)
    {
        if(ind < 0 || ind > rowCount())
            return;
    
        auto pjsondata = prepareData(jsondata);
        if(!pjsondata)
            return;
    
        QAbstractListModel::beginInsertRows(index(ind,0), ind, ind);
        m_jsonObjectList.insert(ind, pjsondata);
        QAbstractListModel::endInsertRows();
    
        emit countChanged();
    }
    
    void JsonObjectStore::remove(int ind, int n)
    {
        if(ind < 0 || (ind + n) > rowCount()){
            qDebug() << "JsonObjectStore::remove index+count too big:" << ind << n << rowCount();
            return;
        }
    
        QAbstractListModel::beginRemoveRows(index(ind,0), ind, ind+(n-1));
        for(int count=ind; count < ind+n && count < m_jsonObjectList.size(); ++count)
            delete m_jsonObjectList[count];
        m_jsonObjectList.remove(ind, n); // does this return an error/crash if extends past list end?
        QAbstractListModel::endRemoveRows();
    
        emit countChanged();
    }
    

    Insert seems to trigger the down stream Instantiator just fine, but I am not seeing the Instantiator call onObjectRemoved. I originally assumed it was the remove method at fault, but I cannot tell what is wrong with it.

                Instantiator {
                    id: editor_repeater
    
                    active: true
                    model: loaded_objects
    
                    property var grid_object: edit_grid_board
                    property int currentIndex: -1
    
                    delegate: EditorObject {
                        id: etdelegate
    
                        params: datarole
                        inst_index: index
    
                        width: editor_repeater.grid_object.cell_width * gw
                        height: editor_repeater.grid_object.cell_height * gh
    
                        anchors.left: parent.left
                        anchors.top: parent.top
                        clip: true
    
                        selected: editor_repeater.currentIndex === index
    
                        onDroppedObject: {
                            if(dropobj.drop.keys.includes("pathkey")){
                                loaded_objects.setProperty(index, inst_obj.dropProperties.path, dropobj.drop.source.dropdata.path.toString())
                                loaded_objects.dirty = true
                            }
                        }
    
                        MouseArea {
                            id: etdelegate_mousearea
    
                            anchors.fill: parent
                            acceptedButtons: Qt.LeftButton | Qt.RightButton
    
                            onClicked: {
                                //console.log("clicked")
                                if(mouse.button === Qt.LeftButton){
                                    editor_repeater.currentIndex = index
                                }else if(mouse.button === Qt.RightButton){
                                    etdelegate_contextmenu.popup()
                                }
                            }
                        }
    
                        Menu {
                            id: etdelegate_contextmenu
    
                            MenuItem {
                                text: "Delete"
                                onTriggered: {
                                    editor_repeater.model.remove(index)
                                }
                            }
                        }
                    }
    
                    onObjectAdded: {
                        var pos_object = grid_object.getDelegateInsertObject(object.gx, object.gy)
                        pos_object.data.push(object)
                    }
                    onObjectRemoved: {
                        console.log(object)
                    }
                }
    

    There is a bunch of junk in Instantiator, but you get the idea. The object does not seem to dissapear like I thought it would. When I right click to call remove again I get the warning about it being out of bounds. So it is not in the QVector m_jsonObjectList anymore. I don't understand why it is not seeing the beginRemoveRows and endRemoveRows.



  • Here is a better example:

    #ifndef CUSTOMMODEL_H
    #define CUSTOMMODEL_H
    
    #include <QObject>
    #include <QAbstractListModel>
    
    class BasicModel : public QAbstractListModel
    {
        Q_OBJECT
        Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
    
        int m_count;
        QVector<int> m_list;
    
    public:
        BasicModel(QObject* parent=nullptr)
            : QAbstractListModel(parent)
        {
        }
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const{
            return m_list.count();
        }
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const{
            if(index.row() < 0 || index.row() >= rowCount())
                return QVariant();
    
            if(role != Qt::DisplayRole)
                return QVariant();
    
            return m_list[index.row()];
        }
    signals:
        void countChanged();
    
    public slots:
        void insert(int ind, int data){
            if(ind < 0 || ind > rowCount())
                return;
    
            QAbstractListModel::beginInsertRows(index(ind,0), ind, ind);
            m_list.insert(ind, data);
            QAbstractListModel::endInsertRows();
    
            emit countChanged();
        }
        void remove(int ind){
            if(ind < 0 || ind >= rowCount())
                return;
    
            QAbstractListModel::beginRemoveRows(index(ind,0), ind, ind);
            m_list.remove(ind);
            QAbstractListModel::endRemoveRows();
    
            emit countChanged();
        }
    };
    
    #endif // CUSTOMMODEL_H
    
    

    main.cpp:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    
    #include "custommodel.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<BasicModel>("BasicModels",1,0,"BasicModel");
    
        QQmlApplicationEngine engine;
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    

    main.qml:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQml.Models 2.15
    
    import BasicModels 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Test Instantiator Custom Model")
    
        BasicModel {
            id: basicmodel
        }
    
        ListModel {
            id: objectmodel
        }
    
        Instantiator {
            id: instantiator
    
            model: basicmodel
    
            delegate: Text {
                text: display
            }
    
            onObjectAdded: {
                console.log("onObjectAdded", object.text)
            }
            onObjectRemoved: {
                console.log("onObjectRemoved", object.text)
            }
    
            Component.onCompleted: {
                instantiator.model.insert(instantiator.model.count, 1)
                instantiator.model.insert(instantiator.model.count, 2)
                console.log(instantiator.model.count)
    
                Qt.callLater(removeItems)
            }
    
            function removeItems(){
                while(instantiator.model.count){
                    instantiator.model.remove(0)
                }
                console.log(instantiator.model.count)
            }
        }
    }
    

    It calls the onObjectRemoved on the ListModel, but not on the BasicModel. I thought ListModel was based on QAbstractListModel, so I don't know what the issue is.



  • This better illustrates the issue:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQml.Models 2.15
    
    import BasicModels 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Test Instantiator Custom Model")
    
        BasicModel {
            id: basicmodel
        }
    
        ListModel {
            id: objectmodel
        }
    
        Instantiator {
            id: instantiator
    
            model: basicmodel
    
            delegate: Text {
                text: display
            }
    
            onObjectAdded: {
                console.log("onObjectAdded", object.text)
            }
            onObjectRemoved: {
                console.log("onObjectRemoved", object.text)
            }
    
            Component.onCompleted: {
                console.log("instantiator")
                instantiator.model.insert(instantiator.model.count, 1)
                instantiator.model.insert(instantiator.model.count, 2)
                console.log(instantiator.model.count)
    
                Qt.callLater(removeItems)
            }
    
            function removeItems(){
                console.log("instantiator")
                while(instantiator.model.count){
                    instantiator.model.remove(0)
                }
                console.log(instantiator.model.count)
            }
        }
    
        Instantiator {
            id: instantiator2
    
            model: objectmodel
    
            delegate: Text {
                text: display
            }
    
            onObjectAdded: {
                console.log("onObjectAdded", object.text)
            }
            onObjectRemoved: {
                console.log("onObjectRemoved", object.text)
            }
    
            Component.onCompleted: {
                console.log("instantiator2")
                instantiator2.model.insert(instantiator.model.count, {display:1})
                instantiator2.model.insert(instantiator.model.count, {display:2})
                console.log(instantiator2.model.count)
    
                Qt.callLater(removeItems)
            }
    
            function removeItems(){
                console.log("instantiator2")
                while(instantiator2.model.count){
                    instantiator2.model.remove(0)
                }
                console.log(instantiator2.model.count)
            }
        }
    }
    
    


  • Okay, stupid mistake:

    void insert(int ind, int data){
            if(ind < 0 || ind > rowCount())
                return;
    
            QAbstractListModel::beginInsertRows(QModelIndex(), ind, ind);
            m_list.insert(ind, data);
            QAbstractListModel::endInsertRows();
    
            emit countChanged();
        }
        void remove(int ind){
            if(ind < 0 || ind >= rowCount())
                return;
    
            QAbstractListModel::beginRemoveRows(QModelIndex(), ind, ind);
            m_list.remove(ind);
            QAbstractListModel::endRemoveRows();
    
            emit countChanged();
        }
    

Log in to reply