Book Image

Mastering Qt 5

By : Guillaume Lazar, Robin Penea
Book Image

Mastering Qt 5

By: Guillaume Lazar, Robin Penea

Overview of this book

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.
Table of Contents (20 chapters)
Mastering Qt 5
Credits
About the Authors
About the Reviewer
www.PacktPub.com
Preface

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.