Tuesday, August 23, 2011

Draw Grid on QGraphicsScene

Derived QGraphisScene class enables child class to reimplement drawBackground() function of the parent class. By reimplementing drawBackground() function, child class draws the background of the scene using painter.
Sample project started as a QT Gui Application and contains main.cpp, CustomQGraphicsScene.h, CustomQGraphicsScene.cpp, Dialog.h and Dialog.cpp files.
Project Directory Structure in Qt Creator IDE :
Qt project cpp and header files for draw grid on qgraphicsscene

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

main.cpp file contains the custom QDialog instance and shows it.
#include <QApplication>
#include "Dialog.h"

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   Dialog w;
   w.show();
   return a.exec();
}


Dialog.h is derived from QDialog class which contains QGraphicsView in it.
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>

class CustomQGraphicsScene;
class QGraphicsView;
class QVBoxLayout;

class Dialog : public QDialog
{
   Q_OBJECT
public:
   Dialog(QWidget *parent = 0);
   ~Dialog();
private:
   CustomQGraphicsScene* scene;
   QGraphicsView* view;
   QVBoxLayout* layout;
};

#endif // DIALOG_H

Dialog.cpp contains implementation details for derived QDialog class.
#include "Dialog.h"
#include "CustomQGraphicsScene.h"
#include <QVBoxLayout>
#include <QGraphicsView>

Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
   scene = new CustomQGraphicsScene(this);
   view = new QGraphicsView(scene,this);
   layout = new QVBoxLayout(this);
   layout->addWidget(view);
   setLayout(layout);
   resize(270,200);
}

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


Custom QDialog creates an instance of derived QGraphicsScene and displays in a QGraphicsView.
CustomQGraphicsScene.h is derived from QGraphicsScene in which drawBackground() function is reimplemented to draw grid on it.
#ifndef CUSTOMQGRAPHICSSCENE_H
#define CUSTOMQGRAPHICSSCENE_H
#include <QGraphicsScene>

class CustomQGraphicsScene : public QGraphicsScene
{
public:
   CustomQGraphicsScene(QObject *parent);

protected:
   void drawBackground(QPainter * painter, const QRectF & rect );
};

#endif // CUSTOMQGRAPHICSSCENE_H

CustomQGraphicsScene.cpp contains implementation details for derived QGraphicsScene class.
#include "CustomQGraphicsScene.h"
#include <QPainter>

static const int GRID_STEP = 30;

inline qreal round(qreal val, int step) {
   int tmp = int(val) + step /2;
   tmp -= tmp % step;
   return qreal(tmp);
}

CustomQGraphicsScene::CustomQGraphicsScene(QObject *parent ) : QGraphicsScene(parent)
{}

void CustomQGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
{
   int step = GRID_STEP;
   painter->setPen(QPen(QColor(200, 200, 255, 125)));
   // draw horizontal grid
   qreal start = round(rect.top(), step);
   if (start > rect.top()) {
      start -= step;
   }
   for (qreal y = start - step; y < rect.bottom(); ) {
      y += step;
      painter->drawLine(rect.left(), y, rect.right(), y);
   }
   // now draw vertical grid
   start = round(rect.left(), step);
   if (start > rect.left()) {
      start -= step;
   }
   for (qreal x = start - step; x < rect.right(); ) {
      x += step;
      painter->drawLine(x, rect.top(), x, rect.bottom());
   }
}


Drawing grid process is divided into two substeps such as drawing vertical and drawing horizontal lines. Derived QGraphicsScene instance in which drawBackground() function is reimplemented looks like following screenShot :

qt result qgraphicsscene with a grid

Wednesday, August 3, 2011

Save QGraphicsScene to XML File By Using QXmlStreamWriter

QGraphicsItems that are residing in a QGraphicsScene can be saved to XML file by using QXMLStreamWriter class.
Sample project started as a QT Gui Application and contains main.cpp, SaveQGraphicsSceneToXML.h, SaveQGraphicsSceneToXML.cpp, MyGraphicsItem.h and MyGraphicsItem.cpp files.
Project Directory Structure in Qt Creator IDE :



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

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

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

SaveQGraphicsSceneToXML.h is derived from QDialog class which contains QGraphicsView in it.
#ifndef SAVEQGRAPHICSSCENETOXML_H
#define SAVEQGRAPHICSSCENETOXML_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_saveSceneToXML();

private:
    QGraphicsScene* scene;
    MyGraphicsItem* item;
    QGraphicsView* view;
    QVBoxLayout* layout;
    QPushButton* btnSaveToXML;
};
#endif // SAVEQGRAPHICSSCENETOXML_H

SaveQGraphicsSceneToXML.cpp contains implementation details for derived QDialog class.
#include "SaveQGraphicsSceneToXML.h"
#include "MyGraphicsItem.h"

#include <QGraphicsScene>
#include <QGraphicsView>
#include <QVBoxLayout>
#include <QPushButton>
#include <QFile>
#include <QList>
#include <QMessageBox>
#include <QXmlStreamWriter>
#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);
    btnSaveToXML = new QPushButton("Save Scene To XML");
    connect(btnSaveToXML, SIGNAL(clicked()), this, SLOT(sl_saveSceneToXML()));
    layout->addWidget(btnSaveToXML);
    setLayout(layout);
    resize(270,200);
}

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

