[SOLVED] createObject() - having a function as a property
-
Hi
Ive searched long and hard for an example / similar problem but couldnt find anything.
It's about creating Objects dynamically and while instantiating i want to assign a anonzmous function to a property but I can't figure out how the syntax looks like or if this works at all. I don't see why it shouldn't
I'm using QT5 and QTQuick 2.0
@
Button.qmlRectangle {
property alias img_id: button_pic
property alias m_id: mouseAreaMouseArea { id: mouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button == Qt.LeftButton){ change_emit.pulse(500); } } }
}
@@
main.qmlRectangle{
id: app
property variant money_back: { 'fifty': 0, 'twenty': 0, 'ten': 0, 'five': 0, 'two': 0, 'one': 0, 'cfifty': 0, 'ctwenty': 0, 'cten': 0 };anchors.fill: parent Row{ id:one_row spacing: 10 Component.onCompleted: { var component; for (var prop in main.money_back){ component = Qt.createComponent("Button.qml"); component.createObject(one_row, { "id": "button_"+prop, "m_id": "buttonclick_"+prop, "m_id.onClicked": { console.log('bla') }, "width": 64, "height": 64, } ); } } } }
@
as you can see i commented the problematic line on line #20
alternativly i have tried:@
"m_id.onClicked": { console.log('bla') }, //syntax error
"m_id.onClicked": "{ console.log('bla') }", //does nothing
"m_id.onClicked": function(){ console.log('bla') }, //prints 'bla' everytime a button is CREATED.. does NOTHING on onClicked!
@edt: oh and i just did this, also doesnt work.
@
var bla = component.createObject(one_row,
{ "id": "button_"+prop,
"m_id": "buttonclick_"+prop,"width": 64, "height": 64, } ); bla.m_id.onClicked = function(){ console.log('bla')}; // error, read-only?? wtf
@
I'm very frustrated :( I don't know much about JavaScript and just learned QML so I don't even know all these fancy language specific words.. I hope the example is clear enough.
thank you for your time.
-
Firstly, are you using QtQuick1.x or QtQuick 2.0?
The syntax semantics are slightly different between those two versions, which I'll now describe:
- Property value initialization:
in QtQuick 1 and QtQuick 2
@
property int someProp: someFunc()
@
will assign the binding expression "return someFunc()" to the property.
The expression will be evaluated after the object has been instantiated by the VME. During the evaluation of someFunc(), any properties which are read from, will be "captured" and if they later notify of changes, the expression will be re-evaluated.in QtQuick 2 only:
@
property var someProp: [someFunc]
@
will assign an array containing a single function to the var someProp. That function may be evaluated by calling someProp0 etc.I might be wrong about that syntax, you may have to do someProp: [(function(){return someFunc()})]
- property value assignment
If the assignment is done in some imperative code block, it usually is NOT a binding.
eg, in QtQuick 1 and QtQuick 2:
@
someProp = someFunc();
@
will simply call the someFunc() function and assign the result to someProp. No re-evaluation will occur if any properties accessed by someFunc later change.You can force the assignment to be a binding, however the syntax is different in QtQuick1 vs QtQuick2. Note also that the resulting binding is a very, very slow binding (no matter how simple the expression is) and so you should avoid doing this if possible.
In QtQuick 1, the imperative binding assignment is:
@
someProp = (function(){return someFunc()})
@Whereas in QtQuick 2 it's more explicit:
@
someProp = Qt.binding(function(){return someFunc()})
@--
The question you've asked is tricky for two reasons:
- you don't want to generate a binding, you actually want to replace the definition of a signal handler
- you are using createObject and want to override the initial property values.
If you're using QtQuick2, you should be able to get this working fairly nicely, perhaps using an intermediate var property (eg, the onClicked handler will always just call the function stored in the var property; then just provide the function as an initial value property for that var prop).
In QtQuick1, it's much more difficult. The initial value properties might even be passed as a QVariantMap or similar, in which case your only hope is to pass the expression as a string, and eval() it in the onClicked handler (ugh). I don't recall, though, so it's possible that some form of JS script value can be passed as an initial property value.
Cheers,
Chris. -
Oh sorry, I'm using the latest and greatest 5.0 ergo QtQuick 2.0
what I'm confused about is when I do a static instance like this
@
main.qmlButton{
//stuff ...
m_id.onClicked{
// main stuff
}}
@a click on the button will do the main stuff AND the emitter.pulse() function inside Button.qml?!
is this intentional?currently im trying to get it work tinker
EDT:
still doesnt work :( the snytax for calling the function got distorted by forum markup thingys so i guessed about the syntax
EDT2:
woah false alarm, in a good way, it works! yesssss,.... finaly i can sleep.. still, the above question remains :) but I wont worry as much so that I cant sleep.. phew.. many many thank yous chris.
@
Button.qmlRectangle {
property alias img_id: button_pic
property var actionMouseArea { id: mouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button == Qt.LeftButton){ change_emit.pulse(500); -action(); // also tried action[0](); ... doesnt work- action[0](); // yes this works!!! wohooo } } }
}
// -------------------
main.qmlRectangle{
id: app
property variant money_back: { 'fifty': 0, 'twenty': 0, 'ten': 0, 'five': 0, 'two': 0, 'one': 0, 'cfifty': 0, 'ctwenty': 0, 'cten': 0 };anchors.fill: parent Row{ id:one_row spacing: 10 Component.onCompleted: { var component; for (var prop in main.money_back){ component = Qt.createComponent("Button.qml"); component.createObject(one_row, { "action": [function(){ console.log('bla') }], "width": 64, "height": 64, } ); } } } }
@
for the future (5.1 ?) is it possible to not have to write it like [function(){ //stuff }]
but like [{ //stuff}] ? or just { //stuff } ?
that would be nice -
Regarding the onClicked handler in both contexts being invoked, I'm really not sure. The id.handler syntax is a strange one, and to be honest I didn't know that it was supported.
Regarding the future syntax possibilities, well, I don't think that will change in 5.0. Certainly, I don't think that assignment like:
@
property var someprop: { a + b }
@
will ever be a function assignment - it'll always be either a syntax error, an object assignment, or a binding assignment (simply because we want to maintain semantics between var properties and other properties).For example:
@
function someFunc() { return 5 }
property int p1: someFunc()
property var p2: someFunc()
@It would be "strange" if the value of p1 was 5, but the value of p2 was [Object function]
As for changing it to just
@
someprop: [console.log("blah")]
@
the problem is that it will always simply evaluate the inner bit, and assign it to the zeroth index of the array.Finally,
@
someprop: [{console.log("blah")}]
@
could be made to work, I guess, assuming that the ecma262r5 spec defines such a block as always being a function, and not possibly an object with an uninitialised property, but I think requiring the function to be specified explicitly is clearer.But anyway, I don't really think that we can (or should) change the syntax dramatically within the 5.x series.
Cheers,
Chris.