Signals and slots are used for communication between objects. The mechanism of signals and slots is the main feature of Qt and probably the part that differs from the features provided by other frameworks.
Introduction
In GUI programming, when we change one widget, we often want another widget to get a notification about it. In general, we want objects of any type to communicate with others. For example, if the user clicks the Close button, we probably want the window's close () function to be called.
Other libraries seek this kind of communication using a callback. A callback is a pointer to a function, so if we want the function to notify us of any events, we pass the pointer to another function (callback) to this function. The function in this case makes a callback when necessary. Callback has two major drawbacks. First, it is not type safe. We can never be sure that the function makes a callback with the correct arguments. Secondly, the callback is rigidly connected to its calling function, since this function must know exactly which callback to do.
Signals and Slots
Qt uses a different technique — signals and slots. A signal is generated when a certain event occurs. A slot is a function that is called in response to a specific signal. Qt widgets have many predefined signals and slots, but we can always make a child class and add our signals and slots in it.

')
The signal and slot mechanism is type safe. The signal signature must match the signature of the destination slot. (In fact, a slot can have a shorter signature than the signal it receives, since it can ignore additional arguments). Since the signatures are comparable, the compiler can help us detect a type mismatch. Signals and slots are loosely coupled. The class that produces the signal does not know and does not care about which slots it will receive. The signal and slot mechanism of Qt ensures that if we connect the signal to the slot, the slot will be called with the signal parameters at the right time. Signals and slots can take any number of arguments of any type. They are completely type safe.
All classes inherited from QObject or its child classes (for example, QWidget) can contain signals and slots. Signals are produced by objects when they change their state so that it may interest other objects. At the same time, he doesn’t know and doesn’t care that his signal may not have a receiver.
Slots can be used to receive signals, but they are also normal member functions. As well as the object does not know anything about the recipients of its signals, the slot does not know anything about the signals that are connected to it. This ensures that fully independent components can be created with Qt.
We can connect as many signals to one slot as we want, and one signal can be connected to as many slots as necessary. It is also possible to connect a signal to another signal (this will cause the generation of a second signal immediately after the appearance of the first).
Signals and slots together constitute a powerful mechanism for creating components.
Small example
A class description in C ++ might look like this:
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
class Counter { public : Counter() { m_value = 0; } int value () const { return m_value; } void setValue( int value ); private : int m_value; }; * This source code was highlighted with Source Code Highlighter .
A class inherited from QObject will look like this:
- #include <QObject>
- class Counter: public QObject
- {
- Q_OBJECT
- public :
- Counter () {m_value = 0; }
- int value () const { return m_value; }
- public slots:
- void setValue ( int value );
- signals:
- void valueChanged ( int newValue);
- private :
- int m_value;
- };
* This source code was highlighted with Source Code Highlighter .
A class inherited from QObject has the same internal state and provides public methods for accessing this state, but it also has support for using signals and slots. This class can tell the outside world that its state has changed by generating a valueChanged () signal and it has a slot to which other objects can send signals.
All classes containing signals and slots must specify the Q_OBJECT macro at the beginning of their description. They must also be descendants (directly or indirectly) of a QObject.
Slots are implemented by the programmer. A possible implementation of the Counter :: setValue () slot is as follows:
- void Counter :: setValue ( int value )
- {
- if ( value ! = m_value) {
- m_value = value ;
- emit valueChanged ( value );
- }
- }
* This source code was highlighted with Source Code Highlighter .
The emit keyword generates an object's valueChanged () signal with a new value as an argument.
In the following example, we create two objects of type Counter and connect the valueChanged () signal of the first with the setValue () slot of the second using the static QObject :: connect () function:
- Counter a, b;
- QObject :: connect (& a, SIGNAL (valueChanged ( int )),
- & b, SLOT (setValue ( int )));
- a.setValue (12); // a.value () == 12, b.value () == 12
- b.setValue (48); // a.value () == 12, b.value () == 48
* This source code was highlighted with Source Code Highlighter .
Calling a.setValue (12) generates a valueChanged (12) signal, which will receive object b in its setValue () slot, i.e. b.setValue (12) will be called. Then b produces the same valueChanged () signal, but since it is not connected to any slot, this signal will be ignored.
I note that the setValue () function sets a new value and only generates a signal value! = M_value. This prevents an infinite loop in the case of a circular connection (for example, if b.valueChanged () were connected to a.setValue ()).
A signal is generated for each connection. If the connection is duplicated, two signals will be generated. A connection can always be broken using the QObject :: disconnect () function.
The above example shows how objects can work together without needing to know something about each other. To enable this, objects must be connected together and this can be achieved by simply calling the QObject :: connect () function or using the automatic connection property of the uic program.
Compile example
The meta-object compiler (meta-object compiler, moc) scans the class descriptions in source files and generates C ++ code that initializes the meta objects. Meta-objects contain the names of all signals and slots, as well as pointers to these functions.
By running the moc program to describe the class containing signals and slots, we get the source code file, which should be compiled and linked to other application object files. When using qmake, the rules for automatically calling moc will be added to the project's Makefile.
Signals
Signals are produced by objects when they change their state so that it may interest other objects. Only the class that defines the signal or its descendants can produce a signal.
When a signal is generated, the slot to which it is connected is usually executed immediately, just like a normal procedure call. When this happens, the signals and signals and slots mechanism is completely independent of any GUI event cycle. The execution of the code following the release of the signal will occur immediately after exiting all slots. The situation is slightly different when using queued connections; in this case, the code after the emit keyword continues execution immediately, and the slots will be executed later.
If several slots are connected to the same signal, the slots will be executed one after the other in a random order after generating a signal.
The signals are automatically generated by the moc program and should not be implemented in the source code. They may not return a value (i.e., use the void type).
Comment on arguments: experience shows that signals and slots are easier to reuse when writing programs if they do not use special types. For example, if the signal QScrollBar :: valueChanged () would use a special type like the hypothetical QScrollBar :: Range, it could only be connected to slots designed specifically for it.
Slots
A slot is called when a signal is produced with which it is connected. A slot is a normal function in C ++ and can be called in the usual way; its only feature is that signals can be connected to it.
Since slots are normal member functions, they follow the usual C ++ rules in a direct call. However, as slots, they can be called by any component, regardless of their access levels, via a signal-slot connection. This means that a signal produced by an object of an arbitrary class can call a protected slot of an object of an unrelated class.
Slots can also be declared virtual, which is sometimes quite convenient.
Compared to callbacks, signals and slots are slightly slower due to the increased flexibility they provide, although the difference for real applications is not noticeable. In general, generating a signal that is connected to some slots is, on average, 10 times slower than calling the receiver directly, when calling a non-virtual function. These overheads are required to locate the object, to safely iterate through all its connections (i.e., verify that the subsequent recipient was not destroyed during the release of the signal) and transfer any parameters in general. Although calling ten non-virtual procedures may seem expensive, it is less expensive than, for example, creating or deleting an object. While we are creating a string, vector or list, which implicitly requires the creation of an object, the costs of signals and slots are responsible for a very small fraction of the costs among all procedure calls.
The same is true whether you make a system call to the slot or indirectly call more than ten functions. On the i586-500, we can generate about 2,000,000 signals per second, connected to one slot, or 1,200,000 signals per second, when connected in two slots. The simplicity and flexibility of the signals and slots mechanism pays for additional costs that the user of the program will not even notice.
It should be noted that libraries that define variables with the names signal or slot can cause warnings or compiler errors when compiling with a program written in Qt. To solve this problem, it is necessary to remove the definition of the interfering preprocessor character using the #undef directive.
Meta-object information
The meta object contains additional information, such as the name of the object. You can also check whether an object inherits a particular class, for example:
- if (widget-> inherits ( "QAbstractButton" )) {
- QAbstractButton * button = static_cast <QAbstractButton *> (widget);
- button-> toggle ();
- }
* This source code was highlighted with Source Code Highlighter .
Meta-object information is also used by qobject_cast <T> (), which is similar to QObject :: inherits (), but less prone to errors:
- if (QAbstractButton * button = qobject_cast <QAbstractButton *> (widget))
- button-> toggle ();
* This source code was highlighted with Source Code Highlighter .
Real example
Below is a simple example of a widget with comments.
- #ifndef LCDNUMBER_H
- #define LCDNUMBER_H
- #include <QFrame>
- class LcdNumber: public QFrame
- {
- Q_OBJECT
* This source code was highlighted with Source Code Highlighter .
The LcdNumber class inherits QObject, which has most information about signals and slots through the QFrame and QWidget classes. It is similar to the built-in QLCDNumber widget.
The Q_OBJECT macro tells the preprocessor to declare several member functions that will be implemented by the moc program; if, when compiling, among other things, the entry “undefined reference to vtable for LcdNumber” appears, then you most likely forgot to run moc or add the result of its work to the link command.
- public :
- LcdNumber (QWidget * parent = 0);
* This source code was highlighted with Source Code Highlighter .
This is not explicitly related to moc, but if we inherit a Qwidget class, we most likely want to have a parent argument in the constructor and pass it to the constructor of the parent class.
Some destructors and member functions are omitted here; moc ignores member functions.
- signals:
- void overflow ();
* This source code was highlighted with Source Code Highlighter .
LcdNumber generates a signal when it is asked to show an impossible value.
If we do not care about overflow or we know that it cannot happen, we can ignore this signal, i.e. Do not connect it anywhere.
On the other hand, if we want to call two different functions to react to this error, then we simply connect this function to two different slots. Qt will call both of them (in no particular order).
- public slots:
- void display ( int num);
- void display ( double num);
- void display ( const QString & str);
- void setHexMode ();
- void setDecMode ();
- void setOctMode ();
- void setBinMode ();
- void setSmallDecimalPoint ( bool point);
- };
- #endif
* This source code was highlighted with Source Code Highlighter .
Slots are functions used to obtain information about changes in the state of other widgets. LcdNumber uses them, as shown in the code above, to set the displayed number. Since the display () function is part of the class interface with the rest of the program, this slot is public.
It is worth noting that the display () function is overloaded. Qt will select the appropriate version when connecting the signal and slot. With a callback, we would have to look for five different names and control the types ourselves.
Some minor member functions have been omitted in this example.
Advanced use of signals and slots
In some cases, information about the sender may be required. Qt provides the Qobject :: sender () function, which returns a pointer to the object that sent the signal.
The QSignalMapper class is required in situations where many signals are connected to the same slot, and this slot must respond to each signal differently.
Suppose we have three buttons that determine which file we want to open: “Tax File”, “Accounts File”, or “Report File”.
To open the desired file, we connect their QPushButton :: clicked () signal to the readFile () slot. Now we use the function of the QSignalMapper class — setMapping () —to convert all signals into a QSignalMapper object.
- signalMapper = new QSignalMapper ( this );
- signalMapper-> setMapping (taxFileButton, QString ( "taxfile.txt" ));
- signalMapper-> setMapping (accountFileButton, QString ( "accountsfile.txt" ));
- signalMapper-> setMapping (reportFileButton, QString ( "reportfile.txt" ));
- connect (taxFileButton, SIGNAL (clicked ()),
- signalMapper, SLOT (map ()));
- connect (accountFileButton, SIGNAL (clicked ()),
- signalMapper, SLOT (map ()));
- connect (reportFileButton, SIGNAL (clicked ()),
- signalMapper, SLOT (map ()));
* This source code was highlighted with Source Code Highlighter .
Now we connect the mapped () signal to the readFile () slot in which different files will be opened depending on the button pressed.
- connect (signalMapper, SIGNAL (mapped ( const QString &)),
- this , SLOT (readFile ( const QString &)));
* This source code was highlighted with Source Code Highlighter .
Using Qt with third-party signals and slots
Qt can be used with a third-party signal and slot mechanism. You can use several mechanisms in one project. To do this, add the following line to the project file (.pro):
CONFIG += no_keywords
This option tells Qt not to define the moc'a keywords - signals, slots, and emit, since these names will be used by the third-party library, for example, Boost. To use Qt signals and slots with the no_keywords flag set, you simply need to replace all the Qt moc keywords in the source files with the corresponding macros Q_SIGNALS, Q_SLOTS, and Q_EMIT.