Qt Observer Pattern
Reference Links
The observer pattern
- Nearly all UI toolkits have a mechanism to detect an user action, and respond to this action.
- Some of them use callbacks, other use listeners, but basically, all of them are inspired by the observer pattern.
- Observer pattern is used when an observable object wants to notify other observers objects about a state change.
- An user has clicked on a button -> a menu should be displayed
- A web page just finished loading -> 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) -> reached the end, so other items should be loaded
- Observer pattern is used everywhere in GUI applications,
- And often leads to some boilerplate code (duplicate code).
- Qt was created with the idea of removing this boilerplate code and providing a nice and clean syntax,
- And the signal and slots mechanism is the answer.
- And often leads to some boilerplate code (duplicate code).
Signals and slots
- Instead of having observable objects and observers, and registering them, Qt provides two high level concepts: signals and slots.
- A signal is a message
- that object can send
- most of the time to inform of a status change
- A slots is a function (handler?)
- that is used to accept and respond to a signal
- A signal is a message
- Examples from QPushButton class.
- Signals
-
clicked
(pressed & released) pressed
released
-
- Slots
QApplication::quit
QWidget::setEnabled
QPushButton::setText
- Signals
- 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
andSLOT
FooObjectA *fooA = new FooObjectA(); FooObjectB *fooB = new FooObjectB(); QObject::connect(fooA, SIGNAL (bared()), fooB, SLOT (baz()));
- assuming that
FooObjectA
have abared
signal, andFooObjectB
have abaz
slot.- write signature of the signal and the slot inside the two macros SIGNAL and SLOT.
-
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 unusual,
- a slot is actually a real method, and can be called as usual in other methods, or whilst responding to a signal.
- It is used this way, with the two macros
- Qt provides the method QObject::connect.
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 to communicate information.
- While playing 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 value. This signal can be connected to a 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 ms);
- and we know from the documentation, that the QProgressBar has this slot:
void QProressBar::setValue(int value);
- You can see that the signal and the slot have the same kind of parameters, especially the type int.
- If you connect a signal to a slot that does not share the same kind of parameters, when the connection is done (at run-time) you will get a warning like:
QObject::connect: Incompatible sender/receiver arguments
- This 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.
- If you connect a signal to a slot that does not share the same kind of parameters, when the connection is done (at run-time) you will get a warning like:
- The code for the connection will look like this:
MediaProgressManager *manager = new MediaProgressManager(); QProgressBar *progress = new QProgressBar(window); QObject::connect(manager, SIGNAL (tick(int)), progress, SLOT (setValue(int)) );
- You can see that you have to provide a signature inside the SIGNAL and SLOT macro, providing the type of values that are passed through the signals.
- You may also provide the name of the variable if you want. (It is actually even better).
- You can see that you have to provide a signature inside the SIGNAL and SLOT macro, providing the type of values that are passed through the signals.
- While playing song,
- For example, it can also be used to communicate information.
Feature 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 is sent if the first signal is sent
Examples
- Responding to an event
- QPushButton provides the clicked signal.
- QApplication provides the quit slot, that closes the application.
- In order to make a click on a button close the app, we have to connect the signal clicked of the button to the quit slot of QApplication instance.
- How to access to the QApplication instance while you are in another class.
- There exists a static function in 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
#include 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.
- There exists a static function in QApplication, with the following signature, that is used to get it:
Transmitting information with signals and slots
- Simpler example for information transmission.
- Only displays a progress bar and a slider (created by QSlider) inside a window, and while the slider is moved, the value of the progress bar is synced with a very simple connection.
void QSlider::valueChanged(int value); void QProgressBar::setValue(int value);
- QSlider automatically emits the signal
valueChanged
with the new value passed as a parameter when the value is changed, and the methodsetValue
of QProgressBar, is used to set the value of the progress bar.
#include <QApplication> #include <QProgressBar> #include <QSlider> int main(int argc, char **argv) { QApplication app(argc, argv); // Create a container window Window window; window.setFixedSize(200, 140); // 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, 50, 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, 90, 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(); }
- Only displays a progress bar and a slider (created by QSlider) inside a window, and while the slider is moved, the value of the progress bar is synced with a very simple connection.