Showing posts with label Qt. Show all posts
Showing posts with label Qt. Show all posts

Wednesday, July 22, 2015

Call Function From PostgreSQL with Qt SQL

Qt SQL enables programmers to call written functions from PostgreSQL database. Example project contains following table and functions from PostgreSQL server.

Configuration required to run this application:

1-) PostgreSQL 9.4.4
2-) Qt Version 5.4.0
3-) Qt Creator 3.2.2

PostgreSQL has got its own database programming language named with PL/pgSQL and following functions implemented in PL/pgSQL.

-- Table: person

-- DROP TABLE person;

CREATE TABLE person
(
  id integer NOT NULL,
  name text NOT NULL,
  age integer NOT NULL,
  address character(50),
  CONSTRAINT person_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE person
  OWNER TO test;


person table is filled with the following sample records/rows in order to see the execution result.

INSERT INTO person(
            id, name, age, address)
    VALUES (1, 'paul', 12, 'adana');

INSERT INTO person(
            id, name, age, address)
    VALUES (2, 'tufan', 15, 'ankara');

INSERT INTO person(
            id, name, age, address)
    VALUES (3, 'colin', 18, 'antep');

First PostgreSQL PL/pgSQL function returns the max age from the person table.
CREATE OR REPLACE FUNCTION maxAge()
RETURNS integer AS $$
declare
 max_age integer;
BEGIN
   SELECT max(age) into max_age FROM person;
   RETURN max_age;
END;
$$ LANGUAGE plpgsql;

maxAge() plpgsql function called with a select statement from pgAdmin III Query Tool.
SELECT maxage();

Output of the call of the maxage() function from PostgreSQL database is "18".

Second PostgreSQL PL/pgSQL function returns the age of the person whose name is sent as a parameter from the person table.
CREATE OR REPLACE FUNCTION getAgeOfPersonByName( nameOfPerson text )
RETURNS integer AS $$
declare
 person_age integer;
BEGIN
   SELECT age into person_age FROM person where name=nameOfPerson;
   RETURN person_age;
END;
$$ LANGUAGE plpgsql;

getAgeOfPersonByName( nameOfPerson text ) plpgsql function called with a select statement from pgAdmin III Query Tool.

SELECT getAgeOfPersonByName('paul');
Output of the call of the getAgeOfPersonByName('paul') function from PostgreSQL database is "12".

After creating the required tables and functions in PostgreSQL, now it is time to write our Qt application that calls these PL/pgSQL functions from PostgreSQL with Qt SQL.

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

1- QtSQLCallFunctionFromPostgreSQL.pro
2- main.cpp


QtSQLCallFunctionFromPostgreSQL.pro file contains project configuration :

TEMPLATE  = app
CONFIG   += console
CONFIG   -= app_bundle
QT       += core sql
QT       -= gui
TARGET = QtSQLCallFunctionFromPostgreSQL
SOURCES += main.cpp
Sample project only contains main.cpp file as source file. maxAge and getAgeOfPersonByName PL/pgSQL functions from PostgreSQL are called and return values are displayed in the terminal.

#include <QCoreApplication>
#include <QtSql>
#include <QDebug>

int main()
{
    const char* driverName = "QPSQL";
    QSqlDatabase db( QSqlDatabase::addDatabase(driverName) );
    db.setConnectOptions();
    db.setHostName("localhost");
    db.setDatabaseName("testdb");
    db.setUserName("test");
    db.setPassword("test");

    db.open();

    QString functionNameToCall = "maxAge";
    QSqlQuery* query = new QSqlQuery(db);
    query->prepare(QString("SELECT %1()").arg(functionNameToCall));
    query->exec();

    QString maxAgeOfPersonTable;
    while (query->next())
    {
        maxAgeOfPersonTable = query->value(0).toString();
    }

    qDebug() << maxAgeOfPersonTable;

    functionNameToCall = "getageofpersonbyname";
    QString name = "paul";
    query->prepare(QString("SELECT %1(?)").arg(functionNameToCall));
    query->addBindValue(name);
    query->exec();

    QString personAge;
    while (query->next())
    {
        personAge = query->value(0).toString();
    }

    qDebug() << personAge;

    delete query;
    db.close();

    return 0;
}


getageofpersonbyname PL/pgSQL function takes a parameter and the parameter is binded by addBindValue function of QSqlQuery. When the Qt application runs in the terminal the following output is displayed.


Sunday, July 19, 2015

Connect to PostgreSQL From Qt Application with Qt Sql

Qt comes with Qt Sql APIs in order to perform database related operations. Qt SQL's APIs consist of mainly three parts.
- Driver Layer
- SQL API Layer
- User Interface Layer

In order to connect to a database from Qt application, related database driver needs to be configured.
After setting connection to a specific database from Qt application, SQL API helps to achieve common database operations such as open-close connection, query tables, etc.

Configuration required to run this application:

1-) PostgreSQL 9.4.4
2-) Qt Version 5.4.0
3-) Qt Creator 3.2.2


