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. view not updating automatically
Forum Updated to NodeBB v4.3 + New Features

view not updating automatically

Scheduled Pinned Locked Moved Solved QML and Qt Quick
21 Posts 4 Posters 3.3k 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.
  • SGaistS SGaist

    @JoeCFD my suggestion is to dump that useless loop. As for the return value, you are correct if following the original logic. I would argue that it depends whether the nullptr is an expected value. For me, trying to set data when the container is not present would rather show that there's a logic issue somewhere.

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

    @SGaist (still looking at your other suggestion.)

    The do {} while (false) loop is a coding convenience. I don't like having multiple return statements in my code, and there are times when I have so many things to test, if I did it with nested if statements, the code would disappear off to the right of the page.

    This approach might seem odd to some, but for me, it's the preferred way to handle such cases.

    JonBJ JoeCFDJ 2 Replies Last reply
    0
    • SGaistS SGaist

      @mzimmers since it's a list, zoneIndex is the row and if you need to add a new row you should use the begin/endInsertRows functions so the update will be triggered automatically.

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

      @SGaist said in view not updating automatically:

      if you need to add a new row you should use the begin/endInsertRows functions

      Interesting...I don't believe the example in the video used those functions. The list object appendItem function does do this, though:

      bool ZoneList::appendItem(Zone item)
      {
          emit preItemAppended();
          m_list.append(item);
          emit postItemAppended();
      }
      

      and in my model code:

      void ZoneModel::setList(ZoneList *list)
      {
          beginResetModel();
          if (m_list != nullptr) {
              m_list->disconnect(this);
          }
          m_list = list;
      
          if (m_list != nullptr) {
              connect(m_list, &ZoneList::preItemAppended, this, [=]() {
                  const int index = m_list->zones().size();
                  beginInsertRows(QModelIndex(), index, index);
              });
              connect(m_list, &ZoneList::postItemAppended, this, [=]() {
                  endInsertRows();
              });
          }
          endResetModel();
      }
      

      So I think it's accomplishing the same thing.

      1 Reply Last reply
      0
      • mzimmersM mzimmers

        @SGaist (still looking at your other suggestion.)

        The do {} while (false) loop is a coding convenience. I don't like having multiple return statements in my code, and there are times when I have so many things to test, if I did it with nested if statements, the code would disappear off to the right of the page.

        This approach might seem odd to some, but for me, it's the preferred way to handle such cases.

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by
        #14

        @mzimmers
        I don't think it has anything to do with your present issue, but the code ends up returning true for "successfully changed data" when the list doesn't exist. One would think that returning false here would make more sense. Or initialize your rc to false, set it true when you do something.

        mzimmersM 1 Reply Last reply
        0
        • mzimmersM mzimmers

          @SGaist (still looking at your other suggestion.)

          The do {} while (false) loop is a coding convenience. I don't like having multiple return statements in my code, and there are times when I have so many things to test, if I did it with nested if statements, the code would disappear off to the right of the page.

          This approach might seem odd to some, but for me, it's the preferred way to handle such cases.

          JoeCFDJ Offline
          JoeCFDJ Offline
          JoeCFD
          wrote on last edited by JoeCFD
          #15

          @mzimmers I use it too. It is very convenient in some scenarios. It is kind of different that default value is true. Return true does not seem to mean setting is successful.

          1 Reply Last reply
          0
          • JonBJ JonB

            @mzimmers
            I don't think it has anything to do with your present issue, but the code ends up returning true for "successfully changed data" when the list doesn't exist. One would think that returning false here would make more sense. Or initialize your rc to false, set it true when you do something.

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

            @JonB that's a good point; I'll probably make that change. Right now, though, that function's never getting called, so I'll leave it be.

            Also, what might be getting lost in this discussion is that most of the ZoneModel updates are working. What is not working is the updating of the equipmentList member of the Zone.

            If this helps, the heirarchy is like this:

            ZoneModel {
                ZoneList {
                    QList<Zone>
                    
            Zone {
                QList<QUuid> equipmentUuids
                
            EquipmentModel {
                EquipmentList {
                    EquipmentItem {
                        QUuid uiud
                        Qstring name
                        other equipment-specific information
            

            So, I need to:

            • access the ZoneModel
            • traverse its list of zones
            • for each zone, get its list of Uuids
            • for each equipment Uuid, traverse the EquipmentModel to get the additional information about the equipment item.

            I realize this is rather complex, and I'm grateful for any suggestions.

            mzimmersM 1 Reply Last reply
            0
            • mzimmersM mzimmers has marked this topic as solved on
            • mzimmersM mzimmers has marked this topic as unsolved on
            • mzimmersM mzimmers

              @JonB that's a good point; I'll probably make that change. Right now, though, that function's never getting called, so I'll leave it be.

              Also, what might be getting lost in this discussion is that most of the ZoneModel updates are working. What is not working is the updating of the equipmentList member of the Zone.

              If this helps, the heirarchy is like this:

              ZoneModel {
                  ZoneList {
                      QList<Zone>
                      
              Zone {
                  QList<QUuid> equipmentUuids
                  
              EquipmentModel {
                  EquipmentList {
                      EquipmentItem {
                          QUuid uiud
                          Qstring name
                          other equipment-specific information
              

              So, I need to:

              • access the ZoneModel
              • traverse its list of zones
              • for each zone, get its list of Uuids
              • for each equipment Uuid, traverse the EquipmentModel to get the additional information about the equipment item.

              I realize this is rather complex, and I'm grateful for any suggestions.

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

              I think I'm actually fairly close on this, and in fact, the C++ portion of the effort may be working fine. In plain prose, here's what I want to do:

              for each zone in the ZoneModel:

              1. get the equipment list for that zone
                for each equipment list item:
                2. extract the UUID of that item
                3. find the item in the EquipmentModel using the UUID above
                4. extract the name of that item

              Here's a snippet of the zone-related code:

              class Zone {
                  Q_GADGET
                  equipmentUuidList m_equipmentList;
              public:
                  Q_PROPERTY(equipmentUuidList equipmentList MEMBER m_equipmentList)
              }
              
              class ZoneModel: public QAbstractListModel
              {
                  Q_OBJECT
              private:
                  ZoneList *m_list;
              public:
                  Q_INVOKABLE Zone getZone(int index); // gets Zone from list
              }
              

              and the equipment-related code:

              class EquipmentItem
              {
                  Q_GADGET
                  Q_PROPERTY(QUuid uuid MEMBER m_uuid READ uuid WRITE setUuid)
                  Q_PROPERTY(QString name MEMBER m_name)
              
                  QUuid m_uuid;
                  QString m_name;
              public:
                  Q_INVOKABLE QUuid uuid() const { return m_uuid; };
                  Q_INVOKABLE QString name() const { return m_name; }
              }
              
              class EquipmentModel : public QAbstractListModel
              {
                  Q_OBJECT
                  Q_PROPERTY(EquipmentList *list READ list WRITE setList NOTIFY listChanged)
              private:
                  EquipmentList *m_list; // a list of EquipmentItem objects
              public:
                  EquipmentList *list() { return m_list; }
                  Q_INVOKABLE EquipmentItem getItem(QUuid uuid);
              }
              

              And my QML:

              ListView {
              id: scenesView
              model: zoneModel
              delegate: Column {
                  property int modelIndex: index
                  property var equipList: zoneModel.getZone(index).equipmentList
                  Text {
                      text: "zone name is " + name
                  }
                  Text {
                      text: "zone UUID is " + uuid
                  }
              
                  ListView {
                      id: equipment
                      model: equipList
                      delegate: Text {
                          text: "zone equipment name is " + equipmentModel.getItem(WHAT GOES HERE?).name()
                      }
                  }
              }
              

              So...how do I use the uuid extracted in the outer view, as a parameter to getItem() in the inner view?

              Thanks for reading...

              mzimmersM 1 Reply Last reply
              0
              • mzimmersM mzimmers referenced this topic on
              • mzimmersM mzimmers

                I think I'm actually fairly close on this, and in fact, the C++ portion of the effort may be working fine. In plain prose, here's what I want to do:

                for each zone in the ZoneModel:

                1. get the equipment list for that zone
                  for each equipment list item:
                  2. extract the UUID of that item
                  3. find the item in the EquipmentModel using the UUID above
                  4. extract the name of that item

                Here's a snippet of the zone-related code:

                class Zone {
                    Q_GADGET
                    equipmentUuidList m_equipmentList;
                public:
                    Q_PROPERTY(equipmentUuidList equipmentList MEMBER m_equipmentList)
                }
                
                class ZoneModel: public QAbstractListModel
                {
                    Q_OBJECT
                private:
                    ZoneList *m_list;
                public:
                    Q_INVOKABLE Zone getZone(int index); // gets Zone from list
                }
                

                and the equipment-related code:

                class EquipmentItem
                {
                    Q_GADGET
                    Q_PROPERTY(QUuid uuid MEMBER m_uuid READ uuid WRITE setUuid)
                    Q_PROPERTY(QString name MEMBER m_name)
                
                    QUuid m_uuid;
                    QString m_name;
                public:
                    Q_INVOKABLE QUuid uuid() const { return m_uuid; };
                    Q_INVOKABLE QString name() const { return m_name; }
                }
                
                class EquipmentModel : public QAbstractListModel
                {
                    Q_OBJECT
                    Q_PROPERTY(EquipmentList *list READ list WRITE setList NOTIFY listChanged)
                private:
                    EquipmentList *m_list; // a list of EquipmentItem objects
                public:
                    EquipmentList *list() { return m_list; }
                    Q_INVOKABLE EquipmentItem getItem(QUuid uuid);
                }
                

                And my QML:

                ListView {
                id: scenesView
                model: zoneModel
                delegate: Column {
                    property int modelIndex: index
                    property var equipList: zoneModel.getZone(index).equipmentList
                    Text {
                        text: "zone name is " + name
                    }
                    Text {
                        text: "zone UUID is " + uuid
                    }
                
                    ListView {
                        id: equipment
                        model: equipList
                        delegate: Text {
                            text: "zone equipment name is " + equipmentModel.getItem(WHAT GOES HERE?).name()
                        }
                    }
                }
                

                So...how do I use the uuid extracted in the outer view, as a parameter to getItem() in the inner view?

                Thanks for reading...

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

                Well, as I suspected, my QML was bad. I got that much figured out:

                ListView {
                    model: zoneModel
                    delegate: Column {
                        id: infoColumn
                        property int zoneIndex: index
                        property var equipList: (index === 0)
                                                ? equipmentModel.uuidList()
                                                : (index > 0)
                                                  ? zoneModel.getZone(index).equipmentList
                                                  : null
                        ListView {
                            height: 80
                            width: 200
                            model: (zoneIndex === 0)
                                   ? equipmentModel.uuidList()
                                   : (zoneIndex > 0)
                                     ? equipList
                                     : null
                            delegate: Text {
                                property var equipUuid: infoColumn.equipList[index]
                                text: "zone equipment name is " + equipmentModel.getEquipment(equipUuid).name()
                            }
                        }
                    }
                }
                

                Unfortunately, the auto-update still isn't working. I'll post more on this later.

                mzimmersM 1 Reply Last reply
                0
                • mzimmersM mzimmers

                  Well, as I suspected, my QML was bad. I got that much figured out:

                  ListView {
                      model: zoneModel
                      delegate: Column {
                          id: infoColumn
                          property int zoneIndex: index
                          property var equipList: (index === 0)
                                                  ? equipmentModel.uuidList()
                                                  : (index > 0)
                                                    ? zoneModel.getZone(index).equipmentList
                                                    : null
                          ListView {
                              height: 80
                              width: 200
                              model: (zoneIndex === 0)
                                     ? equipmentModel.uuidList()
                                     : (zoneIndex > 0)
                                       ? equipList
                                       : null
                              delegate: Text {
                                  property var equipUuid: infoColumn.equipList[index]
                                  text: "zone equipment name is " + equipmentModel.getEquipment(equipUuid).name()
                              }
                          }
                      }
                  }
                  

                  Unfortunately, the auto-update still isn't working. I'll post more on this later.

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

                  I managed a workaround for this -- In my equipment model, I added an emit to the setData() function:

                  void EquipmentModel::setList(EquipmentList *list)
                  {
                      beginResetModel();
                      if (m_list != nullptr) {
                          m_list->disconnect(this);
                      }
                  
                      m_list = list;
                  
                      if (m_list != nullptr) {
                          connect(m_list, &EquipmentList::preItemAppended, this, [=]() {
                              const int index = m_list->equipment().size();
                              beginInsertRows(QModelIndex(), index, index);
                          });
                          connect(m_list, &EquipmentList::postItemAppended, this, [=]() {
                              emit uuidListChanged(uuidList());
                              endInsertRows();
                          });
                  
                          connect(m_list, &EquipmentList::preItemRemoved, this, [=](int index) {
                              beginRemoveRows(QModelIndex(), index, index);
                          });
                          connect(m_list, &EquipmentList::postItemRemoved, this, [=]() {
                              endRemoveRows();
                          });
                      }
                      endResetModel();
                  }
                  

                  Though I'm not sure why this is necessary, because as @SGaist implied above, the endInsertRows() should have emitted this signal, right?

                  JonBJ 1 Reply Last reply
                  0
                  • mzimmersM mzimmers

                    I managed a workaround for this -- In my equipment model, I added an emit to the setData() function:

                    void EquipmentModel::setList(EquipmentList *list)
                    {
                        beginResetModel();
                        if (m_list != nullptr) {
                            m_list->disconnect(this);
                        }
                    
                        m_list = list;
                    
                        if (m_list != nullptr) {
                            connect(m_list, &EquipmentList::preItemAppended, this, [=]() {
                                const int index = m_list->equipment().size();
                                beginInsertRows(QModelIndex(), index, index);
                            });
                            connect(m_list, &EquipmentList::postItemAppended, this, [=]() {
                                emit uuidListChanged(uuidList());
                                endInsertRows();
                            });
                    
                            connect(m_list, &EquipmentList::preItemRemoved, this, [=](int index) {
                                beginRemoveRows(QModelIndex(), index, index);
                            });
                            connect(m_list, &EquipmentList::postItemRemoved, this, [=]() {
                                endRemoveRows();
                            });
                        }
                        endResetModel();
                    }
                    

                    Though I'm not sure why this is necessary, because as @SGaist implied above, the endInsertRows() should have emitted this signal, right?

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #20

                    @mzimmers said in view not updating automatically:

                    because as @SGaist implied above, the endInsertRows() should have emitted this signal, right?

                    I am literally walking into this discussion with no context. But what signal should endInsertRows() have emitted according to you? void QAbstractItemModel::rowsInserted(const QModelIndex &parent, int first, int last) will be emitted:

                    Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.

                    mzimmersM 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @mzimmers said in view not updating automatically:

                      because as @SGaist implied above, the endInsertRows() should have emitted this signal, right?

                      I am literally walking into this discussion with no context. But what signal should endInsertRows() have emitted according to you? void QAbstractItemModel::rowsInserted(const QModelIndex &parent, int first, int last) will be emitted:

                      Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.

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

                      @JonB it was my understanding that the code I posted (without my additional emit) was sufficient to automatically update the QML engine when the list changes. But, either I misunderstood that, or there's something wrong with my implementation, because with that extra emit, the app works as desired, and without it, it doesn't.

                      1 Reply Last reply
                      0
                      • mzimmersM mzimmers has marked this topic as solved on

                      • Login

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