Showing posts with label Serialization. Show all posts
Showing posts with label Serialization. Show all posts

Saturday, March 28, 2015

Serialize C++ Object with QString Instance Variables

boost::serialization library can be used to serialize and deserialize the state of QString variables in a C++ program.
By default boost::serialization library can not serialize a directly given QString variable, so you need an extra Serializer implementation for your serialization and deserialization requirements.

When you try to serialize a QString variable directly by using boost::serialization library, compiler gives errors about missing serialize method as follows :

error: 'class QString' has no member named 'serialize'

In order to use boost::serialization library in your applications, it is required to have libboost-all-dev library on your OS which contains libboost-serialization-dev. Following terminal command installs required boost libraries on linux OS :

$ sudo apt-get install libboost-all-dev

After installing boost serialization library, you can start including related header files into your application.

Following sample project created by qt creator and contains following files :

1-BoostSerializeQString.pro
2-QStringSerializer.h
3-User.h
4-main.cpp



BoostSerializeQString.pro file contains project configuration.

QT       += core
TARGET    = BoostSerializeQString
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE  = app
LIBS     += -lboost_serialization

SOURCES  += main.cpp

HEADERS  += QStringSerializer.h \
            User.h

boost_serialization library is included into this Qt Project by adding "LIBS += -lboost_serialization" line.
In this example User class contains two QString instance variables to serialize and deserialize. When saving and loading User class it is required to save and load QString variable state properly. Also, User class has an intrusive serialize function. For User class case, the serialize function is implemented as a member of the class.

#ifndef USER_H
#define USER_H

class User
{
public:

    User() {}

    User(const QString &name, const QString &surname)
    {
        this->name = name;
        this->surname = surname;
    }

    QString getName() { return name; }
    QString getSurname() { return surname; }

private:
    QString name;
    QString surname;

    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // serialize deserialize QString instance variables
        ar & BOOST_SERIALIZATION_NVP(name);
        ar & BOOST_SERIALIZATION_NVP(surname);
    }

};

#endif // USER_H


QStringSerializer.h file contains save and load functions which are going to be invoked during serialization and deserialization process of QString instance variables.

By declaring non-intrusive serialization mechanism we were able to implement serialization for QString without changing its original class definition.

boost::serialization is able to work on std::string type correctly, so it is required to retrieve std::string value of QString for serialization. And also for the reverse operation it is required to construct QString from loaded std::string value.

QString has got both toStdString and fromStdString functions to achieve these requirements.

Inside serialize and deserialize functions different statements are executed so it is required to implement save and load functions separately.

Depending on the type of the archieve used for saving or loading current QString variable, save or load function is invoked.

#ifndef QSTRINGSERIALIZER_H
#define QSTRINGSERIALIZER_H

namespace boost {
    namespace serialization {

        template<class Archive>
        void save( Archive & ar, const QString& qStringParam, const unsigned int )
        {
            // save class member variables
            std::string stdString = qStringParam.toStdString();
            ar & BOOST_SERIALIZATION_NVP(stdString);
        }

        template<class Archive>
        void load( Archive & ar, QString& qStringParam, const unsigned int )
        {
            // load class member variables
            std::string stdString;
            ar & BOOST_SERIALIZATION_NVP(stdString);
            qStringParam = qStringParam.fromStdString(stdString);
        }

        template<class Archive>
        void serialize(Archive & ar, QString & t, const unsigned int file_version)
        {
            split_free(ar, t, file_version);
        }

    } // namespace serialization
} // namespace boost
#endif // QSTRINGSERIALIZER_H


main method in main.cpp file contains two code blocks which are respectively used for serialization and deserialization of User object with QString instance variables or states. After reconstructing User object, QString instance variables name and surname are initialized with the original values as serialized.

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/nvp.hpp>

#include <QString>
#include <fstream>
#include <iostream>

#include "User.h"
#include "QStringSerializer.h"

using namespace std;