Create sql script for Person table: You can use pgAdmin tools in order to create tables on postgreSQL database.

-- Table: person

-- DROP TABLE person;

CREATE TABLE person
(
  id integer NOT NULL,
  name text NOT NULL,
  age integer NOT NULL,
  address character(50),
  CONSTRAINT person_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE person
  OWNER TO test;


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

1- QSqlConnection.pro
2- QSQLDbHelper.h
3- QSQLDbHelper.cpp
4- main.cpp



QSqlConnection.pro file contains project configuration :

TEMPLATE  = app
CONFIG   += console
CONFIG   -= app_bundle
QT       += core sql
QT       -= gui
SOURCES  += main.cpp \
    QSQLDbHelper.cpp

HEADERS += \
    QSQLDbHelper.h


In this example qt sql project QSQLDbHelper.h class header file contains database related functions.


#ifndef QSQLDBHELPER_H
#define QSQLDBHELPER_H

#include <QtSql>
#include <QString>
#include <QDebug>

class QSQLDbHelper
{
public:
    QSQLDbHelper(const char* driver);
    ~QSQLDbHelper();
    QSqlDatabase* connect( const QString& server,
                           const QString& databaseName,
                           const QString& userName,
                           const QString& password );
    void disConnect();

    int selectRowCountResult(QSqlQuery* query);
    bool executeInsert(QSqlQuery* query);
    bool executeUpdate(QSqlQuery* query);
    bool executeDelete(QSqlQuery* query);

private:
    QSqlDatabase* db;
};

#endif // QSQLDBHELPER_H


QSQLDbHelper.cpp file contains implementation details for the following functions :

1- Class constructor/desctructor
2- Db connect
3- Db disconnect
4- selectRowCount
5- insert operation
6- update operation
7- delete operation


#include "QSQLDbHelper.h"

QSQLDbHelper::QSQLDbHelper(const char* driver)
{
    db = new QSqlDatabase( QSqlDatabase::addDatabase(driver) );
}

QSQLDbHelper::~QSQLDbHelper()
{
    qDebug() << "Called Destructor!";
    delete db;
}

QSqlDatabase* QSQLDbHelper::connect( const QString& server,
                                     const QString& databaseName,
                                     const QString& userName,
                                     const QString& password )
{
    db->setConnectOptions();
    db->setHostName(server);
    db->setDatabaseName(databaseName);
    db->setUserName(userName);
    db->setPassword(password);

    if(db->open()) {
        return db;
    }
    else {
        return NULL;
    }
}

int QSQLDbHelper::selectRowCountResult(QSqlQuery* query)
{
    bool queryRes = query->exec();
    if (query->lastError().type() != QSqlError::NoError || !queryRes)
    {
        qDebug() << query->lastError().text();
        return -1;
    }

    int recordCount = 0;
    while (query->next())
    {
        qDebug() << "Field 1 : " << query->value(0).toString() 
                 << "Field 2 : " << query->value(1).toString();
        ++recordCount;
    }

    return recordCount;
}

bool QSQLDbHelper::executeInsert(QSqlQuery* query)
{
    db->transaction();
    bool queryRes = query->exec();
    if (query->lastError().type() != QSqlError::NoError || !queryRes)
    {
        qDebug() << query->lastError().text();
        db->rollback();
        return false;
    }
    db->commit();
    return true;
}

bool QSQLDbHelper::executeUpdate(QSqlQuery* query)
{
    db->transaction();
    bool queryRes = query->exec();
    if (query->lastError().type() != QSqlError::NoError || !queryRes)
    {
        qDebug() << query->lastError().text();
        db->rollback();
        return false;
    }
    db->commit();
    return true;
}

bool QSQLDbHelper::executeDelete(QSqlQuery* query)
{
    db->transaction();
    bool queryRes = query->exec();
    if (query->lastError().type() != QSqlError::NoError || !queryRes)
    {
        qDebug() << query->lastError().text();
        db->rollback();
        return false;
    }
    db->commit();
    return true;
}

void QSQLDbHelper::disConnect()
{
    qDebug() << "Disconnected From Database!";
    db->close();
}


In order to make database related operations from a qt application it is required to include "QtSql" header file into the related source file.

QSqlDatabase class represents a database in your application. A transaction is started and concluded by using rollback() and commit() functions of QSqlDatabase.

QSqlQuery class executes passed sql queries on postgresql database.

Main method in the main.cpp file calls implemented connect, disconnect and CRUD operation methods from QSQLDbHelper class.


#include <iostream>

#include <QDebug>
#include "QSQLDbHelper.h"

int main()
{
    qDebug() << "Compiled with Qt Version = " << QT_VERSION_STR;

    const char* driverName = "QPSQL";
    QSQLDbHelper* qSQLDbHelper = new QSQLDbHelper(driverName);
    QSqlDatabase* db = qSQLDbHelper->connect("localhost", "testdb", "test", "test");

    if(db->open()) {

        QSqlQuery* query = new QSqlQuery(*db);
        query->setForwardOnly(true);

        // Select empty person table
        QString name = "Paul";
        if( !query->prepare(QString("SELECT id, name from person where name = ? ")) )
        {
            qDebug() <<"Error = " << db->lastError().text();
            return -1;
        }
        else
            query->addBindValue(name);

        int queryResultRowCount = qSQLDbHelper->selectRowCountResult(query);
        qDebug() << "Initial Row Count = " << queryResultRowCount << "\n";

        // insert into empty person table
        QString id = "1";
        QString age = "34";
        QString address = "istanbul";
        if( !query->prepare(
        QString("INSERT INTO person( id, name, age, address) VALUES ( ?, ?, ?, ?)") ))
        {
            qDebug() <<"Error = " << db->lastError().text();
            return -1;
        }
        else
        {
            query->addBindValue(id);
            query->addBindValue(name);
            query->addBindValue(age);
            query->addBindValue(address);
        }

        bool result = qSQLDbHelper->executeInsert(query);
        if( result )
            qDebug() << "Successful insert";
        else
            qDebug() << "Insert failed";

        // Select person table with 1 matching record
        if( !query->prepare(
        QString("SELECT id, name from person where name = ? ")))
        {
            qDebug() <<"Error = " << db->lastError().text();
            return -1;
        }
        else
            query->addBindValue(name);

        queryResultRowCount = qSQLDbHelper->selectRowCountResult(query);
        qDebug() << "After Insert Row Count = " << queryResultRowCount << "\n";


        // Update person table
        name = "Paul2";
        if( !query->prepare(QString("UPDATE person set name=? where id =? ")) )
        {
            qDebug() <<"Error = " << db->lastError().text();
            return -1;
        }
        else
        {
            query->addBindValue(name);
            query->addBindValue(id);
        }

        result = qSQLDbHelper->executeUpdate(query);
        if( result )
            qDebug() << "Successful update";
        else
            qDebug() << "Update failed";

        // Select person table with 0 no matching record
        if( !query->prepare(
        QString("SELECT id, name from person where name = ?")) )
        {
            qDebug() <<"Error = " << query->lastError().text();
            return -1;
        }
        else
        {
            query->addBindValue(name);
        }
        queryResultRowCount = qSQLDbHelper->selectRowCountResult(query);
        qDebug() << "After Update Row Count = " << queryResultRowCount << "\n";


        // Delete from person table whose name is Paul2
        // name = "Paul2";
        if( !query->prepare(QString("Delete from person where name =? ")) )
        {
            qDebug() << "Error = " << db->lastError().text();
            return -1;
        }
        else
        {
            query->addBindValue(name);
        }

        result = qSQLDbHelper->executeDelete(query);
        if( result )
            qDebug() << "Successful delete";
        else
            qDebug() << "Delete failed";

        // Select person table with 0 no matching record
        if( !query->prepare(
        QString("SELECT id, name from person where name = ? ")) )
        {
            qDebug() << "Error = " << db->lastError().text();
            return -1;
        }
        else
        {
            query->addBindValue(name);
        }
        queryResultRowCount = qSQLDbHelper->selectRowCountResult(query);
        qDebug() << "After Delete Row Count = " << queryResultRowCount << "\n";

        delete query;
    }
    else {
        qDebug() << "Something went Wrong:" << db->lastError().text();
    }

    qSQLDbHelper->disConnect();
    delete qSQLDbHelper;

    return 0;
}


In main function database connection is established and related database related operations are being tested. Parametric, dynamic sql queries created by using addBindValue method of QSqlQuery.

Output of the execution of this program shows database operation results from Qt application in terminal.


Tuesday, June 16, 2015

Thread Safe Singleton XMLReader with QMutex and boost property tree

Singleton design pattern is used widely when only one instance of an object needs to be created in the application domain. In order to achieve secure creation of singleton instance in multithreaded environments, locking mechanism must be taken into consideration to provide thread safety.

XML configuration settings file access can be handled by creating a thread-safe singleton XMLParser class. Boost library has got a Property Tree library, boost::property_tree , which populates a tree data structure that is representing the existing XML file content.

boost::property_tree makes it easy to load an existing XML file into application. In order to provide thread safety for singleton XMLParser , QT application development framework supplied QMutex class can be used.

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

1-SingletonXMLParser.pro
2-XmlParser.h
3-XmlParser.cpp
4-main.cpp



SingletonXMLParser.pro file contains project configuration :

QT       += core
TARGET    = SingletonXMLParser
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE  = app
SOURCES  += main.cpp \
            XmlParser.cpp
HEADERS  += XmlParser.h

In this example project, XMLParser.h header file contains the Settings struct which is going to hold data loaded from XML file. Also the name of the XML file is declared in the header file with a #define directive.

Requirements to make XMLParser class a Thread-Safe Singleton is handled by

1- Making constructor private
2- Adding a static XMLParser variable
3- Static Instance creator method
4- QMutex variable for locking


Example ConfSettings.xml file contains following data and placed under an accessible folder :


#ifndef XMLPARSER_H
#define XMLPARSER_H

#define CONF_SETTINGS_FILE "ConfSettings.xml"

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <QMutex>

struct Settings
{
    std::string ipAddress;
    std::string userName;
};

class XMLParser
{
public:
    static XMLParser& instance();
    ~XMLParser();
    Settings getSettings();

private:
    XMLParser();
    Settings configSettings;
    static std::auto_ptr<XMLParser> mInstance;
    static QMutex mMutex;
};

#endif // XMLPARSER_H


XMLParser.cpp file contains implementation details for

1- private constructor
2- static instance creator method
3- static variables


#include "XmlParser.h"

std::auto_ptr<XMLParser> XMLParser::mInstance;
QMutex XMLParser::mMutex(QMutex::Recursive);

XMLParser::XMLParser()
{
    boost::property_tree::ptree config;
    read_xml(CONF_SETTINGS_FILE, config);

    configSettings.ipAddress = config.get<std::string>("ftp.ipAddress");
    configSettings.userName = config.get<std::string>("ftp.userName");
}

XMLParser::~XMLParser()
{
}

XMLParser& XMLParser::instance()
{
    mMutex.lock();
    if (mInstance.get() == 0)
    {
        try
        {
            mInstance.reset(new XMLParser);
        }
        catch (std::bad_alloc&)
        {
            throw;
        }
    }
    mMutex.unlock();
    return *mInstance;
}

Settings XMLParser::getSettings()
{
    return configSettings;
}


In the constructor of the XMLParser class CONF_SETTINGS_FILE is loaded into boost::property_tree::ptree type config variable. Then by applying the get method of boost::property_tree::ptree required elements from the XML document are retrieved into the application.

Static instance() method of XMLParser provides thread safety by calling lock() and unlock() methods of QMutex instance.

Main method in the main.cpp file calls the static instance() method of thread-safe singleton XMLParser class and then loads the config settings into to Settings struct.

#include "XmlParser.h"

int main()
{
    Settings confSettings = XMLParser::instance().getSettings();

    std::cout << confSettings.ipAddress << std::endl;
    std::cout << confSettings.userName << std::endl;

    return 0;
}

Output of the execution of this program shows ipAddress and userName element values from ConfSettings.xml file on the terminal.


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, August 13, 2014

boost::array of std::string items in Qt Creator

boost::array is a template class which enables it to be able to be declared for different type of items. There exists an array as a member inside boost:array template class declaration so boost:array also acts a C-array wrapper with fixed number of items inside it.

Following sample project created by qt creator and contains following files:
1- BoostArrayOfStrings.pro
2- main.cpp



BoostArrayOfStrings.pro file contains project configuration.

TEMPLATE = app
CONFIG += console

SOURCES += main.cpp

INCLUDEPATH += /home/tufan/boost_1_55_0

Boost header files and libraries are located under the directory : /home/tufan/boost_1_55_0

main.cpp file contains main method.

#include <boost/array.hpp>
#include <string.h>

using namespace std;

int main()
{
    typedef boost::array<string,3> boostArray;
    boostArray stringArray;

    stringArray[0] = "FirstStringMember";
    stringArray.at(1) = "SecondStringMember";
    stringArray.at(2) = "ThirdStringMember";

    // some common container operations
    cout << "size:     " << stringArray.size() << endl;
    cout << "empty:    " << boolalpha << stringArray.empty() << endl;
    cout << "front:    " << stringArray.front() << endl;
    cout << "back:     " << stringArray.back() << endl;
    cout << "elements:    " << endl;

    for(boostArray::iterator iter(stringArray.begin()); iter != stringArray.end(); ++iter)
    {
        cout << *iter << endl;
    }

    return 0;
}
boost array contains 3 string items. Members of the array are initialized by using array subscript operator and at() function.

size:     3
empty:    false
front:    FirstStringMember
back:     ThirdStringMember
elements:  
FirstStringMember
SecondStringMember
ThirdStringMember

Monday, November 14, 2011

Call Symbol From Shared Object File (DLL) with QLibrary

    Shared libraries are represented with *.dll and *.so files on windows and unix platforms; respectively. Symbols in a shared object file (*.dll,*.so) are designed to be exported by the library writer. Client imports symbols from shared library. 
    In this sample there are two projects in the Qt Creator projects pane which are SharedLib and SharedLibClient. SharedLib is a C++ Library project which creates a shared object file (dll) with an exported symbol in it. SharedLibClient is a QT Console Application which calls SharedLib dll at runtime. In order to debug SharedLibClient project successfully, required “SharedLib.dll” file is placed under the debug folder of the SharedLibClient project.


SharedLib project contains a simple symbol which sums two integers and returns the result.
SharedLib.pro is the project configuration file and contains: 
QT       -= gui
TARGET = SharedLib
TEMPLATE = lib
DEFINES += SHAREDLIB_LIBRARY
SOURCES += Sharedlib.cpp
HEADERS += Sharedlib.h\
        SharedLib_global.h
SharedLib_global.h is created by the QT Creator IDE for the new C++ Library project and contains:
#ifndef SHAREDLIB_GLOBAL_H
#define SHAREDLIB_GLOBAL_H

#include <QtCore>

#if defined(SHAREDLIB_LIBRARY)
#  define SHAREDLIBSHARED_EXPORT Q_DECL_EXPORT
#else
#  define SHAREDLIBSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif

Sharedlib.h is the header file and contains implementation details for the Sharedlib.
#ifndef SHAREDLIB_H
#define SHAREDLIB_H

#include "SharedLib_global.h"

class SHAREDLIBSHARED_EXPORT SharedLib {
public:
    SharedLib();
    int addNumbers(int num1, int num2);
};

#endif
Sharedlib.cpp file contains the implementation for the addNumbers function.
#include "Sharedlib.h"

extern "C" __declspec(dllexport) int addNumbers(int no1, int no2)
{
    return no1+no2;
}
addNumbers(int,int) symbol is exported as a C function from the SharedLib library. Function is also wrapped in an extern "C" block. If you are building on Windows, the function needs to be explicitly exported from the DLL using the __declspec(dllexport) compiler directive.
In order to create "SharedLib" project in Qt Creator, create a "Qt Shared Library" project and add only one cpp file into it. Inside this cpp file sign your function that you are willing to export with dllexport macro. Then build the project in debug mode. After building the "SharedLib" project in debug mode by Qt Creator IDE, related dll file is generated under the "SharedLibClient\SharedLibClient-build-desktop" folder of SharedLib project for Windows platform.

In order to create "SharedLibClient" project in Qt Creator, create a simple client "Qt Console Application" that is going to use "addNumbers" function from "SharedLib.dll" library. SharedLibClient project contains simply a main.cpp file. In this main.cpp file QLibrary class of QT framework is used to load shared library (SharedLib.dll on Windows) at runtime. One of the amazing features of QLibrary is that it provides platform independent access to specific library at run-time. In order to load specific library at run-time, just give the name of the *.dll or *.so file without suffix to QLibrary constructor. In this case only the name of the library without suffix "SharedLib" is going to be enough to load it into memory.

SharedLibClient.pro is the project configuration file and contains :
QT       += core
QT       -= gui
TARGET = SharedLibClient
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
In main.cpp file, shared library function addNumbers from the SharedLib.dll is called by using the QLibrary resolve function.
#include <QCoreApplication>
#include <QLibrary>
#include <QtDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int result = 0;
    QLibrary myLib("SharedLib");
    typedef int (*MyPrototype)(int,int);
    MyPrototype myFunction = (MyPrototype) myLib.resolve("addNumbers");
    if( myFunction )
        result = myFunction(3,2);
    if( myFunction == 0 || result == 0 ) {
        qDebug() << myLib.errorString();
        qDebug() << "Can not get result from dll!";
        myLib.unload();
        return 0;
    }
    result = result+2;
    qDebug() << result;
    myLib.unload();
    return a.exec();
}

