Tuesday, June 21, 2011

Serialize QGraphicsScene Binary Data By Using QDataStream

QGraphicsScene class enables easy management of QGraphicsItem instances in a 2D environment. QGraphicsScene acts as a container for custom QGraphicsItems.
Custom QDialog with a QGraphicsView helps to visualize QGraphicsItems that are included in a QGraphicsScene. By using addItem() function of QGraphicsScene, custom QGraphicsItems can be added to the QGraphicsScene instance. QGprahicsItem binary data such as x() and y() coordinates can be serialized to a QFile by using QDataStream.
Sample project started as a QT Gui Application and contains main.cpp, SceneToDataStream.h, SceneToDataStream.cpp, MyGraphicsItem.h and MyGraphicsItem.cpp files.
Project Directory Structure in Qt Creator IDE :

SceneToDataStream.pro is the project configuration file and contains:
QT       += core gui
TARGET = SceneToDataStream
TEMPLATE = app
SOURCES += main.cpp\
        SceneToDataStream.cpp \
    MyGraphicsItem.cpp
HEADERS  += SceneToDataStream.h \
    MyGraphicsItem.h

main.cpp file contains the custom QDialog instance and shows it.
#include <QApplication>
#include "SceneToDataStream.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}

SceneToDataStream.h file is the extended QDialog class header file.
#ifndef SCENETODATASTREAM_H
#define SCENETODATASTREAM_H
#include <QDialog>

class QGraphicsScene;
class MyGraphicsItem;
class QGraphicsView;
class QVBoxLayout;

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

public slots:
    void sl_saveSceneToFile();

private:
    QGraphicsScene* scene;
    MyGraphicsItem* item;
    QGraphicsView* view;
    QVBoxLayout* layout;
    QPushButton* btnSave;
};

#endif // SCENETODATASTREAM_H

SceneToDataStream.cpp file contains implementation details for extended QDialog class.
#include "SceneToDataStream.h"
#include "MyGraphicsItem.h"

#include <QGraphicsScene>
#include <QGraphicsView>
#include <QVBoxLayout>
#include <QPushButton>
#include <QFile>
#include <QList>
#include <QMessageBox>
#include <QDir>

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    scene = new QGraphicsScene(this);
    item = new MyGraphicsItem();
    item->setPos(34,35);
    scene->addItem(item);
    view = new QGraphicsView(scene,this);
    layout = new QVBoxLayout(this);
    layout->addWidget(view);
    btnSave = new QPushButton("Save Scene To File");
    connect(btnSave, SIGNAL(clicked()), this, SLOT(sl_saveSceneToFile()));
    layout->addWidget(btnSave);
    setLayout(layout);
    resize(270,200);
}

Dialog::~Dialog()
{
    delete btnSave;
    delete layout;
    delete item;
    delete scene;
    delete view;
}

void Dialog::sl_saveSceneToFile()
{
    QString fileName = QDir::currentPath().append("/sceneData.txt");
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly))
    {
            return;
    }
    QDataStream out(&file);
    QList<QGraphicsItem*> itemList = scene->items();
    int itemListSize = itemList.size();
    out << itemListSize;
    foreach( QGraphicsItem* item, itemList)
    {
        out << item->x();
        out << item->y();
    }
    QMessageBox::warning(this,"Success","Saved Scene Data to File");
    close();
}

sl_saveSceneToFile() slot of Dialog class is connected to QPushButton clicked signal and serializes number of all the items on the scene with item x() and y() coordinates respectively.

MyGraphicsItem is a derived custom QGraphicsItem class that implements boundingRect() and paint() functions of the parent. MyGraphicsItem is a rectangular derived QGraphicsItem and added to the QGraphicsScene.
MyGraphicsItem.h file contains custom QGraphicsItem class declaration.
#ifndef MYGRAPHICSITEM_H
#define MYGRAPHICSITEM_H
#include <QGraphicsItem>
class MyGraphicsItem : public QGraphicsItem
{
public:
    MyGraphicsItem(QGraphicsItem *parent = 0, QGraphicsScene *scene = 0);
    ~MyGraphicsItem();

protected:
    QRectF boundingRect() const;

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);
};

#endif // MYGRAPHICSITEM_H

MyGraphicsItem.cpp file contains implementation details for custom QGraphicsItem class.
#include "MyGraphicsItem.h"
#include <QPainter>
MyGraphicsItem::MyGraphicsItem(QGraphicsItem* parent, QGraphicsScene* scene) : QGraphicsItem(parent,scene)
{
}

MyGraphicsItem::~MyGraphicsItem(){}

QRectF MyGraphicsItem::boundingRect() const
{
    qreal penWidth = 1;
    return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
                  20 + penWidth, 20 + penWidth);
}

void MyGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
           QWidget *widget)
{
    Q_UNUSED(widget)
    Q_UNUSED(option)

    QPen pen;
    pen.setWidthF(2);
    pen.setStyle(Qt::DashLine);
    painter->setPen(pen);
    painter->setBrush(QBrush(QColor(50,120,80)));
    painter->drawRoundedRect(boundingRect(), 25, 25, Qt::RelativeSize);
}

sceneData.txt is the produced file that contains serialized binary data.

2 comments:

  1. Good tutorials, but since you provide parent for all dynamically allocated elements you don't really need to delete them in the constructor, the parent takes care of that. All GUI elements in Qt inherit QObject which takes responsibility of the life cycle of all dynamically allocated children.

    ReplyDelete
  2. You don't save anything but x() and y(). A more general solution saving all attributes would be more interesting.

    ReplyDelete