int main()
{
        {
            // Initialize User object to serialize with data
            User user("userName","userSurname");
            std::ofstream ofs("stateInfoFile.xml");
            boost::archive::xml_oarchive oa(ofs);
            // write class instance to archive
            oa & BOOST_SERIALIZATION_NVP(user);
        }

        {
            User user;
            std::ifstream ifs("stateInfoFile.xml");
            boost::archive::xml_iarchive ia(ifs);
            // read class instance back from archive
            ia & BOOST_SERIALIZATION_NVP(user);

            std::cout << "Name : " << user.getName().toStdString() << std::endl;
            std::cout << "Surname : " << user.getSurname().toStdString() << std::endl;
        }
}


User object state is saved into stateInfoFile.xml file and loaded back from the same xml file again for object reconstruction. stateInfoFile.xml file is an xml file and its content is as follows :


Wednesday, July 27, 2011

Load QGraphicsScene From QDataStream

Qt allows you to read binary data from files into QDataStream. In this sample serialized QGprahicsItem is created on the QGraphicsScene by reading related data from a text file.
Sample project started as a QT Gui Application and contains main.cpp, LoadSceneFromQDataStream.h, LoadSceneFromQDataStream.cpp, MyGraphicsItem.h and MyGraphicsItem.cpp files.
Project Directory Structure in Qt Creator IDE :


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

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

LoadSceneFromQDataStream.h file contains custom QDialog which displays content of binary file in a QGraphicsScene instance.
#ifndef LOADSCENEFROMQDATASTREAM_H
#define LOADSCENEFROMQDATASTREAM_H
#include <QDialog>

class QGraphicsScene;
class QGraphicsView;
class QVBoxLayout;

class Dialog : public QDialog
{
    Q_OBJECT

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

public slots:
    void sl_loadSceneFromFile();

private:
    QGraphicsScene* scene;
    QGraphicsView* view;
    QVBoxLayout* layout;
    QPushButton* btnLoad;
};

#endif // LOADSCENEFROMQDATASTREAM_H

LoadSceneFromQDataStream.cpp file contains implementation details for the custom QDialog.
#include "LoadSceneFromQDataStream.h"
#include "MyGraphicsItem.h"

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

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    scene = new QGraphicsScene(this);
    view = new QGraphicsView(scene,this);
    layout = new QVBoxLayout(this);
    layout->addWidget(view);
    btnLoad = new QPushButton("Load Scene From File");
    connect(btnLoad, SIGNAL(clicked()), this, SLOT(sl_loadSceneFromFile()));
    layout->addWidget(btnLoad);
    setLayout(layout);
    resize(270,200);
}

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

void Dialog::sl_loadSceneFromFile()
{
    QString fileName(QDir::currentPath().append("/sceneData.txt"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly))
    {
            return;
    }
    QDataStream in(&file);
    int itemListSize;
    in >> itemListSize;
    for( int i = 0; i< itemListSize; i++ )
    {
        qreal xCoord = 0;
        qreal yCoord = 0;
        in >> xCoord;
        in >> yCoord;
        MyGraphicsItem* item = new MyGraphicsItem();
        item->setPos(xCoord,yCoord);
        scene->addItem(item);
    }
    QMessageBox::warning(this,"Success","Loaded Scene Items From File");
    close();
}

sl_loadSceneFromFile() opens sceneData.txt file and reads its content into QDataStream instance.
sceneData.txt file is located under LoadSceneFromQDataStream/LoadSceneFromQDataStream folder of the project. sceneData.txt file is created by the previous sample application.

First serialized data to the sceneData.txt file is the number of QGraphicsItems on the QGraphicsScene. It is read into the variable “itemListSize” from sceneData.txt file. Then for each QGraphicsItem on the QGraphicsScene, xCoordinate and yCoordinate of the QGraphicsItem is stored in the file in binary format. By reading x and y coordinate of the QGraphicsItem from sceneData.txt file, item’s position is set. After setting the pos of the QGraphicsItem, it is inserted into the list of QGraphicsItems on the QGraphicsScene by calling the addItem function of QGraphicsScene.

Regenerated QGraphicsItem instance on the QGraphicsScene is a custom/derived QGprahicsItem and is defined in MyGraphicsItem.h file.
#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

Custom QGraphicsItem contains constructor, destructor, boundingRect() and paint() methods.

MyGraphicsItem.cpp file contains implementation details for the 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);
}


When you run the application, a dialog with a "load scene from file" button is displayed.By clicking the qpushbutton, application searches for the sceneData.txt file and loads files content into the QGraphicsScene.



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.