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 :



Thursday, July 28, 2011

Redirect QTest Output To Log File

QtTest classes are created by subclassing QObject class and adding private slots into derived QObject class. Every private slot is treated as a testfunction and executed by QTest::qExec() function.
Sample project started as a QT Console Application and contains main.cpp, TestString.h and TestString.cpp files.
Project Directory Structure in Qt Creator IDE :



LogQTestOutputToFile.pro is the project configuration file and contains:
QT       += testlib core
TARGET = LogQTestOutputToFile
TEMPLATE = app
SOURCES += main.cpp \
    TestString.cpp
HEADERS  += TestString.h


QT += testlib

line which is used to include testlib dependencies into project.

TestString.h file contains private slots which are aimed to test mainly toUpper() behavior of String class.

#include <QtTest/QtTest>
#include <QString>
#include <QObject>

class TestString: public QObject
{
    Q_OBJECT
private slots:
    void initTestCase();
    void toUpper();
    void cleanupTestCase();
};


initTestCase() and cleanupTestCase() private slots are executed by the testing framework and used to initialize and release resources; respectively.

TestString.cpp file contains implementation details for the private slots declared in TestString.h file.
#include "TestString.h"

void TestString::initTestCase()
{
    qDebug("called before everything else, initialize your resources");
}

void TestString::toUpper()
{
    QString str = "Hello";
    QCOMPARE(str.toUpper(), QString("HELLO"));
}

void TestString::cleanupTestCase()
{
    qDebug("called after toUpper(), release allocated resources here");
}


Because this a QT Console Application, main.cpp file contains only main() function which calls QTest::qExec(&testString, testCmd) function to execute testfunctions in the specified test object.
#include <QApplication>
#include <QStringList>
#include <QDir>
#include "TestString.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QStringList testCmd;
    QDir qtestLogDir;
    qtestLogDir.mkdir("UnitTest_Results");
    testCmd<<" "<<"-o"<<"UnitTest_Results/QTestString_log.txt";
    TestString testString;
    QTest::qExec(&testString, testCmd);
    return a.exec();
}

When you run the application, test execution output is displayed at QT Creator Application Output Window. In order to redirect testfunction output to specified log file; output directory and log file name information are passed as argument to QTest::qExec(&testString, testCmd) function.

testCmd<<" "<<"-o"<<"UnitTest_Results/QTestString_log.txt";

command line argument redirects output to UnitTest_Results/QTestString_log.txt file.

QTestString_log.txt file can be found under LogQTestOutputToFile/UnitTest_Results folder.

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.