Serializing QGraphicsScene Content

I am using the Qt QGraphicsScene class, adding predefined elements such as QGraphicsRectItem , QGraphicsLineItem , etc., and I want to serialize the contents of the scene to disk. However, the base class QGraphicsItem (which is used by other elements that I use) does not support serialization, so I need to roll my own code. The problem is that all access to these objects is done using the QGraphicsItem base pointer, so the serialization code I have is terrible:

 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 a good IMHO code. So instead, I could create an ABC class as follows:

 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 } ... }; 

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

But this does not really help me, as the following will happen:

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

Anyway, can I do this job?

+8
c ++ qt
source share
4 answers

I agree with the other posters, QGraphicsItem can really be seen as an element of presentation, and therefore dividing your model data into your own class will probably be better.

However, I think your accident is caused by an inappropriate act.

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 are thinking about how an object that inherits an inherited one is represented in memory, the first base class is first in memory, the second base class is the second, etc. Thus, the pointer to the second base class will be offset [approximately] by the size of the first base class.

So, in order 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 highly recommend separating your model data as previously recommended.

+3
source share

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

If you want to record the location of graphic objects, perhaps you can use a custom QGraphicsItem and draw a QPicture .

+1
source share

The Qt metatype system can be used to interface QGraphicsItem with QDataStream . The following is a complete example that processes the Qt QGraphicsItem -derived base types, as well as object hierarchies, that is, children are saved and restored. The interface is very small - below the header you need to support storing some of the common types.

Adding custom types requires adding only one DECLARE_GRAPHICSITEM_METATYPE(CustomItemType) in the interface.

 // https://github.com/KubaO/stackoverflown/tree/master/questions/qgraphicsitem-stream-51492181 // Interface #include <QDataStream> #include <QGraphicsItem> #include <QGraphicsTransform> #include <QMetaType> #include <algorithm> #include <type_traits> template <class B, class T> struct NonConstructibleFunctionHelper { static void *Construct(void *, const void *) { return {}; } static void Destruct(void *t) { static_cast<T *>(static_cast<B *>(t))->~T(); } static void Save(QDataStream &ds, const void *t) { ds << *static_cast<const T *>(static_cast<const B *>(t)); } static void Load(QDataStream &ds, void *t) { ds >> *static_cast<T *>(static_cast<B *>(t)); } }; template <class B, class T> struct NonCopyableFunctionHelper : NonConstructibleFunctionHelper<B, T> { static void *Construct(void *where, const void *t) { return (!t) ? static_cast<B *>(new (where) T) : nullptr; } }; #define DECLARE_POLYMORPHIC_METATYPE(BASE_TYPE, TYPE) \ DECLARE_POLYMORPHIC_METATYPE_IMPL(BASE_TYPE, TYPE) #define DECLARE_POLYMORPHIC_METATYPE_IMPL(BASE_TYPE, TYPE) \ QT_BEGIN_NAMESPACE namespace QtMetaTypePrivate { \ template <> \ struct QMetaTypeFunctionHelper<TYPE> \ : std::conditional< \ std::is_copy_constructible<TYPE>::value, void, \ std::conditional< \ std::is_default_constructible<TYPE>::value, \ NonCopyableFunctionHelper<BASE_TYPE, TYPE>, \ NonConstructibleFunctionHelper<BASE_TYPE, TYPE>>::type>::type {}; \ QT_END_NAMESPACE \ } \ Q_DECLARE_METATYPE_IMPL(TYPE) #define DECLARE_POLYSTREAMING_METATYPE(BASE_TYPE, TYPE) \ DECLARE_POLYSTREAMING_METATYPE_IMPL(BASE_TYPE, TYPE) #define DECLARE_POLYSTREAMING_METATYPE_IMPL(BASE_TYPE, TYPE) \ DECLARE_POLYMORPHIC_METATYPE_IMPL(BASE_TYPE, TYPE) \ QDataStream &operator<<(QDataStream &, const TYPE &); \ QDataStream &operator>>(QDataStream &, TYPE &); #define DECLARE_GRAPHICSITEM_METATYPE(TYPE) \ DECLARE_POLYSTREAMING_METATYPE_IMPL(QGraphicsItem, TYPE) QDataStream &operator<<(QDataStream &, const QList<QGraphicsItem *> &); void saveProperties(QDataStream &, const QObject *, const QByteArrayList &excluded = {}); void loadProperties(QDataStream &, QObject *); QDataStream &operator<<(QDataStream &, const QGraphicsTransform *); QDataStream &operator>>(QDataStream &, QGraphicsTransform *&); void registerMapping(int typeId, int itemType); template <typename T> typename std::enable_if<std::is_base_of<QGraphicsItem, T>::value>::type registerMapping() { qRegisterMetaTypeStreamOperators<T>(); if (!std::is_base_of<QGraphicsObject, T>::value) // The QObject-derived types don't need such mappings. registerMapping(qMetaTypeId<T>(), T::Type); } QDataStream &operator<<(QDataStream &, const QGraphicsItem *); QDataStream &operator>>(QDataStream &, QGraphicsItem *&); DECLARE_POLYMORPHIC_METATYPE(QGraphicsTransform, QGraphicsRotation) DECLARE_POLYMORPHIC_METATYPE(QGraphicsTransform, QGraphicsScale) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsObject) DECLARE_GRAPHICSITEM_METATYPE(QAbstractGraphicsShapeItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsItemGroup) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsLineItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsPixmapItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsEllipseItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsPathItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsPolygonItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsRectItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsSimpleTextItem) DECLARE_GRAPHICSITEM_METATYPE(QGraphicsTextItem) class ItemStream { QDataStream &ds; QGraphicsItem &item; public: using Version = qint8; static constexpr Version CurVersion = 1; enum { VersionKey = 0x9000 }; ItemStream(QDataStream &ds, class QGraphicsItem &item) : ds(ds), item(item) {} template <typename C, typename T> ItemStream &operator>>(void (C::*set)(T)) { using decayed_type = typename std::decay<T>::type; using value_type = typename std::conditional<std::is_enum<decayed_type>::value, std::underlying_type<decayed_type>, std::decay<T>>::type::type; value_type value; ds >> value; (static_cast<C &>(item).*set)(static_cast<T>(value)); return *this; } static Version formatOf(const QGraphicsItem &item) { auto version = item.data(VersionKey); return Version(version.isValid() ? version.value<int>() : 0); } static void setVersion(QGraphicsItem &item, Version version) { item.setData(VersionKey, version); } }; 

