Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Mastering Qt 5
Mastering Qt 5

Mastering Qt 5: Create stunning cross-platform applications

Arrow left icon
Profile Icon Guillaume Lazar Profile Icon Robin Penea
Arrow right icon
€18.99 per month
Full star icon Full star icon Full star icon Half star icon Empty star icon 3.2 (10 Ratings)
Paperback Dec 2016 526 pages 1st Edition
eBook
€20.98 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m
Arrow left icon
Profile Icon Guillaume Lazar Profile Icon Robin Penea
Arrow right icon
€18.99 per month
Full star icon Full star icon Full star icon Half star icon Empty star icon 3.2 (10 Ratings)
Paperback Dec 2016 526 pages 1st Edition
eBook
€20.98 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m
eBook
€20.98 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing
Table of content icon View table of contents Preview book icon Preview Book

Mastering Qt 5

Chapter 1.  Get Your Qt Feet Wet

If you know C++ but have never touched Qt, or if you have made some intermediate Qt applications, this chapter will ensure that your Qt foundations are safe before studying advanced concepts in the following chapters.

We will teach you to create a simple todo application using Qt Creator. This application will display a list of tasks that you can create/update/delete. We will cover the Qt Creator and Qt Designer interfaces, an introduction to the signal/slot mechanism, the creation of a custom widget with custom signals/slots, and its integration into your application.

You will implement a todo app using new C++14 semantics: lambdas, auto variables, and for loops. Each one of these concepts will be explained in depth and will be used throughout the book.

At the end of this chapter, you will be able to create a desktop application with a flexible UI using Qt widgets and new C++ semantics.

In this chapter, we will cover the following topics:

  • Qt project basic structure
  • Qt Designer interface
  • UI fundamentals
  • Signals and slots
  • Custom QWidget
  • C++14 lambda, auto, for each

Creating a project

The first thing to do is to start Qt Creator.

In Qt Creator, you can create a new Qt project via FileNew File or ProjectApplicationQt Widgets ApplicationChoose.

The wizard will then guide you through four steps:

  1. Location: You must choose a project name and a location.
  2. Kits: Target platforms that your project aims at (Desktop, Android, and so on).
  3. Details: Base class information and name for the generated class.
  4. Summary: Allows you to configure your new project as a subproject and automatically add it to a version control system.

Even if all default values can be kept, please at least set a useful project name such as "todo" or "TodoApp." We won't blame you if you want to call it "Untitled" or "Hello world."

Once done, Qt Creator will generate several files that you can see in the Projects hierarchy view:

Creating a project

The .pro file is Qt's configuration project file. As Qt adds specific file formats and C++ keywords, an intermediate build step is performed, parsing all files to generate final files. This process is done by qmake, an executable from the Qt SDK. It will also generate the final Makefiles for your project.

A basic .pro file generally contains:

  • Qt modules used (coregui, and so on)
  • Target name (todotodo.exe, and so on)
  • Project template (applib, and so on)
  • Sources, headers, and forms

There are some great features that come with Qt and C++14. This book will showcase them in all its projects. For GCC and CLANG compilers, you must add CONFIG += c++14 to the .pro file to enable C++14 on a Qt project, as shown in the following code:

QT       += core gui 
CONFIG   += c++14 
 
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 
 
TARGET = todo 
TEMPLATE = app 
 
SOURCES += main.cpp \ 
           MainWindow.cpp 
 
HEADERS  += MainWindow.h \ 
 
FORMS    += MainWindow.ui \ 

The MainWindow.h and MainWindow.cpp files are the headers/sources for the MainWindow class. These files contain the default GUI generated by the wizard.

The MainWindow.ui file is your UI design file in XML format. It can be edited more easily with Qt Designer. This tool is a WYSIWYG (What You See Is What You Get) editor that helps you to add and adjust your graphical components (widgets).

Here is the main.cpp file, with its well-known function:

#include "MainWindow.h" 
#include <QApplication> 
 
int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 
    MainWindow w; 
    w.show(); 
 
    return a.exec(); 
} 

As usual, the main.cpp file contains the program entry point. It will, by default, perform two actions:

  • Instantiate and show your main window
  • Instantiate a QApplication and execute the blocking main event loop

This is the bottom-left toolbar for Qt Creator:

Creating a project

Use it to build and start your todo application in debug mode:

  1. Check that the project is in Debug build mode.
  2. Use the hammer button to build your project.
  3. Start debugging using the green Play button with a little blue bug.

You will discover a wonderful and beautifully empty window. We will rectify this after explaining how this MainWindow is constructed:

Creating a project

An empty MainWindow screenshot

Tip

Qt tip

  • Press CtrlB (for Windows/Linux) or CommandB (for Mac) to build your project
  • Press F5 (for Windows / Linux) or Command +R (for Mac) to run your application in debug mode

MainWindow structure

This generated class is a perfect yet simple example of Qt framework usage; we will dissect it together. As mentioned previously, the MainWindow.ui file describes your UI design and MainWindow.hMainWindow.cpp is the C++ object where you can manipulate the UI with code.

