Сериализация содержимого QGraphicsScene - PullRequest
8 голосов
/ 08 июня 2010

Я использую класс Qt QGraphicsScene, добавляю предопределенные элементы, такие как QGraphicsRectItem, QGraphicsLineItem и т. Д., И хочу сериализовать содержимое сцены на диск.Однако базовый класс QGraphicsItem (из которого извлекаются другие элементы, которые я использую) не поддерживает сериализацию, поэтому мне нужно свернуть свой собственный код.Проблема в том, что весь доступ к этим объектам осуществляется через базовый указатель QGraphicsItem, поэтому код сериализации, который я имею, ужасен:

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

Это не очень хороший код, ИМХО.Таким образом, вместо этого я мог бы создать класс ABC следующим образом:

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

Затем я могу добавить объекты Rect, используя QGraphicsScene::addItem(new Rect(,,,));

Но это на самом деле мне не поможет, так как произойдет следующее:

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

Как я могу заставить эту работу?

Ответы [ 4 ]

3 голосов
/ 15 июня 2010

Я согласен с другими авторами, QGraphicsItem действительно можно рассматривать как элемент просмотра, и поэтому лучше разделить данные вашей модели на собственный класс.

Тем не менее, я думаю, что ваш сбой вызваннеподходящим составом.

Если вы выполните следующее:

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);

Вы должны увидеть, что r == qi, r == qr, но r! = i.Если вы думаете о том, как объект, который многократно наследуется, представлен в памяти, первый базовый класс находится первым в памяти, второй базовый класс - вторым, и так далее.Таким образом, указатель на второй базовый класс будет смещен на [приблизительно] размер первого базового класса.

Итак, чтобы исправить ваш код, я думаю, вам нужно сделать что-то вроде:

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

Это одна из многих причин, мне нравится, когда это возможно, избегать множественного наследования.Я настоятельно рекомендую разделить данные модели, как рекомендовано ранее.

1 голос
/ 29 июля 2018

Система метатипов Qt может использоваться для взаимодействия QGraphicsItem с QDataStream.Ниже приведен полный пример, который обрабатывает базовые типы, полученные из Qt QGraphicsItem, а также иерархии объектов - т.е. дочерние элементы также сохраняются и восстанавливаются.Интерфейс очень маленький - ниже приведен заголовок, который необходим для хранения некоторых распространенных типов.

Для добавления пользовательских типов необходимо добавить только одну строку DECLARE_GRAPHICSITEM_METATYPE(CustomItemType) в интерфейсе.

// 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);
   }
};

Класс ItemStream облегчает потоковую передачу в установщики свойств и полезен при реализации потоковых операторов.Это позволяет писать просто itemStream >> &Class::setFoo вместо type foo; dataStream >> foo; obj.setFoo(foo);

Этот интерфейс позволяет написать следующий пример - расширен с этот ответ .

// 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();
}

Специализации дляконкретные типы довольно просты.Для каждой декларации DECLARE_GRAPHICSITEM_METATYPE(ItemType) вы должны реализовать QDataStream &operator>>(QDataStream &in, ItemType &g) и QDataStream &operator<<(QDataStream &out, const ItemType &g).Эти операторы потока должны сначала вызвать оператор для непосредственного базового класса.Вы также должны зарегистрировать сопоставления - это можно сделать в main или в статическом инициализаторе.

Эти реализации вдохновлены этим ответом .

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

В ядре реализации реализовано отображение между именами типов метатипов и QGraphicsItem::type() типов элементов для типов, которым это необходимо.В противном случае для типов, производных от QObject (то есть QGraphicsObject), он использует имена типов напрямую.Это также обрабатывает сохранение списков элементов без дублированных элементов.

// 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 голос
/ 08 июня 2010

Сериализация QGraphicsItem не очень хорошая идея. QGrahpicsScene должен представлять ваши данные. Вместо сериализации представления лучше сериализовать данные / модель вашего приложения.

Если вы хотите записать расположение графических объектов, возможно, вы можете использовать пользовательский QGraphicsItem и рисовать в QPicture .

0 голосов
/ 08 июня 2010

Если вы действительно хотите сериализовать эти элементы, создайте

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

для каждого из QGraphicsItem подклассов, которые вы хотите сериализовать, эти функции не являются членами. Этот подход также объясняется в QDataStream документации

Но я поддерживаю ответ Стивенса. Возможно, вы захотите вытащить фактические данные из сцены и поместить их в отдельный класс моделей

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...