The ItemStream class simplifies conversion to property definitions and is useful when implementing streaming operators. This allows you to simply write itemStream >> &Class::setFoo instead of type foo; dataStream >> foo; obj.setFoo(foo); type foo; dataStream >> foo; obj.setFoo(foo); type foo; dataStream >> foo; obj.setFoo(foo);

This interface allows you to write the following example - an extended one from this answer .

 // Test #include <QtWidgets> QPixmap makePixmap() { QPixmap pix(100, 50); pix.fill(Qt::transparent); QPainter p(&pix); p.setPen(Qt::darkCyan); p.setFont({"helvetica,arial", 15}); p.drawText(pix.rect(), Qt::AlignCenter, "Hello!"); return pix; } int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget ui; QGridLayout layout(&ui); QGraphicsScene scene; QGraphicsView view(&scene); QPushButton save("Save"); QPushButton load("Load"); layout.addWidget(&view, 0, 0, 1, 2); layout.addWidget(&load, 1, 0); layout.addWidget(&save, 1, 1); auto *eitem = scene.addEllipse(QRect(10, 10, 80, 50), QPen(Qt::green), QBrush(Qt::black)); auto *xform = new QGraphicsRotation; xform->setAngle(10); eitem->setPos(100, 10); eitem->setRotation(60); eitem->setTransformations({xform}); auto *litem = scene.addLine(QLineF(0, 0, 100, 100), QPen(Qt::red)); litem->setPos(10, 10); litem->setRotation(100); scene.createItemGroup({eitem, litem}); auto *ritem = scene.addRect(QRect(10, 0, 100, 100), QPen(Qt::blue), QBrush(Qt::red)); ritem->setPos(10, 100); ritem->setRotation(10); QPainterPath path; path.moveTo(100, 100); path.lineTo(10, 0); path.addRect(QRect(0, 0, 100, 22)); auto *pitem = scene.addPath(path, QPen(Qt::green), QBrush(Qt::black)); pitem->setPos(100, 22); pitem->setRotation(120); scene.addPixmap(makePixmap()); auto *stitem = scene.addSimpleText("Simple Text", {"arial", 17}); stitem->setPos(-50, -10); stitem->setPen(QPen(Qt::darkGreen)); auto *titem = scene.addText("Text", {"arial", 17, true}); titem->setPos(-100, 0); titem->setDefaultTextColor(Qt::darkYellow); auto const flags = QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable; for (auto *it : scene.items()) it->setFlags(flags); QByteArray data; QObject::connect(&save, &QPushButton::clicked, [&scene, &data]() { qDebug() << "writing ..."; QBuffer dev(&data); if (dev.open(QIODevice::WriteOnly)) { QDataStream out(&dev); out << scene.items(Qt::AscendingOrder); scene.clear(); qDebug() << "done writing"; } }); QObject::connect(&load, &QPushButton::clicked, [&scene, &data]() { qDebug() << "reading ..."; QBuffer dev(&data); if (dev.open(QIODevice::ReadOnly)) { QList<QGraphicsItem *> items; QDataStream in(&dev); in >> items; for (auto *item : items) scene.addItem(item); qDebug() << "done reading"; } }); ui.show(); return a.exec(); } 

