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

QAbstractItemModel beginInsertRow, reenumerate other rows?



  • Hi
    I have question:
    I'm adding data to QAbstractItemModel that was previously set. I have

    struct DevPlaceDataStruct {
        int         row;
        int         dev_id;
        QString     text;
    };
    

    and of course

    private:
        QMap<int,DevPlaceDataStruct*> modelMap;
    

    Model is indexed in modelMap by (int)row. So if I add a new row, I need to reenumerate the rest of rows, right?
    beginInsertRows only notifies the model about the change?
    So in consequence I need to change modelMap from the row position I'm inserting to the end ?

    best,
    Marek


  • Qt Champions 2019

    Why not simply use a QVector<DevPlaceDataStruct*> instead? Then use the index as row and all works without additional stuff.



  • @Christian-Ehrlicher actually... I haven't thought about this earlier, because every subclass of QAbstractListModel I have seen used QMap not QVector but yes it should work.
    Thanks,
    Marek


  • Moderators

    @Marek can you marked this as solved if @Christian-Ehrlicher's answer solves the issue for you? Thanks.



  • @AndyS Yes I will, it looks like it is sound sollution, I just couldn't find time to test it ;) I will try it ASAP.



  • Hi
    Actually I have problem with both methods QVector and QMap it gives me the same result.
    Problem is when I remove row=0 and then I add item which should go into row=0.
    Result is that I have in view: Item1, Item2, black space, item3.
    When I add row in the middle or at the end it is OK.
    When I'm checking debug it looks like it should work, row is correct, model count is correct

    DevPlaceModel::removeRow dev_id: 74  row: 0  count: 2
    DevPlaceModel::addRow dev_id: 74  place_number: 1  row: 0  count: 3
    

    I have attached cpp, h and qml file.
    Cpp and h has commented out version with QMap, so it works with QVector, but both methods have a problem with row=0.
    At the beginning I'm settings model from another class by calling
    beginSetModel();
    addPlace(int dev_id,int place_number);
    endSetModel();

    When model is set I'm using
    addRow(int dev_id,int place_number);
    removeRow(int dev_id);

    Can someone shed some light?

    Best,
    Marek

    #include <QtCore>
    #include <QAbstractListModel>
    #include <QtXml/QDomNode>
    #include <QtXml/QDomElement>
    #include <QGeoCoordinate>
    #include <QGeoRectangle>
    #include "helper.h"
    
    
    struct DevPlaceDataStruct {
        int         row;
        int         dev_id;
        int         place_number;
        QString     text;
    };
    
    class DevPlaceModel : public QAbstractListModel
    {
        Q_OBJECT
    public:
        enum ItemRoles {
            IdRole = Qt::UserRole + 1,
            TextRole = Qt::UserRole + 2,
            RowRole = Qt::UserRole + 3,
        };
    
        DevPlaceModel(QObject *parent);
    
        virtual int rowCount(const QModelIndex&) const { return vector.count(); }
      //  virtual int rowCount(const QModelIndex&) const { return modelMap.count(); }
        virtual QVariant data(const QModelIndex &index, int role) const;
    
        QHash<int, QByteArray> roleNames() const;
        Q_INVOKABLE int getRow(int dev_id);
        Q_INVOKABLE int getDevId(int row);
    public slots:
        void beginSetModel();
        void addPlace(int dev_id,int place_number);
        void addRow(int dev_id,int place_number);
        void removeRow(int dev_id);
        void endSetModel();
    signals:
    
    private slots:
        QVariant getRoleData(int role,int row) const;
    private:
        QVector<DevPlaceDataStruct*> vector;
      //  QMap<int,DevPlaceDataStruct*> modelMap;
        QHash<int, QByteArray> roles;
        int     curr_dev_id;
    };
    
    #include "dev-place-model.h"
    
    DevPlaceModel::DevPlaceModel(QObject *parent):QAbstractListModel(parent) {
    
        roles[IdRole] = "id_role";
        roles[TextRole]= "text_role";
        roles[RowRole]= "row_role";
    
        curr_dev_id=0;
    }
    QHash<int, QByteArray> DevPlaceModel::roleNames() const {
        return roles;
    }
    QVariant DevPlaceModel::data(const QModelIndex &index, int role) const {
        if(!index.isValid()) {
            return QVariant();
        }
        if(roles.contains(role) && vector.count()-1>=index.row()) {
            return getRoleData(role,index.row());
        }
    //    if(roles.contains(role) && modelMap.contains(index.row())) {
    //        return getRoleData(role,index.row());
    //    }
        return QVariant();
    }
    QVariant DevPlaceModel::getRoleData(int role, int row) const {
        DevPlaceDataStruct *p=vector.at(row);
    //    DevPlaceDataStruct *p=modelMap[row];
        switch(role) {
        case IdRole:
            return QVariant(p->dev_id);
        case TextRole:
            return QVariant(p->text);
        case RowRole:
    //        return QVariant(p->row);
            return QVariant(row);
        default:
            qWarning()<<"DevPlaceModel::getRoleData there is no role:"<<role;
        }
    }
    void DevPlaceModel::beginSetModel() {
        beginResetModel();
        for(int i=0;i<vector.count();i++) {
            delete vector.at(i);
        }
        vector.clear();
    
    //    beginResetModel();
    //    if(modelMap.count()) {
    //        QMapIterator<int,DevPlaceDataStruct*> i(modelMap);
    //        while(i.hasNext()) {
    //            i.next();
    //            delete i.value();
    //        }
    //        modelMap.clear();
    //    }
    }
    void DevPlaceModel::addPlace(int dev_id, int place_number) {
        qDebug()<<"DevPlaceModel::addPlace dev_id:"<<dev_id<<" place_number:"<<place_number;
        DevPlaceDataStruct *p=new DevPlaceDataStruct;
        p->dev_id=dev_id;
        p->text=QString("Miejsce: %1").arg(place_number);
        p->place_number=place_number;
        vector.append(p);
    //    p->row=modelMap.count();
    //    modelMap.insert(p->row,p);
    }
    void DevPlaceModel::endSetModel() {
        emit endResetModel();
    }
    void DevPlaceModel::addRow(int dev_id, int place_number) {
        int row=-1;
        for(int i=0;i<vector.count();i++) {
            DevPlaceDataStruct *d=vector.at(i);
            if(d->place_number>=place_number) {
                row=((i-1)<0 ? 0 : i-1);
                break;
            }
        }
        if(row==-1) {
            row=vector.count();
        }
        DevPlaceDataStruct *em=new DevPlaceDataStruct;
        em->dev_id=dev_id;
        em->text=QString("Miejsce: %1").arg(place_number);
        em->place_number=place_number;
        emit beginInsertRows(QModelIndex(),row,row);
        vector.insert(row,em);
        emit endInsertRows();
        qDebug()<<"DevPlaceModel::addRow dev_id:"<<dev_id<<" place_number:"<<place_number<<" row:"<<row<<" count:"<<vector.count();
    
    //    int row=-1;
    //    bool found=false;
    //    int new_idx=0;
    //    QMap<int,DevPlaceDataStruct*> new_modelMap;
    //    QMapIterator<int,DevPlaceDataStruct*> i(modelMap);
    //    while(i.hasNext()) {
    //        i.next();
    //        if(!found && i.value()->place_number>=place_number) {
    //            row=((i.value()->row-1)<0 ? 0 : i.value()->row-1);
    //            found=true;
    //            new_idx++;
    //        }
    //        i.value()->row==new_idx;
    //        new_modelMap.insert(new_idx,i.value());
    //        new_idx++;
    
    //    }
    //    if(row==-1) {
    //        row=modelMap.count();
    //    }
    //    DevPlaceDataStruct *em=new DevPlaceDataStruct;
    //    em->dev_id=dev_id;
    //    em->text=QString("Miejsce: %1").arg(place_number);
    //    em->place_number=place_number;
    //    em->row=row;
    //    emit beginInsertRows(QModelIndex(),row,row);
    //    new_modelMap.insert(em->row,em);
    //    modelMap.clear();
    //    modelMap=new_modelMap;
    //    emit endInsertRows();
    //    qDebug()<<"DevPlaceModel::addRow dev_id:"<<dev_id<<" place_number:"<<place_number<<" row:"<<row<<" count:"<<modelMap.count();
    
    }
    void DevPlaceModel::removeRow(int dev_id) {
        int row=-1;
        for(int i=0;i<vector.count();i++) {
            DevPlaceDataStruct *d=vector.at(i);
            if(d->dev_id==dev_id) {
                row=i;
                break;
            }
        }
        if(row>=0) {
            beginRemoveRows(QModelIndex(),row,row);
            delete vector.at(row);
            vector.remove(row);
            endRemoveRows();
        }
        qDebug()<<"DevPlaceModel::removeRow dev_id:"<<dev_id<<" row:"<<row<<" count:"<<vector.count();
    
    //    int row=-1;
    //    int new_idx=0;
    //    QMap<int,DevPlaceDataStruct*> new_modelMap;
    //    QMapIterator<int,DevPlaceDataStruct*> i(modelMap);
    //    while(i.hasNext()) {
    //        i.next();
    //        if(i.value()->dev_id==dev_id) {
    //            row=i.key();
    //        }
    //        else {
    //            i.value()->row=new_idx;
    //            new_modelMap.insert(new_idx,i.value());
    //            new_idx++;
    //        }
    //    }
    //    if(row<0) {
    //        qWarning()<<"DevPlaceModel::removeRow no row:"<<row;
    //        return;
    //    }
    //    beginRemoveRows(QModelIndex(),row,row);
    //    delete modelMap[row];
    //    modelMap.clear();
    //    modelMap=new_modelMap;
    //    endRemoveRows();
    //    qDebug()<<"DevPlaceModel::removeRow dev_id:"<<dev_id<<" row:"<<row<<" count:"<<modelMap.count();
    }
    int DevPlaceModel::getRow(int dev_id) {
        for(int i=0;i<vector.count();i++) {
            DevPlaceDataStruct *d=vector.at(i);
            if(d->dev_id==dev_id)
                return i;
        }
        return -1;
    
    //    QMapIterator<int,DevPlaceDataStruct*> i(modelMap);
    //    while(i.hasNext()) {
    //        i.next();
    //        if(i.value()->dev_id==dev_id)
    //            return i.value()->row;
    //    }
    //    return 0;
    }
    int DevPlaceModel::getDevId(int row) {
        if(vector.count()-1>=row) {
            qWarning()<<"DevPlaceModel::getDevId there is no row:"<<row;
            return 0;
        }
        return vector.at(row)->dev_id;
    
    //    if(!modelMap.contains(row)) {
    //        qWarning()<<"DevPlaceModel::getDevId there is no row:"<<row;
    //        return 0;
    //    }
    //    return modelMap[row]->dev_id;
    }
    
    QControls2.ComboBox {
                id:placeCombo
                anchors.top:addressText.bottom
                anchors.horizontalCenter: parent.horizontalCenter
                width:parent.width*0.6
                height:parent.height*0.15
                model: DevPlaceModel
                textRole:"text_role"
               // currentIndex:DevPlaceModel.getRow(root.dev_id)
            }
    


  • @Marek
    Two observations:

    1. It does not actually matter, but why does your beginSetModel() call beginResetModel(); directly while endSetModel() does an emit endResetModel();?

    2. Your addRow()'s row=((i-1)<0 ? 0 : i-1); returns 0 for all 3 row == -1 and row == 0 and row == 1. Is that related to your problem?



  • @JonB

    1. My mistake shold call endResetModel
    2. This is correct I think, It is in loop from i=0 to vector.count. So when I have items (2,3) in vector and I'm adding item 1, it will go to row=0
      When I have items 1,3 and I'm adding item 2 it will go to row=1
      Right?


  • @Marek
    I don't know, I don't claim to follow your code. I'm just "surprised" that an insert where row == 0 and row == 1 both map to row 0.



  • @JonB IMHO model is OK, when I remove and then add item 1 (I have 1,2,3) from debug I have:

    DevPlaceModel::removeRow dev_id: 74  row: 0  count: 2
    DevPlaceModel::addRow dev_id: 74  place_number: 1  row: 0  count: 3  vector: QVector(0x5636891742c0, 0x56368c2e0a90, 0x56368c23d700)
    

    So view should have 3 items, instead there is like in image (0_1536919537683_test.png



  • Hi
    Sorry for late reply. I don't have this problem in view like ListView, maybe it is some bug in QControls Combo, I need to prepare sample and report.
    Definitelly using QVector instead of QMap is more convinient in model,
    thanks for the hint.
    Best,
    Marek


Log in to reply