Solved QVariant to QJSValue
-
Situation:
I expose a Service instance to QML as a root context property, which has a Q_INVOKABLE method that takes a QVariantMap as well as a callback function (basically a callable QJSValue) and try to call the callback given the parameters from the QVariantMap I pass to it in the data object (as a JavaScript callback argument).Problem:
Unfortunatelly all the properties I set to the data object are undefined, I still can't manage to convert the individual QVariant objects to QJSValue objects to assign those to the data object argument as properties that I pass to the callback function. Any Q_GADGET or QString or int etc. are undefined on the data object when console.log'ing them.Question:
The question is how to pass any kind of QVariantMap containing any kind of QVariant objects including custom types such as Q_GADGET to a JavaScript callback function in QML.Source:
service.hpp#pragma once #include <QObject> #include <QJSValue> #include <QQmlEngine> class Service : public QObject { Q_OBJECT protected: QQmlEngine* _engine; public: Service(QQmlEngine* engine); void encode(const QVariantMap& data, QJSValue& val); Q_INVOKABLE void call(const QVariantMap& params, QJSValue callback); };
service.cpp
#include "service.hpp" #include <QVariant> #include <QVariantMap> #include <QJSValue> #include <QQmlEngine> #include <QDebug> #include <QMetaType> Service::Service(QQmlEngine* engine) : _engine(engine) { } void Service::encode(const QVariantMap& data, QJSValue& val) { for( QVariantMap::const_iterator dataItr(data.constBegin()); dataItr != data.constEnd(); ++dataItr ) { if(dataItr->userType() == QMetaType::QVariantMap) { //process maps recursively QJSValue mapval(_engine->newObject()); encode(qvariant_cast<QVariantMap>(*dataItr), mapval); val.setProperty(dataItr.key(), mapval); continue; } QObject* objvar(qvariant_cast<QObject*>(*dataItr)); qDebug() << " object: " << objvar; val.setProperty(dataItr.key(), _engine->newQObject(objvar)); } } void Service::call(const QVariantMap& params, QJSValue callback) { qDebug() << "CALL " << params; if(!callback.isCallable()) { return; } QJSValue val(_engine->newObject()); encode(params, val); callback.call({val}); }
person.hpp
#pragma once #include <QObject> #include <QMetaType> #include <QString> class Person { Q_GADGET Q_PROPERTY(QString name READ name) Q_PROPERTY(QString id READ id) protected: QString _name; int _id; public: Person(); Person(const QString& name, int id); QString name() const; int id() const; }; Q_DECLARE_METATYPE(Person)
person.cpp
#include "person.hpp" #include <QString> Person::Person() : _name("untitled"), _id(-1) { } Person::Person(const QString& name, int id) : _name(name), _id(id) { } QString Person::name() const { return _name; } int Person::id() const { return _id; }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "person.hpp" #include "service.hpp" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); Person director("Director", 404); Person worker("Worker", 404); QQmlApplicationEngine* engine(new QQmlApplicationEngine); Service* service(new Service(engine)); QQmlContext* ctx(engine->rootContext()); ctx->setContextProperty( "service", service ); ctx->setContextProperty( "directorPerson", QVariant::fromValue(director) ); ctx->setContextProperty( "workerPerson", QVariant::fromValue(worker) ); engine->load(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); }
main.qml
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") Button { anchors.centerIn: parent spacing: 8 text: "test" onClicked: service.call( { company: "Something Inc.", tax_id: 6991625524, staff: { person: directorPerson, departament: { worker: workerPerson } } }, function(data) { console.log("DATA:") console.log(" data.company:", data.company) console.log(" data.tax_id:", data.tax_id) console.log(" data.staff:", data.staff) console.log(" data.staff.director:", data.staff.director) console.log(" data.staff.departament:", data.staff.departament) console.log(" data.staff.departament.worker:", data.staff.departament.worker) } ) } }
-
Use http://doc.qt.io/qt-5/qjsengine.html#toScriptValue to convert from QVariantMap to QJSValue:
void Service::call(const QVariantMap ¶ms, QJSValue callback) { qDebug() << "CALL " << params; if (!callback.isCallable()) { return; } QJSValue value = _engine->toScriptValue<QVariantMap>(params); callback.call({value});
}
-
@AnatolyS said in QVariant to QJSValue:
Use http://doc.qt.io/qt-5/qjsengine.html#toScriptValue to convert from QVariantMap to QJSValue:
void Service::call(const QVariantMap ¶ms, QJSValue callback) { qDebug() << "CALL " << params; if (!callback.isCallable()) { return; } QJSValue value = _engine->toScriptValue<QVariantMap>(params); callback.call({value});
}
QJSEngine::toScriptValue perfectly solves the problem. Many thanks!