void Dialog::sl_saveSceneToXML()
{
    QString fileName(QDir::currentPath().append("//sceneData.xml"));
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly))
    {
            return;
    }
    QXmlStreamWriter xmlWriter(&file);
    xmlWriter.setAutoFormatting(true);
    xmlWriter.writeStartDocument();
    xmlWriter.writeStartElement("SceneData");
    xmlWriter.writeAttribute("version", "v1.0");
    xmlWriter.writeStartElement("GraphicsItemList");
    foreach( QGraphicsItem* item, scene->items())
    {
        if( item->type() == MyGraphicsItem::Type )
        {
            MyGraphicsItem* myItem = (MyGraphicsItem*)item;
            xmlWriter.writeStartElement("MyGraphicsItem");
            xmlWriter.writeAttribute("xCoord", QString::number(myItem->x()));
            xmlWriter.writeAttribute("yCoord", QString::number(myItem->y()));
            xmlWriter.writeEndElement();  //end of MyGraphicsItem
        }
    }
    xmlWriter.writeEndElement();   //end of GraphicsItemList
    xmlWriter.writeEndElement();   //end of SceneData
    QMessageBox::warning(this,"Success","Saved Scene Data to XML File");
    close();
}

QGraphicsScene contains custom QGraphicsItem in it. Derived QGraphicsItem position is set and then added onto the QGraphicsScene. Different QGraphicsItems can be added onto the QGraphicsScene, too.

btnSaveToXML QPushButton is connected to sl_saveSceneToXML slot of current Dialog instance. By clicking on the QPushButton instance which has a text property set to "Save Scene To XML" sl_saveSceneToXML slot is called.

sl_saveSceneToXML slot saves the content of QGraphicsScene into the sceneData.xml file. QxmlStreamWriter is used to write data into the opened file.

xmlWriter.writeStartDocument(); line writes a document which starts with XML version number "1.0" and also writes the encoding "UTF-8" information.

xmlWriter.writeStartElement("SceneData"); line writes the start element with “SceneData”.

xmlWriter.writeAttribute("version", "v1.0"); line adds “version” attribute with “v1.0” value to “SceneData” element.

xmlWriter.writeStartElement("GraphicsItemList"); line writes "GraphicsItemList" under “SceneData” element.

Foreach custom QGraphicsItem instance that is residing in the QGraphicsScene a new element is inserted into the xml file with writeStartElement("MyGraphicsItem") function of QXmlStreamWriter. Previously setted xCoord and yCoord attributes of custom QGraphicsItem are written into the xml file with writeAttribute("yCoord", Qstring::number(myItem->y())); function of QxmlStreamWriter.
Foreach writeStartElement() function, corresponding writeEndElement() function is called to close the previous start element. Each QGraphicsItem instance is identified by its item->type() and written into the XML file.

“GraphicsItemList” and “SceneData” elements are closed by calling xmlWriter.writeEndElement(); .

Saved XML file content is :

 
 
     
         
     


Tuesday, August 2, 2011

Save Screenshot of QGraphicsScene

In order to display items that are residing in QGraphicsScene, QGraphicsView is used. In the sample application Custom QDialog contains a QGraphicsView with a QGraphicsScene inside it.

Project Directory Structure in Qt Creator IDE :



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


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


Dialog.h file contains custom QDialog interface.
#ifndef DIALOG_H
#define DIALOG_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_saveSceneAsImage();

private:
    QGraphicsScene* scene;
    QGraphicsView* view;
    QVBoxLayout* layout;
    QPushButton* btnSaveScene;
};
#endif // DIALOG_H


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

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QVBoxLayout>
#include <QPushButton>
#include <QString>
#include <QFileDialog>
#include <QPixmap>

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    scene = new QGraphicsScene(this);
    scene->addEllipse(22,3,12,13,QPen(QColor("red")));
    scene->addLine(1,2,8,9,QPen("blue"));
    scene->addRect(12,12,6,7,QPen("green"));
    view = new QGraphicsView(scene,this);
    layout = new QVBoxLayout(this);
    btnSaveScene = new QPushButton("Take Scene SnapShot");
    connect(btnSaveScene, SIGNAL(clicked()), this, SLOT(sl_saveSceneAsImage()));
    layout->addWidget(view);
    layout->addWidget(btnSaveScene);
    setLayout(layout);
    resize(270,200);
}

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

void Dialog::sl_saveSceneAsImage()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save Scene", "", "Image (*.png)");
    QPixmap pixMap = QPixmap::grabWidget(view);
    pixMap.save(fileName);
    close();
}


QGraphicsScene content can be saved as a png image file. Sample QGraphicsScene contains items of type line,rectangle and ellipse. Taken snapshot displays all these items inside of saved snapshot.

sl_saveSceneAsImage() slot of custom QDialog uses Qpixmap::grabWidget function to create a pixmap and paints the QGraphicsView inside it. Because all the child items are also painted in then QGraphicsScene becomes painted inside snapshot, too.

As a result Qpixmap instance is saved to specified location with :
pixMap.save(fileName);


Sample QGraphicsScene content :




Saved QGraphicsScene Snapshot :