It is important to take a look at the header file MainWindow.h. Our MainWindow object inherits from Qt's QMainWindow class:

#include <QMainWindow> 
 
namespace Ui { 
class MainWindow; 
} 
 
class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 
 
public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 
private: 
    Ui::MainWindow *ui; 
}; 

As our class inherits from the QMainWindow class, on top of the header file, we add the corresponding include. The second part is the forward declaration of the Ui::MainWindow, as we only declare a pointer.

The Q_OBJECT can look a little strange to a non-Qt developer. This macro allows the class to define its own signals/slots and more globally Qt's meta-object system. These features will be covered later in this chapter.

This class defines a public constructor and destructor. The latter is pretty common. But the constructor takes a parameter parent. This parameter is a QWidget pointer that is null by default.

A QWidget is a UI component. It can be a label, a textbox, a button, and so on. If you define a parent-child relationship between your window, layout, and other UI widgets, memory management of your application will be easier. Indeed, in this case, deleting the parent is enough because its destructor will take care of also deleting its child, which in turn will delete its children and so on.

Our MainWindow class extends QMainWindow from the Qt framework. We have a ui member variable in the private fields. The type is a pointer of Ui::MainWindow, which is defined in the ui_MainWindow.h file generated by Qt. It's the C++ transcription of the UI design file MainWindow.ui. The ui member variable will allow you to interact with your UI components (QLabelQPushButton, and so on) from C++, as shown in the following figure:

MainWindow structure

Tip

C++ tip

If your class only uses pointers or references for a class type, you can avoid including the header by using forward declaration. That will drastically reduce compilation time.

Now that the header part is done, we can talk about the MainWindow.cpp source file.

In the following code snippet, the first include is our class header. The second one is the include required by the generated class Ui::MainWindow. This include is required as we only use a forward declaration in the header:

#include "MainWindow.h" 
#include "ui_MainWindow.h" 
 
MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 

In many cases, Qt generates a good piece of code using the initializer list. The parent argument is used to call the superclass constructor QMainWindow. Our private member variable ui is also initialized now.

Now that ui is initialized, we must call the setupUi function to initialize all widgets used by the MainWindow.ui design file:

As we initialize a pointer in the constructor, it must be cleaned in the destructor:

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

Qt Designer

Qt Designer is a major tool for developing Qt applications. This WYSIWYG editor will help you easily design your GUI. If you switch between Edit mode and Design mode for the MainWindow.ui file, you will see the real XML content and the designer:

Qt Designer

The designer displays several parts:

  • Form Editor: This is a visual representation of the form (empty for now)
  • Widget Box: This contains all widgets that can be used with your form
  • Object Inspector: This displays your form as a hierarchical tree
  • Property Editor: This enumerates the properties of the selected widget
  • Action Editor/Signal & Slots Editor: This handles connections between your objects

It's time to embellish this empty window! Let's drag and drop a Label widget from the Display Widgets section on the form. You can change the name and the text properties from the properties editor.

As we are making a todo application, we suggest these properties:

  • objectNamestatusLabel
  • textStatus: 0 todo/0 done

This label will later display the count of todo tasks and the count of tasks already done. OK, save, build, and start your application. You should now see your new label in the window.

You can now add a push button with those properties:

  • objectNameaddTaskButton
  • textAdd task

You should get a result close to this:

Qt Designer

Tip

Qt tip

You can edit the text property of a widget directly on your form by double-clicking on it!

Signals and slots

The Qt framework brings a flexible message exchange mechanism through three concepts: signals, slots, and connections:

  • signal is a message sent by an object
  • slot is a function that will be called when this signal is triggered
  • The connect function specifies which signal is linked to which slot

Qt already provides signals and slots for its classes, which you can use in your application. For example, QPushButton has a signal clicked(), which will be triggered when the user clicks on the button. The QApplication class has a slot quit() function, which can be called when you want to terminate your application.

Here is why you will love Qt signals and slots:

  • A slot remains an ordinary function, so you can call it yourself
  • A single signal can be linked to different slots
  • A single slot can be called by different linked signals
  • A connection can be made between a signal and a slot from different objects, and even between objects living inside different threads!

Keep in mind that, to be able to connect a signal to a slot, their methods' signatures must match. The count, order, and type of arguments must be identical. Note that signals and slots never return values.

This is the syntax of a Qt connection:

connect(sender, &Sender::signalName,  
    receiver, &Receiver::slotName); 

The first test that we can do to use this wonderful mechanism is to connect an existing signal with an existing slot. We will add this connect call to the MainWindow constructor:

MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 
    connect(ui->addTaskButton, &QPushButton::clicked, 
    QApplication::instance(), &QApplication::quit); 
} 

Let's analyze how a connection is done:

  • sender: This is the object that will send the signal. In our example, it is the QPushButton named addTaskButton added from the UI designer.
  • &Sender::signalName: This is the pointer to the member signal function. Here, we want do something when the clicked signal is triggered.
  • receiver: This is the object that will receive and handle the signal. In our case, it is the QApplication object created in main.cpp.
  • &Receiver::slotName: This is a pointer to one of the receiver's member slot functions. In this example, we use the built-in quit() slot from Qapplication, which will exit the application.

