Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. I'm still struggling with model/view
Qt 6.11 is out! See what's new in the release blog

I'm still struggling with model/view

Scheduled Pinned Locked Moved Solved General and Desktop
27 Posts 3 Posters 10.5k Views 2 Watching
  • 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.
  • VRoninV Offline
    VRoninV Offline
    VRonin
    wrote on last edited by
    #13

    Subclassing models is a minefield. That's why I suggested using QStandardItemModel instead. The idea was that you can store

    struct DeviceDetails
    {
        QString macAddr;
        QString devName;
        QString latestTimestamp;
    };
    

    as:

    enum DeviceRoles{
    macAddr = Qt::UserRole
    , devName
    , latestTimestamp
    };
    auto item = new QStandardItem;
    item->setData(/*...*/,DeviceRoles::macAddr);
    item->setData(/*...*/,DeviceRoles::devName);
    item->setData(/*...*/,DeviceRoles::latestTimestamp);
    myModel.appendRow(item);
    

    The model subclass is probably the most correct way but before embarking into it, please read the chapter of Advanced Qt programming I linked in the previous thread and run your custom model through the Model Test

    P.S.
    Allocating QStandardItem on the stack is problematic. QStandardItemModel will take ownership of the item and try to delete it. That means that if you declare the item before the model

    QStandardItem myItem;
    QStandardItemModel myModel;
    

    You'll get a crash as well as if you try to remove that row you'll get a crash

    "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

    mzimmersM 1 Reply Last reply
    4
    • VRoninV VRonin

      Subclassing models is a minefield. That's why I suggested using QStandardItemModel instead. The idea was that you can store

      struct DeviceDetails
      {
          QString macAddr;
          QString devName;
          QString latestTimestamp;
      };
      

      as:

      enum DeviceRoles{
      macAddr = Qt::UserRole
      , devName
      , latestTimestamp
      };
      auto item = new QStandardItem;
      item->setData(/*...*/,DeviceRoles::macAddr);
      item->setData(/*...*/,DeviceRoles::devName);
      item->setData(/*...*/,DeviceRoles::latestTimestamp);
      myModel.appendRow(item);
      

      The model subclass is probably the most correct way but before embarking into it, please read the chapter of Advanced Qt programming I linked in the previous thread and run your custom model through the Model Test

      P.S.
      Allocating QStandardItem on the stack is problematic. QStandardItemModel will take ownership of the item and try to delete it. That means that if you declare the item before the model

      QStandardItem myItem;
      QStandardItemModel myModel;
      

      You'll get a crash as well as if you try to remove that row you'll get a crash

      mzimmersM Offline
      mzimmersM Offline
      mzimmers
      wrote on last edited by
      #14

      @VRonin OK, that makes a lot more sense. So, is the missing field in your setData() examples a pointer to my structure element? Is that how the model keeps track of changes to that data?

      VRoninV 1 Reply Last reply
      0
      • mzimmersM mzimmers

        @VRonin OK, that makes a lot more sense. So, is the missing field in your setData() examples a pointer to my structure element? Is that how the model keeps track of changes to that data?

        VRoninV Offline
        VRoninV Offline
        VRonin
        wrote on last edited by
        #15

        No, my example was using QStandardItemModel. the missing argument is the actual value you want to save in the model, something like item->setData(QStringLiteral("00:A0:C9:14:C8:29"),DeviceRoles::macAddr);

        "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

        1 Reply Last reply
        1
        • mzimmersM Offline
          mzimmersM Offline
          mzimmers
          wrote on last edited by
          #16

          Oh, I see. So, am I then responsible for manually updating the item when data changes?

          VRoninV 1 Reply Last reply
          0
          • mzimmersM mzimmers

            Oh, I see. So, am I then responsible for manually updating the item when data changes?

            VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by VRonin
            #17

            @mzimmers said in I'm still struggling with model/view:

            am I then responsible for manually updating the item when data changes?

            Correct, but that would always be the case. if you subclass QAbstractItemModel it's your responsibility to signal changes of data emitting the dataChanged signal

            "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

            1 Reply Last reply
            1
            • mzimmersM Offline
              mzimmersM Offline
              mzimmers
              wrote on last edited by
              #18

              So, if I understand this, the general program flow would be:

              1. the worker thread receives updates from the various devices
              2. the worker passes the updates (via a signal) to the model
              3. the model then either adds rows or modifies existing rows
              4. the UI automatically reflects the change to the model.

              That about right?

              One thing I'm still not clear on, is how to modify data in existing rows, for example, changing the timestamp on a device that's already been added.

              VRoninV 1 Reply Last reply
              0
              • mzimmersM mzimmers

                So, if I understand this, the general program flow would be:

                1. the worker thread receives updates from the various devices
                2. the worker passes the updates (via a signal) to the model
                3. the model then either adds rows or modifies existing rows
                4. the UI automatically reflects the change to the model.

                That about right?

                One thing I'm still not clear on, is how to modify data in existing rows, for example, changing the timestamp on a device that's already been added.

                VRoninV Offline
                VRoninV Offline
                VRonin
                wrote on last edited by
                #19

                @mzimmers said in I'm still struggling with model/view:

                That about right?

                What workflow did you decide to adopt?

                • Use QStandardItemModel
                • or subclass QAbstractItemModel

                "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

                1 Reply Last reply
                1
                • mzimmersM Offline
                  mzimmersM Offline
                  mzimmers
                  wrote on last edited by
                  #20

                  I'm now trying to use QStandardItemModel. Unfortunately, the data isn't showing up in the QTableView yet, so I'll have to work through to see where it's failing.

                  1 Reply Last reply
                  0
                  • VRoninV Offline
                    VRoninV Offline
                    VRonin
                    wrote on last edited by VRonin
                    #21

                    I'm now trying to use QStandardItemModel

                    Then:

                    1. the worker thread receives updates from the various devices
                    2. the worker passes the updates (via a signal) to a QObject in the main thread that owns the model
                    3. the object inserts/removes/updates the model
                    4. the UI automatically reflects the change to the 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

                    1 Reply Last reply
                    0
                    • mzimmersM Offline
                      mzimmersM Offline
                      mzimmers
                      wrote on last edited by
                      #22

                      Yes, that's how I'm doing it. (My model class is DeviceModel.) Just as a test, I put this into the c'tor:

                      DeviceModel::DeviceModel()
                      {
                          QString s1 = "aaa", s2 = "bbb", s3 = "ccc";
                          QVariant qv;
                      
                          qv = s1;
                          item.setData(qv, DeviceRoles::macAddr);
                          qv = s2;
                          item.setData(qv, DeviceRoles::devName);
                          qv = s3;
                          item.setData(qv, DeviceRoles::latestTimestamp);
                          appendRow(&item);
                      }
                      
                      I'd expect to see this in my QTableView, but I don't. What might I be missing?
                      
                      VRoninV 1 Reply Last reply
                      0
                      • mzimmersM mzimmers

                        Yes, that's how I'm doing it. (My model class is DeviceModel.) Just as a test, I put this into the c'tor:

                        DeviceModel::DeviceModel()
                        {
                            QString s1 = "aaa", s2 = "bbb", s3 = "ccc";
                            QVariant qv;
                        
                            qv = s1;
                            item.setData(qv, DeviceRoles::macAddr);
                            qv = s2;
                            item.setData(qv, DeviceRoles::devName);
                            qv = s3;
                            item.setData(qv, DeviceRoles::latestTimestamp);
                            appendRow(&item);
                        }
                        
                        I'd expect to see this in my QTableView, but I don't. What might I be missing?
                        
                        VRoninV Offline
                        VRoninV Offline
                        VRonin
                        wrote on last edited by
                        #23

                        I'm now trying to use QStandardItemModel

                        My model class is DeviceModel

                        Your model class should be QStandardItemModel. Are you subclassing the model?

                        P.S.

                        item.

                        as mentioned previously, you should avoid the allocation of QStandardItems on the stack as the model is supposed to own them

                        "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

                        1 Reply Last reply
                        1
                        • mzimmersM Offline
                          mzimmersM Offline
                          mzimmers
                          wrote on last edited by mzimmers
                          #24

                          Yes:

                          class DeviceModel : public QStandardItemModel
                          {
                          private:
                              ModelData deviceTable;
                              QStandardItem item;
                          ...
                          

                          EDIT:

                          Ohhhhhh...okay.

                          1 Reply Last reply
                          0
                          • mrjjM Offline
                            mrjjM Offline
                            mrjj
                            Lifetime Qt Champion
                            wrote on last edited by
                            #25

                            so it would be like

                            DeviceModel::DeviceModel()
                            {
                                QString s1 = "aaa", s2 = "bbb", s3 = "ccc";
                                QVariant qv;
                                 QStandardItem * item = new   QStandardItem (); // model will clean up
                                qv = s1;
                                item->setData(qv, DeviceRoles::macAddr);
                                qv = s2;
                                item->setData(qv, DeviceRoles::devName);
                                qv = s3;
                                item->setData(qv, DeviceRoles::latestTimestamp);
                                appendRow(item); 
                            }
                            
                            1 Reply Last reply
                            0
                            • VRoninV Offline
                              VRoninV Offline
                              VRonin
                              wrote on last edited by VRonin
                              #26

                              You should have something like this:

                              #include <QApplication>
                              #include <QStandardItemModel>
                              #include <QTableView>
                              #include <QDateTime>
                              #include <QTimer>
                              #include <functional>
                              class ModelManager : public QObject{
                                  Q_OBJECT
                                  Q_DISABLE_COPY(ModelManager)
                              public:
                                  enum DeviceCols{
                                      dc_macAddr
                                      ,dc_devName
                                      ,dc_latestTimestamp
                                      
                                      ,dc_count
                                  };
                                  explicit ModelManager(QObject* parent = Q_NULLPTR)
                                      :QObject(parent)
                                      , m_model(new QStandardItemModel(this))
                                  {
                                      m_model->insertColumns(0,dc_count);
                                      m_model->setHeaderData(dc_macAddr,Qt::Horizontal,tr("Mac Address"));
                                      m_model->setHeaderData(dc_devName,Qt::Horizontal,tr("Name"));
                                      m_model->setHeaderData(dc_latestTimestamp,Qt::Horizontal,tr("Time Stamp"));
                                  }
                                  QAbstractItemModel* model() const {return m_model;}
                              public slots:
                                  void addDevice(const QString& macAdr, const QString& name, const QDateTime& timeStamp){
                                      const int newRow = m_model->rowCount();
                                      m_model->insertRow(newRow);
                                      m_model->setData(m_model->index(newRow,dc_macAddr),macAdr);
                                      m_model->setData(m_model->index(newRow,dc_devName),name);
                                      m_model->setData(m_model->index(newRow,dc_latestTimestamp),timeStamp);
                                  }
                                  void updateDevice(int row, const QString& macAdr, const QString& name, const QDateTime& timeStamp){
                                      if(row<0 || row>=m_model->rowCount())
                                          return;
                                      m_model->setData(m_model->index(row,dc_macAddr),macAdr);
                                      m_model->setData(m_model->index(row,dc_devName),name);
                                      m_model->setData(m_model->index(row,dc_latestTimestamp),timeStamp);
                                  }
                              private:
                                  QAbstractItemModel* m_model;
                              };
                              
                              
                              int main(int argc, char *argv[])
                              {
                                  QApplication application(argc, argv);
                                  ModelManager manager;
                                  manager.addDevice(QStringLiteral("Foo"),QStringLiteral("Bar"),QDateTime::currentDateTime());
                                  manager.addDevice(QStringLiteral("Foo1"),QStringLiteral("Bar1"),QDateTime::currentDateTime());
                                  manager.addDevice(QStringLiteral("Foo2"),QStringLiteral("Bar2"),QDateTime::currentDateTime());
                                  QTableView view;
                                  view.setModel(manager.model());
                                  view.show();
                                  // simulate adding a device and updating one
                                  QTimer::singleShot(1000,&manager,std::bind(&ModelManager::addDevice,&manager,QStringLiteral("AddMac"),QStringLiteral("AddName"),QDateTime::currentDateTime().addSecs(1)));
                                  QTimer::singleShot(2000,&manager,std::bind(&ModelManager::updateDevice,&manager,1,QStringLiteral("UpdateMac"),QStringLiteral("UpdateName"),QDateTime::currentDateTime().addSecs(2)));
                                  return application.exec();
                              }
                              

                              Of course, instead of the QTimer the updates should come from your workers

                              "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

                              1 Reply Last reply
                              4
                              • mzimmersM Offline
                                mzimmersM Offline
                                mzimmers
                                wrote on last edited by
                                #27

                                Beautiful.

                                I made a minor change to the update:

                                void Device::update(DeviceDetails d)
                                {
                                    int rowCnt = m_model->rowCount();
                                    int row ;
                                
                                    for(row = 0; row < rowCnt; ++row)
                                    {
                                        if(m_model->index(row, dc_macAddr).data().toString() == d.macAddr)
                                        {
                                            break;
                                        }
                                    }
                                
                                    if (row == rowCnt) // didn't find the Mac address; add a new row.
                                    {
                                        add(d);
                                    }
                                    else
                                    {
                                        m_model->setData(m_model->index(row, dc_macAddr), d.macAddr);
                                        m_model->setData(m_model->index(row, dc_devName), d.devName);
                                        m_model->setData(m_model->index(row, dc_latestTimestamp), d.latestHB);
                                    }
                                }
                                

                                Using the QStandardItemModel is indeed much, much simpler than subclassing. Thanks for all the 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
                                • Unsolved