Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Proper way of resizing model in QTableView
QtWS25 Last Chance

Proper way of resizing model in QTableView

Scheduled Pinned Locked Moved Unsolved General and Desktop
qtableviewmvcmodel
5 Posts 3 Posters 2.1k 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.
  • J Offline
    J Offline
    JoseTomasTocino
    wrote on 19 Oct 2018, 09:59 last edited by
    #1

    I have a QTableView that I'm populating with a subclass of QAbstractTableModel. The model is updated via a slot that receives the information and emits dataChanged. However, that doesn't seem to be enough for the view to show the new rows. I've tried also emitting layoutChanged after dataChanged in my slot and it works, but I'm not sure if that's correct. I've read the docs and dunno whether I should use beginInsertRows instead.

    I want to know if I'm doing it correctly or there's a different way.

    Anyway, here's a minimal compilable example for you to try, you can just copy and paste it in a .cpp and compile it.

    #include <QApplication>
    #include <QAbstractTableModel>
    #include <QDateTime>
    #include <QVBoxLayout>
    #include <QTimer>
    #include <QDebug>
    
    class MyModel : public QAbstractTableModel
    {
    public:
        int rowCount(const QModelIndex &parent) const { return mRecords.count(); }
        int columnCount(const QModelIndex &parent) const { return 2; }
    
        QVariant data(const QModelIndex &index, int role) const
        {
            if (role != Qt::DisplayRole)
                return QVariant();
    
            Record r = mRecords.at(index.row());
    
            return index.column() == 0 ? r.date : r.value;
        }
    
        QVariant headerData(int section, Qt::Orientation orientation, int role) const
        {
            if (orientation != Qt::Horizontal)
                return QVariant();
    
            if (role != Qt::DisplayRole)
                return QVariant();
    
            if (section == 0)
                return QString("Date");
    
            else if (section == 1)
                return QString("Number");
    
            return QVariant();
        }
    
    public slots:
        void addValue()
        {
            Record r = {
                QDateTime::currentDateTime().toString(),
                QString::number(qrand() % 1000)
            };
    
            mRecords << r;
    
            qDebug() << "Added record" << r.date << r.value;
    
            emit dataChanged(index(mRecords.count() - 1, 0), index(mRecords.count() - 1, 1));
            emit layoutChanged();
        }
    
    
    private:
        struct Record {
            QString date;
            QString value;
        };
    
        QList<Record> mRecords;
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QWidget w;
    
        QTableView * table = new QTableView(&w);
        MyModel model;
        table->setModel(&model);
        QVBoxLayout * layout = new QVBoxLayout(&w);
        layout->addWidget(table);
    
        QTimer t;
        QObject::connect(&t, &QTimer::timeout, [&] { model.addValue(); });
        t.start(250);
    
        w.show();
    
        return a.exec();
    }
    
    
    J 1 Reply Last reply 19 Oct 2018, 10:16
    0
    • J JoseTomasTocino
      19 Oct 2018, 09:59

      I have a QTableView that I'm populating with a subclass of QAbstractTableModel. The model is updated via a slot that receives the information and emits dataChanged. However, that doesn't seem to be enough for the view to show the new rows. I've tried also emitting layoutChanged after dataChanged in my slot and it works, but I'm not sure if that's correct. I've read the docs and dunno whether I should use beginInsertRows instead.

      I want to know if I'm doing it correctly or there's a different way.

      Anyway, here's a minimal compilable example for you to try, you can just copy and paste it in a .cpp and compile it.

      #include <QApplication>
      #include <QAbstractTableModel>
      #include <QDateTime>
      #include <QVBoxLayout>
      #include <QTimer>
      #include <QDebug>
      
      class MyModel : public QAbstractTableModel
      {
      public:
          int rowCount(const QModelIndex &parent) const { return mRecords.count(); }
          int columnCount(const QModelIndex &parent) const { return 2; }
      
          QVariant data(const QModelIndex &index, int role) const
          {
              if (role != Qt::DisplayRole)
                  return QVariant();
      
              Record r = mRecords.at(index.row());
      
              return index.column() == 0 ? r.date : r.value;
          }
      
          QVariant headerData(int section, Qt::Orientation orientation, int role) const
          {
              if (orientation != Qt::Horizontal)
                  return QVariant();
      
              if (role != Qt::DisplayRole)
                  return QVariant();
      
              if (section == 0)
                  return QString("Date");
      
              else if (section == 1)
                  return QString("Number");
      
              return QVariant();
          }
      
      public slots:
          void addValue()
          {
              Record r = {
                  QDateTime::currentDateTime().toString(),
                  QString::number(qrand() % 1000)
              };
      
              mRecords << r;
      
              qDebug() << "Added record" << r.date << r.value;
      
              emit dataChanged(index(mRecords.count() - 1, 0), index(mRecords.count() - 1, 1));
              emit layoutChanged();
          }
      
      
      private:
          struct Record {
              QString date;
              QString value;
          };
      
          QList<Record> mRecords;
      };
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
      
          QWidget w;
      
          QTableView * table = new QTableView(&w);
          MyModel model;
          table->setModel(&model);
          QVBoxLayout * layout = new QVBoxLayout(&w);
          layout->addWidget(table);
      
          QTimer t;
          QObject::connect(&t, &QTimer::timeout, [&] { model.addValue(); });
          t.start(250);
      
          w.show();
      
          return a.exec();
      }
      
      
      J Offline
      J Offline
      JonB
      wrote on 19 Oct 2018, 10:16 last edited by
      #2

      @JoseTomasTocino
      I don't know about your code. But dataChanged signal must only be emitted for existing rows whose content is changed. You must emit begin/endInsert/DeleteRows instead for row inserts/deletes to make it work correctly.

      1 Reply Last reply
      4
      • J Offline
        J Offline
        JoseTomasTocino
        wrote on 22 Oct 2018, 11:18 last edited by
        #3

        I already stated in my post that I think I should use beginInsertRows / endInsertRows, I was asking for the proper way of doing it.

        1 Reply Last reply
        0
        • V Offline
          V Offline
          VRonin
          wrote on 22 Oct 2018, 11:43 last edited by VRonin
          #4

          http://doc.qt.io/qt-5/qabstractitemmodel.html#subclassing

          Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model's dimensions both before and after they occur:

          • An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and endInsertRows() immediately afterwards.
          void addValue()
              {
                  Record r = {
                      QDateTime::currentDateTime().toString(),
                      QString::number(qrand() % 1000)
                  };
          beginInsertRows(QModelIndex(),mRecords.size(),mRecords.size());
                  mRecords << r;
          endInsertRows();
                  qDebug() << "Added record" << r.date << r.value;
              }
          

          P.S.
          Make your model go through the model test there's a couple of points that should get triggered by that test in your model

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          J 1 Reply Last reply 22 Oct 2018, 12:07
          2
          • V VRonin
            22 Oct 2018, 11:43

            http://doc.qt.io/qt-5/qabstractitemmodel.html#subclassing

            Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model's dimensions both before and after they occur:

            • An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and endInsertRows() immediately afterwards.
            void addValue()
                {
                    Record r = {
                        QDateTime::currentDateTime().toString(),
                        QString::number(qrand() % 1000)
                    };
            beginInsertRows(QModelIndex(),mRecords.size(),mRecords.size());
                    mRecords << r;
            endInsertRows();
                    qDebug() << "Added record" << r.date << r.value;
                }
            

            P.S.
            Make your model go through the model test there's a couple of points that should get triggered by that test in your model

            J Offline
            J Offline
            JoseTomasTocino
            wrote on 22 Oct 2018, 12:07 last edited by
            #5

            @VRonin That's really useful, thank you very much. Also I didn't know about the "Model test", that's nice too.

            1 Reply Last reply
            0

            5/5

            22 Oct 2018, 12:07

            • Login

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