You can now compile and run this short example. You will terminate the application if you click on the addTaskButton of your MainWindow.

Tip

Qt tip

You can connect a signal to another signal. The second signal will be emitted when the first one is triggered.

Now that you know how to connect a signal to an existing slot, let's see how to declare and implement a custom addTask() slot in our MainWindow class. This slot will be called when the user clicks on ui->addTaskButton.

This is the updated MainWindow.h:

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 
 
public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 
 
public slots: 
    void addTask(); 
 
private: 
    Ui::MainWindow *ui; 
}; 

Qt uses a specific slot keyword to identify slots. Since a slot is a function, you can always adjust the visibility (publicprotected or private) depending on your needs.

Add this slot implementation in the MainWindow.cpp file:

void MainWindow::addTask() 
{ 
    qDebug() << "User clicked on the button!"; 
} 

Qt provides an efficient way of displaying debug information with the QDebug class. An easy way to obtain a QDebug object is to call the qDebug() function. Then, you can use the stream operator to send your debug information.

Update the top of the file like this:

#include <QDebug> 
 
MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 
    connect(ui->addTaskButton, &QPushButton::clicked, 
    this, &MainWindow::addTask); 
} 

Since we now use qDebug() in out slot, we must include <QDebug>. The updated connect now calls our custom slot instead of quitting the application.

Build and run the application. If you click on the button, you will see your debug message inside the Qt Creator Application Output tab.

Custom QWidget

We now have to create the Task class that will hold our data (task name and completed status). This class will have its form file separated from MainWindow. Qt Creator provides an automatic tool to generate a base class and the associated form.

Click on FileNew File or ProjectQtQt Designer Form Class. There are several form templates; you will recognize Main Window, which Qt Creator created for us when we started the todo app project. Select Widget and name the class Task, then click on Next. Here is a summary of what Qt Creator will do:

  1. Create a Task.h file and a Task.cpp file.
  2. Create the associated Task.ui and do the plumbing to connect it to Task.h.
  3. Add these three new files to todo.pro so they can be compiled.

Finish and, voilà, the Task class is ready to be filled. We will jump into the Task.ui first. Start by dragging and dropping a Check Box (put checkbox in the objectName) and a Push Button (objectNameremoveButton):

Custom QWidget

My alignment looks great, let's ship this to the customers!

Unless you have a pixel-perfect eye, your items are not very well aligned. You need to indicate how your widgets should be laid out and how they should react when the window geometry changes (for example, when the user resizes the window). For this, Qt has several default layout classes:

  • Vertical Layout: In this layout, widgets are vertically stacked
  • Horizontal Layout: In this layout, widgets are horizontally stacked
  • Grid Layout: In this layout, widgets are arranged in a grid that can be subdivided into smaller cells
  • Form Layout: In this layout, widgets are arranged like a web form, a label, and an input

Every layout will try to constrain all widgets to occupy equal surfaces. It will either change the widgets' shape or add extra margins, depending on each widget's constraints. A Check Box will not be stretched but a Push Button will.

In our Task object, we want this to be horizontally stacked. In the Form Editor tab, right-click on the window and select Lay out | Lay out Horizontally. Each time you add a new widget in this layout, it will be arranged horizontally.

Now add a Push Button (objectName = editButton) line just after the checkbox object.

The Form Editor window offers a realistic preview of how your UI will render. If you stretch the window now, you can observe how each widget will react to this event. When resizing horizontally, you can note that the push buttons are stretched. It looks bad. We need something to "hint" to the layout that these buttons should not be stretched. Enter the Spacer widget. Take the Horizontal Spacer in the widget box and drop it after the checkbox object:

Custom QWidget

A spacer is a special widget that tries to push (horizontally or vertically) adjacent widgets to force them to take up as little space as possible. The editButton and removeButton objects now take up only the space of their text and will be pushed to the edge of the window when it is resized.

You can add sub layouts of any type in a form (vertical, horizontal, grid, form) and create a complex-looking application with a combination of widgets, spacers, and layouts. These tools are targeted at designing a good-looking desktop application that can react properly to different window geometries.

The Designer part is finished, so we can switch to the Task source code. Since we created a Qt Designer Form class, Task is closely linked to its UI. We will use this as leverage to store our model in a single place. When we create a Task object, it has to have a name:

#ifndef TASK_H 
#define TASK_H 
 
#include <QWidget> 
#include <QString> 
 
namespace Ui { 
class Task; 
} 
 
class Task : public QWidget 
{ 
    Q_OBJECT 
 
public: 
    explicit Task(const QString& name, QWidget *parent = 0); 
    ~Task(); 
 
    void setName(const QString& name); 
    QString name() const; 
    bool isCompleted() const; 
     
private: 
    Ui::Task *ui; 
}; 
 
#endif // TASK_H 

The constructor specifies a name, and as you can see there are no private fields storing any state of the object. All this will be done in the form part. We also added some getters and setters that will interact with the form. It is better to have a model completely separated from the UI, but our example is simple enough to merge them. Moreover, Task implementation details are hidden from the outside world and can still be refactored later on. Here is the content of the Task.cpp file:

