QML receives undefined for roles from dataChanged signal



  • 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.


  • Moderators

    @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.



  • 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
    

  • Moderators

    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.



  • 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)
        }
    

  • Moderators

    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.



  • 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)
        }
    

  • Moderators

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



  • 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
        };
    

  • Moderators

    @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)
    

  • Moderators

    @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.


  • Moderators

    @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])
    


  • 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.


  • Moderators

    @KiNgFrUiT You're Welcome :) Happy Coding.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.