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

Introduction to Qt Test

What is Unit Testing ?

Unit Testing is a process in which a way to test the small pieces of code that can be logically isolated from the system. Piece of code (known as unit) may be an individual function, method, procedure, module, or object. The purpose of task is to validate each unit of the software code performs as expected. Unit Testing is done during the development phase of an application by the developers.

What is Qt Test ?

Qt Test is a unit testing framework released from the Qt developers to test the applications or libraries based on Qt framework. It provides all the functionality commonly found in unit testing frameworks as well as extensions for testing graphical user interfaces.

Create Qt Test Project

Qt Creator facilitate the wizard to create the project for unit testing. You can use the "Auto Test Project" wizard to create the Qt Test project.

  • Select File > New Project > Other Project > Auto Test Project > Choose to create a project with boilerplate code for a Qt test.

                                     

  • In the Project and Test Information dialog, specify settings for the project and test:
    • In the Test framework field, select Qt Test.
    • For a Qt test, select the GUI Application check box to create a Qt application.
    • In 'Test case name' field, enter the name of the test case.
    • Select the 'Requires QApplication' check box to add the include statement for QApplication to the main.cpp file of the project.
    • Select the 'Generate initialization and cleanup code' checkbox to add functions to your test that are executed by the testing framework to initialize and clean up the test.
    • Select the build system to use for building the project: qmake, CMake, or Qbs.


Qt Creator creates the test project in the specified project directory. Edit the .cpp file to add private slots for each test function in your test.

Creating a Test
Creating a test, subclass QObject and add one or more private slots in your test class. Each private slot is a test function in your test. In addition, you can define the following private slots that are not treated as test functions. When present, they will be executed by the testing framework and can be used to initialize and clean up either the entire test or the current test function.

  • initTestCase() will be called before the first test function is executed.
  • initTestCase_data() will be called to create a global test data table.
  • cleanupTestCase() will be called after the last test function was executed.
  • init() will be called before each test function is executed.
  • cleanup() will be called after every test function.

Every test should leave the system in a usable state, so it can be run repeatedly. Cleanup operations should be handled in cleanupTestCase(), so they get run even if the test fails.

QTest Namespace
Qt Test Framework provides QTest namespace and QAbstractItemModelTester, QSignalSpy classes to facilities various functions and declarations for unit testing. 

  • QTest - All public methods are in the QTest namespace. 
  • QSignalSpy - Enables introspection for Qt's signals and slots.
  • QAbstractItemModelTester - Allows for non-destructive testing of item models.
Finally....
I am so exhausted with lots of theory. So lets break it here and we meet in the next session with some interesting examples for better understanding. Bye...

Validating Inputs using Qt

Introduction:

Validating the inputs is real and challenging task for the software developer. Users is always seem to type in inputs the developer did not expect. It's not really their fault, the developer needs to validate the input or allow the user to give the input that what you expect.

Qt developers have a wide range of input validators to choose from. In this article I'll review the various input validators available, and show the ways to help our users using QCompleter.


Mask Based Validation:

The easiest way to validate input in a QLineEdit is to use a mask. A mask is a special string that tells the line edit what type of characters the user can type next. If a user types a character that is not in range, it is simply ignored by the line edit.

A mask has both normal characters and special character. Each special character represents a character group. For example, the following code creates a QLineEdit that takes a numeric value in the form: +91-99-99-123423 (like mobile numbers in India).

#include <QtGui/QtGui>

int main(argc, **argv)
{
    QApplication app(argc, argv);

    QLineEdit e;
    e.setInputMask("9999-999-9999-999");
    e.show();

    app.exec();
}

Using QValidator:

Qt has an abstract class named QValidator that establishes the interface for validating the data. Two of QValidator's concrete subclasses can be used for numeric range checking: QIntValidator and QDoubleValidator.

QValidator has a pure virtual method called validate() that returns the following enumerated values:

  • Invalid – Not satisfied the required conditions. 
  • Intermediate – Not satisfied the required conditions. However, further inputs may produce an acceptable result. 
  • Acceptable – Satisfies the required conditions.

Regular Expression:

Regular expressions are powerful tools for validating the input. Regular expressions are offered by many languages like C++0x, python, Perl, Java and etc,. Qt provides class called QRegExp, which implements most of the Perl-style extended regular expression.

The QRegExp class provides pattern matching using regular expressions. It fully supports Unicode. QRegExp will expect or built the pattern string to qualify the valid input from expressions, quantifiers, and assertions.

  • Expression: The expression is a simple character. It can also be a set of characters enclosed in square brackets. For Ex: [ABCD] or [A-D].

  • Quantifier: Specify the number of occurrences of an expression that may appear in the matching expression. For Ex: X{1,1} means match one and only one X. X{1,5} means match a sequence of X characters that contains at least one X but no more than five. The below characters are used as a quantifiers. 
    • +        1 or more occurrences.
    • ?         0 or 1 occurrences. 
    • *         0 or more occurrences. 
    • {i,j}    At least i but not more than j occurrences. 

  • Assertions: Specify the boundaries of a matching effort. The below characters are used as an Assertions. 
    • The caret (^), if it is the first character in the regex, indicates that the match starts at the beginning of the string. 
    • The dollar sign ($), when it is the last character in the regex, means that the effort to match must continue to the end of the string. 
    • In addition, there are word boundary (\b) or non-word boundary (\B) assertions that help to focus the attention of the regex. 

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QRegExpValidator>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    int m_BotI, m_TopI;
    double m_BotD, m_TopD;

    QRegExpValidator *m_pValid;