#include "Task.h" 
#include "ui_Task.h" 
 
Task::Task(const QString& name, QWidget *parent) : 
        QWidget(parent), 
        ui(new Ui::Task) 
{ 
    ui->setupUi(this); 
    setName(name); 
} 
 
Task::~Task() 
{ 
    delete ui; 
} 
 
void Task::setName(const QString& name) 
{ 
    ui->checkbox->setText(name); 
} 
 
QString Task::name() const 
{ 
    return ui->checkbox->text(); 
} 
 
bool Task::isCompleted() const 
{ 
   return ui->checkbox->isChecked(); 
} 

The implementation is straightforward; we store the information in our ui->checkbox and the name() and isCompleted() getters take their data from the ui->checkbox.

Adding a task

We will now rearrange the layout of MainWindow to be able to display our todo tasks. Right now, there is no widget where we can display our tasks. Open the MainWindow.ui file and edit it to get the following result:

Adding a task

If we detail the content, we have:

  • A vertical layout for centralWidget containing the toolbarLayout file and the tasksLayout file.
  • A vertical spacer pushing these layouts to the top, forcing them to take up the smallest possible space.
  • We got rid of menuBarmainToolBar, and statusBar. Qt Creator created them automatically, we simply don't need them for our purposes. You can guess their uses from their names.

Do not forget to rename the MainWindow title to Todo by selecting the MainWindow in the Object Inspector window and editing the QwidgetwindowTitle property. Your app deserves to be named properly.

Tip

Qt Tip

Press ShiftF4 in Designer mode to switch between the form editor and the source.

Now that the MainWindow UI is ready to welcome tasks, let's switch to the code part. The application has to keep track of the new tasks. Add the following in the MainWindow.h file:

#include <QVector> 
 
#include "Task.h" 
 
class MainWindow : public QMainWindow 
{ 
    // MAINWINDOW_H 
 
public slots: 
    void addTask(); 
 
private: 
    Ui::MainWindow *ui; 
    QVector<Task*> mTasks; 
}; 

The QVector is the Qt container class providing a dynamic array, which is an equivalent of the std::vector. As a general rule, STL containers are more customizable but might miss some features compared to Qt containers. If you use C++11 smart pointers, you should favor std containers, but we will get into that later.

In the Qt documentation of QVector, you might stumble upon the following statement: "For most purposes, QList is the right class to use". There is a debate about this in the Qt community:

  • Do you often need to insert objects larger than a pointer at the beginning or in the middle of your array? Use a QList class.
  • Need contiguous memory allocation? Less CPU and memory overhead? Use a QVector class.

The already added slot addTask() will now be called each time we want to add a new Task object to the mTasks function.

Let's fill our QVector tasks each time addTaskButton is clicked. First, we connect the clicked() signal in the MainWindow.cpp file:

MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow), 
    mTasks() 
{ 
    ui->setupUi(this); 
    connect(ui->addTaskButton, &QPushButton::clicked,  
    this, &MainWindow::addTask); 
}; 

Tip

C++ tip

As a best practice, try to always initialize member variables in the initializer list and respect the order of variable declarations. Your code will run faster and you will avoid unnecessary variable copies. Take a look at the standard C++ documentation at https://isocpp.org/wiki/faq/ctors#init-lists.

The body of the addTask() function should look like this:

void MainWindow::addTask() 
{ 
        qDebug() << "Adding new task"; 
        Task* task = new Task("Untitled task"); 
        mTasks.append(task); 
        ui->tasksLayout->addWidget(task); 
} 

We created a new task and added it to our mTask vector. Because Task is a QWidget, we also added it directly to the tasksLayout. An important thing to note here is that we never managed this new task's memory. Where is the delete task instruction? This is a key feature of the Qt Framework we started to broach earlier in the chapter; the QObject class parenting automatically handles object destruction.

In our case, the ui->tasksLayout->addWidget(task) call has an interesting side-effect; the ownership of the task is transferred to tasksLayout. The QObject* parent defined in Task constructor is now tasksLayout, and the Task destructor will be called when tasksLayout releases its own memory by recursively iterating through its children and calling their destructor.

This will happen at this precise moment:

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

