Qt for beginners Signals and slots: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
[toc align_right= | [toc align_right="yes" depth="3"] | ||
[[Category:Qt_for_beginners]] | |||
[[Category:Tutorial]] | |||
[[Category:HowTo]] | |||
= Qt for beginners — Signals and slots = | = Qt for beginners — Signals and slots = | ||
[[Qt_for_beginners_pretty_button| | [[Qt_for_beginners_pretty_button|<<< A pretty button]] | [[Qt_for_beginners|Summary]] | [[Qt_for_beginners_Signals_and_slots_2|Signals and slots 2 >>>]] | ||
'''''Note:''' Unfortunately, the images are no longer available. See the official | '''''Note:''' Unfortunately, the images are no longer available. See the official "Getting Started with Qt Widgets":http://doc.qt.io/qt-5/gettingstartedqt.html page for an alternative tutorial.'' | ||
This chapter covers a very useful and important feature of QT called signals and slots. | This chapter covers a very useful and important feature of QT called signals and slots. | ||
Line 13: | Line 16: | ||
=== The observer pattern === | === The observer pattern === | ||
Nearly all UI toolkits have a mechanism to detect a user action, and respond to this action. Some of them use ''callbacks'', others use ''listeners'', but basically, all of them are inspired by the | Nearly all UI toolkits have a mechanism to detect a user action, and respond to this action. Some of them use ''callbacks'', others use ''listeners'', but basically, all of them are inspired by the "observer pattern":http://en.wikipedia.org/wiki/Observer_pattern. | ||
Observer pattern is used when an ''observable'' object wants to notify other ''observers'' objects about a state change. Here are some concrete examples : | Observer pattern is used when an ''observable'' object wants to notify other ''observers'' objects about a state change. Here are some concrete examples : | ||
* A user has clicked on a button, and a menu should be displayed. | |||
* A web page just finished loading, and a process should extract some information from this loaded page. | |||
* An user is scrolling through a list of items (in an app store for example), and reached the end, so other items should be loaded. | |||
Observer pattern is used everywhere in GUI application, and often lead to some | Observer pattern is used everywhere in GUI application, and often lead to some "boilerplate code":http://en.wikipedia.org/wiki/Boilerplate_code. Qt was created with the idea of removing these boilerplate code and providing a nice and clean syntax, and the signal and slots mechanism is the answer. | ||
=== Signals and slots === | === Signals and slots === | ||
Line 40: | Line 46: | ||
* QPushButton::''setText'' | * QPushButton::''setText'' | ||
In order to respond to a signal, a slot must be ''connected'' to a signal. Qt provides the method QObject::'''connect'''. It is used this way, with the two macros ''' | In order to respond to a signal, a slot must be ''connected'' to a signal. Qt provides the method QObject::'''connect'''. It is used this way, with the two macros '''<code>SIGNAL</code>''' and '''<code>SLOT</code>''' | ||
<code> | |||
FooObjectA *fooA = new FooObjectA(); | |||
FooObjectB *fooB = new FooObjectB(); | |||
QObject::connect(fooA, SIGNAL (bared()), fooB, SLOT (baz())); | QObject::connect(fooA, SIGNAL (bared()), fooB, SLOT (baz())); | ||
</code> | |||
assuming that | assuming that <code>FooObjectA</code> have a <code>bared</code> signal, and <code>FooObjectB</code> have a <code>baz</code> slot. | ||
You have to write the signature of the signal and the slot inside the two macros ''SIGNAL'' and ''SLOT''. If you want to get some information about what these macros do, please read the last section of this chapter. | You have to write the signature of the signal and the slot inside the two macros ''SIGNAL'' and ''SLOT''. If you want to get some information about what these macros do, please read the last section of this chapter. | ||
Line 56: | Line 66: | ||
The hypothetical class used to check the progress might have a signal that have this signature : | The hypothetical class used to check the progress might have a signal that have this signature : | ||
<code> | <code> | ||
void MediaProgressManager::tick(int miliseconds); | |||
</code> | |||
and we know from the documentation, that the QProgressBar have this slot | and we know from the documentation, that the QProgressBar have this slot | ||
<code> | <code> | ||
void QProgressBar::setValue(int value); | |||
</code> | |||
You can see that the signal and the slot have the same kind of parameters, especially the type. If you connect a signal to a slot that do not share the same kind of parameters, while the connection is done (at run-time) you will get a warning like | You can see that the signal and the slot have the same kind of parameters, especially the type. If you connect a signal to a slot that do not share the same kind of parameters, while the connection is done (at run-time) you will get a warning like | ||
<code> | |||
QObject::connect: Incompatible sender/receiver arguments | |||
</code> | |||
It is because the signal transmits the information to the slot using the parameters. The first parameter of the signal is passed to the first one of the slot, and the same for second, third, and so forth. | It is because the signal transmits the information to the slot using the parameters. The first parameter of the signal is passed to the first one of the slot, and the same for second, third, and so forth. | ||
Line 72: | Line 88: | ||
The code for the connection will look like this: | The code for the connection will look like this: | ||
<code> | <code> | ||
MediaProgressManager *manager = new MediaProgressManager(); | |||
QProgressBar '''progress = new QProgressBar(window); | |||
QObject::connect(manager, SIGNAL (tick(int)), fooB, SLOT (setValue(int))); | |||
</code> | |||
You can see that you have to provide a signature inside the ''SIGNAL'' and ''SLOT'' macro, providing the type of the value that are passed through the signals. You may not provide the name of the variable if you want. (It is actually even better). | |||
h3. Features of signals and slots | |||
''' A signal can be connected to several slots | |||
* Many signals can be connected to a slot | |||
* A signal can be connected to a signal : it is signal relaying. The second signal is send if the first signal is sent. | |||
== Examples == | == Examples == | ||
Line 86: | Line 111: | ||
In order to make a click on a button to close the app, we have to connect the signal ''clicked'' of the button to the ''quit'' slot of QApplication instance. We can modify the code from previous chapter to do this, but before, you might wonder how to access to the QApplication instance while you are in another class. Actually, it is pretty simple, since there exists a static function in [[Doc:QApplication]], with the following signature, that is used to get it | In order to make a click on a button to close the app, we have to connect the signal ''clicked'' of the button to the ''quit'' slot of QApplication instance. We can modify the code from previous chapter to do this, but before, you might wonder how to access to the QApplication instance while you are in another class. Actually, it is pretty simple, since there exists a static function in [[Doc:QApplication]], with the following signature, that is used to get it | ||
<code> | <code> | ||
QApplication * QApplication::instance() | |||
</code> | |||
This leads to the following modification of our previous code | This leads to the following modification of our previous code | ||
''window.cpp'' | ''window.cpp'' | ||
<code> | |||
#include "window.h" | |||
#include | #include <QPushButton> | ||
#include <QApplication> | |||
Window::Window(QWidget '''parent) : | Window::Window(QWidget '''parent) : | ||
QWidget(parent) | |||
{ | |||
// Set size of the window | |||
setFixedSize(100, 50); | |||
// Create | // Create and position the button | ||
m_button = new QPushButton("Hello World", this); | |||
m_button->setGeometry(10, 10, 80, 30); | |||
// | // NEW : Do the connection | ||
connect(m_button, SIGNAL (clicked()), QApplication::instance(), SLOT (quit())); | |||
} | |||
/ | </code> | ||
While clicking on the button inside of the window, the application should quit. | |||
h3. Transmitting information with signals and slots | |||
Here is a simpler example for information transmission. It only displays a progress bar and a slider (created by [[Doc:QSlider]]) inside a window, and while the slider is moved, the value of the progress bar is synced with a very simple connection. | |||
The interesting signals and slots are | |||
<code> | |||
void QSlider::valueChanged(int value); | |||
void QProgressBar::setValue(int value); | |||
</code> | |||
QSlider automatically emits the signal <code>valueChanged</code> with the new value passed as parameter while the value is changed, and the method <code>setValue</code> of QProgressBar, is used, as we have seen, to set the value of the progress bar. | |||
This leads to the following code | |||
<code> | |||
#include <QApplication> | |||
#include <QProgressBar> | |||
#include <QSlider> | |||
int main(int argc, char'''*argv) | |||
{ | |||
QApplication app (argc, argv); | |||
// Create a container window | |||
QWidget window; | |||
window.setFixedSize(200, 80); | |||
// Create a progress bar | |||
// with the range between 0 and 100, and a starting value of 0 | |||
QProgressBar *progressBar = new QProgressBar(&window); | |||
progressBar->setRange(0, 100); | |||
progressBar->setValue(0); | |||
progressBar->setGeometry(10, 10, 180, 30); | |||
// Create a horizontal slider | |||
// with the range between 0 and 100, and a starting value of 0 | |||
QSlider *slider = new QSlider(&window); | |||
slider->setOrientation(Qt::Horizontal); | |||
slider->setRange(0, 100); | |||
slider->setValue(0); | |||
slider->setGeometry(10, 40, 180, 30); | |||
window.show(); | window.show(); | ||
// Connection | // Connection | ||
// This connection set the value of the progress bar | |||
// while the slider's value changes | |||
QObject::connect(slider, SIGNAL (valueChanged(int)), progressBar, SLOT (setValue(int))); | |||
return app.exec | return app.exec(); | ||
} | |||
</code> | |||
== Technical aspect == | == Technical aspect == | ||
This section can be skipped for now if you only want to program with Qt. Just know that you need to put '''SIGNAL''' and '''SLOT''' around the signals and slots while calling | This section can be skipped for now if you only want to program with Qt. Just know that you need to put '''SIGNAL''' and '''SLOT''' around the signals and slots while calling <code>connect</code>. If you want to know how Qt works, it is better to read this. | ||
=== The meta-object === | === The meta-object === | ||
Qt provides a ''meta-object'' system. Meta-object (literally | Qt provides a ''meta-object'' system. Meta-object (literally "over the object") is a way to achieve some programming paradigms that are normally impossible to achieve with pure C++ like | ||
* '''Introspection''' : capability of examining a type at run-time | * '''Introspection''' : capability of examining a type at run-time | ||
Line 135: | Line 211: | ||
Code produced by moc includes signals and slots signatures, methods that are used to retrieve meta-information from those marked classes, properties handling … All these information can be accessed using the following method | Code produced by moc includes signals and slots signatures, methods that are used to retrieve meta-information from those marked classes, properties handling … All these information can be accessed using the following method | ||
<code> | <code> | ||
const QMetaObject * QObject::metaObject () const | |||
</code> | |||
[[Doc:QMetaObject]] class contains all the methods that deals with meta-object. | [[Doc:QMetaObject]] class contains all the methods that deals with meta-object. | ||
Line 141: | Line 219: | ||
=== Important macros === | === Important macros === | ||
The most important macro is '''Q_OBJECT'''. Signal - Slot connections and their syntax cannot be intepreted by regular C++ compiler. The moc is provided to translate the QT syntax like | The most important macro is '''Q_OBJECT'''. Signal - Slot connections and their syntax cannot be intepreted by regular C++ compiler. The moc is provided to translate the QT syntax like "connect", "signals", "slots", etc into regular C++ syntax. | ||
This is done by specifying the '''Q_OBJECT''' macro in the header containing class definitions that use such syntax. | |||
''mywidget.h'' | |||
<code> | |||
class MyWidget : public QWidget | |||
{ | |||
Q_OBJECT | |||
public: | |||
MyWidget(QWidget '''parent = 0); | |||
… | |||
} | |||
</code> | |||
Others marker macros for moc are | |||
''' '''signals''' | |||
* public / protected / private '''slots''' | |||
that mark the different methods that need to be extended. | that mark the different methods that need to be extended. | ||
'''SIGNAL''' and '''SLOT''' are also two very important and useful macros. When a signal is emitted, the metao-bject system is used to compare the signature of the signal, to check connection, and to find the slot using it's signature. These macros are actually used to convert the provided method signature into a string that match the one stored in the metaobject. | '''SIGNAL''' and '''SLOT''' are also two very important and useful macros. When a signal is emitted, the metao-bject system is used to compare the signature of the signal, to check connection, and to find the slot using it's signature. These macros are actually used to convert the provided method signature into a string that match the one stored in the metaobject. |
Revision as of 08:50, 25 February 2015
[toc align_right="yes" depth="3"]
Qt for beginners — Signals and slots
<<< A pretty button | Summary | Signals and slots 2 >>>
Note: Unfortunately, the images are no longer available. See the official "Getting Started with Qt Widgets":http://doc.qt.io/qt-5/gettingstartedqt.html page for an alternative tutorial.
This chapter covers a very useful and important feature of QT called signals and slots.
Introduction
The observer pattern
Nearly all UI toolkits have a mechanism to detect a user action, and respond to this action. Some of them use callbacks, others use listeners, but basically, all of them are inspired by the "observer pattern":http://en.wikipedia.org/wiki/Observer_pattern.
Observer pattern is used when an observable object wants to notify other observers objects about a state change. Here are some concrete examples :
- A user has clicked on a button, and a menu should be displayed.
- A web page just finished loading, and a process should extract some information from this loaded page.
- An user is scrolling through a list of items (in an app store for example), and reached the end, so other items should be loaded.
Observer pattern is used everywhere in GUI application, and often lead to some "boilerplate code":http://en.wikipedia.org/wiki/Boilerplate_code. Qt was created with the idea of removing these boilerplate code and providing a nice and clean syntax, and the signal and slots mechanism is the answer.
Signals and slots
Instead of having observable and observers, and registering them, Qt provides two high level tools : signals and slots.
- A signal is a message that an object can send, most of the time to inform of a status change.
- A slot is a function that is used to respond to a signal.
Here are some example of signals and slots from our well known Doc:QPushButton class.
- clicked
- pressed
- released
As you can see, their names are quite explicit. These signals are sent when the user clicked (pressed then released) , pressed or released the button.
Here are some slots, from different classes
- QApplication::quit
- QWidget::setEnabled
- QPushButton::setText
In order to respond to a signal, a slot must be connected to a signal. Qt provides the method QObject::connect. It is used this way, with the two macros
SIGNAL
and
SLOT
FooObjectA *fooA = new FooObjectA();
FooObjectB *fooB = new FooObjectB();
QObject::connect(fooA, SIGNAL (bared()), fooB, SLOT (baz()));
assuming that
FooObjectA
have a
bared
signal, and
FooObjectB
have a
baz
slot.
You have to write the signature of the signal and the slot inside the two macros SIGNAL and SLOT. If you want to get some information about what these macros do, please read the last section of this chapter.
Remark : Basically, signals and slots are methods, that might or might not have arguments, but that never return anything. While the notion of a signal as a method is generally quite blur, a slot is actually a real method, and can be called as usual in other methods, or while respoing to a signal.
Transmitting information
The signals and slots mechanism is useful to respond to buttons clicks, but it can do much more than that. For example, It can also be used communicate information. Lets say while playing a song, a progress bar is needed to show how much time remains before the song is over. A media player might have a class that is used to check the progress of the media. An instance of this class might periodically send a tick signal, with the progress. This signal can be connected to a Doc:QProgressBar, that can be used to display the progress.
The hypothetical class used to check the progress might have a signal that have this signature :
void MediaProgressManager::tick(int miliseconds);
and we know from the documentation, that the QProgressBar have this slot
void QProgressBar::setValue(int value);
You can see that the signal and the slot have the same kind of parameters, especially the type. If you connect a signal to a slot that do not share the same kind of parameters, while the connection is done (at run-time) you will get a warning like
QObject::connect: Incompatible sender/receiver arguments
It is because the signal transmits the information to the slot using the parameters. The first parameter of the signal is passed to the first one of the slot, and the same for second, third, and so forth.
The code for the connection will look like this:
MediaProgressManager *manager = new MediaProgressManager();
QProgressBar '''progress = new QProgressBar(window);
QObject::connect(manager, SIGNAL (tick(int)), fooB, SLOT (setValue(int)));
You can see that you have to provide a signature inside the SIGNAL and SLOT macro, providing the type of the value that are passed through the signals. You may not provide the name of the variable if you want. (It is actually even better).
h3. Features of signals and slots
A signal can be connected to several slots
- Many signals can be connected to a slot
- A signal can be connected to a signal : it is signal relaying. The second signal is send if the first signal is sent.
Examples
Responding to an event
Remember our button app ? Let's try to actually make something with this app, like be able to close it while clicking on the button. We already know that Doc:QPushButton provides the clicked signal. We also have to know that Doc:QApplication provides the quit slot, that closes the application.
In order to make a click on a button to close the app, we have to connect the signal clicked of the button to the quit slot of QApplication instance. We can modify the code from previous chapter to do this, but before, you might wonder how to access to the QApplication instance while you are in another class. Actually, it is pretty simple, since there exists a static function in Doc:QApplication, with the following signature, that is used to get it
QApplication * QApplication::instance()
This leads to the following modification of our previous code
window.cpp
#include "window.h"
#include <QPushButton>
#include <QApplication>
Window::Window(QWidget '''parent) :
QWidget(parent)
{
// Set size of the window
setFixedSize(100, 50);
// Create and position the button
m_button = new QPushButton("Hello World", this);
m_button->setGeometry(10, 10, 80, 30);
// NEW : Do the connection
connect(m_button, SIGNAL (clicked()), QApplication::instance(), SLOT (quit()));
}
While clicking on the button inside of the window, the application should quit.
h3. Transmitting information with signals and slots
Here is a simpler example for information transmission. It only displays a progress bar and a slider (created by Doc:QSlider) inside a window, and while the slider is moved, the value of the progress bar is synced with a very simple connection.
The interesting signals and slots are
void QSlider::valueChanged(int value);
void QProgressBar::setValue(int value);
QSlider automatically emits the signal
valueChanged
with the new value passed as parameter while the value is changed, and the method
setValue
of QProgressBar, is used, as we have seen, to set the value of the progress bar.
This leads to the following code
#include <QApplication>
#include <QProgressBar>
#include <QSlider>
int main(int argc, char'''*argv)
{
QApplication app (argc, argv);
// Create a container window
QWidget window;
window.setFixedSize(200, 80);
// Create a progress bar
// with the range between 0 and 100, and a starting value of 0
QProgressBar *progressBar = new QProgressBar(&window);
progressBar->setRange(0, 100);
progressBar->setValue(0);
progressBar->setGeometry(10, 10, 180, 30);
// Create a horizontal slider
// with the range between 0 and 100, and a starting value of 0
QSlider *slider = new QSlider(&window);
slider->setOrientation(Qt::Horizontal);
slider->setRange(0, 100);
slider->setValue(0);
slider->setGeometry(10, 40, 180, 30);
window.show();
// Connection
// This connection set the value of the progress bar
// while the slider's value changes
QObject::connect(slider, SIGNAL (valueChanged(int)), progressBar, SLOT (setValue(int)));
return app.exec();
}
Technical aspect
This section can be skipped for now if you only want to program with Qt. Just know that you need to put SIGNAL and SLOT around the signals and slots while calling
connect
. If you want to know how Qt works, it is better to read this.
The meta-object
Qt provides a meta-object system. Meta-object (literally "over the object") is a way to achieve some programming paradigms that are normally impossible to achieve with pure C++ like
- Introspection : capability of examining a type at run-time
- Asynchronous function calls
To use such meta-object capabilites in an application, one can subclass [[Doc:QObject] and mark it so that the meta-object compiler (moc), can interpret and translate it.
Code produced by moc includes signals and slots signatures, methods that are used to retrieve meta-information from those marked classes, properties handling … All these information can be accessed using the following method
const QMetaObject * QObject::metaObject () const
Doc:QMetaObject class contains all the methods that deals with meta-object.
Important macros
The most important macro is Q_OBJECT. Signal - Slot connections and their syntax cannot be intepreted by regular C++ compiler. The moc is provided to translate the QT syntax like "connect", "signals", "slots", etc into regular C++ syntax. This is done by specifying the Q_OBJECT macro in the header containing class definitions that use such syntax.
mywidget.h
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget '''parent = 0);
…
}
Others marker macros for moc are signals
- public / protected / private slots
that mark the different methods that need to be extended.
SIGNAL and SLOT are also two very important and useful macros. When a signal is emitted, the metao-bject system is used to compare the signature of the signal, to check connection, and to find the slot using it's signature. These macros are actually used to convert the provided method signature into a string that match the one stored in the metaobject.