public slots:
   void installMask();
   void showResult();

   void computeResult();

   void installPattern();
   void showRegResult();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(ui->txtMask, SIGNAL(returnPressed()), this, SLOT(installMask()));
    connect(ui->txtInput, SIGNAL(returnPressed()), this, SLOT(showResult()));

    connect(ui->txtMask, SIGNAL(textChanged(QString)), this, SLOT(installMask()));
    connect(ui->txtInput, SIGNAL(textChanged(QString)), this, SLOT(showResult()));

    m_BotI = 5;
    m_TopI = 9;
    m_BotD = 99.9;
    m_TopD = 149.9;

    QIntValidator* iValid(new QIntValidator(m_BotI, m_TopI, this));
    QDoubleValidator* dValid(new QDoubleValidator(m_BotD, m_TopD, 2, this));
    ui->txtWork->setValidator(iValid);
    ui->txtPay->setValidator(dValid);

    connect(ui->txtWork, SIGNAL(returnPressed()), this, SLOT(computeResult()));
    connect(ui->txtPay, SIGNAL(returnPressed()), this, SLOT(computeResult()));


    m_pValid = new QRegExpValidator(this);

    connect(ui->txtPattern, SIGNAL(returnPressed()), this, SLOT(installPattern()));
    connect(ui->txtRegInput, SIGNAL(returnPressed()), this, SLOT(showRegResult()));

    connect(ui->txtPattern, SIGNAL(textChanged(QString)), this, SLOT(installPattern()));
    connect(ui->txtRegInput, SIGNAL(textChanged(QString)), this, SLOT(showRegResult()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::installMask() {
   ui->txtInput->setInputMask(ui->txtMask->text());
}

void MainWindow::showResult() {
    QString str = ui->txtInput->text();
    ui->txtResult->setText(str);
}


void MainWindow::computeResult() {

    double d = ui->txtPay->text().toDouble();
    int i = ui->txtWork->text().toInt();
    double res = d * i;
    ui->txtTotal->setText(QString("%1").arg(res));
}

void MainWindow::installPattern() {
    QRegExp rx(ui->txtPattern->text());
    m_pValid->setRegExp(rx);
    ui->txtRegInput->setValidator(m_pValid);
}

void MainWindow::showRegResult() {

    QString str = ui->txtRegInput->text();
    ui->txtRegResult->setText(str);
}

Note: In Qt 5, the new QRegularExpression class provides a Perl compatible implementation of regular expressions and is recommended in place of QRegExp.

References:

QTimer - How to work with timer

Introduction:

A timer is a specialized type of clock for measuring time intervals. In the software development, timer is used to call the particular task which needs to run at a specific interval. Timer will independently count the ticks and if it reaches the specified tick value, then it will trigger the event which will be useful for programmer to detect and call the specific functionally to execute. Qt provides a specialized class called QTimer for achieving this concepts.


QTimer:

The QTimer class provides a high-level programming interface for timers. To use it, create a QTimer, connect its timeout() signal to the appropriate slots, and call start(). From then on, it will emit the timeout() signal at constant intervals.

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(1000);

It's good practice to give a parent to your QTimer to use Qt's memory management system. The above code will call the 'update()' function in every 1000ms.

An alternative to using QTimer is to call QObject::startTimer() for your object and reimplement the QObject::timerEvent() event handler in your class (which must inherit QObject). The disadvantage is that timerEvent() does not support such high-level features.

The accuracy of timers depends on the underlying operating system and hardware. Most platforms support a resolution of 1 millisecond, though the accuracy of the timer will not equal this resolution in many real-world situations.

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H


#include <QMainWindow>
#include <QTimer>
#include <QTime>


namespace Ui {
class MainWindow;
}


class MainWindow : public QMainWindow
{
    Q_OBJECT


public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();


private:
    Ui::MainWindow *ui;
    QTimer *timer;


    QTime tt;


public slots:
    void timerUpdate();
};


#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);


    ui->lblTime->setText(tt.currentTime().toString("hh:mm:ss"));


    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
    timer->start(1000);
}


MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::timerUpdate()
{
    tt.addSecs(1);
    ui->lblTime->setText(tt.currentTime().toString("hh:mm:ss"));
}

Event Filters in Qt


Introduction

An event is an object that inherits QEvent class in Qt. Events are delivered to objects which inherits from QObject through calling QObject::event(). Event delivery means that an event has occurred, the QEvent indicates precisely what, and the QObject needs to respond. Most events are specific to QWidget and its subclasses. However, there are important events that aren't related to graphics (For example socket activation) which is the event used by QSocketNotifier for its work.

Some events come from the window system (Eg. QMouseEvent), some from other sources (Eg. QTimerEvent), and some comes from the application program. Qt is symmetric, as usual, so you can send events in exactly the same ways as Qt's own event loop does.



Installing the Event Filter

Event filter is a method used to capture the relevant or interested event using QEvent class. If it is installed on a widget and gives us a chance to manipulate events before they reach that widget. Every QObject class can be installed as an Event Filter, and all it needs to do is implement the eventFilter(QObject *obj, QEvent *evt) method. The method should return "true" if we want to block the event (so it won't reach the target widget), or false if it can pass through the filter.

Best thing about Event Filtering is that in many cases it saves us from subclassing a native widget. Here's a simple use case of counting the backspace clicks on a TextEdit.

Understand with an example

 The below example will give you a clear picture about the Event filter.

main.cpp

#include "mainwindow.h"
#include <qapplication>
#include <qtgui>
#include <qdebug>

int main(int argc, char **argv)
{

  QApplication app(argc, argv);

  MainWindow mw;
  mw.show();

  app.exec();

}

 

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <qmainwindow>
#include <qtextedit>
#include <qlabel>


namespace Ui {
class MainWindow;
}


class MainWindow : public QMainWindow
{
  public:
    MainWindow();


  protected:
    bool eventFilter(QObject *obj, QEvent *ev);


  private:
    QTextEdit *textEdit;
    QLabel    *bpCounter;
    QLabel    *bpTab;
    int        bpCnt;
    int        tabCnt;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QKeyEvent>

MainWindow::MainWindow()
{
  bpCnt = 0;
  tabCnt = 0;

  bpCounter = new QLabel(QString("Backspace Count: %1").arg(bpCnt));
  bpTab = new QLabel(QString("Tab Count: %1").arg(tabCnt));

  textEdit = new QTextEdit;
  setCentralWidget(textEdit);

  statusBar()->addWidget(bpCounter);
  statusBar()->addWidget(bpTab);
  textEdit->installEventFilter(this);
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
  if (obj == textEdit) {
    if (event->type() == QEvent::KeyPress) {
      QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
      if ( keyEvent->key() == Qt::Key_Backspace ) {
        bpCounter->setText(QString("Backspace Count: %1").arg(bpCnt++));
      }
      else if ( keyEvent->key() == Qt::Key_Tab )
      {
        // 4 spaces
        textEdit->insertPlainText(QString().fill(' ', 4));
        bpTab->setText(QString("Tab Count: %1").arg(tabCnt++));
        return true;
      }
    }

    // pass the event to the widget
    return false;
  }
  else {
    // pass the event on to the parent class
    return QMainWindow::eventFilter(obj, event);
  }
};
Application settings using QSettings

Application settings using QSettings

Introduction

Generally the user expects the application to keep the settings across the sessions. For example, setting up the application's window size, window colour, theme, position, various application specific options and etc,. This settings will be stored generally in registry, but some times in INI file on Windows OS depending upon the application developer, property list on macOS, INI file on unix/linux.The main advantages is to customize the application at some extend based on user preferences and reduce keying the same data in each session.

QSettings Class

Qt provides an elegant solution for achieving this concept using QSettings class. QSettings is an abstraction around these technologies, enabling you to save and restore application settings in a portable manner. It also supports custom storage formats. This class is derived from QObject class.

Create Instance for QSettings

You can create the QSettings instance either in heap or stack. You've to pass two parameters like name of your company or organization and the name of your application, when creating instance for QSettings class.

For Example:

QSettings *settings = new QSettings("QtCompany", "My App");

or

QSettings settings("QtCompany", "My App");


This will read/write the folders in the registry using Company name and the application name. Another way is to read/write the INI file. The setting can be stored in the INI file instead of storing the settings into the registry with simple changes. For example,

QString file = QApplication::applicationDirPath()+"/setting.ini";
QSettings settings(file, QSettings::IniFormat);

Usage

QSettings provides the method called setValue and it is used to set the Key and Value combination in the registry. To write a settings, use setValue() method. For example, 


setting->setValue("Name", ui->txtName->text());


Here the "Name" is the property (Key) and the next parameter is the value of the Key. If the specified Key is already exists then the existing value is overwritten by the new value. There is a method called "value", to retrieve the value like this,

ui->txtName->setText(setting->value("Name").toString());


Some more Functions

The below table is giving you some more function and its description about the function to utilize the QSettings class effectively.

FunctionDescription
contains()To test whether a given key exists
remove()To remove the setting associated with a key
clear()To remove all keys
allKeys()To obtain the list of all keys
beginGroup()save or restore many settings with the same prefix (start point)
endGroup()save or restore many settings with the same prefix (end point)


mainwindow.cpp


#include "mainwindow.h"

#include "ui_mainwindow.h"

#include <QSettings>
 

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(ui->btnSave, SIGNAL(clicked()), this, SLOT(writeData()));
    setting = new QSettings("QtCompany", "My App");
 
    // comment below line and check the output for the clear understanding
    readData(); 
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::writeData() {

    setting->beginGroup("MainWindow");
    setting->setValue("Name", ui->txtName->text());
    setting->setValue("Age", ui->txtAge->text().toInt());
    setting->setValue("Address", ui->txtAddress->toPlainText());
    setting->setValue("Gender", ui->rdMale->isChecked());

    setting->setValue("size", size());
    setting->setValue("pos", pos());
    setting->endGroup();
}

void MainWindow::readData() {

    setting->beginGroup("MainWindow");
    ui->txtName->setText(setting->value("Name").toString());
    ui->txtAge->setText(setting->value("Age").toString());
    ui->txtAddress->setPlainText(setting->value("Address").toString());

    bool gender = setting->value("Gender").toBool();
    ui->rdMale->setChecked(gender);
    ui->rdFemale->setChecked(!gender);

    resize(setting->value("size", QSize(400, 400)).toSize());
    move(setting->value("pos", QPoint(200, 200)).toPoint());
    setting->endGroup();
}
 

mainwindow.h

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H


#include <QMainWindow>
#include <QSettings>


namespace Ui {
     class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
private:
    Ui::MainWindow *ui;
    QSettings *setting;

    void readData();
 
private slots:
    void writeData();
};

#endif // MAINWINDOW_H 
 
You can see the registry changes in the below image after running the above problem.
 
 


That's it!!! Hope I covered all the basic concepts and I hope, this will help you lot.

Catagory

More »

Catagory