Qt for beginners Signals and slots

From Qt Wiki
Revision as of 14:26, 23 February 2015 by Maintenance script (talk | contribs)
Jump to navigation Jump to search

[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&quot;: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&quot;: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&quot;: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 <code&gt;SIGNAL&lt;/code&gt; and <code&gt;SLOT&lt;/code&gt;

<br />FooObjectA *fooA = new FooObjectA();<br />FooObjectB *fooB = new FooObjectB();

QObject::connect(fooA, SIGNAL (bared()), fooB, SLOT (baz()));<br />

assuming that <code&gt;FooObjectA&lt;/code&gt; have a <code&gt;bared&lt;/code&gt; signal, and <code&gt;FooObjectB&lt;/code&gt; have a <code&gt;baz&lt;/code&gt; 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 :

<br />void MediaProgressManager::tick(int miliseconds);<br />

and we know from the documentation, that the QProgressBar have this slot

<br />void QProgressBar::setValue(int value);<br />

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&gt;
QObject::connect: Incompatible sender/receiver arguments
</code&gt;

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.

signal-slot connection

The code for the connection will look like this:

<br />MediaProgressManager *manager = new MediaProgressManager();<br />QProgressBar '''progress = new QProgressBar(window);
<br />QObject::connect(manager, SIGNAL (tick(int)), fooB, SLOT (setValue(int)));<br />


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

<br />QApplication * QApplication::instance()<br />

This leads to the following modification of our previous code

window.cpp

<br />#include &quot;window.h&amp;quot;

#include &lt;QPushButton&amp;gt;<br /> #include &lt;QApplication&amp;gt;

Window::Window(QWidget '''parent) :<br /> QWidget(parent)<br /> {<br /> // Set size of the window<br /> setFixedSize(100, 50);
<br /> // Create and position the button<br /> m_button = new QPushButton(&quot;Hello World&amp;quot;, this);<br /> m_button-&gt;setGeometry(10, 10, 80, 30);
<br /> // NEW : Do the connection<br /> connect(m_button, SIGNAL (clicked()), QApplication::instance(), SLOT (quit()));<br /> }
<br />


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


<br />void QSlider::valueChanged(int value);<br />void QProgressBar::setValue(int value);<br />


QSlider automatically emits the signal <code&gt;valueChanged&lt;/code&gt; with the new value passed as parameter while the value is changed, and the method <code&gt;setValue&lt;/code&gt; of QProgressBar, is used, as we have seen, to set the value of the progress bar.
This leads to the following code


<br />#include &lt;QApplication&amp;gt;<br />#include &lt;QProgressBar&amp;gt;<br />#include &lt;QSlider&amp;gt;
<br />int main(int argc, char'''*argv)<br />{<br /> QApplication app (argc, argv);

// Create a container window<br /> QWidget window;<br /> window.setFixedSize(200, 80);

// Create a progress bar<br /> // with the range between 0 and 100, and a starting value of 0<br /> QProgressBar *progressBar = new QProgressBar(&amp;window);<br /> progressBar-&gt;setRange(0, 100);<br /> progressBar-&gt;setValue(0);<br /> progressBar-&gt;setGeometry(10, 10, 180, 30);

// Create a horizontal slider<br /> // with the range between 0 and 100, and a starting value of 0<br /> QSlider *slider = new QSlider(&amp;window);<br /> slider-&gt;setOrientation(Qt::Horizontal);<br /> slider-&gt;setRange(0, 100);<br /> slider-&gt;setValue(0);<br /> slider-&gt;setGeometry(10, 40, 180, 30);

window.show();

// Connection<br /> // This connection set the value of the progress bar<br /> // while the slider's value changes<br /> QObject::connect(slider, SIGNAL (valueChanged(int)), progressBar, SLOT (setValue(int)));

return app.exec&amp;amp;#40;&amp;#41;;<br />}<br />

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 <code&gt;connect&lt;/code&gt;. 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&quot;) 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

<br />const QMetaObject * QObject::metaObject () const<br />

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&quot;, "signals&quot;, "slots&quot;, 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

<br />class MyWidget : public QWidget<br />{<br /> Q_OBJECT<br />public:<br /> MyWidget(QWidget '''parent = 0);<br /><br />}<br />


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.