How to save a QGraphicsItem QList to a file and then read it? Qt c++
Asked Answered
F

1

2

I have a list that contains QGraphicsItem. This list allows me to draw lines. I looked in the documentation but I did not see how can we save the points in a file? Then read the points of the file to be able to display them?

Here is my code :

 QList<QGraphicsItem *> graphicsitemList;
 for (int i=0 ; i<graphicsitemList.size(); i++ ){
     for (int j=0 ; j <4; j++){
          this->scene->addLine((float)graphicsitemList[i]->scenePos().x(),
                      (float)graphicsitemList[i]->scenePos().y(),
                      (float)graphicsitemList[indice[j]]->scenePos().x(),
                (float)[indice[j]]->scenePos().y(),pen);

                //I tried this solution but I'm not sure it works.
                  QFile filePoint("points.txt");
        if(filePoint.open(QIODevice::WriteOnly)){
            QDataStream out(&filePoint);
            out << graphicsitemList[i]->scenePos().x();
            out<<',';
            out << graphicsitemList[i]->scenePos().y();
            out<<';';
            out << graphicsitemList[j]->scenePos().x();
            out<<',';
            out << graphicsitemList[j]->scenePos().y();
            out<<';';
            filePoint.close();
      }
  }
Fichte answered 24/7, 2018 at 6:48 Comment(10)
You might consider using QSetting to save your geometries points.Propeller
@Propeller Ok thank you, what you propose to me is to record them directly in QSettings? It's easier than in a text file or is it the best solution?Fichte
@eyllanesc Initially I wanted to save the list of points in a text file and not in QSettings. The link you propose to me uses QSettings ...Fichte
@Tom13000 In the same, only change the part that saves.Regenerate
@Tom13000, QSettings (ini format) saves data in a text file. The benefit is that QSetting already knows how to read/write QPoints and other QVariant types.Propeller
@Regenerate That's precisely my question. How do you save points in an example svg file or text file ....Fichte
@Tom13000 svg file ?, I think you want to run without knowing how to crawl, what's your problem? Although your method is not the best it should work.Regenerate
@Regenerate in my methode save all the points in a text file. My problem is that I do not see how to read them for the displays correctly because I have them separated by "," or ";"Fichte
@Regenerate I also wanted to save the points in a file. Because I have an image list and following the displayed image I do not display the same traits all the time.Fichte
See e.g. this answer.Neoteny
R
3

If you want to save the QGraphicsXXXItems in a file you must save the main properties that define them, in the following code I show how to save the main properties of the following items:

  • QGraphicsLineItem
  • QGraphicsSimpleTextItem
  • QGraphicsRectItem
  • QGraphicsPolygonItem
  • QGraphicsPathItem
  • QGraphicsPixmapItem

this answer does not cover the relationship of parents and children or the QGraphicsObject.

utils.h

#ifndef UTILS_H
#define UTILS_H

#include <QDataStream>
#include <QFont>
#include <QGraphicsLineItem>
#include <QPen>

QDataStream &operator<<(QDataStream & out, QGraphicsItem * item){
    out << item->pos()
        << item->scale()
        << item->rotation()
        << item->transform()
        << item->transformOriginPoint()
        << item->flags()
        << item->isEnabled()
        << item->isSelected()
        << item->zValue();
    return out;
}
QDataStream &operator>>(QDataStream & in, QGraphicsItem* & item){
    QPointF pos;
    qreal scale;
    qreal rotation;
    QTransform transform;
    QPointF transformOriginPoint;
    QGraphicsItem::GraphicsItemFlags flags;
    bool isEnabled;
    bool isSelected;
    qreal zValue;
    in >> pos
            >> scale
            >> rotation
            >> transform
            >> transformOriginPoint
            >> flags
            >> isEnabled
            >> isSelected
            >> zValue;
    item->setPos(pos);
    item->setScale(scale);
    item->setRotation(rotation);
    item->setTransform(transform);
    item->setTransformOriginPoint(transformOriginPoint);
    item->setFlags(flags);
    item->setEnabled(isEnabled);
    item->setSelected(isSelected);
    item->setZValue(zValue);
    return in;
}

QDataStream &operator<<(QDataStream & out, QAbstractGraphicsShapeItem * item){
    out << dynamic_cast<QGraphicsItem *>(item);
    out << item->pen() << item->brush();
    return out;
}
QDataStream &operator>>(QDataStream & in, QAbstractGraphicsShapeItem* & item){
    QGraphicsItem *tmp = dynamic_cast<QGraphicsItem *>(item);
    in >> tmp;
    QPen pen;
    QBrush brush;
    in >> pen >> brush;
    item->setBrush(brush);
    item->setPen(pen);
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsEllipseItem * item){
    out << dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    out << item->rect() << item->startAngle() << item->spanAngle();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsEllipseItem* & item){
    QAbstractGraphicsShapeItem *tmp = dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    in >> tmp;
    QRectF rect;
    int startAngle;
    int spanAngle;
    in >> rect >> startAngle >> spanAngle;
    item->setRect(rect);
    item->setStartAngle(startAngle);
    item->setSpanAngle(spanAngle);
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsPathItem * item){
    out << dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    out << item->path();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsPathItem* & item){
    QAbstractGraphicsShapeItem *tmp = dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    in >> tmp;
    QPainterPath path;
    in >> path;
    item->setPath(path);
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsPolygonItem * item){
    out << dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    out << item->polygon()<< item->fillRule();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsPolygonItem* & item){
    QAbstractGraphicsShapeItem *tmp = dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    in >> tmp;
    QPolygonF polygon;
    int rule;
    in >> polygon>>rule;
    item->setPolygon(polygon);
    item->setFillRule(Qt::FillRule(rule));
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsRectItem * item){
    out << dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    out << item->rect();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsRectItem* & item){
    QAbstractGraphicsShapeItem *tmp = dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    in >> tmp;
    QRectF rect;
    in >> rect;
    item->setRect(rect);
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsSimpleTextItem * item){
    out << dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    out << item->text()<<item->font();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsSimpleTextItem* & item){
    QAbstractGraphicsShapeItem *tmp = dynamic_cast<QAbstractGraphicsShapeItem *>(item);
    in >> tmp;
    QString text;
    QFont font;
    in >> text >> font;
    item->setText(text);
    item->setFont(font);
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsLineItem * item){
    out << dynamic_cast<QGraphicsItem *>(item);
    out << item->pen()<<item->line();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsLineItem* & item){
    QGraphicsItem *tmp = dynamic_cast<QGraphicsItem *>(item);
    in >> tmp;
    QPen pen;
    QLineF line;
    in >> pen >> line;
    item->setPen(pen);
    item->setLine(line);
    return in;
}

QDataStream &operator<<(QDataStream & out, QGraphicsPixmapItem * item){
    out << dynamic_cast<QGraphicsItem *>(item);
    out << item->pixmap()<<item->offset()<<item->transformationMode()<<item->shapeMode();
    return out;
}

QDataStream &operator>>(QDataStream & in, QGraphicsPixmapItem* & item){
    QGraphicsItem *tmp = dynamic_cast<QGraphicsItem *>(item);
    in >> tmp;
    QPixmap pixmap;
    QPointF offset;
    int transformationMode;
    int shapeMode;
    in >> pixmap>> offset>> shapeMode >> transformationMode;
    item->setPixmap(pixmap);
    item->setOffset(offset);
    item->setTransformationMode(Qt::TransformationMode(transformationMode));
    item->setShapeMode(QGraphicsPixmapItem::ShapeMode(shapeMode));
    return in;
}

static void saveItems(QList<QGraphicsItem *> items, QDataStream & out){
    for(QGraphicsItem *item : items){
        out << item->type();
        switch (item->type()) {
        case QGraphicsLineItem::Type:
            out << dynamic_cast<QGraphicsLineItem *>(item);
            break;
        case QGraphicsSimpleTextItem::Type:
            out << dynamic_cast<QGraphicsSimpleTextItem *>(item);
            break;
        case QGraphicsRectItem::Type:
            out << dynamic_cast<QGraphicsRectItem *>(item);
            break;
        case QGraphicsPolygonItem::Type:
            out << dynamic_cast<QGraphicsPolygonItem *>(item);
            break;
        case QGraphicsPathItem::Type:
            out << dynamic_cast<QGraphicsPathItem *>(item);
            break;
        case QGraphicsPixmapItem::Type:
            out << dynamic_cast<QGraphicsPixmapItem *>(item);
            break;
        }
    }
}

static QList<QGraphicsItem *> readItems(QDataStream & in){
    QList<QGraphicsItem *> items;
    int type;
    while (!in.atEnd()) {
        in >> type;
        switch (type) {
        case QGraphicsLineItem::Type: {
            QGraphicsLineItem *item = new QGraphicsLineItem;
            in >> item;
            items << item;
            break;
        }
        case QGraphicsSimpleTextItem::Type:{
            QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem;
            in >> item;
            items << item;
            break;
        }
        case QGraphicsRectItem::Type:{
            QGraphicsRectItem *item = new QGraphicsRectItem;
            in >> item;
            items << item;
            break;
        }
        case QGraphicsPolygonItem::Type:{
            QGraphicsPolygonItem *item = new QGraphicsPolygonItem;
            in >> item;
            items << item;
            break;
        }
        case QGraphicsPathItem::Type:{
            QGraphicsPathItem *item = new QGraphicsPathItem;
            in >> item;
            items << item;
            break;
        }
        case QGraphicsPixmapItem::Type:{
            QGraphicsPixmapItem *item = new QGraphicsPixmapItem;
            in >> item;
            items << item;
            break;
        }
        }
    }
    return  items;
}

#endif // UTILS_H

Example:

main.cpp

#include "utils.h"

#include <QApplication>
#include <QFile>
#include <QGraphicsLineItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QTimer>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsView view(new QGraphicsScene);


    QGraphicsEllipseItem *eitem = view.scene()->addEllipse(QRect(10, 10, 80, 50), QPen(Qt::green), QBrush(Qt::black));
    eitem->setPos(100, 10);
    eitem->setRotation(60);

    QGraphicsLineItem *litem = view.scene()->addLine(QLineF(0, 0, 100, 100), QPen(Qt::red));
    litem->setPos(10, 10);
    litem->setRotation(100);

    QGraphicsRectItem *ritem = view.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));
    QGraphicsPathItem *pitem = view.scene()->addPath(path, QPen(Qt::green), QBrush(Qt::black));
    pitem->setPos(100, 22);
    pitem->setRotation(120);

    QGraphicsPixmapItem *pixmapitem = new QGraphicsPixmapItem;
    pixmapitem->setPixmap(QPixmap(":/image.png"));
    view.scene()->addItem(pixmapitem);

    QGraphicsItem::GraphicsItemFlags flags = QGraphicsItem::ItemIsMovable
            | QGraphicsItem::ItemIsSelectable
            | QGraphicsItem::ItemIsFocusable
            | QGraphicsItem::ItemClipsToShape
            | QGraphicsItem::ItemClipsChildrenToShape;



    for(QGraphicsItem *it : view.scene()->items()){
        it->setFlags(flags);
    }

    QTimer::singleShot(4000, [&view](){
        qDebug()<< "writing ...";
        QFile fileOut("file.txt");
        if(fileOut.open(QIODevice::WriteOnly)){
            QDataStream out(&fileOut);
            saveItems(view.scene()->items(), out);
            fileOut.close();
            qDebug()<<"clear items";
            view.scene()->clear();
        }
    });

    QTimer::singleShot(5000, [&view](){
        qDebug()<< "reading ...";
        QFile fileIn("file.txt");
        if(fileIn.open(QIODevice::ReadOnly)){
            QDataStream in(&fileIn);
            QList<QGraphicsItem *> items = readItems(in);
            for(QGraphicsItem *item : items){
                view.scene()->addItem(item);
            }
        }
    });


    view.show();
    return a.exec();
}

