accessing aggregates (QVector of a struct)
-
@mzimmers said in accessing aggregates (QVector of a struct):
Column {
id: myColumn
property var myArray: []
Component.onCompleted: DataSource.getData().forEach(function(bottle) {
// template.createObject(this, { text: bottle.name });
myArray.push(bottle.name)
}, this)you should not modify myArray directly in the function but create a local array then copy its content into myArray
Column { id: myColumn property var myArray: [] Component.onCompleted: { var arr = [] DataSource.getData().forEach(function(bottle) { //myArray.push(bottle.name) arr.push(bottle.name) }, this); myColumn.myArray = arr; }
wrote on 8 Mar 2021, 22:26 last edited by@LeLev that works! Thanks for your patience on this.
May I ask why my approach didn't work?
-
@LeLev that works! Thanks for your patience on this.
May I ask why my approach didn't work?
wrote on 8 Mar 2021, 22:34 last edited by ODБOï 3 Aug 2021, 22:53@mzimmers
here is an example to illustrate, uncomment one or the other Component.onCompleted, you will observe that when we call
col.myArr.push("N° " + i) directly, the view is empty at the beginning but myArr actually contains the itemsWindow { width: 640 height: 480 visible: true Column{ id:col property var myArr : [] // calling myArr.push() directly in the function. // You will need to somehow "refresh" the view, (in this example see the button that resets the repeater model) // Component.onCompleted: { // var arr = [] // for(var i=0;i<10;i++){ // col.myArr.push("N° " + i) // } // } //OR ------------------------------------------ // creating a local array // Component.onCompleted: { // var arr = [] // for(var i=0;i<10;i++){ // arr.push("N° " + i) // } // col.myArr = arr; // } //-------------------------------------------------- Repeater{ id:rep model: col.myArr.length Text { id: txt text: col.myArr[index] } } //-------------------------------------------------- Button{ text: 'refresh' onClicked:{ rep.model = 0; rep.model = col.myArr.length; console.log(col.myArr) } // just to reset the repeater } } }
It's great that your solution worked but why not implement something where you can simply write
Text { text: DataSource.getDataByIndex(0).name }
-
@mzimmers
here is an example to illustrate, uncomment one or the other Component.onCompleted, you will observe that when we call
col.myArr.push("N° " + i) directly, the view is empty at the beginning but myArr actually contains the itemsWindow { width: 640 height: 480 visible: true Column{ id:col property var myArr : [] // calling myArr.push() directly in the function. // You will need to somehow "refresh" the view, (in this example see the button that resets the repeater model) // Component.onCompleted: { // var arr = [] // for(var i=0;i<10;i++){ // col.myArr.push("N° " + i) // } // } //OR ------------------------------------------ // creating a local array // Component.onCompleted: { // var arr = [] // for(var i=0;i<10;i++){ // arr.push("N° " + i) // } // col.myArr = arr; // } //-------------------------------------------------- Repeater{ id:rep model: col.myArr.length Text { id: txt text: col.myArr[index] } } //-------------------------------------------------- Button{ text: 'refresh' onClicked:{ rep.model = 0; rep.model = col.myArr.length; console.log(col.myArr) } // just to reset the repeater } } }
It's great that your solution worked but why not implement something where you can simply write
Text { text: DataSource.getDataByIndex(0).name }
wrote on 8 Mar 2021, 23:03 last edited by@LeLev I may have spoken a bit too soon...when I try to apply this from the example to my application, I get the error again.
Column { id: myColumn property var myArray: [] Component.onCompleted: { var arr = [] BottleList.getBottleListQv().forEach(function(bottle) { arr.push(bottle.name) }, this); myColumn.myArray = arr; } Bottle { cellText: myColumn.myArray[0]//"W7" }
The array element notation seems like it's correct; can you see what I'm doing wrong?
Regarding writing a function for the name: I would do that, and I still may, but I want to grow this to return several values in the struct: name, position, capacity, fill level, etc. So far, this approach looks better for that.
-
@LeLev I may have spoken a bit too soon...when I try to apply this from the example to my application, I get the error again.
Column { id: myColumn property var myArray: [] Component.onCompleted: { var arr = [] BottleList.getBottleListQv().forEach(function(bottle) { arr.push(bottle.name) }, this); myColumn.myArray = arr; } Bottle { cellText: myColumn.myArray[0]//"W7" }
The array element notation seems like it's correct; can you see what I'm doing wrong?
Regarding writing a function for the name: I would do that, and I still may, but I want to grow this to return several values in the struct: name, position, capacity, fill level, etc. So far, this approach looks better for that.
wrote on 8 Mar 2021, 23:16 last edited by ODБOï 3 Aug 2021, 23:24@mzimmers said in accessing aggregates (QVector of a struct):
I get the error again.
what error do you get ? Did you read/test my example with all the comments about the arrays, i think the error you get is related to that
-
@LeLev I may have spoken a bit too soon...when I try to apply this from the example to my application, I get the error again.
Column { id: myColumn property var myArray: [] Component.onCompleted: { var arr = [] BottleList.getBottleListQv().forEach(function(bottle) { arr.push(bottle.name) }, this); myColumn.myArray = arr; } Bottle { cellText: myColumn.myArray[0]//"W7" }
The array element notation seems like it's correct; can you see what I'm doing wrong?
Regarding writing a function for the name: I would do that, and I still may, but I want to grow this to return several values in the struct: name, position, capacity, fill level, etc. So far, this approach looks better for that.
@mzimmers said in accessing aggregates (QVector of a struct):
@LeLev I may have spoken a bit too soon...when I try to apply this from the example to my application, I get the error again.
At the time you reference the array it's empty. That's the reason.
Component.onCompleted
is executed after the property toBottle.cellText
is bound, so the JS engine throws you an error, there's no such thing asmyArray[0]
at this point; the array is empty. -
@kshegunov OK, I think I understand 1-4. But where is the getData() function explicitly referenced/called? (I think your getData() is the equivalent to my QVariantList BottleList::getBottleListQv().)
I still don't understand how to retrieve the information to use it in my bottle objects.
Bottle { id: bottle1 cellX: 25 cellY: 105 cellHeight: 75 cellWidth: 75 bottleScaleFactor: scaleFactor cellText: "W7" // how to replace this with something from getData()? cellColor: "red" }
Thanks...
Moderatorswrote on 8 Mar 2021, 23:31 last edited by kshegunov 3 Aug 2021, 23:34@mzimmers said in accessing aggregates (QVector of a struct):
I still don't understand how to retrieve the information to use it in my bottle objects.
You can instantiate them at runtime (as I did with the text item), you just need to replace the
Text
with your template for theBottle
. Alternatively you can use theRepeater
@LeLev's been pushing, but then set thedelegate
property to a component which is going to create your bottle items (the repeater with a set delegate pretty much does what my example does). Note I don't know if themodel
property is going to work with a list of gadgets, you need to check.@LeLev said in accessing aggregates (QVector of a struct):
it does almost exactly the same thing as my very first example/answer.
Almost but with fewer objects created under the hood. Not that it really matters as the QML engine is an elephant anyway.
-
@mzimmers said in accessing aggregates (QVector of a struct):
I still don't understand how to retrieve the information to use it in my bottle objects.
You can instantiate them at runtime (as I did with the text item), you just need to replace the
Text
with your template for theBottle
. Alternatively you can use theRepeater
@LeLev's been pushing, but then set thedelegate
property to a component which is going to create your bottle items (the repeater with a set delegate pretty much does what my example does). Note I don't know if themodel
property is going to work with a list of gadgets, you need to check.@LeLev said in accessing aggregates (QVector of a struct):
it does almost exactly the same thing as my very first example/answer.
Almost but with fewer objects created under the hood. Not that it really matters as the QML engine is an elephant anyway.
wrote on 9 Mar 2021, 00:01 last edited by ODБOï 3 Sept 2021, 00:02@kshegunov said in accessing aggregates (QVector of a struct):
Alternatively you can use the Repeater @LeLev's been pushing
I just use a Repeater to answer to this question :
@mzimmers said in accessing aggregates (QVector of a struct):
@LeLev ah. So, now my code looks like this:
Column {
id: myColumn
property var myArray: []
Component.onCompleted: DataSource.getData().forEach(function(bottle) {
// template.createObject(this, { text: bottle.name });
myArray.push(bottle.name)
}, this)
Text {
text: myColumn.myArray[0]
}
Text {
text:myColumn.myArray[1]
}
Text {
text: myColumn.myArray[2]
}
}just shorter to write with a repeater
i'm really not pushing mzimmers to use it. it was just an example to show what is happening with "property var myArray: []" initialisation -
@mzimmers said in accessing aggregates (QVector of a struct):
@LeLev I may have spoken a bit too soon...when I try to apply this from the example to my application, I get the error again.
At the time you reference the array it's empty. That's the reason.
Component.onCompleted
is executed after the property toBottle.cellText
is bound, so the JS engine throws you an error, there's no such thing asmyArray[0]
at this point; the array is empty.wrote on 9 Mar 2021, 00:03 last edited by@kshegunov said in accessing aggregates (QVector of a struct):
@mzimmers said in accessing aggregates (QVector of a struct):
@LeLev I may have spoken a bit too soon...when I try to apply this from the example to my application, I get the error again.
At the time you reference the array it's empty. That's the reason.
Component.onCompleted
is executed after the property toBottle.cellText
is bound, so the JS engine throws you an error, there's no such thing asmyArray[0]
at this point; the array is empty.That makes sense. So, according to the docs, I can use the onCompleted() handler with any object. Can I somehow use it with my Bottle (defined in QML) object?
-
@kshegunov said in accessing aggregates (QVector of a struct):
Alternatively you can use the Repeater @LeLev's been pushing
I just use a Repeater to answer to this question :
@mzimmers said in accessing aggregates (QVector of a struct):
@LeLev ah. So, now my code looks like this:
Column {
id: myColumn
property var myArray: []
Component.onCompleted: DataSource.getData().forEach(function(bottle) {
// template.createObject(this, { text: bottle.name });
myArray.push(bottle.name)
}, this)
Text {
text: myColumn.myArray[0]
}
Text {
text:myColumn.myArray[1]
}
Text {
text: myColumn.myArray[2]
}
}just shorter to write with a repeater
i'm really not pushing mzimmers to use it. it was just an example to show what is happening with "property var myArray: []" initialisationwrote on 9 Mar 2021, 00:07 last edited by@LeLev it's a good suggestion; I'm still looking at the code.
I probably don't really need the Column any more, as far as that goes. But I guess I need something to attach the Component.onCompleted() to.
-
@kshegunov said in accessing aggregates (QVector of a struct):
Alternatively you can use the Repeater @LeLev's been pushing
I just use a Repeater to answer to this question :
@mzimmers said in accessing aggregates (QVector of a struct):
@LeLev ah. So, now my code looks like this:
Column {
id: myColumn
property var myArray: []
Component.onCompleted: DataSource.getData().forEach(function(bottle) {
// template.createObject(this, { text: bottle.name });
myArray.push(bottle.name)
}, this)
Text {
text: myColumn.myArray[0]
}
Text {
text:myColumn.myArray[1]
}
Text {
text: myColumn.myArray[2]
}
}just shorter to write with a repeater
i'm really not pushing mzimmers to use it. it was just an example to show what is happening with "property var myArray: []" initialisation@LeLev said in accessing aggregates (QVector of a struct):
i'm really not pushing mzimmers to use it. it was just an example to show what is happening with "property var myArray: []" initialisation
That came out badly. I didn't mean to imply it's wrong in any way, or that your answer is bad. A poor choice of words, sorry.
@mzimmers said in accessing aggregates (QVector of a struct):
That makes sense. So, according to the docs, I can use the onCompleted() handler with any object. Can I somehow use it with my Bottle (defined in QML) object?
Yes all the items can attach to it. Think of it as a "global" signal (sort of).
-
@LeLev said in accessing aggregates (QVector of a struct):
i'm really not pushing mzimmers to use it. it was just an example to show what is happening with "property var myArray: []" initialisation
That came out badly. I didn't mean to imply it's wrong in any way, or that your answer is bad. A poor choice of words, sorry.
@mzimmers said in accessing aggregates (QVector of a struct):
That makes sense. So, according to the docs, I can use the onCompleted() handler with any object. Can I somehow use it with my Bottle (defined in QML) object?
Yes all the items can attach to it. Think of it as a "global" signal (sort of).
wrote on 9 Mar 2021, 00:25 last edited by ODБOï 3 Sept 2021, 00:28@kshegunov no worries , i just wanted to clarify
@mzimmers said in accessing aggregates (QVector of a struct):
Can I somehow use it with my Bottle (defined in QML) object?
you can refactor the code in the Component.onCompleted into a function and call it when you need
-
@kshegunov no worries , i just wanted to clarify
@mzimmers said in accessing aggregates (QVector of a struct):
Can I somehow use it with my Bottle (defined in QML) object?
you can refactor the code in the Component.onCompleted into a function and call it when you need
wrote on 9 Mar 2021, 00:32 last edited by@LeLev somehow, I'm overcomplicating this. I already have a function:
Q_INVOKABLE QString getBottleName(int index) { return m_bottleList[index].m_name; }
The obstacle here is, anything I do is getting called at program startup, before my data structure is populated (as ksheg indicated). I can't figure out how to "delay" my assignment of the cellText property of the bottle until after that vector/list is built.
-
@LeLev somehow, I'm overcomplicating this. I already have a function:
Q_INVOKABLE QString getBottleName(int index) { return m_bottleList[index].m_name; }
The obstacle here is, anything I do is getting called at program startup, before my data structure is populated (as ksheg indicated). I can't figure out how to "delay" my assignment of the cellText property of the bottle until after that vector/list is built.
Moderatorswrote on 9 Mar 2021, 00:47 last edited by kshegunov 3 Sept 2021, 00:47@mzimmers said in accessing aggregates (QVector of a struct):
The obstacle here is, anything I do is getting called at program startup, before my data structure is populated (as ksheg indicated). I can't figure out how to "delay" my assignment of the cellText property of the bottle until after that vector/list is built.
You can't, not without going to dynamic creation of the items. The properties are bound (think similar to
QObject::connect
) when the items are instantiated. Either you use dynamic creation (e.g. what @LeLev and I suggested), or you need to attach to each of the item's you have statically added to your interface and fetch the value in thecompleted()
handler.<rant>
The docs sing a very smooth song about integrating QML and C++, but the truth is ... well you already know what the truth is. With a couple of exceptions none of theQQuickItem
s have public interfaces that can be used from C++ and you're stuck jumping trough hoops to tie up the API ...
</rant> -
wrote on 9 Mar 2021, 00:55 last edited by
So, as currently implemented, even if my backing data is updated, it doesn't do anything for updating the QML screen...is that about right?
If I use the repeater idea, I guess I'll have to use it for my entire object -- in fact, my entire array of objects. Because using it to create an array won't updating anything that uses that array (for the reasons you cited), will it?
-
So, as currently implemented, even if my backing data is updated, it doesn't do anything for updating the QML screen...is that about right?
If I use the repeater idea, I guess I'll have to use it for my entire object -- in fact, my entire array of objects. Because using it to create an array won't updating anything that uses that array (for the reasons you cited), will it?
Moderatorswrote on 9 Mar 2021, 00:59 last edited by kshegunov 3 Sept 2021, 01:01@mzimmers said in accessing aggregates (QVector of a struct):
So, as currently implemented, even if my backing data is updated, it doesn't do anything for updating the QML screen...is that about right?
Very much, yes.
If I use the repeater idea, I guess I'll have to use it for my entire object -- in fact, my entire array of objects. Because using it to create an array won't updating anything that uses that array (for the reasons you cited), will it?
Yes, and you still won't get notifications if the data changes.
If you want to get notifications about data changing in QML, switch to
QObject
derived classes. And provide aQObject *
to the QML, then when you create the items dynamically and you pass on the properties, you pass on as bindings, not values. Then theQObject
being changed from the C++ is going to be reflected in the QtQuick scene.<rant 2>
If QML had a C++ API, you could've derived from the correct item and added the data to the item, not create and instantiate yet another type just for keeping the data ...
</rant 2> -
@mzimmers said in accessing aggregates (QVector of a struct):
So, as currently implemented, even if my backing data is updated, it doesn't do anything for updating the QML screen...is that about right?
Very much, yes.
If I use the repeater idea, I guess I'll have to use it for my entire object -- in fact, my entire array of objects. Because using it to create an array won't updating anything that uses that array (for the reasons you cited), will it?
Yes, and you still won't get notifications if the data changes.
If you want to get notifications about data changing in QML, switch to
QObject
derived classes. And provide aQObject *
to the QML, then when you create the items dynamically and you pass on the properties, you pass on as bindings, not values. Then theQObject
being changed from the C++ is going to be reflected in the QtQuick scene.<rant 2>
If QML had a C++ API, you could've derived from the correct item and added the data to the item, not create and instantiate yet another type just for keeping the data ...
</rant 2>wrote on 9 Mar 2021, 15:42 last edited by@kshegunov thanks. I have some follow-on questions, but as they don't focus on the original intent of this thread, and as this thread has gotten fairly long already, I'm going to mark this as solved, and open a new thread.
Thanks to everyone for the assistance on this.
46/52