Specializations for specific types are pretty simple. For each DECLARE_GRAPHICSITEM_METATYPE(ItemType) you need to implement QDataStream &operator>>(QDataStream &in, ItemType &g) and QDataStream &operator<<(QDataStream &out, const ItemType &g) . These stream statements must first call the statement for the base base class. You must also register mappings - this can be done in main or in a static initializer.

These implementations are inspired by this answer .

 // Implementation Specializations static bool specInit = [] { qRegisterMetaType<QGraphicsRotation>(); qRegisterMetaType<QGraphicsScale>(); registerMapping<QGraphicsItemGroup>(); registerMapping<QGraphicsLineItem>(); registerMapping<QGraphicsPixmapItem>(); registerMapping<QGraphicsEllipseItem>(); registerMapping<QGraphicsPathItem>(); registerMapping<QGraphicsPolygonItem>(); registerMapping<QGraphicsRectItem>(); registerMapping<QGraphicsSimpleTextItem>(); registerMapping<QGraphicsTextItem>(); return true; }(); QDataStream &operator<<(QDataStream &out, const QGraphicsEllipseItem &g) { out << static_cast<const QAbstractGraphicsShapeItem &>(g); out << g.rect() << g.startAngle() << g.spanAngle(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsEllipseItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QAbstractGraphicsShapeItem &>(g); ItemStream(in, g) >> &QGI::setRect >> &QGI::setStartAngle >> &QGI::setSpanAngle; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsPathItem &g) { out << static_cast<const QAbstractGraphicsShapeItem &>(g); out << g.path(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsPathItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QAbstractGraphicsShapeItem &>(g); ItemStream(in, g) >> &QGI::setPath; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsPolygonItem &g) { out << static_cast<const QAbstractGraphicsShapeItem &>(g); out << g.polygon() << g.fillRule(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsPolygonItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QAbstractGraphicsShapeItem &>(g); ItemStream(in, g) >> &QGI::setPolygon >> &QGI::setFillRule; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsRectItem &g) { out << static_cast<const QAbstractGraphicsShapeItem &>(g); out << g.rect(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsRectItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QAbstractGraphicsShapeItem &>(g); ItemStream(in, g) >> &QGI::setRect; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsSimpleTextItem &g) { out << static_cast<const QAbstractGraphicsShapeItem &>(g); out << g.text() << g.font(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsSimpleTextItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QAbstractGraphicsShapeItem &>(g); ItemStream(in, g) >> &QGI::setText >> &QGI::setFont; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsItemGroup &g) { return out << static_cast<const QGraphicsItem &>(g); } QDataStream &operator>>(QDataStream &in, QGraphicsItemGroup &g) { return in >> static_cast<QGraphicsItem &>(g); } QDataStream &operator<<(QDataStream &out, const QGraphicsLineItem &g) { out << static_cast<const QGraphicsItem &>(g); out << g.pen() << g.line(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsLineItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QGraphicsItem &>(g); ItemStream(in, g) >> &QGI::setPen >> &QGI::setLine; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsPixmapItem &g) { out << static_cast<const QGraphicsItem &>(g); out << g.pixmap() << g.offset() << g.transformationMode() << g.shapeMode(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsPixmapItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QGraphicsItem &>(g); ItemStream(in, g) >> &QGI::setPixmap >> &QGI::setOffset >> &QGI::setTransformationMode >> &QGI::setShapeMode; return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsTextItem &g) { out << static_cast<const QGraphicsObject &>(g) << g.font() << g.textWidth() << g.defaultTextColor() << g.toHtml() << g.tabChangesFocus() << g.textInteractionFlags(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsTextItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QGraphicsObject &>(g); ItemStream(in, g) >> &QGI::setFont >> &QGI::setTextWidth >> &QGI::setDefaultTextColor >> &QGI::setHtml >> &QGI::setTabChangesFocus >> &QGI::setTextInteractionFlags; return in; } 

