3
votes

I am trying to figure out how I am supposed to use enumerations in Qt so that I pack them in a QVariant and convert them to a QJsonValue and later JSON.

Following the docs I ended up declaring my enums:

enum BeautifulColors { Red, Green, Blue };
Q_DECLARE_METATYPE(BeautifulColors);

That way I can use setValue(..) on QVariant to set my custom defined enums as value.

The problem however is QJsonValue::fromVariant(), the docs says:

Converts variant to a QJsonValue and returns it. (...) For all other QVariant types a conversion to a QString will be attempted. If the returned string is empty, a Null QJsonValue will be stored, otherwise a String value using the returned QString.

The conversion to QString fails and and my QJsonValue object ends up being Null.

Following the documentation further is confusing: There is a Q_EUM macro for enumeration definition within QObject. However since QObject is non-copy able I don't think QVariant is supposed to hold it. There are certainly some hacky was to get it working, but that is not what I am looking for. What is the recommended way in Qt to define enums so that they can be used as datatypes and converted into JSON and read from JSON?

Update

Tried the following:

rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H

#include <QObject>

class Rectangle : public QObject
{
    Q_OBJECT

public:

    enum Color
    {
        Red,
        Green,
        Blue,
    };

    Q_ENUM(Color)

    Rectangle(double x, double y, Color color, QObject *parent = 0);

private:

    double _x;
    double _y;
    Color _color;
};

#endif

rectangle.cpp
#include "rectangle.h"

Rectangle::Rectangle(double x, double y, Rectangle::Color color, QObject *parent)
    : QObject(parent)
    , _x(x)
    , _y(y)
    , _color(color)
{

}

main.cpp
#include <QVariant>
#include <QDebug>
#include <QString>

#include "rectangle.h"
int main(int argc, char *argv[])
{
    int id = qMetaTypeId<Rectangle::Color>();
    Rectangle::Color blueColor = Rectangle::Blue;
    QVariant myVariant;
    myVariant.setValue(blueColor);
    qDebug() << id;
    qDebug() << myVariant.toString();
}

Now it has a typ id and a string representation! But not the class holding it:

int idRectangle = qMetaTypeId<Rectangle>();

Does not compile and I cannot register it with Q_DECLARE_MEATYPE, because it does not have a constructor. What if I need QVariants toString() to work with any class?

Second Update

Using the Q_GADGET macro I now get a (different) type id for the enum and the class holding it. However I still only get a string representation for the enum.

2

2 Answers

3
votes

Q_ENUM or Q_ENUMS are needed to generate the necessary QMetaEnum structure to toString/fromString functionality.

They need to be placed in a QObject derived class with the Q_OBJECT marker or in any class with the Q_GADGET marker in order for moc to process them and generate the necessary code.

The class they are defined in is not the one being stored in the variant though when you store an enum value. You can consider this class more like a "namespace" for your enum.

3
votes

I figured the rest out, to be able to use QVariant::toString() a conversion must be registered for the type held by the QVariant.

This was added by KDAB in Qt 5.2 (http://log.cedricbonhomme.org/55/66102.html) but is nowhere mentioned in the doc QVariant::toString()! :(

Anyways it also works for plain enums, the following example will output "Apple"

enum Fruit {
    Apple,
    Pear,
    Orange
};

Q_DECLARE_METATYPE(Fruit)

QString Fruit2QString(Fruit fruit)
{
    switch(fruit)
    {
        case Apple:
            return "Apple";
        case Pear:
            return "Pear";
        case Orange:
            return "Orange";
    }

    return "asdf";
}

int main(int argc, char *argv[])
{
    std::function<QString(Fruit)> f = &Fruit2QString;
    bool success = QMetaType::registerConverter<Fruit, QString>(f);
    Q_ASSERT(success);
    QVariant v;
    v.setValue(Fruit::Apple);
    qDebug() << v.toString();
}