After running the SharedLibClient project in debug mode, the result is displayed on the console output.

main function of “SharedLibClient” project accomplishes following steps sequentially :
- Loads the library by passing the library file name in the QLibrary constructor
- Declares a function pointer to the symbol
- Calls the function of the exported library by resolve function of QLibrary

If QLibrary can not load the symbol from library, then the function pointer will be assigned to 0.

Benefits of Using QLibrary to Load Shared Object (DLL) Files at Run-Time :
- You do not need to have the header and lib files to compile the application.
- Just put your dll file that you want to load at run-time next to your executable .
- Executable can start without having dll next to it because dll is going to be loaded at run-time when it is required.
- Helps to generate a smaller executable file as a result.

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 :



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.



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.

Wednesday, June 8, 2011

Reimplementing closeEvent of QDialog

It is always required to ask the user before closing the dialog whether he/she is sure about that. closeEvent event handler can also be used to save the position of the dialog before it is being closed. closeEvent of QDialog is being called before than the destructor of the extended QDialog instance.
Sample extended QDialog shows a QMessageBox instance when the user clicks on the close button that is placed at the top right corner of the dialog and asks for confirmation before closing the dialog. Depending on the user’s selection, dialog closes or stays open.
Sample project started as a QT Gui Application and contains main.cpp, QDialogCloseEvent.h and QDialogCloseEvent.cpp files.
Project Directory Structure in Qt Creator IDE :

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

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

