Using Qt 5.6 QML (upgrade is not an option)... I need to pass a structure containing an array of structures between QML and C++ (both ways).
struct arrayStruct {
bool someParam;
};
struct someStruct {
bool topParam;
QVector< arrayStruct > arrayOfStructs;
};
With the following code, I can create an instance of the structure with the array and pass that around, but I can't modify the content of the array. I can't figure out what I'm missing...
// Create an object in a QML context
var workObj = objArrayComponent.createObject(mainForm)
// These things work
workObj.topParam = true
workObj.objArray = [ { someParam : true }, { someParam : false }, {someParam : true } ]
// Neither of the following work...
// After this call, the value remains false
workObj.objArray[1].someParam = true;
// After this call, the length remains 3
workObj.objArray.push( { someParam : true } )
// This works
workObj.objArray = [ { someParam : false }, { someParam : true }, {someParam : false } ]
I also tried a version of the code where I split out the READ/WRITE and created my own getter/setter. I verified the setter was called when the entire workObj.objArray was set, but it was not called for the other changes.
Full code below...
objarray.h (there is no objarray.cpp, MEMBER creates getter/setter):
#include <QObject>
#include <QVariant>
class SomeObj : public QObject
{
Q_OBJECT
public:
explicit SomeObj(QObject *parent = nullptr) : QObject(parent) {}
Q_PROPERTY( bool someParam MEMBER mSomeParam NOTIFY someParamChanged )
signals:
void someParamChanged();
public slots:
private:
bool mSomeParam;
};
class ObjArray : public QObject
{
Q_OBJECT
Q_PROPERTY( bool topParam MEMBER mTopParam NOTIFY topParamChanged )
Q_PROPERTY( QVariantList objArray MEMBER mObjArray NOTIFY objArrayChanged )
public:
explicit ObjArray(QObject *parent = nullptr) : QObject(parent) {}
signals:
void topParamChanged();
void objArrayChanged();
public slots:
private:
bool mTopParam;
QVariantList mObjArray;
};
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include "objarray.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<ObjArray>("obj.array", 1, 0, "ObjArray");
qmlRegisterType<SomeObj>("some.obj", 1, 0, "SomeObj");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
import obj.array 1.0
import some.obj 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MainForm {
id: mainForm
Component {
id: objArrayComponent
ObjArray {}
}
anchors.fill: parent
mouseArea.onClicked: {
// Create an object in the form's context
var workObj = objArrayComponent.createObject(mainForm)
workObj.topParam = true
workObj.objArray = [ { someParam : true }, { someParam : false }, {someParam : true } ]
// So far so good
// results: true, (3): [true, false, true]
console.log( "results: " + workObj.topParam + ", " +
"(" + workObj.objArray.length + "): [" +
workObj.objArray[0].someParam + ", " +
workObj.objArray[1].someParam + ", " +
workObj.objArray[2].someParam + "] "
)
// This works
workObj.topParam = false
// Neither of the following work...
workObj.objArray[1].someParam = true;
workObj.objArray.push( { someParam : true } )
// Value is not changed
// Array size doesn't change
// results: false, (3): [true, false, true]
console.log( "results: " + workObj.topParam + ", " +
"(" + workObj.objArray.length + "): [" +
workObj.objArray[0].someParam + ", " +
workObj.objArray[1].someParam + ", " +
workObj.objArray[2].someParam + "] "
)
// This works
workObj.objArray = [ { someParam : false }, { someParam : true }, {someParam : false } ]
// results: false, (3): [false, true, false]
console.log( "results: " + workObj.topParam + ", " +
"(" + workObj.objArray.length + "): [" +
workObj.objArray[0].someParam + ", " +
workObj.objArray[1].someParam + ", " +
workObj.objArray[2].someParam + "] "
)
console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
Qt.quit()
}
}
}
EDIT:
Apparently QML variant arrays can't be modified, and I ran out of time to find a better solution, so I decided to just go with a quick-and-dirty get/set. This is fine for small structs, but scaling to bigger structs will be annoying.
no error checking for compactness...
public:
Q_INVOKABLE void setValue( QString name, int index, bool value )
{
if( name == "someParam" )
mStoreIt[index].someParam = value;
}
Q_INVOKABLE bool getValue( QString name, int index )
{
if( name == "someParam" )
return mStoreIt[index].someParam;
}
private:
QVector<someObj> mStoreIt;