In the following link you can find the complete example.

Regenerate answered 24/7, 2018 at 12:4 Comment(6)
Using the end of stream as a termination point is not a good idea: it ties you to the data being saved last in the stream. Instead, store the size of the list. You can also implement a generic operator<<(QDataStream&, QGraphicsItem*) and operator>>(QDataStream&, QGraphicsItem &*) that implements polymorphism, as well as Q_DECLARE_METATYPE(QGraphicsItem*). At that point, you can stream QList<QGraphicsItem*> directly and Qt will handle storing individual items as well as the list size. It will also recreate items on reading.Neoteny
@KubaOber Okay, I think I understand, I'm going to try and warn you. thanks ;)Regenerate
@KubaOber As a difference between the QGraphicsXXXItem, use dynamic_cast?Regenerate
Since QGraphicsItem implements its own RTTI system, you'd want qgraphicsitem_cast instead. This answer has a complete example that also merges it with RTTI in QWidgetListItem for a double-win :) But you don't need the casts: you can register the streaming operators for all the types, and let Qt automagically save/load the data polymorphically via QMetaTypeLoad. It's quite a powerful system!Neoteny
@KubaOber I understand your code, but I would like to have an intermediate Layer that stores the properties of the QAbstractGraphicsShapeItem, and that layer inherit the classes that inherit from QAbstractGraphicsShapeItem. How could that be done? :)Regenerate
I was searching for a similar method to retrieve graphic items for days, thanks so much for posting! Although I did run into a issue when using your code, if I don't add a image to my scene everything is perfect. But if I add a image the graphic items like ellipse and rectangle seem to be behind the image all of a sudden, I think this is because of the order of text file is there any easy way to reverse it?Implore

© 2022 - 2024 — McMap. All rights reserved.