When MainWindow is released (remember, it's a stack variable allocated in the main.cpp file), it will call delete ui, which in turn will bring down the whole QObject hierarchy. This feature has interesting consequences. First, if you use the QObject parenting model in your application, you will have much less memory to manage. Second, it can collide with some new C++11 semantics, specifically the smart pointers. We will get into that in later chapters.

Using a QDialog

We deserve something better than an untitled task. The user needs to define its name when it's created. The easiest path would be to display a dialog where the user can input the task name. Fortunately Qt offers us a very configurable dialog that fits perfectly in addTask():

#include <QInputDialog> 
... 
void MainWindow::addTask() 
{ 
    bool ok; 
    QString name = QInputDialog::getText(this,  
        tr("Add task"), 
        tr("Task name"), 
        QLineEdit::Normal, 
        tr("Untitled task"),               &ok); 
    if (ok && !name.isEmpty()) { 
        qDebug() << "Adding new task"; 
        Task* task = new Task(name); 
        mTasks.append(task); 
        ui->tasksLayout->addWidget(task); 
    } 
} 

The QinputDialog::getText function is a static blocking function that displays the dialog. When the user validates/cancels the dialog, the code continues. If we run the application and try to add a new task, we'll see this:

Using a QDialog

The QInputDialog::getText signature looks like this:

QString QinputDialog::getText( 
  QWidget* parent,  
      const QString& title,  
      const QString& label,  
      QLineEdit::EchoMode mode = QLineEdit::Normal,  
      const QString& text = QString(),  
      bool* ok = 0, ...)

Let's break it down:

  • parent: This is the parent widget (MainWindow) to which the QinputDialog is attached. This is another instance of the QObject class's parenting model.
  • title: This is the title displayed in the window title. In our example, we use tr("Add task"), which is how Qt handles i18n in your code. We will see later on how to provide multiple translations for a given string.
  • label: This is the label displayed right above the input text field.
  • mode: This is how the input field is rendered (password mode will hide the text).
  • ok: This is a pointer to a variable that is set to true if the user presses OK and to false if the user presses Cancel.
  • QString: The returned QString is what the user has typed.

There are a few more optional parameters we can safely ignore for our example.

Distributing code responsibility

Great, the user can now specify the task name when it's created. What if he makes an error when typing the name? The next logical step is to rename the task after we created it. We'll take a slightly different approach. We want our Task to be as autonomous as possible. If we attach it to another component (rather than MainWindow), this renaming feature has to keep working. Thus, this responsibility has to be given to the Task class:

// In Task.h 
public slots: 
    void rename(); 
 
// In Task.cpp 
#include <QInputDialog> 
 
Task::Task(const QString& name, QWidget *parent) : 
       QWidget(parent), 
       ui(new Ui::Task) 
{ 
   ui->setupUi(this); 
   setName(name); 
   connect(ui->editButton, &QPushButton::clicked, this, &Task::rename); 
} 
... 
void Task::rename() 
{ 
    bool ok; 
    QString value = QInputDialog::getText(this, tr("Edit task"), 
                                          tr("Task name"), 
                                          QLineEdit::Normal, 
                                          this->name(), &ok); 
    if (ok && !value.isEmpty()) { 
        setName(value); 
    } 
} 

We add a public slot rename() to connect it to a signal. The body of rename() reuses what we had previously covered with QInputDialog. The only difference is the QInputDialog default value, which is the current task name. When setName(value) is called, the UI is instantly refreshed with the new value; there's nothing to synchronize or update, the Qt main loop will do its job.

The nice thing is that Task::rename() is completely autonomous. Nothing has been modified in MainWindow, so we have effectively zero coupling between our Task and the parent QWidget.

Emitting a custom signal using lambdas

The remove task is straightforward to implement, but we'll study some new concepts along the way. The Task has to notify its owner and parent (MainWindow) that the removeTaskButtonQPushButton has been clicked. We'll implement this by defining a custom signal removed in Task.h files:

class Task : public QWidget 
{ 
    ... 
public slots: 
    void rename(); 
signals: 
    void removed(Task* task); 
   ... 
}; 

Like we did for the slots, we have to add the Qt keyword signals in our header. Since a signal is used only to notify another class, the public keyword is not needed (it even raises a compilation error). A signal is simply a notification sent to the receiver (the connected slot); it implies that there is no function body for the removed(Task* task) function. We added the task parameter to allow the receiver to know which task asked to be removed. The next step is to emit the removed signal upon the removeButton click. This is done in the Task.cpp file:

Task::Task(const QString& name, QWidget *parent) : 
        QWidget(parent), 
        ui(new Ui::Task) 
{ 
    ui->setupUi(this); 
    ... 
    connect(ui->removeButton, &QPushButton::clicked, [this] { 
        emit removed(this); 
    }); 
} 

This code excerpt shows a very interesting feature of C++11: lambdas. In our example, the lambda is the following part:

[this] { 
        emit removed(this); 
    }); 

What we did here is to connect the clicked signal to an anonymous inline function, a lambda. Qt allows signal relaying by connecting a signal to another signal if their signatures match. It's not the case here; the clicked signal has no parameter and the removed signal needs a Task*. A lambda avoids the declaration of a verbose slot in Task. Qt 5 accepts a lambda instead of a slot in a connect, and both syntaxes can be used.

Our lambda executes a single line of code: emit removed(this)Emit is a Qt macro that will immediately trigger the connected slot with what we passed in a parameter. As we said earlier, removed(Task* this) has no function body, its purpose is to notify the registered slot of an event.

Lambdas are a great addition to C++. They offer a very practical way of defining short functions in your code. Technically, a lambda is the construction of a closure capable of capturing variables in its scope. The full syntax goes like this:

[ capture-list ] ( params ) -> ret { body }

Let’s study each part of this statement:

  • capture-list: This defines what variables will be visible inside the lambda scope.
  • params: This is the function parameters type list that can be passed to the lambda scope. There are no parameters in our case, We might have written [this] () { ... }, but C++11 lets us skip the parentheses altogether.
  • ret: This is the return type of the lambda function. Just like params, this parameter can be omitted if the return type is void.
  • body: This is obviously your code body where you have access to your capture-list, and params, and which must return a variable with a type ret.

In our example, we captured the this pointer to be able to:

  • Have a reference on the removed() function, which is a part of the Task class. If we did not capture this, the compiler would have shouted error: 'this' was not captured for this lambda function emit removed(this);.
  • Pass this to the removed signal; the caller needs to know which task triggered removed.

The capture-list relies on standard C++ semantics: capture variables by copy or by reference. Let us say that we wanted to print a log of the constructor parameter name and we capture it by reference in our lambda:

connect(ui->removeButton, &QPushButton::clicked, [this, &name] { 
        qDebug() << "Trying to remove" << name; 
        this->emit removed(this); 
    }); 

This code will compile fine. Unfortunately, the runtime will crash with a dazzling segmentation fault when we try to remove a Task. What happened? As we said, our lambda is an anonymous function that will be executed when the clicked() signal has been emitted. We captured the name reference, but this reference may be - and is - invalid once we get out of Task constructor (more precisely, from the caller scope). The qDebug() function will then try to display an unreachable code and crash.

You really want to be careful with what you capture and the context in which your lambda will be executed. In this example, the segmentation fault can be amended by capturing the name by copy:

connect(ui->removeButton, &QPushButton::clicked, [this, name] { 
        qDebug() << "Trying to remove" << name; 
        this->emit removed(this); 
    }); 

Tip

C++ Tip

  • You can capture by copy or reference all variables that are reachable in the function where you define your lambda with the syntax [=] and [&].
  • The this variable is a special case of the capture list. You cannot capture it by reference [&this] and the compiler will warn you if you are in this situation: [=, this]. Don't do this. Kittens will die.

Our lambda is passed directly as a parameter to the connect function. In other words, the lambda is variable. This has many consequences: we can call it, assign it, and return it. To illustrate a "fully formed" lambda, we can define one that returns a formatted version of the task name. The sole purpose of this snippet is to investigate the lambda function's machinery. Don't include the following code in your todo app, your colleagues might call you something like a "functional zealot":

connect(ui->removeButton, &QPushButton::clicked, [this, name] { 
    qDebug() << "Trying to remove" << 
        [] (const QString& taskName) -> QString { 
            return "-------- " + taskName.toUpper(); 
    }(name); 
    this->emit removed(this); 
}); 

Here we did a tricky thing. We called qDebug(); inside this call we defined a lambda which is immediately executed. Let's analyze it:

  • []: We performed no capture. The lambda does not depend on the enclosing function.
  • (const Qstring& taskName): When this lambda is called, it will expect a QString to work on.
  • -> QString: The returned value of the lambda will be a QString.
  • return "------- " + taskName.toUpper(): the body of our lambda. We return a concatenation of a string and the uppercase version of the parameter taskName. As you can see, string manipulation becomes a lot easier with Qt.
  • (name): Here comes the catch. Now that the lambda function is defined, we can call it passing the name parameter. In a single instruction, we define it and call it. The QDebug() function will simply print the result.

The real benefit of this lambda will emerge if we are able to assign it to a variable and call it multiple times. C++ is statically typed, so we must provide the type of our lambda variable. In the language specification, a lambda type cannot be explicitly defined. We'll see soon how we can do it with C++11. For now, let's finish our remove feature.

Task now emits the removed() signal. This signal has to be consumed by MainWindow:

// in MainWindow.h 
public slots: 
    void addTask(); 
    void removeTask(Task* task); 
 
// In MainWindow.cpp 
void MainWindow::addTask() 
{ 
    ... 
    if (ok && !name.isEmpty()) { 
        qDebug() << "Adding new task"; 
        Task* task = new Task(name); 
        connect(task, &Task::removed,  
       this, &MainWindow::removeTask); 
    ... 
    } 
} 
 
void MainWindow::removeTask(Task* task) 
{ 
    mTasks.removeOne(task); 
    ui->tasksLayout->removeWidget(task); 
    task->setParent(0); 
    delete task; 
} 

The MainWindow::removeTask() must match the signal signature. The connection is made when the task is created. The interesting part comes in the implementation of  MainWindow::removeTask().

The task is first removed from the mTasks vector. It is then removed from tasksLayout. Here, tasksLayout releases its ownership of task (that is, tasksLayout ceases to be the task class's parent).

So far so good. The next two lines are interesting. The ownership transfer does not completely release the task class ownership. If we commented these lines, here is how removeTask() will look:

void MainWindow::removeTask(Task* task) 
{ 
    mTasks.removeOne(task); 
    ui->tasksLayout->removeWidget(task); 
    // task->setParent(0); 
    // delete task; 
} 

If you add a log message in Task destructor and execute the program, this log message will be displayed. Nonetheless, the Qt documentation tells us in Qlayout::removeWidget part: The ownership of a widget remains the same as when it was added.

Instead, what really happens is that the task class's parent becomes centralWidget, the tasksLayout class's parent. We want Qt to forget everything about task, that's why we call task->setParent(0). We can then safely delete it and call it a day.

Simplifying with the auto type and a range-based for loop

The final step to a complete CRUD of our tasks is to implement the completed task feature. We'll implement the following:

  • Click on the checkbox to mark the task as completed
  • Strike the task name
  • Update the status label in MainWindow

The checkbox click handling follows the same pattern as removed:

// In Task.h 
signals: 
    void removed(Task* task); 
    void statusChanged(Task* task); 
private slots: 
    void checked(bool checked); 
 
// in Task.cpp 
Task::Task(const QString& name, QWidget *parent) : 
        QWidget(parent), 
        ui(new Ui::Task) 
{ 
    ... 
 
    connect(ui->checkbox, &QCheckBox::toggled,  
    this, &Task::checked); 
} 
 
... 
 
void Task::checked(bool checked) 
{ 
    QFont font(ui->checkbox->font()); 
    font.setStrikeOut(checked); 
    ui->checkbox->setFont(font); 
    emit statusChanged(this); 
} 

We define a slot checked(bool checked) that will be connected to the checkbox::toggled signal. In our slot checked(), we strike out the checkbox text according to the bool checked value. This is done using the QFont class. We create a copy font from the checkbox->font(), modify it, and assign it back to ui->checkbox. If the original font was in bold, with a special size, its appearance would be guaranteed to stay the same.

Tip

Play around with the font object in Qt Designer. Select the checkbox in the Task.ui file and go to Properties EditorQWidgetfont.

The last instruction notifies MainWindow that the Task status has changed. The signal name is statusChanged, rather than checkboxChecked, to hide the implementation details of the task. Add the following code in the MainWindow.h file:

// In MainWindow.h 
public: 
    void updateStatus(); 
public slots: 
    void addTask(); 
    void removeTask(Task* task); 
    void taskStatusChanged(Task* task); 
 
// In MainWindow.cpp 
MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow), 
    mTasks() 
{ 
    ... 
    updateStatus(); 
    } 
} 
 
void MainWindow::addTask() 
{ 
   ... 
   if (ok && !name.isEmpty()) { 
       ... 
       connect(task, &Task::removed, this, 
               &MainWindow::removeTask); 
       connect(task, &Task::statusChanged, this, 
               &MainWindow::taskStatusChanged); 
       mTasks.append(task); 
       ui->tasksLayout->addWidget(task); 
       updateStatus(); 
   } 
} 
 
void MainWindow::removeTask(Task* task) 
{ 
   ... 
   delete task; 
   updateStatus(); 
} 
 
void MainWindow::taskStatusChanged(Task* /*task*/) 
{ 
    updateStatus(); 
} 
 
void MainWindow::updateStatus() 
{ 
    int completedCount = 0; 
    for(auto t : mTasks)  { 
        if (t->isCompleted()) { 
            completedCount++; 
        } 
    } 
    int todoCount = mTasks.size() - completedCount; 
 
    ui->statusLabel->setText( 
        QString("Status: %1 todo / %2 completed") 
                             .arg(todoCount) 
                             .arg(completedCount)); 
} 

We defined a slot taskStatusChanged, which is connected when a task is created. The single instruction of this slot is to call updateStatus(). This function iterates through the tasks and updates the statusLabel. The updateStatus() function is called upon task creation and deletion.

In updateStatus(), we meet more new C++11 semantics:

for(auto t : mTasks)  { 
    ...  
} 

The for keyword lets us loop over a range-based container. Because QVector is an iterable container, we can use it here. The range declaration (auto t) is the type and variable name that will be assigned at each iteration. The range expression (mTasks) is simply the container on which the process will be done. Qt provides a custom implementation of the for (namely foreach) loop targeted at prior versions of C++; you don't need it anymore.

The auto keyword is another great new semantic. The compiler deduces the variable type automatically based on the initializer. It relieves a lot of pain for cryptic iterators such as this:

std::vector::const_iterator iterator = mTasks.toStdVector() 
                                           .stdTasks.begin(); 
 
// how many neurones did you save? 
auto autoIter = stdTasks.begin(); 

Since C++14, auto can even be used for function return types. It's a fabulous tool, but use it sparingly. If you put auto, the type should be obvious from the signature name/variable name.

Tip

The auto keyword can be combined with const and references. You can write a for loop like this: for (const auto & t : mTasks) { ... }.

Remember our half bread lambda? With all the covered features, we can write:

auto prettyName = [] (const QString& taskName) -> QString { 
    return "-------- " + taskName.toUpper(); 
}; 
connect(ui->removeButton, &QPushButton::clicked,  
    [this, name, prettyName] { 
        qDebug() << "Trying to remove" << prettyName(name); 
        this->emit removed(this); 
}); 

Now that's something beautiful. Combining auto with lambda makes very readable code and opens up a world of possibilities.

The last item to study is the QString API. We used it in updateStatus():

