AAAAAAAAARGH
AAAAAAAAARGH

Reputation: 53

QML/QT How to convert object from C++ to QML?

It took me a while, but eventually I managed to convert a JavaScript/QML POJO to a custom object not derived from QObject.

I think it's easier to understand my issue with a working example. So let's start with this:

struct SomeType { /* Just a plain struct that does not derive from QObject! */ }; 

SomeType FooFactory::convertQMLToSomeType(const QJSValue& val) {
  SomeType result = /*... some kind of conversion takes place here ... */
  return result;
}

void FooFactory::registerTypes(QQmlEngine& engine) {
  QMetaType::registerConverter<QJSValue, SomeType>(FooFactory::convertQMLToSomeType);
}

What this does is it registers a converter for the transformation of QSValue to SomeType. So now, whenever I do something like this in QML

my_prop = { "foo": "some plain javascript object" };

assuming my_prop is exposed like so in the corresponding C++ class:

Q_PROPERTY(SomeType my_prop MEMBER _myProp);
SomeType _myProp;

// Somewhere else outside the class, this is needed for registering the converter
Q_DECLARE_METATYPE(SomeType);

the string is implicitly converted into a SomeType without the need of doing things manually.

..

Great!

But what about the opposite direction of the conversion? I Need QML to deal with strings, not QVariant(SomeType) objects (QT always uses QVariant wrappers internally to store user defined types when dealing with the meta system).

I already tried registering an inverse converter like this:

QMetaType::registerConverter<SomeType, QJSValue>(FooFactory::convertBackToQML);

or this

QMetaType::registerConverter<QVariant(SomeType), QJSValue>(FooFactory::convertBackToQML);

but none of these approaches work. I believe the second line is quite promising, but I wasn't even able to compile that one due to problems with registering the static meta type.

So, how would I solve this? As a short reminder, I am not able to derive SomeType from QObject, and yes, I am aware that this is the most common way to do these kinds of things.

Does anyone have an idea? Or am I barking up the wrong tree? Many thanks in advance!

Upvotes: 0

Views: 1107

Answers (2)

Dmitri Ovodok
Dmitri Ovodok

Reputation: 176

If you want to operate with your structure as with a plain JavaScript object, then another option may be using QVariantMap as a type for your property. Then you could define the needed conversions in getter and setter of your property:

class Whichever : public QObject {
    Q_OBJECT
    Q_PROPERTY(QVariantMap my_prop READ myProp WRITE setMyProp)

    QVariantMap myProp() const {
         QVariantMap map;
         map["some_field"] = _myProp.someField;
         // Fill the other fields as needed.
         return map;
    }

    void setMyProp(const QVariantMap& map) {
        _myProp.someField = map["some_field"].toString();
        // Fill the other _myProp fields as needed.
    }
};

Conversions between QVariantMap and the actual JavaScript objects would be handled by QML engine automatically. Nested JavaScript objects should also be possible by nesting the corresponding QVariantMaps.

Of course, this option makes property declaration not so explicit about which type does it correspond to, and perhaps it will require a bit more boilerplate code if you need to declare multiple properties of this type (you can define and use your own macro for that though if needed). But this is probably one of the easiest ways to achieve what you have described.

Upvotes: 1

JarMan
JarMan

Reputation: 8277

This may not be what you want, but it might also be your only option. You can create a QObject wrapper class around your struct. Just create properties for whatever values in the struct you want to expose.

class SomeWrapper : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString someData READ someData WRITE setSomeData NOTIFY someDataChanged)

public:
    explicit SomeWrapper(QObject *parent = nullptr) : QObject(parent) {}

    QString someData() { return m_struct.someData; }
    void setSomeData(QString data)
    {
        if (data != m_struct.someData)
        {
            m_struct.someData = data;
            emit someDataChanged();
        }
    }

signals:
    void someDataChanged();

private:
    SomeType m_struct;
};

Upvotes: 2

Related Questions