GUI#

Introduction#

Qt allows building cross-platforms GUI applications.
A designer can help creating the view before compilation, but a window can be entirely coded directly.
It is important to understand and apply some principles to achieve non-blocking, flawless applications.
To create a Qt application, choose File -> New project -> Qt Widgets Application.

Window structure#

The base of an application is made from a QMainWindow. If created thanks to the designer, a .h, a .cpp and a .ui files will be created.
This window holds from one to multiple QWidget and allows for a menu to be displayed :
Main Window Structure

Modality#

You may need to present the user with some extra windows (input boxes, options frame …).
A notion is important here : modality.
Widgets can have one of the three following modalities :
  1. NonModal : the window does not block the inputs from its parent (will act as a separate window)

  2. WindowModal : the window block inputs for its parent window, parent window’s sibling window, the parent window of the parent window and the parent window’s sibling window

  3. ApplicationModal : blocks all other windows inputs (e.g. used for an input dialog)

Designer Window creation#

To modify the main window or create a subsidiary one (to be used as widget for example), right-click on your ui folder and select Add New ... -> Qt -> Graphical Interface Class Qt Designer. You then have access to multiple pre-defined widgets.

  1. Start by adding one layout (drag and drop), e.g. Horizontal Layout.

  2. The layout will be floating on the Window. To set a flaw to your window, right click on it -> Lay out the page -> select a layout, e.g. Vertical Layout.

  3. You can then add widgets as you wish. Use the different layouts to organize your window.

  4. On the right window, you will see its tree structure and can modify data of your widgets.

Window Tree Structure
To create an interactive layout (i.e. resize the widget following the window size), do not fix the widgets size. Instead, set their size policy to Expanding.
Then, under the layout, modify the layoutStretch to select which percentage each widget takes on screen. Those are weights for each column/raw of your layout.
Multiple Windows size

Window not updating after modification#

If the window does not update after you modified something, the change is not important enough to have Qt refresh the files.
To force it update, right click on you project -> Clean then rebuild.

Callbacks#

If you want to get updated once the user interacts with the window or a widget, you can right click on it -> Go to slot ... and double click the signal you are interested in.
For a button, you can get a callback once the user clicks it, release it, when the button is resized … .
Widget Callbacks
It will then create a corresponding slot inside your window .h/.cpp file, that will be called every time the action is performed.
Refer to the Signal / Slots page.

Promoting a Widget#

Widget promotion allows you to extend one of the widget to one of your custom class.
You must inherit from the base widget, implement the .h and .cpp (refer to the documentation to override the methods you want), and then go to the designer -> right click on the widget you want to promote -> Promote to ....
In the window, add the name and .h path of your custom class (if it is referenced in your PATH, just give the relative path to it and check Global Header; else, it’s relative path to the root of the project).
Click on Add, select it and click Promote.
Widget Promotion

Custom Widget#

Like the Widget Promotion system, it is possible to build your custom widget.
It allows to draw easily About windows, prepare some widgets and “hot-swap” them from your main window …
Just put a Widget on your window and promote it to your custom, widget-derived class.
If you want to show it as a new window (about, options ..), implement the required callback and, when triggered, add code like follow :
 1// In callback from the menu click ...
 2
 3// Create About window, and gives the mainwindow as parent -> is WindowModal and linked to it
 4About myaboutwindow(&mainwindow);
 5// Execute it
 6w.exec();
 7// The exec will lock until window is closed
 8
 9// ------------------------------------------------
10
11// With no parent, calling exec will make the widget ApplicationModal
12About myaboutwindow(nullptr);
13w.exec();
14
15// -------------------------------------------------
16
17// As further method will return immediately, if object is non-static -> will be destroyed even before being shown
18static About myaboutwindow(nullptr);
19w.show(); // returns immediately, making the window NonModal

Note

Using a static variable here may not be the best option, as it is very locally managed and could not be, e.g., closed by the program controller.

