Strange binding behavior when using binding on array's element
-
I have an Item with a property. This property contains array of javascript objects. Each javascript object has properties.
When I set binding for one of object's properties to some variable and its (variable) value changes triggering the binding then all properties in the array get reevaluated.Consider the following code:
import QtQuick 2.4 import QtQuick.Controls 1.3 ApplicationWindow { id: container width: 640 height: 480 property int clicksCounter: 0 property string name : "default name" Item { id: testObject property var myArray : [{ name : container.name, boolFlag : false }] } Rectangle { x: 10 y: 10 width: 100 height: 100 color: "red" MouseArea { anchors.fill: parent onClicked: { container.clicksCounter++ console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : Changing value for property container.name and setting true to 'boolFlag' property of first array's element\n") var element = testObject.myArray[0] console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : [BEFORE] testObject.myArray[0].name: " + element.name + ', testObject.myArray[0].boolFlag: ' + element.boolFlag) container.name = "new name" element.boolFlag = true console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : [AFTER] testObject.myArray[0].name: " + element.name + ', testObject.myArray[0].boolFlag: ' + element.boolFlag + "\n") } } } Rectangle { x: 120 y: 10 width: 100 height: 100 color: "blue" MouseArea { anchors.fill: parent onClicked: { container.clicksCounter++ var element = testObject.myArray[0] console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : testObject.myArray[0].name: " + element.name + ', testObject.myArray[0].boolFlag: ' + element.boolFlag + "\n") } } } }
Clicking red square changes the value of array, clicking blue square shows current boolean value of the flag.
Here are the results of clicking the squares:(1)
qml: CLICK #1[BLUE SQUARE] : testObject.myArray[0].name: default name, testObject.myArray[0].boolFlag: false(2)
qml: CLICK #2[RED SQUARE] : Changing value for property container.name and setting true to 'boolFlag' property of first array's elementqml: CLICK #2[RED SQUARE] : [BEFORE] testObject.myArray[0].name: default name, testObject.myArray[0].boolFlag: false
qml: CLICK #2[RED SQUARE] : [AFTER] testObject.myArray[0].name: default name, testObject.myArray[0].boolFlag: true(3)
qml: CLICK #3[BLUE SQUARE] : testObject.myArray[0].name: new name, testObject.myArray[0].boolFlag: falseWhy is testObject.myArray[0].boolFlag equals FALSE on click 3 if its value was set to TRUE on click 2?
-
-
Okay, I managed to reproduce it. But for this I had to add C++ part.
C++ code is this:
// main.cpp #include <QGuiApplication> #include <QQuickWindow> #include <QQmlApplicationEngine> #include <QQmlContext> class Test : public QObject { Q_OBJECT Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY valueChanged) QString getEmptyString(){ return ""; } public: Test(QObject* parent = 0) : QObject(parent) {} Q_INVOKABLE void triggerNotify() { valueChanged(); } signals: void valueChanged(); }; int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Test* test = new Test(&engine); engine.rootContext()->setContextProperty("__test__", (QObject*)test); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, [](QObject* rootObject) { QQuickWindow *appWindow = qobject_cast<QQuickWindow *>(rootObject); appWindow->show(); }); engine.load(QUrl(QStringLiteral("qrc:/ui/main.qml"))); return app.exec(); }
QML code is this:
// main.qml import QtQuick 2.4 import QtQuick.Controls 1.3 ApplicationWindow { id: container width: 640 height: 480 property int clicksCounter: 0 Item { id: testObject property var myArray : [{ name : "CustomName" + __test__.emptyString, boolFlag : false }] } Rectangle { x: 10 y: 10 width: 100 height: 100 color: "red" MouseArea { anchors.fill: parent onClicked: { container.clicksCounter++ console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : Set testObject.myArray[0] to TRUE\n") testObject.myArray[0].boolFlag = true console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : DONE\n") } } } Rectangle { x: 120 y: 10 width: 100 height: 100 color: "blue" MouseArea { anchors.fill: parent onClicked: { container.clicksCounter++ console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : Triggering notify by calling C++ <Test::triggerNotify> method \n") console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : [BEFORE] testObject.myArray[0].name: " + testObject.myArray[0].name + ', testObject.myArray[0].boolFlag: ' + testObject.myArray[0].boolFlag) __test__.triggerNotify() console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : [AFTER] testObject.myArray[0].name: " + testObject.myArray[0].name + ', testObject.myArray[0].boolFlag: ' + testObject.myArray[0].boolFlag) } } } }
Here is what I get:
qml: CLICK #1[RED SQUARE] : Set testObject.myArray[0] to TRUE
qml: CLICK #1[RED SQUARE] : DONEqml: CLICK #2[BLUE SQUARE] : Triggering notify by calling C++ Test::triggerNotify method
qml: CLICK #2[BLUE SQUARE] : [BEFORE] testObject.myArray[0].name: CustomName, testObject.myArray[0].boolFlag: true
qml: CLICK #2[BLUE SQUARE] : [AFTER] testObject.myArray[0].name: CustomName, testObject.myArray[0].boolFlag: falseSo what happens here is that after I set testObject.myArray[0].boolFlag from FALSE to TRUE and call test.triggerNotify() method my flag automatically resets to its initial value. Same goes if any other type used - int, string, etc. Why does this happen?
[UPDATE] QT 5.5 is used, Visual Studio 2013 Update 4 x32.
-
@geniuss Here is you answer:
http://doc.qt.io/qt-5/qml-var.html#change-notification-semantics
Edit: But that doesn't justify why it becomes false O_o -
@geniuss I would suggest you to ask at Qt Interest Mailing List. You can get an answer directly from Qt Engineers regarding these internals.