QDialogCloseEvent.h file is the extended QDialog class header file.
#ifndef QDIALOGCLOSEEVENT_H
#define QDIALOGCLOSEEVENT_H
#include <QDialog>
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
protected:
    void closeEvent(QCloseEvent * event);
};
#endif // QDIALOGCLOSEEVENT_H

QDialogCloseEvent.cpp file contains implementation details for extended QDialog class.
#include "QDialogCloseEvent.h"
#include <QMessageBox>
#include <QCloseEvent>
Dialog::Dialog(QWidget *parent) : QDialog(parent)
{
    resize(300,90);
}
Dialog::~Dialog(){}
void Dialog::closeEvent(QCloseEvent *event)
{
    QMessageBox msgBox;
    msgBox.setText("Are you sure you want to close?");
    msgBox.setStandardButtons(QMessageBox::Close | QMessageBox::Cancel);
    msgBox.setDefaultButton(QMessageBox::Close);
    int result = msgBox.exec();
    switch (result) {
      case QMessageBox::Close:
          event->accept();
          break;
      case QMessageBox::Cancel:
          event->ignore();
          break;
      default:
          QDialog::closeEvent(event);
          break;
    }
}

Custom QDialog with QMessageBox:

Application created by QT Creator and uses Qt4.7 .

Tuesday, June 7, 2011

