Reputation: 305
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
Upvotes: 5
Views: 7694
Reputation: 13691
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 myID
changed. 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
Upvotes: 1
Reputation: 7170
You need to loop through your model to find the element.
Example:
import QtQuick 2.2
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
ApplicationWindow {
function findElement(myModel, myId) {
for(var i = 0; i < myModel.count; i++) {
var element = myModel.get(i);
if(myId == element.myId) {
console.log("Found element: ", i);
return element.myId;
}
}
return "";
}
id: window
visible: true
TableView {
y: 70
width: 500
TableViewColumn {
role: "myId"
title: "myId"
width: 100
}
TableViewColumn {
role: "myValue"
title: "myValue"
width: 100
}
model: myListModel
ListModel {
id: myListModel
ListElement
{
myId: "1"
myValue: "12"
}
ListElement
{
myId: "2"
myValue: "0"
}
ListElement
{
myId: "21"
myValue: "123"
}
}
}
Label
{
id: myLabel1
property int labelInt: 1
x: 0
text: findElement(myListModel, labelInt);
}
Label
{
id: myLabel2
property int labelInt: 22
x: 100
text: findElement(myListModel, labelInt);
}
}
Upvotes: 1