tags:

views:

178

answers:

3

I am using the Qt QGraphicsScene class, adding pre-defined items such as QGraphicsRectItem, QGraphicsLineItem, etc. and I want to serialize the scene contents to disk. However, the base QGraphicsItem class (that the other items I use derive from) doesn't support serialization so I need to roll my own code. The problem is that all access to these objects is via a base QGraphicsItem pointer, so the serialization code I have is horrible:

QGraphicsScene* scene = new QGraphicsScene;
scene->addRect(QRectF(0, 0, 100, 100));
scene->addLine(QLineF(0, 0, 100, 100));
...
QList<QGraphicsItem*> list = scene->items();
foreach (QGraphicsItem* item, items)
{
  if (item->type() == QGraphicsRectItem::Type)
  {
    QGraphicsRectItem* rect = qgraphicsitem_cast<QGraphicsRectItem*>(item);
    // Access QGraphicsRectItem members here
  }
  else if (item->type() == QGraphicsLineItem::Type)
  {
    QGraphicsLineItem* line = qgraphicsitem_cast<QGraphicsLineItem*>(item);
    // Access QGraphicsLineItem members here
  }
  ...
}

This is not good code IMHO. So, instead I could create an ABC class like this:

class Item
{
public:
  virtual void serialize(QDataStream& strm, int version) = 0;
};

class Rect : public QGraphicsRectItem, public Item
{
public:
  void serialize(QDataStream& strm, int version)
  {
    // Serialize this object
  }
  ...
};

I can then add Rect objects using QGraphicsScene::addItem(new Rect(,,,));

But this doesn't really help me as the following will crash:

QList<QGraphicsItem*> list = scene->items();
foreach (QGraphicsItem* item, items)
{
  Item* myitem = reinterpret_class<Item*>(item);
  myitem->serialize(...) // FAIL
}

Any way I can make this work?

A: 

Serializing QGraphicsItem is not a good idea. A QGrahpicsScene is supposed to represent your data. Instead of serializing the representation, it's better to serialize the data/model of your application.

If you want to record arrangement of graphics entities, maybe you can use a custom QGraphicsItem and draw into a QPicture.

Stephen Chu
The items contain everything I need to serialize, so can be considered the data/model in this case.
Rob
BTW there doesn't seem to be support for linking a QGraphicsScene to a separate model.
Rob
A: 

If you really want to serialize these items create

QDataStream &operator<<(QDataStream &, const AppropriateSubclass&);
QDataStream &operator>>(QDataStream &, AppropriateSubclass&);

for each of the QGraphicsItem subclasses that you would like to serialize, these functions are non-member function. The approach is also explained in the QDataStream documentation

But I support stephens answer, you might want to consider pulling the actual data out of the scene and putting it in a separate model class

Harald Scheirich
This doesn't work unless I cast each QGraphicsItems to the correct type before attempting to stream it.
Rob
+1  A: 

I agree with the other posters, the QGraphicsItem could really be viewed as a view item and so separating your model data into its own class would probably be better.

That said, I think your crash is caused by an inappropriate cast.

If you do the following:

Rect *r = new Rect();
QGraphicsItem *qi = reinterpret_cast<QGraphicsItem*>(r);
QGraphicsRectItem *qr = reinterpret_cast<QGraphicsRectItem*>(r);
Item *i = reinterpret_cast<Item*>(r);
qDebug("r = %p, qi = %p, qr = %p, i = %p", r, qi, qr, i);

You should see that r == qi, r == qr, but r != i. If you think about how an object that multiply inherits is represented in memory, the first base class is first in memory, the second base class is second, and so forth. So the pointer to the second base class will be offset by [approximately] the sizeof the first base class.

So to fix your code, I think you need to do something like:

QList<QGraphicsItem*> list = scene->items();
foreach (QGraphicsItem* item, items)
{
  Rect* myrect = reinterpret_class<Rect*>(item);  // needed to figure out the offset to the Item part
  Item* myitem = reinterpret_class<Item*>(myrect);
  myitem->serialize(...);
}

This is one of many reasons, I like to avoid multiple inheritance whenever possible. I strongly recommend separating you model data, as previously recommended.

Chris Morlier