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 &params, 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 &params, 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!


Log in to reply