If you are looking to “hot-swap” one of your widget (e.g. you placed a widget somewhere in your window, that should show options based on a currently selected object), the following code can help :

 1// From the ui.cpp file, passing a QWidget* new_widg
 2
 3// Recover your old widget
 4QWidget* old_widg = this->ui->mywidget;
 5// Prepare new widget
 6new_widg->setParent(this->ui->layoutContainingOldWidget);
 7new_widg->setObjectName(QString::fromUtf8("mywidget));
 8// Replace
 9this->ui->layoutContainingOldWidget->replaceWidget(this->ui->mywidget, new_widg);
10this->ui->mywidget = widg;
11// Cleanup
12if(old_widg){delete old_widg;}

Note

Replacing with another flaw may disregard the layout stretch values, hiding the widget from the view.

Input dialogs#

Qt offers multiple input dialogs to get values, strings, files … from the user :

 1// Get a double
 2bool ok;
 3double value = QInputDialog::getDouble(&parent, "Title of window", "Window text", 1 /*value*/, 0 /*min value*/, 2 /*max value*/, 3 /*decimals*/, &ok);
 4if(ok)
 5{
 6    // do something with the vlue
 7}
 8
 9// ---------------------------------------------
10
11// Int
12int value = QInputDialog::getInt(&parent, "Title of window", "Window text", 0 /*value*/, -500 /*min*/, 500 /*max*/, 1 /*step*/,&ok);
13
14// ---------------------------------------------
15
16// Text (QInputDialog::getMultiLineText also possible)
17QString str =       QInputDialog::getText(&parent, "Title of window", "Window text", QLineEdit::Normal, "Default", &ok);
18std::string str_std = str.toStdString();
19const char* str_c = str_std.c_str();
20
21// ---------------------------------------------
22
23// Items list
24QStringList items;
25items << "Option 1" << "Option 2" << "Option 3";
26QString item = QInputDialog::getItem(&parent, "Title of window", "Window text", items, 0 /*base selected*/, false /*if can edit items*/, &ok);
27
28// ---------------------------------------------
29
30// File picker
31QString filepath = QFileDialog::getOpenFileName(&parent, "Title", "/" /*default dir, make it exec current*/, "FileFormat1 (*.txt); Fileformat2 (*.yaml)");
32
33// ---------------------------------------------
34
35// File saver
36QString filepath = QFileDialog::getSaveFileName(&parent, "Title", "/" /*default dir, make it exec current*/, "Yaml (*.yaml); Text (*.txt)");
37if(filepath != "")
38{
39    // do something
40}
Message boxes

Information window#

You can quickly show message boxes to inform the user about what is happening in your program (an error occurred, confirm discarding current file …) :

 1// You can check the clicked button by its retval
 2int retval;
 3    // Error box
 4retval = QMessageBox::critical(&parent, "Critical", "Test Message");
 5    // Information box
 6retval = QMessageBox::information(&parent, "Information", "Test Message");
 7    // Ask user with yes/no
 8retval = QMessageBox::question(&parent, "Question", "Test Message");
 9    // Warning box
10retval = QMessageBox::warning(&parent, "Warning", "Test Message");
11
12// The following allows to show simple about box (but better to create it through your own widget !)
13QMessageBox::about(&parent, "About", "Test Message");
14    // Qt About window - MUST BE present for Qt Open Source programs
15QMessageBox::aboutQt(&parent, "About Qt");
Message boxes

The displayed buttons can be modified, an extra text can be displayed on user click … See Qt - QMessageBox class.

Message box with extended text area

Controlling the application#

A pitfall you should avoid is to concentrate all your code inside your window.
It is easier to directly add your code in the callbacks of your window and to do what has to be done.
But once your project gets bigger, you will have to manage all the cases (when did the user click on this button ? should I inform someone ? did I already do this ?).
Also, your window will have to know your other objects, your other objects may need to know your window, and you will end with a very intricated system with no possibility to easily export parts (libraries) from.
In such case, it is recommended to use a Factory pattern along with its Controller.

Factory#

The Factory pattern allows you to create your object and dispatch the needed references easily.
Such pattern takes place in three steps :
  1. Create your objects -> called the init phase
    • All the used objects are stocked inside the factory, mainly the Controller

  2. Initialize the relations -> called the build phase
    • Setup the relations where X should know Y but Y should know X too

  3. Run the program -> called the run phase
    • Mainly, it will just call your Controller run method

Following this ensure all your objects will be initialized correctly, and you can easily add relations afterward if you forgot them.
Basically, your main function should only contains those three calls. In Qt, it will be ended by the return app.exec().

MVC-lite#

The Model-View-Controller pattern allows to decouple the data from its representation.
It is particularly powerful when you want to dissociate your Core from your GUI, and looks like :
MVC pattern
The MVC-lite is a personal concept used along with Qt and its UI designer, where data and model are linked through your UI and code files.
Basically, your UI corresponds to your View, but will also hold the corresponding data, thus representing the Model. When a data is modified, a callback allows to update automatically the View from its known Model.
In reality, when using the designer, Qt creates both your ``mywindow.h`` file, along with a new ``mywindow_ui.h`` file containing all your widgets as the Model. The relations between both are automatically created.

Note

Here, the MVC is considered “lite” as the designer will handle all relations and callbacks automatically, letting us with mostly the implementation of the Controller. You only need to update its model.
To approach a real MVC pattern, you should modify all your mywindow_ui.h files (considered as your Models) to recover their elements and implement a subsidiary way to register callbacks to inform any View when a data is modified.
It may become very complicated for a small improvement (also, the _ui.h file can be regenerated by Qt, discarding your modifications). Think about this possibility only if multiple Views require the same data change information.
While creating your app, you should have a Controller class which will call modifications on your UI data (Model) while registering callbacks from the user gestures.
The Controller will hold all data required to do the tasks (your Core system), and update your UI data directly by calling specific functions in mywindow.h.

C++ Qt GUI