4
votes

Why can my Q_GADGET be read perfectly in QML (JS) but not my Q_OBJECT?

Running Qt 5.8.0 on Ubuntu 14.04.

I'm attempting to return a list (QVariantMap) of objects to QML. I'm keeping it simple right now, no pointers, etc.. just copies of the objects.

struct Blob
{
   Q_GADGET

   Q_PROPERTY(QString uuid MEMBER uuid_ CONSTANT)
   Q_PROPERTY(QVector3D centroid MEMBER centroid_)
   // ...

   Blob() {};
   ~Blob() = default;
   Blob(const Blob& blob);
 };
 Q_DECLARE_METATYPE(Blob)

There are a bunch of QString and QVector3D members on Blob.

In main.cpp, I also register the type:

qmlRegisterType<Blob>("matt", 1, 0, "Blob");

Then my JS code is able to read all the properties (for loop iterating over them) on the object without issue.

But if I use a Q_OBJECT

struct Blob : public QObject
{
   Q_OBJECT

   Q_PROPERTY(QString uuid MEMBER uuid_ CONSTANT)
   Q_PROPERTY(QVector3D centroid MEMBER centroid_)
   // ...

   explicit Blob(QObject parent = nullptr) : QObjecT(parent) {};
   ~Blob() = default;
   Blob(const Blob& blob);
 };
 Q_DECLARE_METATYPE(Blob)

Then the JS receives an object where all the properies are the keys from the QVariantMap, but whose values are empty objects.

Note, before sending it to the JS, I convert it to a QVariant and back again just to confirm that that works, e.g.

Blob b;
qDebug() << "Created: " << b;
QVariant var = QVariant::fromValue(b);
qDebug() << "   Converted back = " << var.value<Blob>().toQString();

I'd prefer to use a Q_OBJECT such that I have slots/signals.

1

1 Answers

0
votes

The reason I was seeing a difference between Q_OBJECT and Q_GADGET was that I was making copies of my object, which is allowed on a Q_GADGET (a value) object, but not on a Q_OBJECT (an identity object.) See identities instead of values.

The solution is to always work on Q_OBJECTs with pointers. This maintains their identity, and avoid copies.

Also, my original intent was to use a smart pointer, but the reasons why that is a bad approach are explained in this answer.

The comment by @dtech also explained that Q_DECLARE_METATYPE is redundant on a Q_OBJECT.

Thus, my final declaration is:

i.e.

class Blob : public QObject
{
  Q_OBJECT

  Q_PROPERTY(QString uuid MEMBER uuid_ CONSTANT)
  Q_PROPERTY(QVector3D centroid MEMBER centroid_)
  // ...

  explicit Blob(QObject parent = nullptr) : QObjecT(parent) {};
  ~Blob() = default;
  Blob(const Blob& blob);
};

With this, I can easily put a raw pointer to these objects in a QVariantMap, and they can be read on the QML/JS side.