Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QAbstractTableModel is not updating (TableView model)
Forum Updated to NodeBB v4.3 + New Features

QAbstractTableModel is not updating (TableView model)

Scheduled Pinned Locked Moved Solved QML and Qt Quick
9 Posts 3 Posters 1.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    BD9a
    wrote on last edited by
    #1

    Hello. I have an TableModelResults class (QAbstractTableModel subclass). This class is used to create an Table Model to display Results History as an Table - I need an option to add new elements to that Table. This class is included in Manager class (it will be an manager to everything in this project).

    In Manager class there's an function (which can be called from QML) to add or remove, all or single element (this function is calling TableModelResults class function).

    Everything works fine except refreshing the TableView model in QML. Why?

    tablemodelresults.h

    class TableModelResults : public QAbstractTableModel
    {
        Q_OBJECT
    
    public:
        TableModelResults(QObject *parent = nullptr);
        int rowCount(const QModelIndex & = QModelIndex()) const override;
    
        int columnCount(const QModelIndex & = QModelIndex()) const override;
    
        QVariant data(const QModelIndex &index, int role) const override;
    
        QHash<int, QByteArray> roleNames() const override;
        void addResult(QString timestamp, QString name, QString price, QString whisper);
    private:
        QVector<QVector<QString>> table;
    };
    

    tablemodelresults.cpp (addResult is messy, I tried everything in this function lol)

    TableModelResults::TableModelResults(QObject *parent) : QAbstractTableModel(parent)
    {
    
        addResult("1", "2", "3", "4");
        addResult("1", "2", "3", "4");
        addResult("1", "2", "3", "4");
    }
    
    int TableModelResults::rowCount(const QModelIndex &) const
    {
        return table.size();
    }
    
    int TableModelResults::columnCount(const QModelIndex &) const
    {
        return 4;
    }
    
    QVariant TableModelResults::data(const QModelIndex &index, int role) const
    {
        switch (role) {
        case Qt::DisplayRole:
            return table.at(index.row()).at(index.column());
        default:
            break;
        }
    
        return QVariant();
    }
    
    QHash<int, QByteArray> TableModelResults::roleNames() const
    {
        return { {Qt::DisplayRole, "display"} };
    }
    
    void TableModelResults::addResult(QString timestamp, QString name, QString price, QString whisper)
    {
        emit layoutAboutToBeChanged();
    
        int row = rowCount(QModelIndex());
        beginInsertRows(QModelIndex(),row,row-1);
    
        insertRow(row);
        table.append({timestamp, name, price, whisper});
    
        setData(index(row, 0), QVariant::fromValue<QString>(timestamp), Qt::DisplayRole);
        setData(index(row, 1), QVariant::fromValue<QString>(name), Qt::DisplayRole);
        setData(index(row, 2), QVariant::fromValue<QString>(price), Qt::DisplayRole);
        setData(index(row, 3), QVariant::fromValue<QString>(whisper), Qt::DisplayRole);
    
        endInsertRows();
        emit layoutChanged();
        emit dataChanged(index(0, 0), index(columnCount(), rowCount()));
        int idx = table.size() - 1;
        emit dataChanged(index(idx, 0), index(idx, 0));
    
        qDebug() << table.size();
    }
    
    Christian EhrlicherC 1 Reply Last reply
    0
    • B BD9a

      @JonB Yeah, as I said it's messy (just little bit lol). I tried everything, but finally I guess I should use beginInsertRows(), insertRow()(how to use it in my case?), endInsertRows() but what about table.append() when data is actually grabbed from this vector?

      Also, I tried to do it with:

          beginRemoveRows(QModelIndex(), 0, 0);
      
          table.removeAt(0);
      
          endRemoveRows();
          emit dataChanged(index(0,0), index(0, 4)); //index 0 3 didnt worked too
      

      but nothing happened.

      JonBJ Online
      JonBJ Online
      JonB
      wrote on last edited by JonB
      #8

      @BD9a
      Well one thing at a time, you really need to read and understand what that subclassing subtopic is telling you, because at the moment you are all over the place not doing the right things. And I think you'll find when you do that will solve your "redraw" issues.

      For example, it tells you:

      Models that provide interfaces to resizable data structures can provide implementations of insertRows(), ...

      An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.

      You must provide overrides of insertRows() etc. in your subclass. That is what would call your table.append(). Then the outside world --- or some utility like your addResult() --- calls insertRows() or insertRow(). Then it will all work.

      As for your emit dataChanged(). That would belong only in your override of setData(). It is only for changing existing data. When you insert or delete rows you do not emit it; instead that job is performed by the beginInsertRows() etc. you have in your insertRows() implementation. Views notice dataChanged(), beginInsertRows() etc. and act accordingly to update.

      I will say one other thing. You seem to have a data model of rows of 4 string columns. I don't know what your objective/expertise is, but you could/might just use a QStandardItemModel and then you would not have to implement all this stuff. I don't know anything about QML or how this would interact with that, nor whether it's suitable for your case.

      B 1 Reply Last reply
      2
      • B BD9a

        Hello. I have an TableModelResults class (QAbstractTableModel subclass). This class is used to create an Table Model to display Results History as an Table - I need an option to add new elements to that Table. This class is included in Manager class (it will be an manager to everything in this project).

        In Manager class there's an function (which can be called from QML) to add or remove, all or single element (this function is calling TableModelResults class function).

        Everything works fine except refreshing the TableView model in QML. Why?

        tablemodelresults.h

        class TableModelResults : public QAbstractTableModel
        {
            Q_OBJECT
        
        public:
            TableModelResults(QObject *parent = nullptr);
            int rowCount(const QModelIndex & = QModelIndex()) const override;
        
            int columnCount(const QModelIndex & = QModelIndex()) const override;
        
            QVariant data(const QModelIndex &index, int role) const override;
        
            QHash<int, QByteArray> roleNames() const override;
            void addResult(QString timestamp, QString name, QString price, QString whisper);
        private:
            QVector<QVector<QString>> table;
        };
        

        tablemodelresults.cpp (addResult is messy, I tried everything in this function lol)

        TableModelResults::TableModelResults(QObject *parent) : QAbstractTableModel(parent)
        {
        
            addResult("1", "2", "3", "4");
            addResult("1", "2", "3", "4");
            addResult("1", "2", "3", "4");
        }
        
        int TableModelResults::rowCount(const QModelIndex &) const
        {
            return table.size();
        }
        
        int TableModelResults::columnCount(const QModelIndex &) const
        {
            return 4;
        }
        
        QVariant TableModelResults::data(const QModelIndex &index, int role) const
        {
            switch (role) {
            case Qt::DisplayRole:
                return table.at(index.row()).at(index.column());
            default:
                break;
            }
        
            return QVariant();
        }
        
        QHash<int, QByteArray> TableModelResults::roleNames() const
        {
            return { {Qt::DisplayRole, "display"} };
        }
        
        void TableModelResults::addResult(QString timestamp, QString name, QString price, QString whisper)
        {
            emit layoutAboutToBeChanged();
        
            int row = rowCount(QModelIndex());
            beginInsertRows(QModelIndex(),row,row-1);
        
            insertRow(row);
            table.append({timestamp, name, price, whisper});
        
            setData(index(row, 0), QVariant::fromValue<QString>(timestamp), Qt::DisplayRole);
            setData(index(row, 1), QVariant::fromValue<QString>(name), Qt::DisplayRole);
            setData(index(row, 2), QVariant::fromValue<QString>(price), Qt::DisplayRole);
            setData(index(row, 3), QVariant::fromValue<QString>(whisper), Qt::DisplayRole);
        
            endInsertRows();
            emit layoutChanged();
            emit dataChanged(index(0, 0), index(columnCount(), rowCount()));
            int idx = table.size() - 1;
            emit dataChanged(index(idx, 0), index(idx, 0));
        
            qDebug() << table.size();
        }
        
        Christian EhrlicherC Online
        Christian EhrlicherC Online
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #2

        @BD9a said in QAbstractTableModel is not updating (TableView model):

        beginInsertRows(QModelIndex(),row,row-1);

        your 'end' is wrong. See the documentation: https://doc.qt.io/qt-5/qabstractitemmodel.html#beginInsertRows

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        B 1 Reply Last reply
        1
        • Christian EhrlicherC Christian Ehrlicher

          @BD9a said in QAbstractTableModel is not updating (TableView model):

          beginInsertRows(QModelIndex(),row,row-1);

          your 'end' is wrong. See the documentation: https://doc.qt.io/qt-5/qabstractitemmodel.html#beginInsertRows

          B Offline
          B Offline
          BD9a
          wrote on last edited by
          #3

          @Christian-Ehrlicher So, if Im inserting row by row, the "end" should be row or row+1? I guess row+1.

          JonBJ 1 Reply Last reply
          0
          • B BD9a

            @Christian-Ehrlicher So, if Im inserting row by row, the "end" should be row or row+1? I guess row+1.

            JonBJ Online
            JonBJ Online
            JonB
            wrote on last edited by
            #4

            @BD9a
            In the link @Christian-Ehrlicher gave you it shows how to append. Hint: one row would be row, row.

            B 1 Reply Last reply
            0
            • JonBJ JonB

              @BD9a
              In the link @Christian-Ehrlicher gave you it shows how to append. Hint: one row would be row, row.

              B Offline
              B Offline
              BD9a
              wrote on last edited by
              #5

              @JonB Okay so this part is fixed (I guess):

                  beginInsertRows(QModelIndex(),row,row);
                  insertRow(row, QModelIndex());
                  table.append({timestamp, name, price, whisper});
                  endInsertRows();
              

              I will use insertRow function (cuz I will add rows / data one by one).
              Still doesnt work, GUI is not updating. Is this QModelIndex okay?
              Code after changes:

              void TableModelResults::addResult(QString timestamp, QString name, QString price, QString whisper)
              {
                  emit layoutAboutToBeChanged();
              
                  int row = rowCount(QModelIndex());
                  beginInsertRows(QModelIndex(),row,row);
                  table.append({timestamp, name, price, whisper});
                  insertRow(row, QModelIndex());
              
                  setData(index(row, 0), QVariant::fromValue<QString>(timestamp), Qt::DisplayRole);
                  setData(index(row, 1), QVariant::fromValue<QString>(name), Qt::DisplayRole);
                  setData(index(row, 2), QVariant::fromValue<QString>(price), Qt::DisplayRole);
                  setData(index(row, 3), QVariant::fromValue<QString>(whisper), Qt::DisplayRole);
              
                  endInsertRows();
                  emit layoutChanged();
                  emit dataChanged(index(0, 0), index(columnCount(), rowCount()));
                  int idx = table.size() - 1;
                  emit dataChanged(index(idx, 0), index(idx, 0));
              
                  qDebug() << table.size();
              }
              
              JonBJ 1 Reply Last reply
              0
              • B BD9a

                @JonB Okay so this part is fixed (I guess):

                    beginInsertRows(QModelIndex(),row,row);
                    insertRow(row, QModelIndex());
                    table.append({timestamp, name, price, whisper});
                    endInsertRows();
                

                I will use insertRow function (cuz I will add rows / data one by one).
                Still doesnt work, GUI is not updating. Is this QModelIndex okay?
                Code after changes:

                void TableModelResults::addResult(QString timestamp, QString name, QString price, QString whisper)
                {
                    emit layoutAboutToBeChanged();
                
                    int row = rowCount(QModelIndex());
                    beginInsertRows(QModelIndex(),row,row);
                    table.append({timestamp, name, price, whisper});
                    insertRow(row, QModelIndex());
                
                    setData(index(row, 0), QVariant::fromValue<QString>(timestamp), Qt::DisplayRole);
                    setData(index(row, 1), QVariant::fromValue<QString>(name), Qt::DisplayRole);
                    setData(index(row, 2), QVariant::fromValue<QString>(price), Qt::DisplayRole);
                    setData(index(row, 3), QVariant::fromValue<QString>(whisper), Qt::DisplayRole);
                
                    endInsertRows();
                    emit layoutChanged();
                    emit dataChanged(index(0, 0), index(columnCount(), rowCount()));
                    int idx = table.size() - 1;
                    emit dataChanged(index(idx, 0), index(idx, 0));
                
                    qDebug() << table.size();
                }
                
                JonBJ Online
                JonBJ Online
                JonB
                wrote on last edited by
                #6

                @BD9a
                I do not recognise what code model you are following. You have beginInsertRows(), insertRow() and your table.append(() all in the same function, not to mention liberal setData()s, layoutChanged() and dataChanged().

                You are subclassing QAbstractTableModel so I'd expect you to be following https://doc.qt.io/qt-5/qabstractitemmodel.html#subclassing.

                B 1 Reply Last reply
                1
                • JonBJ JonB

                  @BD9a
                  I do not recognise what code model you are following. You have beginInsertRows(), insertRow() and your table.append(() all in the same function, not to mention liberal setData()s, layoutChanged() and dataChanged().

                  You are subclassing QAbstractTableModel so I'd expect you to be following https://doc.qt.io/qt-5/qabstractitemmodel.html#subclassing.

                  B Offline
                  B Offline
                  BD9a
                  wrote on last edited by BD9a
                  #7

                  @JonB Yeah, as I said it's messy (just little bit lol). I tried everything, but finally I guess I should use beginInsertRows(), insertRow()(how to use it in my case?), endInsertRows() but what about table.append() when data is actually grabbed from this vector?

                  Also, I tried to do it with:

                      beginRemoveRows(QModelIndex(), 0, 0);
                  
                      table.removeAt(0);
                  
                      endRemoveRows();
                      emit dataChanged(index(0,0), index(0, 4)); //index 0 3 didnt worked too
                  

                  but nothing happened.

                  JonBJ 1 Reply Last reply
                  0
                  • B BD9a

                    @JonB Yeah, as I said it's messy (just little bit lol). I tried everything, but finally I guess I should use beginInsertRows(), insertRow()(how to use it in my case?), endInsertRows() but what about table.append() when data is actually grabbed from this vector?

                    Also, I tried to do it with:

                        beginRemoveRows(QModelIndex(), 0, 0);
                    
                        table.removeAt(0);
                    
                        endRemoveRows();
                        emit dataChanged(index(0,0), index(0, 4)); //index 0 3 didnt worked too
                    

                    but nothing happened.

                    JonBJ Online
                    JonBJ Online
                    JonB
                    wrote on last edited by JonB
                    #8

                    @BD9a
                    Well one thing at a time, you really need to read and understand what that subclassing subtopic is telling you, because at the moment you are all over the place not doing the right things. And I think you'll find when you do that will solve your "redraw" issues.

                    For example, it tells you:

                    Models that provide interfaces to resizable data structures can provide implementations of insertRows(), ...

                    An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.

                    You must provide overrides of insertRows() etc. in your subclass. That is what would call your table.append(). Then the outside world --- or some utility like your addResult() --- calls insertRows() or insertRow(). Then it will all work.

                    As for your emit dataChanged(). That would belong only in your override of setData(). It is only for changing existing data. When you insert or delete rows you do not emit it; instead that job is performed by the beginInsertRows() etc. you have in your insertRows() implementation. Views notice dataChanged(), beginInsertRows() etc. and act accordingly to update.

                    I will say one other thing. You seem to have a data model of rows of 4 string columns. I don't know what your objective/expertise is, but you could/might just use a QStandardItemModel and then you would not have to implement all this stuff. I don't know anything about QML or how this would interact with that, nor whether it's suitable for your case.

                    B 1 Reply Last reply
                    2
                    • JonBJ JonB

                      @BD9a
                      Well one thing at a time, you really need to read and understand what that subclassing subtopic is telling you, because at the moment you are all over the place not doing the right things. And I think you'll find when you do that will solve your "redraw" issues.

                      For example, it tells you:

                      Models that provide interfaces to resizable data structures can provide implementations of insertRows(), ...

                      An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.

                      You must provide overrides of insertRows() etc. in your subclass. That is what would call your table.append(). Then the outside world --- or some utility like your addResult() --- calls insertRows() or insertRow(). Then it will all work.

                      As for your emit dataChanged(). That would belong only in your override of setData(). It is only for changing existing data. When you insert or delete rows you do not emit it; instead that job is performed by the beginInsertRows() etc. you have in your insertRows() implementation. Views notice dataChanged(), beginInsertRows() etc. and act accordingly to update.

                      I will say one other thing. You seem to have a data model of rows of 4 string columns. I don't know what your objective/expertise is, but you could/might just use a QStandardItemModel and then you would not have to implement all this stuff. I don't know anything about QML or how this would interact with that, nor whether it's suitable for your case.

                      B Offline
                      B Offline
                      BD9a
                      wrote on last edited by
                      #9

                      @JonB Okay, I found an issue.
                      Finally I will use this QStandardItemModel.

                      The problem was that. I have Model class included to Manager class. Manager class was included into main.
                      In main I declared an Manager class and qmlRegisterType<TableModelResults>("com.results.table", 1, 0, "TableModel");. In QML as model I used TableModel, but to modify data I used an function from Manager class.

                      First time used qmlRegisterType, thank You for help.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved