Reputation: 249
I have several QObject
classes, which may have QList
properties, which store lists of QObject
derived classes:
class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<AnotherQObject*> otherObjects READ otherObjects)
public:
QList<AnotherQObject*> otherObjects() const;
}
QList<AnotherObject*>
has of course been registered with Q_DECLARE_METATYPE
and qRegisterMetaType()
.
Now I want to read this property with Qt's meta object system. At the point where I want to read the list, I only care about (and know) the fact that the list contains pointers to objects, which have been derived from QObject
, but not the exact type.
When I convert the property's QVariant
to QList<QObject *>
the list is empty.
QObject *object = getObjectFromSomeWhere();
const char* propertyName = getPropertyNameFromSomeWhere(); // "otherObjects"
QVariant list = object->property(name);
QList<QObject *> objectList = list.value<QList<QObject *> >(); // empty
QList<AnotherQObject*> anotherObjectList =
static_cast<Object*>(object)->otherObjects() // not empty
Is it possible to access this list? Do you have other suggestions?
EDIT: Although I posted my solution, I would still love comments :-)
Upvotes: 2
Views: 3175
Reputation: 472
If you're using Qt 5.2 or higher there is a QSequentialIterable class that allows you to do this without the overhead of extra templates, modified macros, and additional registrations.
http://qt-project.org/doc/qt-5/qsequentialiterable.html
If you have a QList<T*>
stored in a QVariant
, where T is derived from QObject
, you can get a QSequentialIterable
instance from the variant, which allows you to iterate over each item in the list as a QVariant
. Then you can simply get the value of each item as a QObject*
, adding to a QList<QObject*>
as you go (make sure you test the assumption that the type is derived from QObject
, though):
QList<QObject*> qobjectList;
if (variant.canConvert(QMetaType::QVariantList)) {
QSequentialIterable iterable = variant.value<QSequentialIterable>();
foreach (const QVariant& item, iterable) {
QObject* object = item.value<QObject*>();
if (object) { qobjectList.append(object); }
}
}
Upvotes: 3
Reputation: 249
I think I solved the issue quite minimalistically. I created a template class, which can convert a QVariant
containing a QList<TemplateParameter *>
to QList<QObject *>
.
Also I have a singleton, which stores a QMap<int, Converter *>
, which maps from QVariant::userType()
to the according converter.
This allows me to register each class, which I want to store in the properties as follows:
VariantToObjectListConverter::instance()->registerConverter<AnotherQObject>();
I can then convert unknown QVariants
:
QVariant list = object->property("otherObjects");
QList<QObject *> objects = VariantToObjectListConverter::instance()->convert(variant);
This is the necessary code:
class ConverterBase
{
public:
virtual ~ConverterBase() {}
virtual QList<QObject *> convert(const QVariant &variant) const = 0;
};
template<class Object>
class Converter : public ConverterBase
{
public:
QList<QObject *> convert(const QVariant &variant) const
{
QList<Object *> list = variant.value<QList<Object *> >();
QList<QObject *> result;
Q_FOREACH(Object *object, list) result.append(object);
return result;
}
};
class VariantToObjectListConverter
{
public:
Q_GLOBAL_STATIC(VariantToObjectListConverter, instance)
~VariantToObjectListConverter()
{
QHashIterator<int, ConverterBase *> i(m_converters);
while (i.hasNext()) {
i.next();
delete i.value();
}
}
template<class Object>
void registerConverter()
{
QVariant v = QVariant::fromValue<QList<Object *> >(QList<Object *>());
m_converters.insert(v.userType(), new Converter<Object>());
}
QList<QObject *> convert(const QVariant &variant) const
{
ConverterBase *converter = m_converters.value(variant.userType());
if (!converter)
return QList<QObject *>();
return converter->convert(variant);
}
private:
QHash<int, ConverterBase *> m_converters;
VariantToObjectListConverter() {}
};
I could now add a macro, which calls registerConverter()
, Q_DECLARE_METATYPE
and qRegisterMetaType()
, so that the user only has to call
REGISTER_TYPE(TypeName)
Upvotes: 2