Skip to content
Advertisement

Get value for given index in QML LISTMODEL

I have a QML ListModel like follows:

ListModel {
id: myListModel
ListElement {myId: "1", myValue = "12"}
ListElement {myId: "2", myValue = "0"}
...
ListElement {myId: "21", myValue = "123"}
}

Then I have some simple QML labels like follows:

Label {
id: myLabel1
property int labelInt: 1
text: myListModel.get().myValue; //Here is my problem!
}

Label {
id: myLabel2
property int labelInt: 22
text: myListModel.get().myValue; //Here is my problem!
}

My problem is to fill that brackets in myListMode.get().myValue. In fact I need a condition: if myListModel has some myId that equals my current labelInt, then return me the corresponding myId, otherwise leave empty space:

I tried with:

myListModel.get(myListModel.get(myId = labelInt).myId).myValue;

but I am not sure It’s the proper way.

Can you help me? Thank you

Advertisement

Answer

If you speed is your desire, you should map you entries to the myId.

In QML you can do this with a combination of a Instantitator and a JSObject.

property var tracker: ({})

Instantiator {
    model: tm
    delegate: QtObject {
        id: instDummy
        property QtObject modelData: model
        property string __oldID
        property string myID: model.myID

        onMyIDChanged: {
            if (myID && __oldID !== myID) {
                delete tracker[__oldID]
                __oldID = myID
                tracker[__oldID] = instDummy
                console.log('changed', Object.keys(tracker))
            }
        }

        Component.onCompleted: {
            __oldID = myID
        }
        Component.onDestruction: {
            console.log(__oldID, modelData.myID, delete tracker[__oldID])
            console.log('removed', Object.keys(tracker))
        }
    }
}

Explanation:

The Instantiator creates a QtObject for each entry in the model. This QtObject stores a reference to the model data of the entry. The two properties myID and __oldID are used to keep the Map (JSObject) tracker up to date.

myID is bound to the model.myID so, whenever this would change it would trigger the signal on myIDchanged. Now I need to change the key in the map from the old value to the new value. Therefore I delete the entry with the key __oldID, then I update __oldID to the new myID and add the object back into the map, with the new key.

I don’t want __oldID to be bound, therefore I assign it in Component.onCompleted.

When the entry is removed, I remove the object and the reference from the Map as well, so I keep my map as clean as possible.

When a new entry is added, the Instantiator will automatically add a new QtObject for this.


Now you can query your myID in O(1) with tracker[myID]. The Instantiator takes care that all changes will be tracked. It only changes, when there is a change to the model in the specific row.

Note that this method will take a toll on your memory, as it will basically duplicate your model in the hashmap.

To avoid this, you might implement a ProxyModel in C++, e.g. based on the QIdentityProxyModel to which you add a mapping of the myID and the actuall model index. Handle the dataChanged-signal to keep that map up-to-date.

PS: I expect that at least the myID is unique. Otherwise like this, you might lose some references. Use buckets then, to keep multiple entries in the map for shared myID‘s

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement