Lab 11 - Qt - signals and slots copy

Qt - signals and slots

Signals and slots

The basics

Qt implements a mechanism of signals and slots. It is a tool that allows you to manage asynchronous events (occurring in random moments without blocking the execution of the code that waits for them) from the program, the user or the operating system. The use of signals and slots is very simple. Every class in Qt can implement signals, i.e. functions emitting an event. At the same time each class can implement slots, i.e. functions that capture and process the above signals. For example, a QPushButton class has a clicked() signal that is emitted when the user presses a button.

In order to handle the event from the button, in the main window class we need to prepare a suitable slot. For this purpose, in the class declaration we put the directive slots: with access modifier (in this case private), and immediately below it the declaration of the function that will handle the event:

class MainWindow : public QMainWindow {

...

private slots:
    void my_button_slot();
};

Then in the .cpp file we define our event handling function. For now, it will remain empty:

void MainWindow::my_button_slot()
{

}

πŸ› πŸ”₯ Assignment πŸ› πŸ”₯

01_widow_layout


Note: During the previous classes you could notice that Qt Creator can generate the code described above for the controls placed on the form - usually it is worth to use this function, but for the demonstration of the slots mechanism we deliberately omit it.

Connecting signals to slots

Signals and slots should be connected together using a dedicated connect(...) function. It connects two objects: the sender (signal) and the receiver (slot). The connect must be called only once. This call can be placed e.g. in a class constructor. In order to connect a button pressed event with our prepared slot, it is necessary to make a call, e.g. in MainWindow class constructor:

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

    connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::my_button_slot);
}

The connect function takes the following arguments in this case:

Note: if we put a button called pushButton (default name) on the form and add a slot called on_pushButton_clicked to the class of the window containing the button, Qt will implicitly (without using the connect function in the code) connect the clicked signal to this slot. To avoid this behavior (only for demonstrations), the slot is called my_button_slot in the above examples.


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯


Additional information

One signal can trigger multiple slots and vice-versa - one slot can be triggered by multiple signals from different objects.

More about signals and slots can be found in the description on the Qt project subpage.

Modal dialog boxes are windows that, when displayed, block access to the main window until they are closed. They can be used to set the configuration parameters of the program. These windows, usually inherit from the QDialog class and contain the Ok and Cancel buttons.

Each window has to be created as an separate class. The simplest way to do that is using a Qt Creator build-in wizard. To do that click File β†’ New File or Project…. On the Files and Classes list choose Qt, and next Qt Designer Form Class. Confirm using Choose…:

02_new_qt_dialog

In next window choose Dialog with Buttons Bottom and click Next. This is followed by naming procedure of our freshly created classes. Edit the Class name field with will cause all the remaining fields to be filled accordingly. Confirm by clicking Next and Finish. The project tree will be added with .ui, .cpp and .h files.


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯


For the dialog creation to be possible it is needed to include its class within the mainwindow.cpp, depending on the dialog window class name, e.g.:

#include "settingsdialog.h"

Opening of the modal window, blocking the parent window interface, can be done in following manner:

void MainWindow::open_settings()
{
    SettingsDialog dialog; // creation of the dialog window object

    dialog.exec(); // execution/showing of the window; mainwindow will hold until closing the dialog
}

πŸ› πŸ”₯ Assignment πŸ› πŸ”₯


Next step needed for making the settings window work is passing the current settings values into it, in order to present them to the user. There are few possible ways to to that. For example the SettingsDialog class can implement a public method for passing and setting the values. Another option is using the SettingsDialog constructor.


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯

ui->pushButton->isEnabled() // bool type
ui->descriptionLabel->text() // QString type
ui->buttonEnabledCheckBox->setChecked(button_enabled);
ui->descriptionTextEdit->setText(description_text);

One of the options to catch the window closing event is to use the implicitly placed OK and Cancel buttons. If the dialog was created using a wizard with Dialog with Buttons Bottom, this caused the Qt Designer to automatically add an QDialogButtonBox widget with objectName of buttonBox. QDialogButtonBox object emits and QDialogButtonBox::accepted() signal when user clicks OK.


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯

connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::buttonBoxClicked);

At this point, after hitting OK the dialog window will be closed, but modification of the elements in the settings dialog will not influence anything on the main window, the settings has not been yet passed to MainWindow. One option to pass the information between classes (SettingsDialog to MainWindow in this case) is also using signals/slots. SettingsDialog should emit a signal that includes all the parameters of the object in the form, which than have to be captured. In Qt all the object inheriting QObject can emit a signal using a emit keyword. In order to do that we have to add a signals: directive into the class declaration, a below it a declaration of the signal together with the parameters to be passed, e.g.:

class SettingsDialog : public QDialog
{
    /****/

signals:
    void changesAccepted(bool button_enabled, QString description_text);

    /****/
};

πŸ› πŸ”₯ Assignment πŸ› πŸ”₯

void changesAccepted(bool button_enabled, QString description_text);
void SettingsDialog::buttonBoxClicked()
{
    emit changesAccepted(ui->buttonEnabledCheckBox->isChecked(), ui->descriptionTextEdit->text());
}

The last step is to capture the signal in the main window, in order to to that in MainWindow class a private slot is needed, which will be connected to the signal emitted by SettingsDialog, and then properly served. It is crucial for the slot to have the same parameter list as the corresponding signal. The order and types of the arguments are important, name is unrestricted.


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯

//mainwindow.h
private slots:
    void changeValues(bool button_enabled, QString description_text);
//mainwindow.cpp
void MainWindow::changeValues(bool button_enabled, QString description_text)
{
    ui->pushButton->setEnabled(button_enabled);
    ui->descriptionLabel->setText(description_text);
}
void MainWindow::open_settings()
{
    SettingsDialog dialog(ui->pushButton->isEnabled(), ui->descriptionLabel->text());

    connect(&dialog, &SettingsDialog::changesAccepted, this, &MainWindow::changeValues);

    dialog.exec();
}

QMenu and QAction

For classes inheriting from QMainWindow (usually describing the main window of the program), a user menu (QMenu) and a toolbar are assigned by default. The menu structure and toolbar can be created from Qt Designer:

05_menu_edit


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯


Adding entries do the QMenu will automatically cause a creation of QAction object with generated names based on the text entered:

06_action_menu

In an easy way we can assign an icon to the QAction. For doing that we have to highlight the QAction object we want to edit and than modify the icon field be choosing Choose File.... You have to remember that when using an external file it will not be included into the binary .exe, thus the file has to be placed in the execution folder of the program. In case of Qt Creator the programs are executed in the corresponding build folder for the project.

07_action_icon

Easy way to define a keyboard shortcut exist. Pressing a keyboard shortcut will cause the same action as clicking the menu entry. For defining edit the shortcut field:

08_action_shortcut

Clicking the menu entry (and using a keyboard shortcut) will emit a QAction::triggered signal, which can be connected with a slot using a connect.


πŸ› πŸ”₯ Assignment πŸ› πŸ”₯


Final assignments πŸ› πŸ”₯

Text editor

Use Qt to write a simple text editor.

The editor should allow you to:

Add appropriate widgets to the window and actions to the File menu.

Use the QFileDialog class to select files.

Load the entire contents of the open file into the text window (the QFile class is used for file operations).

Allow to overwrite the file (save under the same path) or save it as a new file.

Add Find and replace to the program. Create a window class (File β†’ New File or Project β†’ Qt β†’ Qt Designer Form Class β†’ Widget) where you can enter two strings of characters (source and target) and two buttons: Replace and Replace all.

Add the Edit menu with the Find and replace action.

Connect their signals to the slots in the main window and implement the required functionality.

You can find some implementation details in the Application Example.


Authors: Tomasz MaΕ„kowski, Jakub TomczyΕ„ski, Dominik PieczyΕ„ski