Accessing an enum stored in a QVariant
Asked Answered
M

5

7

I have registered an enumeration type "ClefType" within my header file - this enum is registered with the MetaObject system using the Q_DECLARE_METATYPE and Q_ENUMS macros. qRegisterMetaType is also called in the class constructor.

This allows me to use this type in a Q_PROPERTY, this all works fine. However, later on, I need to be able to get hold of the Q_PROPERTY of this enum type, given the object - in a form that is suitable for serialization.

Ideally, it would be useful to store the integer value for that enum member, because I don't want this to be specific to the type of enum that is used - eventually I want to have several different enums.

// This is inside a loop over all the properties on a given object
QMetaProperty property = metaObject->property(propertyId);
QString propertyName = propertyMeta.name();
QVariant variantValue = propertyMeta.read(serializeObject);

// If, internally, this QVariant is of type 'ClefType',
// how do I pull out the integer value for this enum?

Unfortunately variantValue.toInt(); does not work - custom enums don't seem to be directly 'castable' to an integer value.

Thanks in advance,

Henry

Mouthpart answered 1/4, 2010 at 12:24 Comment(0)
I
1

You can use the >> and << operators of QVariant to accomplish this.

Saving (where MyClass *x = new MyClass(this); and out is a QDataStream):

const QMetaObject *pObj = x->pObj();
for(int id = pObj->propertyOffset(); id < pObj->propertyCount(); ++id)
{
    QMetaProperty pMeta = pObj->property(id);
    if(pMeta.isReadable() && pMeta.isWritable() && pMeta.isValid())
    {
        QVariant variantValue = pMeta.read(x);
        out << variantValue;
    }
}

Loading:

const QMetaObject *pObj = x->pObj();
for(int id = pObj->propertyOffset(); id < pObj->propertyCount(); ++id)
{
    QMetaProperty pMeta = pObj->property(id);
    if(pMeta.isReadable() && pMeta.isWritable() && pMeta.isValid())
    {
        QVariant variantValue;
        in >> variantValue;
        pMeta.write(x, variantValue);
    }
}

You will need to call

    qRegisterMetaType<CMyClass::ClefType>("ClefType");
    qRegisterMetaTypeStreamOperators<int>("ClefType");

in addition to using Q_OBJECT, Q_ENUMS and Q_PROPERTY. Calling qRegisterMetaTypeStreamOperators<int> tells Qt to use the int versions of operator<< and operator>>.

By the way: using qRegisterMetaType<CMyClass::ClefType>() instead of the form that takes a name doesn't work for me. It might if you used the returned id to lookup the name, but this is much easier.

FYI, here is the MyClass definition:

class CMyClass : public QObject
{
    Q_OBJECT
    Q_ENUMS(ClefType)
    Q_PROPERTY(ClefType cleftype READ getCleftype WRITE setCleftype)
public:
    CMyClass(QObject *parent) : QObject(parent), m_cleftype(One)
    {
        qRegisterMetaType<CMyClass::ClefType>("ClefType");
        qRegisterMetaTypeStreamOperators<int>("ClefType");
    }
    enum ClefType { Zero, One, Two, Three };
    void setCleftype(ClefType t) { m_cleftype = t; }
    ClefType getCleftype() const { return m_cleftype; }
private:
    ClefType m_cleftype;
};

Q_DECLARE_METATYPE(CMyClass::ClefType)
Inhere answered 6/4, 2010 at 20:58 Comment(1)
That works great - thanks a lot for your in depth response! Much appreciatedMouthpart
C
2

Try:

int x = variantValue.value<ClefType>();
Canute answered 1/4, 2010 at 13:4 Comment(1)
While this will work specifically for ClefType, i'd ideally like this to work for any enumerated type.Mouthpart
I
1

You can use the >> and << operators of QVariant to accomplish this.

Saving (where MyClass *x = new MyClass(this); and out is a QDataStream):

const QMetaObject *pObj = x->pObj();
for(int id = pObj->propertyOffset(); id < pObj->propertyCount(); ++id)
{
    QMetaProperty pMeta = pObj->property(id);
    if(pMeta.isReadable() && pMeta.isWritable() && pMeta.isValid())
    {
        QVariant variantValue = pMeta.read(x);
        out << variantValue;
    }
}

Loading:

const QMetaObject *pObj = x->pObj();
for(int id = pObj->propertyOffset(); id < pObj->propertyCount(); ++id)
{
    QMetaProperty pMeta = pObj->property(id);
    if(pMeta.isReadable() && pMeta.isWritable() && pMeta.isValid())
    {
        QVariant variantValue;
        in >> variantValue;
        pMeta.write(x, variantValue);
    }
}

You will need to call

    qRegisterMetaType<CMyClass::ClefType>("ClefType");
    qRegisterMetaTypeStreamOperators<int>("ClefType");

in addition to using Q_OBJECT, Q_ENUMS and Q_PROPERTY. Calling qRegisterMetaTypeStreamOperators<int> tells Qt to use the int versions of operator<< and operator>>.

