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. QML receives undefined for roles from dataChanged signal
Forum Updated to NodeBB v4.3 + New Features

QML receives undefined for roles from dataChanged signal

Scheduled Pinned Locked Moved Solved QML and Qt Quick
14 Posts 2 Posters 7.6k 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.
  • K Offline
    K Offline
    KiNgFrUiT
    wrote on last edited by KiNgFrUiT
    #1

    I have a QAbstractListModel that works ok but updates are not very efficient, I want to know which role's data has changed.
    I wish to use the roles parameter from the dataChanged signal but QML always gets undefined for the array data.
    I am using a model that extends QAbstractListModel.

    I am emitting from c++ like this:

        QVector<int> roles;
        roles.push_back(ModeRole);
        roles.push_back(StatusRole);
        emit dataChanged(index(0), index(barrierCount - 1), roles);
    

    QML receiving code:

    onDataChanged: console.log("dataChanged", topLeft, bottomRight, roles, roles.length)
    

    Console output:

    qml: dataChanged QModelIndex(1,0,0x0,BarrierModel(0x13f86d720)) QModelIndex(1,0,0x0,BarrierModel(0x13f86d720)) QVariant(QVector<int>) undefined
    

    I found this unresolved thread where someone else appears to be having the same issue:
    Re: How to emit dataChanged to a single role of QAbstractListModel?

    Im using Qt5.6.1 on Windows 7 x64, but that shouldnt matter.

    1 Reply Last reply
    0
    • p3c0P Offline
      p3c0P Offline
      p3c0
      Moderators
      wrote on last edited by p3c0
      #2

      @KiNgFrUiT
      I think it is undefined because it doesnot understand what QVector is and thus it cant convert that QVariant into corresponding data type(perhaps JS array) on QML side and hence doesnot understand what length is.

      Now its fine that you emit dataChanged with roles as its parameter. IMO there is no use on listening to that on QML side i.e you can't do anything with those roles there. Once you emit dataChanged its the resposibilty of the view to update to new changes. We dont have to explicitly set them again in QML. If they are not updated properly then there's fair chance that proper index's have not been supplied to datachanged.
      Also is index your custom method? Because the pure virtual one in QAbstractItemModel requires atleast 2 parameters.

      157

      1 Reply Last reply
      0
      • K Offline
        K Offline
        KiNgFrUiT
        wrote on last edited by KiNgFrUiT
        #3

        Thanks for your reply.

        According to the following page, QML automatically treats QVector as a Javascript array.
        http://doc.qt.io/qt-5/qtqml-cppintegration-data.html

        The index function is just the one from QAbstractListModel. QAbstractItemListModel was a typo.

        QModelIndex QAbstractListModel::index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const
        
        1 Reply Last reply
        0
        • p3c0P Offline
          p3c0P Offline
          p3c0
          Moderators
          wrote on last edited by p3c0
          #4

          As said here:

          There are two ways in which such sequences can be exposed to QML: as a Q_PROPERTY of the given sequence type; or as the return type of a Q_INVOKABLE method.

          emit dataChanged(index(0), index(barrierCount - 1), roles);

          How did you manage to pass just single argument to index ? Didn't the compiler complain ?

          so even if we get pass this my question is what will you do in the onDataChanged handler in QML ?
          All I wanted to say is that if we pass appropriate data to dataChanged the view should update appropriately.

          157

          1 Reply Last reply
          0
          • K Offline
            K Offline
            KiNgFrUiT
            wrote on last edited by KiNgFrUiT
            #5

            QML function and connection code is as follows:

                property var mode: 0
                property var status: 1
            
                function barrierDataChanged(topLeft, bottomRight, roles) {
                    console.log("dataChanged", topLeft, bottomRight, roles, roles.length)
            
                    var modelIndex = barrierModel.getBarrierIndex(barrierId)
            
                    if(topLeft.row == modelIndex) {
                        if(roles.contains("mode")) { // contains function is just a guess for now
                            mode = barrierModel.get(modelIndex).mode
                        }
                        else if(roles.contains("status")) { // contains function is just a guess for now
                            status = barrierModel.get(modelIndex).status
                        }
                    }
                }
            
                Connections {
                    target: barrierModel
                    onDataChanged: barrierDataChanged(topLeft, bottomRight, roles)
                }
            
            1 Reply Last reply
            0
            • p3c0P Offline
              p3c0P Offline
              p3c0
              Moderators
              wrote on last edited by
              #6

              Alright. What are you going to do once you get those values in barrierDataChanged ?
              I think you should probably rephrase your original question. I dont see any connection between your question I have a QAbstractItemListModel QAbstractListModel that works ok but updates are not very efficient, not all roles need to be updated. and the code you posted.

              157

              1 Reply Last reply
              0
              • K Offline
                K Offline
                KiNgFrUiT
                wrote on last edited by KiNgFrUiT
                #7

                Currently I need to update mode and status every time dataChanged is emitted, as I cannot tell which role has changed.

                If I can get the role parameter to contain useful data, instead of undefined, then the desired implementation will be more efficient as only the variable associated with the changed role needs updating.

                Existing Code:

                    property var mode: 0
                    property var status: 1
                
                    function barrierDataChanged(topLeft, bottomRight) {
                        var modelIndex = barrierModel.getBarrierIndex(barrierId)
                
                        if(topLeft.row == modelIndex) {
                             mode = barrierModel.get(modelIndex).mode
                             status = barrierModel.get(modelIndex).status
                        }
                    }
                
                    Connections {
                        target: barrierModel
                        onDataChanged: barrierDataChanged(topLeft, bottomRight)
                    }
                

                Desired Code:

                    property var mode: 0
                    property var status: 1
                
                    function barrierDataChanged(topLeft, bottomRight, roles) {
                        console.log("dataChanged", topLeft, bottomRight, roles, roles.length)
                
                        var modelIndex = barrierModel.getBarrierIndex(barrierId)
                
                        if(topLeft.row == modelIndex) {
                            if(roles.contains("mode")) { // contains function is just a guess for now
                                mode = barrierModel.get(modelIndex).mode
                            }
                            else if(roles.contains("status")) { // contains function is just a guess for now
                                status = barrierModel.get(modelIndex).status
                            }
                        }
                    }
                
                    Connections {
                        target: barrierModel
                        onDataChanged: barrierDataChanged(topLeft, bottomRight, roles)
                    }
                
                1 Reply Last reply
                0
                • p3c0P Offline
                  p3c0P Offline
                  p3c0
                  Moderators
                  wrote on last edited by
                  #8

                  @KiNgFrUiT Are you talking something as shown in the following example ?
                  http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel-subclass

                  157

                  1 Reply Last reply
                  0
                  • K Offline
                    K Offline
                    KiNgFrUiT
                    wrote on last edited by KiNgFrUiT
                    #9

                    Yes, in that example the Animal roles are type and size.
                    In my application the roles are mode and status etc, I have simplified my code for this discussion.

                        enum BarrierRole
                        {
                            BarrierIdRole = Qt::UserRole + 1,
                            NameRole,
                            ModeRole,
                            StatusRole,
                            BulbCountRole,
                            BulbFaultsRole,
                            InductionLoopRole
                        };
                    
                    p3c0P 1 Reply Last reply
                    0
                    • p3c0P Offline
                      p3c0P Offline
                      p3c0
                      Moderators
                      wrote on last edited by
                      #10

                      @KiNgFrUiT The only possibility left according to me(or try finding a way to make QVector available to QML, may be Q_DECLARE_METATYPE?? not sure) here is to create a Q_INVOKABLE in C++ model and call it from QML. This function will take arguments as both QModelIndex and the QVector<int> of roles. Then this function will resolve the values and return the data to QML.
                      For eg:
                      C++

                      QVariant MyModel::getValueByRole(QModelIndex t, QModelIndex b, QVector<int> role)
                      {
                          qDebug() << t.data(role[0]);
                      }
                      

                      QML

                      myModel.getValueByRole(topLeft, bottomRight, roles)
                      

                      157

                      p3c0P 1 Reply Last reply
                      0
                      • K KiNgFrUiT

                        Yes, in that example the Animal roles are type and size.
                        In my application the roles are mode and status etc, I have simplified my code for this discussion.

                            enum BarrierRole
                            {
                                BarrierIdRole = Qt::UserRole + 1,
                                NameRole,
                                ModeRole,
                                StatusRole,
                                BulbCountRole,
                                BulbFaultsRole,
                                InductionLoopRole
                            };
                        
                        p3c0P Offline
                        p3c0P Offline
                        p3c0
                        Moderators
                        wrote on last edited by
                        #11

                        @KiNgFrUiT It works in Qt 5.7. I just went through the documentation on the web and the installed one. I was using Qt 5.6 earlier. There were differences. It seems that they have made available QVector<int> to QML in the latest version. If you are still looking for this issue try upgrading Qt to the latest version.

                        157

                        1 Reply Last reply
                        1
                        • p3c0P p3c0

                          @KiNgFrUiT The only possibility left according to me(or try finding a way to make QVector available to QML, may be Q_DECLARE_METATYPE?? not sure) here is to create a Q_INVOKABLE in C++ model and call it from QML. This function will take arguments as both QModelIndex and the QVector<int> of roles. Then this function will resolve the values and return the data to QML.
                          For eg:
                          C++

                          QVariant MyModel::getValueByRole(QModelIndex t, QModelIndex b, QVector<int> role)
                          {
                              qDebug() << t.data(role[0]);
                          }
                          

                          QML

                          myModel.getValueByRole(topLeft, bottomRight, roles)
                          
                          p3c0P Offline
                          p3c0P Offline
                          p3c0
                          Moderators
                          wrote on last edited by
                          #12

                          @p3c0 But as expected it returns an int and since as per your code you require a string to compare you will need some additional code on CPP side to get role value from the key. For eg:

                          QString MyModel::getRoleValue(int key)
                          {
                              return roleNames().value(key);
                          }
                          

                          This will return rolename string using the key that you receive in onDataChanged handler's roles parameter. eg. roles[0], roles[1] ...

                          Or instead you can directly get the value using data() which requires a QModelIndex and the role.

                          var str = myModel.data(topLeft, roles[0])
                          

                          157

                          1 Reply Last reply
                          0
                          • K Offline
                            K Offline
                            KiNgFrUiT
                            wrote on last edited by
                            #13

                            I just updated to 5.7, as per your suggestion, and everything works as expected.
                            Thank you so much for your assistance on this issue.

                            1 Reply Last reply
                            0
                            • p3c0P Offline
                              p3c0P Offline
                              p3c0
                              Moderators
                              wrote on last edited by
                              #14

                              @KiNgFrUiT You're Welcome :) Happy Coding.

                              157

                              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