The implementation core implements a mapping between the metatype type names and QGraphicsItem::type() elements for types that need it. Otherwise, for types derived from QObject (i.e., QGraphicsObject ), it uses type names directly. It also processes product lists without duplicate items.

 // Implementation Core #include <set> void saveProperties(QDataStream &ds, const QObject *obj, const QByteArrayList &excluded) { QVariantMap map; QByteArray name; auto const &mo = obj->metaObject(); for (int i = 0; i < mo->propertyCount(); ++i) { auto const &mp = mo->property(i); if (!mp.isStored(obj)) continue; name = mp.name(); if (!excluded.contains(name)) { auto prop = mp.read(obj); if (!prop.isNull() && (!excluded.contains(name + '?') || prop != QVariant(prop.userType(), nullptr))) map.insert(QLatin1String(name), prop); } } for (auto &name : obj->dynamicPropertyNames()) map.insert(QLatin1String(name), obj->property(name)); qDebug() << obj->metaObject()->className() << map; ds << map; } void loadProperties(QDataStream &ds, QObject *obj) { QVariantMap map; ds >> map; for (auto it = map.cbegin(); it != map.cend(); ++it) obj->setProperty(it.key().toLatin1(), it.value()); } QDataStream &operator<<(QDataStream &ds, const QGraphicsTransform *obj) { ds << obj->metaObject()->className(); saveProperties(ds, obj); return ds; } QDataStream &operator>>(QDataStream &ds, QGraphicsTransform *&obj) { QByteArray className; ds >> className; auto const type = QMetaType::type(className); obj = static_cast<QGraphicsTransform *>(QMetaType::create(type)); if (obj) loadProperties(ds, obj); return ds; } struct pair { int typeId; int itemType; }; struct CoreData { QReadWriteLock mappingLock; QVector<pair> mapping; }; Q_GLOBAL_STATIC(CoreData, coreData) int getTypeIdForItemType(int itemType) { if (auto *const d = coreData()) { QReadLocker lock(&d->mappingLock); for (auto &m : qAsConst(d->mapping)) if (m.itemType == itemType) return m.typeId; } return QMetaType::UnknownType; } void registerMapping(int typeId, int itemType) { if (getTypeIdForItemType(itemType) == typeId) return; if (auto *const d = coreData()) { QWriteLocker lock(&d->mappingLock); for (auto &m : qAsConst(d->mapping)) if (m.typeId == typeId && m.itemType == itemType) return; d->mapping.push_back({typeId, itemType}); } } QByteArray peekByteArray(QDataStream &ds) { qint32 size; auto read = ds.device()->peek(reinterpret_cast<char *>(&size), sizeof(size)); if (read != sizeof(size)) { ds.setStatus(QDataStream::ReadPastEnd); return {}; } if (ds.byteOrder() == QDataStream::BigEndian) size = qFromBigEndian(size); auto buf = ds.device()->peek(size + 4); if (buf.size() != size + 4) { ds.setStatus(QDataStream::ReadPastEnd); return {}; } if (buf.endsWith('\0')) buf.chop(1); return buf.mid(4); } QDataStream &operator<<(QDataStream &ds, const QList<QGraphicsItem *> &list) { std::set<QGraphicsItem *> seen; QList<QGraphicsItem *> items; struct State { QList<QGraphicsItem *>::const_iterator it, end; }; QVector<State> stack; stack.push_back({list.begin(), list.end()}); while (!stack.isEmpty()) { auto &level = stack.back(); while (level.it != level.end) { QGraphicsItem *item = *level.it++; if (!item || seen.find(item) != seen.end()) continue; // skip empty items and seen items if (stack.size() == 1) // push direct items only items.push_back(item); seen.insert(item); const auto &children = item->childItems(); if (!children.isEmpty()) { stack.push_back({children.begin(), children.end()}); break; } } if (level.it == level.end) stack.pop_back(); } ds << quint32(items.size()); for (auto *item : items) ds << item; return ds; } QDataStream &operator<<(QDataStream &ds, const QGraphicsItem *item) { const QGraphicsObject *obj = item->toGraphicsObject(); const int typeId = obj ? QMetaType::type(obj->metaObject()->className()) : getTypeIdForItemType(item->type()); if (typeId != QMetaType::UnknownType) QMetaType::save(ds, typeId, item); else ds << ""; return ds; } QDataStream &operator>>(QDataStream &ds, QGraphicsItem *&item) { QByteArray const typeName = peekByteArray(ds); if (ds.status() != QDataStream::Ok) return ds; const int typeId = QMetaType::type(typeName); item = static_cast<QGraphicsItem *>(QMetaType::create(typeId)); if (item) QMetaType::load(ds, typeId, item); return ds; } QByteArray getTypeName(const QGraphicsItem &g) { const QGraphicsObject *obj = g.toGraphicsObject(); if (obj) return obj->metaObject()->className(); return QMetaType::typeName(getTypeIdForItemType(g.type())); } QDataStream &operator<<(QDataStream &out, const QGraphicsItem &g) { out << getTypeName(g) << ItemStream::CurVersion << bool(g.toGraphicsObject()) << int(g.type()) << g.pos() << g.scale() << g.rotation() << g.transform() << g.transformations() << g.transformOriginPoint() << g.flags() << g.isEnabled() << g.isSelected() << g.isVisible() << g.acceptDrops() << g.acceptHoverEvents() << g.acceptTouchEvents() << g.acceptedMouseButtons() << g.filtersChildEvents() << g.cursor() << g.inputMethodHints() << g.opacity() << g.boundingRegionGranularity() << g.toolTip() << g.zValue() << g.childItems(); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsItem &g) { using QGI = std::decay<decltype(g)>::type; QByteArray typeName; bool isObject; int type; ItemStream::Version version; QTransform transform; QList<QGraphicsItem *> children; in >> typeName >> version; Q_ASSERT(getTypeName(g) == typeName); if (version > ItemStream::CurVersion) { qWarning() << "unsupported QGraphicsItem version" << version << "maximum is:" << ItemStream::CurVersion; in.setStatus(QDataStream::ReadCorruptData); return in; } ItemStream::setVersion(g, version); in >> isObject >> type; ItemStream iin(in, g); iin >> &QGI::setPos >> &QGI::setScale >> &QGI::setRotation; in >> transform; g.setTransform(transform); iin >> &QGI::setTransformations >> &QGI::setTransformOriginPoint >> &QGI::setFlags >> &QGI::setEnabled >> &QGI::setSelected; if (version >= 1) { iin >> &QGI::setVisible >> &QGI::setAcceptDrops >> &QGI::setAcceptHoverEvents >> &QGI::setAcceptTouchEvents >> &QGI::setAcceptedMouseButtons >> &QGI::setFiltersChildEvents >> &QGI::setCursor >> &QGI::setInputMethodHints >> &QGI::setOpacity >> &QGI::setBoundingRegionGranularity >> &QGI::setToolTip; } iin >> &QGI::setZValue; in >> children; for (auto *c : qAsConst(children)) c->setParentItem(&g); return in; } QDataStream &operator<<(QDataStream &out, const QGraphicsObject &g) { static const QByteArrayList excluded{ "effect", "enabled", "opacity", "parent", "pos", "rotation", "scale", "transformOriginPoint", "visible", "x", "y", "z", "children", "height?", "width?"}; out << static_cast<const QGraphicsItem &>(g); saveProperties(out, &g, excluded); return out; } QDataStream &operator>>(QDataStream &in, QGraphicsObject &g) { in >> static_cast<QGraphicsItem &>(g); loadProperties(in, &g); return in; } QDataStream &operator<<(QDataStream &out, const QAbstractGraphicsShapeItem &g) { out << static_cast<const QGraphicsItem &>(g); out << g.pen() << g.brush(); return out; } QDataStream &operator>>(QDataStream &in, QAbstractGraphicsShapeItem &g) { using QGI = std::decay<decltype(g)>::type; in >> static_cast<QGraphicsItem &>(g); ItemStream(in, g) >> &QGI::setPen >> &QGI::setBrush; return in; } 
+1
source share

If you really want to serialize these elements, create

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

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

But I support stephens answer, you might think about pulling the actual data from the scene and putting it in a separate model class

0
source share

All Articles