By the way: using qRegisterMetaType<CMyClass::ClefType>() instead of the form that takes a name doesn't work for me. It might if you used the returned id to lookup the name, but this is much easier.

FYI, here is the MyClass definition:

class CMyClass : public QObject
{
    Q_OBJECT
    Q_ENUMS(ClefType)
    Q_PROPERTY(ClefType cleftype READ getCleftype WRITE setCleftype)
public:
    CMyClass(QObject *parent) : QObject(parent), m_cleftype(One)
    {
        qRegisterMetaType<CMyClass::ClefType>("ClefType");
        qRegisterMetaTypeStreamOperators<int>("ClefType");
    }
    enum ClefType { Zero, One, Two, Three };
    void setCleftype(ClefType t) { m_cleftype = t; }
    ClefType getCleftype() const { return m_cleftype; }
private:
    ClefType m_cleftype;
};

Q_DECLARE_METATYPE(CMyClass::ClefType)
Inhere answered 6/4, 2010 at 20:58 Comment(1)
That works great - thanks a lot for your in depth response! Much appreciatedMouthpart
R
1

I had this same problem, and came up with the solution below, which works for any enumerated type:

int x = property.enumerator().value(*reinterpret_cast<const int *>(variantValue.constData()));
Recreation answered 1/12, 2010 at 3:31 Comment(0)
H
1

You can use the new Q_ENUM macro (added in Qt 5.5) and not worry about calling qRegisterMetaType(). I wrote a test app (using Google Test) to prove to myself that it worked:

#include <gtest/gtest.h>

#pragma warning(push, 0)
#include <QObject>
#include <QtTest/QSignalSpy>
#pragma warning(pop)

class tColoredObject : public QObject
{
    Q_OBJECT

public:
    enum class eColor
    {
        Red = 1,
        Blue = 2,
        Green = 3
    };
    Q_ENUM(eColor)

    tColoredObject() : m_color(eColor::Red) {}

    void SendSignal(tColoredObject::eColor color) { emit TestSignal(color); }

signals:
    void TestSignal(tColoredObject::eColor color);

private:
    eColor m_color;
};

TEST(Enum, EnumValue)
{
    const QVariant varRed = QVariant::fromValue(tColoredObject::eColor::Red);
    const QVariant varBlue = QVariant::fromValue(tColoredObject::eColor::Blue);
    const QVariant varGreen = QVariant::fromValue(tColoredObject::eColor::Green);

    EXPECT_EQ(varRed.toUInt(), static_cast<uint>(tColoredObject::eColor::Red));
    EXPECT_EQ(varBlue.toUInt(), static_cast<uint>(tColoredObject::eColor::Blue));
    EXPECT_EQ(varGreen.toUInt(), static_cast<uint>(tColoredObject::eColor::Green));

    EXPECT_TRUE(varRed.canConvert<tColoredObject::eColor>());
    EXPECT_TRUE(varBlue.canConvert<tColoredObject::eColor>());
    EXPECT_TRUE(varGreen.canConvert<tColoredObject::eColor>());

    EXPECT_EQ(varRed.value<tColoredObject::eColor>(), tColoredObject::eColor::Red);
    EXPECT_EQ(varBlue.value<tColoredObject::eColor>(), tColoredObject::eColor::Blue);
    EXPECT_EQ(varGreen.value<tColoredObject::eColor>(), tColoredObject::eColor::Green);
}

TEST(Enum, EmitFunctionSucceeds)
{
    tColoredObject objColor;
    QSignalSpy OnTestSignal(&objColor, &tColoredObject::TestSignal);

    objColor.TestSignal(tColoredObject::eColor::Blue);
    EXPECT_EQ(1, OnTestSignal.count());
    QList<QVariant> arguments = OnTestSignal.takeFirst();
    EXPECT_EQ(tColoredObject::eColor::Blue, arguments.at(0).value<tColoredObject::eColor>());
}
Hammerhead answered 19/10, 2018 at 20:37 Comment(0)
D
0

My non-fancy solution was to typecast to/from int. I don't know if it works because of new compilers (the original question has more than a decade), but it works when setting the value with a QSettings:

enum iconSep_e {
    sepNone,
    sepSpace,
    sepTab,
};

And inside the function...

QSettings settings;
settings.setValue(QStringLiteral("iconSep"), sepTab);

And when retrieving, I switch on an int directly, but use the enum as case values:

QVariant var = settings.value(QStringLiteral("iconSep"));
if (!var.isNull()) {
    int iconSep = var.toInt();
    switch (iconSep) {
    case sepNone:
        ui->radioIconSepNone->setChecked(true);
        break;
    case sepSpace:
        ui->radioIconSepSpace->setChecked(true);
        break;
    case sepTab:
        ui->radioIconSepTab->setChecked(true);
        break;
    }
}

I made some tests and a typecast works too:

enum iconSep_e iconSep = static_cast<enum iconSep_e>(var.toInt());
Dorinda answered 12/7, 2021 at 18:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.