Show QImage in a QLabel

QImage enables to construct images from file paths on the disk. Constructed QImages can be displayed in a QLabel. By setting the pixmap value of QLabel from setPixmap function, QLabel displays content of QImage.
Sample project started as a QT Gui Application and contains main.cpp, ShowQImageDialog.h and ShowQImageDialog.cpp files.
ShowQImageDialog constructor creates a QGridLayout instance that is containing a QScrollArea inside it and displays the image in this area.
Project Directory Structure in Qt Creator IDE :


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

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

ShowQImageDialog.h file is the extended QDialog class header file.
#ifndef SHOWQIMAGEDIALOG_H
#define SHOWQIMAGEDIALOG_H
#include <QDialog>
#include <QGridLayout>
#include <QScrollArea>
#include <QLabel>
#include <QImage>

class ShowQImageDialog : public QDialog
{
    Q_OBJECT

public:
    ShowQImageDialog(QWidget *parent = 0);
    ~ShowQImageDialog();
private:
    QGridLayout* gridLayout;
    QImage* inputImg;
    QLabel* imgDisplayLabel;
    QScrollArea* scrollArea;
};

#endif // SHOWQIMAGEDIALOG_H

ShowQImageDialog.cpp file contains implementation details for extended QDialog class.
#include "ShowQImageDialog.h"
ShowQImageDialog::ShowQImageDialog(QWidget *parent) : QDialog(parent)
{
    gridLayout = new QGridLayout();
    inputImg = new QImage("/home/tufan/wallpaper.png");
    imgDisplayLabel = new QLabel("");
    imgDisplayLabel->setPixmap(QPixmap::fromImage(*inputImg));
    imgDisplayLabel->adjustSize();
    scrollArea = new QScrollArea();
    scrollArea->setWidget(imgDisplayLabel);
    scrollArea->setMinimumSize(256,256);
    scrollArea->setMaximumSize(512,512);
    gridLayout->addWidget(scrollArea,0,0);
    setLayout(gridLayout);
}

