I wrote a code which exposes an C++ object to QML and I would like to check declared properties of the object with native JS things, but they does not work as it expected. I wrote FizzBuzzDerived.properties method and it works file as I would expect Object.keys work.
Result in case of calling the functions
Object.keys(FizzBuzz); // -> []
Object.values(FizzBuzz); // -> []
JSON.stringify(FizzBuzz); // -> {}
FizzBuzz.properties(); // -> ["objectName", "fizz", "buzz", "foo", "bar"]
Here is the code.
main.cpp
#include "myplugin.h"
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlExtensionPlugin>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyPlugin plugin;
plugin.registerTypes("ObjectStorage");
const QUrl url = QStringLiteral("qrc:/main.qml");
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
myplugin.h
#ifndef MYPLUGIN_H
#define MYPLUGIN_H
#include "objectstorage.h"
#include <QObject>
#include <QQmlExtensionPlugin>
class MyPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("ObjectStorage"));
qmlRegisterSingletonType<FizzBuzzDerived>("Model", 1, 0, "FizzBuzz", ObjectStorage::provider);
}
};
#endif // MYPLUGIN_H
objectstorage.h
#ifndef OBJECTSTORAGE_H
#define OBJECTSTORAGE_H
#include "fizzbuzzderived.h"
#include <QObject>
#include <QQmlEngine>
class ObjectStorage : public QObject
{
Q_OBJECT
public:
static QObject *provider(QQmlEngine *qml, QJSEngine *js)
{
Q_UNUSED(qml)
Q_UNUSED(js)
FizzBuzzDerived *object = new FizzBuzzDerived();
object->setFizz(45);
object->setBuzz(24.42);
object->setFoo(false);
object->setBar(Bar::B);
return object;
}
};
#endif // OBJECTSTORAGE_H
fizzbuzzderived.h
#ifndef FIZZBUZZDERIVED_H
#define FIZZBUZZDERIVED_H
#include "fizzbuzz.h"
#include <QObject>
#include <QDebug>
class FizzBuzzDerived : public QObject
{
Q_OBJECT
Q_PROPERTY(int fizz READ fizz WRITE setFizz NOTIFY fizzChanged);
Q_PROPERTY(double buzz READ buzz WRITE setBuzz NOTIFY buzzChanged)
Q_PROPERTY(bool foo READ foo WRITE setFoo NOTIFY fooChanged)
Q_PROPERTY(int bar READ bar WRITE setBar NOTIFY barChanged)
public:
explicit FizzBuzzDerived(QObject *parent = nullptr);
Q_INVOKABLE QList<QString> properties() const;
int fizz() const;
double buzz() const;
bool foo() const;
int bar() const;
public slots:
void setFizz(int fizz);
void setBuzz(double buzz);
void setFoo(bool foo);
void setBar(int bar);
signals:
void fizzChanged(int fizz);
void buzzChanged(double buzz);
void fooChanged(bool foo);
void barChanged(int bar);
private:
FizzBuzz m_object;
};
Q_DECLARE_METATYPE(FizzBuzzDerived *)
#endif // FIZZBUZZDERIVED_H
fizzbuzzderived.cpp
#include "fizzbuzzderived.h"
#include <QMetaProperty>
FizzBuzzDerived::FizzBuzzDerived(QObject *parent)
: QObject(parent)
{
}
QList<QString> FizzBuzzDerived::properties() const
{
QList<QString> names;
const QMetaObject *metaObject = this->metaObject();
for (int i = 0; i < metaObject->propertyCount(); ++i) {
QMetaProperty property = metaObject->property(i);
names << QString(property.name());
}
return names;
}
int FizzBuzzDerived::fizz() const
{
return m_object.fizz;
}
double FizzBuzzDerived::buzz() const
{
return m_object.buzz;
}
bool FizzBuzzDerived::foo() const
{
return m_object.foo;
}
int FizzBuzzDerived::bar() const
{
return m_object.bar;
}
void FizzBuzzDerived::setFizz(int fizz)
{
if (m_object.fizz != fizz) {
m_object.fizz = fizz;
emit fizzChanged(m_object.fizz);
}
}
void FizzBuzzDerived::setBuzz(double buzz)
{
if (m_object.buzz != buzz) {
m_object.buzz = buzz;
emit buzzChanged(m_object.buzz);
}
}
void FizzBuzzDerived::setFoo(bool foo)
{
if (m_object.foo != foo) {
m_object.foo = foo;
emit fooChanged(m_object.foo);
}
}
void FizzBuzzDerived::setBar(int bar)
{
if (m_object.bar != bar) {
m_object.bar = static_cast<Bar>(bar);
emit barChanged(m_object.bar);
}
}
main.qml
import QtQuick 2.0
import Model 1.0
Item {
property FizzBuzz item: FizzBuzz
Component.onCompleted: {
const object = item;
const props = object.properties();
console.info(`object: ${object}`)
console.info(props.map(prop => `${prop}: ${JSON.stringify(object[prop])}`).join(", "));
}
}
Tried to get the object keys from C++ side and it works. Don't have a clue why this is happening.
By the way, I use Qt 5.14.2.
I recommend having a
QVariantMaporQVariantproperty. When QML/JS sees this it will marshal it into a proper Javascript object which means all Javascript functions such as JSON.stringify() will now work.Changing your private
FizzBuzz m_objecttoQVariantMap m_jsonshould do the trick.Also, for your signal handlers, I do not recommend offering properties since this will make it ambiguous later on whether handlers are accessing properties or arguments since they're both named the same. In fact, you should rely more on your properties and make your signal handlers parameterless.
Your QML code should be: