Adding a task
We will now rearrange the layout of MainWindow to be able to display our todo tasks. At this moment, there is no widget where we can display our tasks. Open the MainWindow.ui file. We will use Qt designer to create the UI:
- Drag and drop
Horizontal layoutinside the central widget and rename ittoolbarLayout - Right-click on the central widget and select
Lay out vertically - Drag and drop the label, spacer, and button inside
toolbarLayout - Drag and drop
Vertical layoutundertoolbarLayout(a blue helper line will be displayed) and rename ittasksLayout - Add a vertical spacer under
tasksLayout(again, check the blue helper line):

Voilà! Your MainWindow form is finished. Later in the chapter you will learn how to dynamically create and add some Task widgets to the empty tasksLayout.
To sum up, we have:
- A vertical layout for
centralWidgetthat contains thetoolbarLayoutitem and thetasksLayoutitem. - A vertical spacer pushing these layouts to the top, forcing them to take up the smallest possible space.
- Gotten rid of
menuBar,mainToolBar, andstatusBar. Qt Creator created them automatically, we simply don't need them for our purposes. You can guess their uses from their names.
Don't forget to rename the MainWindow title to Todo by selecting MainWindow in the Object Inspector window and editing the Qwidget | windowTitle property. Your app deserves to be named properly.
Note
Press Shift + F4 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 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 std::vector. Generally speaking, the rule says that STL containers are more customizable, but they may 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 may stumble upon the following statement: For most purposes,QListis 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
QListclass. - Need contiguous memory allocation? Less CPU and memory overhead? Use a
QVectorclass.
The already-added addTask() slot will now becalledeach time we want to add a new Taskobject to the mTasksfunction.
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);
}; Note
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. For more information, 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 the Task object is a QWidget, we also added it directly to tasksLayout. An important thing to note here is that we never managed our new task's memory. Where is the delete task instruction? This is a key feature of the Qt Framework we started to mention earlier in the chapter; the QObject class parenting automatically handles object destruction.
In the preceding code snippet, the ui->tasksLayout->addWidget(task) call has an interesting side-effect: the ownership of the task is transferred to the layout's widget. The QObject* parent defined in the Task constructor is now centralWidget of the MainWindow. The Task destructor will be called when MainWindow releases its own memory by recursively iterating through its children and calling their destructor.
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 the details about this in later chapters.