ShowQImageDialog::~ShowQImageDialog()
{
    delete inputImg;
    delete imgDisplayLabel;
    delete scrollArea;
    delete gridLayout;
}

ShowQImageDialog:

Application created by QT Creator and uses Qt4.7 .

Check for valid file extension provided by QFileDialog

QFileDialog is used to select files from a specific directory. getOpenFileName() static function of QFileDialog returns the name of the selected file. Also, a filter can be set for the type of the file selected by the user by separating types with ';;'.
Ex:
"All Files (*.*);;JPEG (*.jpeg *.jpg);;PNG (*.png)"
In order to check for file extension of a file name provided by BrowseDialog which is extending QDialog and containing QFileDialog in its sl_browseImage() slot,
bool isValidImageFile(const QString& str)
function is implemented.
isValidImageFile function takes the full path of the file and splits it into pieces to find the file extension from the original QString instance.
File extension is placed after the last period character, so the last member of the QStringList contains the file extension that we are looking for.
By getting the upper case of the file extension which is provided by the client, we can search for a suitable match in our accepted file extension list. If the provided file extension is not in our list, then the derived custom QDialog opens a QMessageBox and gives a warning to the client.
Sample project started as a QT Gui Application and contains main.cpp, BrowseFileDialog.h and BrowseFileDialog.cpp files.

Project Directory Structure in Qt Creator IDE :

BrowseFileDialog.pro is the project configuration file contains:
TARGET = BrowseFileDialog
CONFIG   += core gui
TEMPLATE = app
SOURCES += main.cpp \
    BrowseFileDialog.cpp
HEADERS += \
    BrowseFileDialog.h

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

BrowseFileDialog.h file is the derived QDialog class header file. Custom QDialog contains signals and slots to be associated with buttons on the dialog.
#ifndef BROWSEFILEDIALOG_H
#define BROWSEFILEDIALOG_H
#include <QDialog>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>

class BrowseFileDialog : public QDialog
{
    Q_OBJECT
public:
    BrowseFileDialog(QWidget *parent = 0);
    ~BrowseFileDialog();
public slots:
    void sl_browseImage();
private:
    void initLayout();
    void initConnections();
    bool isValidImageFile(const QString& str);
    QLabel* label;
    QLineEdit* lineEdit;
    QPushButton* pushButtonBrowse;
    QPushButton* pushButtonOK;
    QPushButton* pushButtonCancel;
};
#endif // BROWSEFILEDIALOG_H

BrowseFileDialog.cpp file contains implementation details for extended QDialog class.
#include "BrowseFileDialog.h"
#include <QHBoxLayout>
#include <QFileDialog>
#include <QString>
#include <QMessageBox>
#include <QSpacerItem>
BrowseFileDialog::BrowseFileDialog(QWidget *parent) : QDialog(parent)
{
    initLayout();
    initConnections();
}

BrowseFileDialog::~BrowseFileDialog()
{
    delete label;
    delete lineEdit;
    delete pushButtonBrowse;
    delete pushButtonOK;
    delete pushButtonCancel;
}

void BrowseFileDialog::initLayout()
{
    QGridLayout* mainLayout = new QGridLayout(this);
    QHBoxLayout* hBoxLayout = new QHBoxLayout();
    hBoxLayout->setSpacing(0);
    hBoxLayout->setMargin(0);
    label = new QLabel("Image File Path :");
    lineEdit = new QLineEdit(this);
    lineEdit->setReadOnly(true);
    pushButtonBrowse = new QPushButton("Browse...");
    hBoxLayout->addWidget(label);
    hBoxLayout->addWidget(lineEdit);
    hBoxLayout->addWidget(pushButtonBrowse);
    QHBoxLayout* actionWidgetsHBoxLayout = new QHBoxLayout();
    pushButtonCancel = new QPushButton("Cancel");
    pushButtonOK = new QPushButton("OK");
    actionWidgetsHBoxLayout->addStretch();
    actionWidgetsHBoxLayout->addWidget(pushButtonCancel);
    actionWidgetsHBoxLayout->addWidget(pushButtonOK);
    mainLayout->addLayout(hBoxLayout,0,0);
    mainLayout->addLayout(actionWidgetsHBoxLayout,1,0);
    setLayout(mainLayout);
}

void BrowseFileDialog::initConnections()
{
    connect(pushButtonBrowse, SIGNAL(clicked()), this, SLOT(sl_browseImage()));
    connect(pushButtonOK, SIGNAL(clicked()), this, SLOT(accept()));
    connect(pushButtonCancel, SIGNAL(clicked()), this, SLOT(reject()));
}