ui->statusLabel->setText( 
        QString("Status: %1 todo / %2 completed") 
                             .arg(todoCount) 
                             .arg(completedCount)); 

The people behind Qt put a lot of work into making string manipulation bearable in C++. This is a perfect example, where we replace the classic C sprintf with a more modern and robust API. Arguments are position-based only, no need to specify the type (less error-prone), and the arg(...) function accepts all kinds of types.

Tip

Take some time to skim through the QString documentation at http://doc.qt.io/qt-5/qstring.html. It shows how much you can do with this class and you'll see yourself using fewer and fewer examples of std string or even cstring.

Summary

In this chapter, we created a desktop Qt application from scratch. Qt is well known for its signal/slot mechanism and you must be confident with this paradigm. We also introduced some important C++14 features that will be used all through this book.

It's now time to discover some qmake secrets and what is really done when you build your Qt project. In the next chapter, we will also talk about how to create and organize an application with some platform-dependent code that must run on Windows, Mac OS, and Linux.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Unleash the power of Qt 5 with C++14
  • Integrate useful third-party libraries such as OpenCV
  • Package and deploy your application on multiple platforms

Description

Qt 5.7 is an application development framework that provides a great user experience and develops full-capability applications with Qt Widgets, QML, and even Qt 3D. This book will address challenges in successfully developing cross-platform applications with the Qt framework. Cross-platform development needs a well-organized project. Using this book, you will have a better understanding of the Qt framework and the tools to resolve serious issues such as linking, debugging, and multithreading. Your journey will start with the new Qt 5 features. Then you will explore different platforms and learn to tame them. Every chapter along the way is a logical step that you must take to master Qt. The journey will end in an application that has been tested and is ready to be shipped.

Who is this book for?

This book will appeal to developers and programmers who would like to build GUI-based applications. Knowledge of C++ is necessary and the basics of Qt would be helpful.

What you will learn

  • Create stunning UIs with Qt Widget and Qt Quick
  • Develop powerful, cross-platform applications with the Qt framework
  • Design GUIs with the Qt Designer and build a library in it for UI preview
  • Handle user interaction with the Qt signal/slot mechanism in C++
  • Prepare a cross-platform project to host a third-party library
  • Build a Qt application using the OpenCV API
  • Use the Qt Animation framework to display stunning effects
  • Deploy mobile apps with Qt and embedded platforms

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Dec 15, 2016
Length: 526 pages
Edition : 1st
Language : English
ISBN-13 : 9781786467126
Category :
Languages :
Tools :

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details

Publication date : Dec 15, 2016
Length: 526 pages
Edition : 1st
Language : English
ISBN-13 : 9781786467126
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 115.97
QT5 Blueprints
€41.99
Mastering Qt 5
€36.99
Qt5 C++ GUI Programming Cookbook
€36.99
Total 115.97 Stars icon

Table of Contents

14 Chapters
1. Get Your Qt Feet Wet Chevron down icon Chevron up icon
2. Discovering QMake Secrets Chevron down icon Chevron up icon
3. Dividing Your Project and Ruling Your Code Chevron down icon Chevron up icon
4. Conquering the Desktop UI Chevron down icon Chevron up icon
5. Dominating the Mobile UI Chevron down icon Chevron up icon
6. Even Qt Deserves a Slice of Raspberry Pi Chevron down icon Chevron up icon
7. Third-Party Libraries Without a Headache Chevron down icon Chevron up icon
8. Animations - Its Alive, Alive! Chevron down icon Chevron up icon
9. Keeping Your Sanity with Multithreading Chevron down icon Chevron up icon
10. Need IPC? Get Your Minions to Work Chevron down icon Chevron up icon
11. Having Fun with Serialization Chevron down icon Chevron up icon
12. You Shall (Not) Pass with QTest Chevron down icon Chevron up icon
13. All Packed and Ready to Deploy Chevron down icon Chevron up icon
14. Qt Hat Tips and Tricks Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Half star icon Empty star icon 3.2
(10 Ratings)
5 star 40%
4 star 0%
3 star 20%
2 star 20%
1 star 20%
Filter icon Filter
Top Reviews

Filter reviews by




lfxciii Dec 21, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This book helped me alot. They also have a udemy course which very helpful. These guys need to create more books, they have excellent understanding and good at transfering their knowledge the reader
Amazon Verified review Amazon
thought Jan 10, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Sometimes I need help, you should help sometimes dude, I am in a chapter 6 I have a problem with configuring Qt for raspberry or discovering Qt3D there are none explanation to create Apple.qml. Is it with QML File(Qt Quick 2)?
Amazon Verified review Amazon
Mahamadou Diakite Aug 08, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This a great book for learning Qt framework!I learned a lot from it.
Amazon Verified review Amazon
Paulo E. Jun 20, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Un très bon livre, bien expliqué, beaucoup d'exemples. Je le recommande. Soit pour des initiés, soit pour les plus expérimentés.
Amazon Verified review Amazon
Cliente Amazon Mar 25, 2017
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
Anche se il testo è stato ripreso da qt5 qb quanto basta che tratta le stesse cose mi è risultato utile per comprendere l'ambiente di programmazione.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.