Signals and slots

On this round we will use Colorpicker program both as an example and as a template for an exercise. Here will study its user interface components and functionalities. Copy the project from template/12/colorpicker_designer to student/12/colorpicker_designer, and execute the program within it.

Below you can see the user interface of the colorpicker template

../../_images/colorpicker_template.png

and that of the completed version after doing the exercise given in the next Plussa section.

../../_images/colorpicker_final.png

The final user interface of the Colorpicker program has three sliders and three spin boxes that you can use to choose the color components of a color’s RGB value, meaning the amount of red, green, and blue colors. On the screen, there is also a label showing the chosen color, which is formed from the values selected in the user interface components at a certain time. To study the functionality of the program, change the values of the color components and see how it changes the color presented on the screen.

You can conclude that the color label is connected to the values of all sliders, because it changes color when the value of any of the components mentioned above changes.

Signal & slot mechanism

In Qt, all the components are implemented by inheriting them from the class QWidget, just like QMainWindow, mentioned in the previous material section.

../../_images/widgetit_kaavio.png

Joining user interface components to each other like described above is done with the so-called signal-slot mechanism. The simple way to convey this idea is that all the classes inherited from the class QObject can send signals and receive them using slots. Sending a signal is done with the keyword emit. For example, a slider could have

emit valueChanged(value_);

or

emit valueChanged(42);
../../_images/coin-in-slot-svgrepo-com.png

In the Finnish version of this text, we did not bother translating the word slot. You can think of it as a hole of a ”one-armed bandit”, or a slot machine, where inserting a coin activates a function (such as starting a fruit game or the music of a jukebox).

Looking at program code level, the signal looks like a function. If you wish, you can think of sending a signal as a function call. The difference between it and an ordinary function call is that the component sending the signal does not know who/what receives the signal. Also, there can be several receivers.

../../_images/box.png

If you change the values of the sliders in the Colorpicker program, it causes emitting the signal valueChanged. The way Qt works is that sending a signal activates the slots connected to it. In the final Colorpicker version, the slider emits the signal valueChanged, and that activates the setValue slot of the ”corresponding” spin box, which in turn changes the value of the spin box. Similarly, editing the value of a spin box changes both the value of the ”corresponding” slider and the color of the color label.

These relationships can be represented as:

../../_images/colorpicker_connections.png

This material is a very simplified presentation of how Qt signals work. In case you wish to get deeper into the matter, we recommend you read the Qt documentation on signals and slots. The Qt documentation explains everything on a general level, whereas this course material uses a single program as the example.

Program code of Colorpicker

Let us examine the program code of Colorpicker. The file main.cpp is not different to the empty window we examined on the previous material section. Therefore, we will start with the file MainWindow.hh.

As with the previous section, the class MainWindow is inherited from QMainWindow, and the first thing written in its interface is Q_OBJECT. Also, the definitions of the constructor and destructor are in the public interface of the class.

In the section private slots, you can find those slots that are implemented in the main window just for the use of the class. This means that only the class itself can connect signals to them. If the class additionally had the section public slots, it would include signals that you can use from outside the class as well. The visibility of slot sections is similar to the class interfaces we learned earlier.

The class MainWindow has the implementation of the slot onColorChanged that defines how a window is supposed to work when the user edits the value of the color.

The private part of a class contains definitions of the member variables, just like with other classes. As we learned on the previous programming course, a user interface class can have user interface components as its member variables. You should have as member variables only those user interface components that you must be able to handle in the program code. This is why we here have no widgets. In this case, all widgets have been defined in Qt Designer that you will learn to use in the next exercise section. In program code, we can refer to them via the user interface (ui).

The class interface also includes the constant definition for the maximum value of RGB values.

Next, open the file MainWindow.cpp. The initializing list of the constructor initializes all the user interface components that are member variables, just as we are used to do with member variables. We will complete some settings (setMinimum and setMaximum) for the user interface components in the body of the constructor, because we cannot do that directly in their constructor.

The most important thing to do at the end of the constructor’s body is connecting each changing signal of a horizontal slider to the slot onColorChanged of the MainWindow object. We already mentioned the purpose of onColorChanged. Your task is to connect the changing signals of the sliders and the spin boxes in pairs.

The final situation of signals and slots is shown in the previous figure. By comparing the code and the figure, try to find out which connections are already implemented and which connections are left for you to be completed.

At the end of the constructor, we call the slot onColorChanged() like we call any function, so that the color of the color label is changed to the initial value of the color selectors.

The implementation of the slot onColorChanged is short. It creates an object of the type QColor that can be shown in the object QLabel. The RGB value, obtained from the horizontal slider objects, is passed to the constructor of the object to be created.

At the end, it is good to stop and think about why the slot onColorChanged was implemented in the class MainWindow. However, it is a colorLabel object that changes color. The slot must be able to handle horizontal slider objects, which means it is easiest to implement it in the main window. The slider objects are children of the main window, which is why they can be handled in the main window. (Of course, it would be possible to pass slider objects or references to them as parameters outside of the class as well, but we will not consider that solution here.)

Final notes

In order to use Qt, the programmer must understand many of the characteristics of the C++ programming language, because all the user interface components are implemented as inherited classes, and the objects created from them are stored using pointers. Now we understand why graphical user interfaces were not the starting topic of this course, even if it would have potentially been nice to study them right after the introduction to graphical user interfaces, studied at the end of the previous programming course.