void BrowseFileDialog::sl_browseImage()
{
    QString inputImagePath=QFileDialog::getOpenFileName(this,tr("Select Image File"),QDir::currentPath(),
                                                        tr("All Files (*.*);;JPEG (*.jpeg *.jpg);;PNG (*.png)"));
    if(isValidImageFile(inputImagePath))
        lineEdit->setText(inputImagePath);
    else {
            QMessageBox::information(this, tr("Error!"),
                    tr("Please Provide a valid input image with JPG,JPEG or PNG extension!"));
    }
}

bool BrowseFileDialog::isValidImageFile(const QString& strImageFileName)
{
    QStringList acceptedImageFileTypeList;
    acceptedImageFileTypeList << "JPG"<< "JPEG" << "PNG";
    QStringList splittedStrList = strImageFileName.split(".");
    if( ((splittedStrList.size())-1) >= 0) {
        QString fileExtensionToCheck = splittedStrList[((splittedStrList.size())-1)];
        if( acceptedImageFileTypeList.contains(fileExtensionToCheck.toUpper()) )
            return true;
        else
            return false;
    }
    else
        return false;
}

BrowseFileDialog:

Application created by QT Creator and uses Qt4.7 .

Sunday, May 8, 2011

Create Custom QDialogs With QLayout Classes

Qt Designer is used to design user interfaces for QT applications. In addition to using QT Designer to generate user interfaces, you can populate your dialogs with widgets on them dynamically at runtime by using layout classes. To achieve this, you need to extend qdialog class and add widgets to your derived qdialog instance dynamically at runtime.
Qt's layout classes are used to place widgets on parent dialogs or widgets. Instead of manually placing widgets on main containers and creating static user interfaces, layout classes help to place widgets easily. There are different QT layout classes but QHBoxLayout and QVBoxLayout are the most popular of them. By using the addWidget() method of layout class, you place items on the layout. After adding widgets to the layout properly, it is required to call setLayout() method of the main container to make layout visible on it.
Sample project started as a QT Gui Application and contains main.cpp, DynamicQDialog.h and DynamicQDialog.cpp files.
DynamicDialog.pro is the project configuration file contains:
TARGET = DynamicDialog
CONFIG   += qt gui
TEMPLATE = app
SOURCES += main.cpp \
    DynamicQDialog.cpp
HEADERS += \
    DynamicQDialog.h

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

DynamicQDialog.h file is the derived QDialog class header file. Custom QDialog contains signals and slots to be associated with buttons on the dialog.
#ifndef DYNAMICQDIALOG_H
#define DYNAMICQDIALOG_H
#include <QDialog>
class DynamicQDialog : public QDialog
{
    Q_OBJECT
public:
    DynamicQDialog(QWidget * parent = 0, Qt::WindowFlags f = 0);
    ~DynamicQDialog();
public slots:
    void sl_accept();
    void sl_cancel();
signals:
};
#endif // DYNAMICQDIALOG_H

DynamicQDialog.cpp file contains implementation details for derived QDialog class. 2 QPushButtons added to the QHBoxLayout dynamically at runtime and displayed on the DynamicQDialog instance.
#include "DynamicQDialog.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QMessageBox>

DynamicQDialog::DynamicQDialog(QWidget* parent, Qt::WindowFlags flags): QDialog( parent, flags )
{
    QGridLayout* mainGrid = new QGridLayout;
    QVBoxLayout* topLayout = new QVBoxLayout;
    topLayout->addWidget(new QTextEdit);
    mainGrid->addLayout(topLayout,0,0);

    QHBoxLayout* hLayout = new QHBoxLayout;
    QPushButton* btn;
    for( int i=0; i<2 br="" i="">        if( i == 0 ) {
            btn = new QPushButton("OK");
            connect(btn, SIGNAL(clicked()), this, SLOT(sl_accept()));
        }
        else if( i == 1 ) {
            btn = new QPushButton("Cancel");
            connect(btn, SIGNAL(clicked()), this, SLOT(sl_cancel()));
        }
        hLayout->addWidget(btn);
    }
    mainGrid->addLayout(hLayout,1,0);

    setLayout(mainGrid);
}

DynamicQDialog::~DynamicQDialog(){}

void DynamicQDialog::sl_accept(){
    QMessageBox msgBox;
    msgBox.setText("OK Clicked!");
    msgBox.exec();
}

void DynamicQDialog::sl_cancel(){
    QMessageBox msgBox;
    msgBox.setText("Cancel Clicked!");
    msgBox.exec();
}



Application created by QT Creator and uses Qt4.7 .