PDA

View Full Version : Help with Q_PROPERTY with object pointer



lni
12th January 2009, 19:43
Hello,

I am trying to have a generic save/restore for QObject which contain QObject derived pointer, as shown in the example, the "Parent" has "Object", but I can't use
Q_PROPERTY( QObject *object READ object WRITE setObject )

I would like to use
Q_PROPERTY( Object *object READ object WRITE setObject )

In this way, I don't need to cast from QObject to Object. Is there a way for that? Thanks!



#include <QObject>
#include <QString>
#include <QFile>
#include <QDebug>
#include <QMap>
#include <QDataStream>
#include <QMetaObject>
#include <QByteArray>
#include <QVariant>
#include <QMetaProperty>
#include <QList>
#include <QMetaType>
#include <QBuffer>
#include <iostream>

class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(QString string READ string WRITE setString)

public:

Object() {
}

const QString& string() const {
return str;
}

void setString(const QString &ss) {
str = ss;
}

void dump() const {
std::cout << str.toStdString() << std::endl;
}

private:

QString str;

};


class Parent : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject *object READ object WRITE setObject)

public:

Parent() {
}

QObject * object() const {
return obj;
}

void setObject(QObject * oo) {
Q_ASSERT(!oo || qobject_cast<Object*>(oo));
obj = qobject_cast<Object*>(oo);
}

private:

Object *obj;

};

class BaseInterface
{
public:

virtual ~BaseInterface() {}
virtual QObject *create( const QString &objectName) const = 0;

};

template <typename T>
class Creator : public BaseInterface
{
public:

QObject *create( const QString &objectName) const {
T *ret = new T;
ret->setObjectName(objectName);
return ret;
}
};

class Factory
{
public:

static QObject *create(const QString &className, const QString &objectName) {
BaseInterface *i = interfaces.value(className, 0);
assert (i);
return i->create(objectName);
}

static void registerClass(const QString &className, BaseInterface *interface) {
if (!interface) {
delete interfaces.value(className, 0);
interfaces.remove(className);
} else {
interfaces[className] = interface;
}
}

static QMap<QString, BaseInterface*> interfaces;

};

QMap<QString, BaseInterface*> Factory::interfaces;

static void save(QObject *o, QIODevice *dev)
{
const QMetaObject *mo = o->metaObject();
QMap<QByteArray, QVariant> properties;
QMap<QByteArray, QByteArray> objectStarProperties;
QMap<QByteArray, QVariantList> listProp;;

const int props = mo->propertyCount();
for (int i=0; i<props; ++i) {
const QMetaProperty prop = mo->property(i);
if (prop.userType() == QMetaType::QObjectStar) {
QObject *obj = qVariantValue<QObject*>(prop.read(o));
if (obj) {
QBuffer buf;
buf.open(QIODevice::WriteOnly);
save(obj, &buf);
objectStarProperties[prop.name()] = buf.buffer();
}
} else if (prop.userType() == QMetaType::QVariantList) {
listProp[prop.name()] = prop.read(o).toList();
} else {
properties[prop.name()] = prop.read(o);
}
}

QDataStream ds(dev);
ds << QString::fromLatin1(mo->className());
ds << properties;
ds << objectStarProperties;

ds << listProp.size();
QMapIterator<QByteArray, QVariantList> it1( listProp );
while( it1.hasNext() ) {
it1.next();
QVariantList vlist = it1.value();
ds << vlist.size() << it1.key();
}

}
static QObject *read(QIODevice *dev, QObject *parent = 0)
{
QString className;
QMap<QByteArray, QVariant> properties;
QMap<QByteArray, QByteArray> objectStarProperties;

QDataStream ds(dev);

ds >> className;
ds >> properties;
ds >> objectStarProperties;

int nlists;
ds >> nlists;

QList<int> iList;
QList<QByteArray> propNameList;
for ( int idx=0; idx<nlists; idx++ ) {
int il;
QByteArray pnl;
ds >> il >> pnl;
iList.append( il );
propNameList.append( pnl );
}

QObject *o = Factory::create(className,
properties.value("objectName").toString());
if (!o) {
qWarning() << "Can't create object of type" << className;
return 0;
}

for (QMap<QByteArray, QVariant>::const_iterator it = properties.begin();
it != properties.end(); ++it) {
if (!o->setProperty(it.key().constData(), it.value())) {
qWarning() << "Can't set" << it.key();
}
}

for (QMap<QByteArray, QByteArray>::const_iterator it =
objectStarProperties.begin();
it != objectStarProperties.end(); ++it) {
QBuffer buf;
buf.setData(it.value());
buf.open(QIODevice::ReadOnly);
QObject *obj = read(&buf, 0);
o->setProperty(it.key(), qVariantFromValue<QObject*>(obj));
}

for ( int idx=0; idx<nlists; idx++ ) {
QVariantList objList;
for (int ii=0; ii<iList[idx]; ++ii) {
QObject* oo = read(dev, o);
objList << qVariantFromValue<QObject*>( oo );
}
o->setProperty( propNameList[idx], objList );
}

return o;

}

#include "main.moc"

int main()
{
Factory::registerClass("Parent", new Creator<Parent>());
Factory::registerClass("Object", new Creator<Object>());

Object *o = new Object;
o->setString("foobar");

Parent *p = new Parent;
p->setObject(o);

qobject_cast<Object*>( p->object() )->dump();

QFile file("tt.ss");
file.open(QIODevice::WriteOnly);

save(p, &file);
file.close();

file.open(QIODevice::ReadOnly);

QObject *root = read(&file);
Q_ASSERT(root);
Parent *pp = qobject_cast<Parent*>(root);

qobject_cast<Object*>( pp->object() )->dump();

return 0;
}

jpn
16th January 2009, 17:31
Properties tend to be value types. Does it make sense to serialize a pointer value to a file?