-
Notifications
You must be signed in to change notification settings - Fork 0
1.05
Explanation of the contents of a topic page @ Week 1 Topic 1
Objectives: Observer pattern: Object communication with signals and slots
Comment: The key of Qt. All asynchronous functionality is based on (well event handling first) but then to signals and slots. The important thing to learn is that this is type-safe (callbacks typically are not), and many-to-many connections (virtual methods are typically one to one or one to many, if several virtual functions are used).
- What is the Q_OBJECT macro (E: this is only here for my own reference ((1.03)))?
- What is object communication?
- Why is object communication?
- What are callbacks?
- How do signals compare to callbacks?
- What does "type-safe" mean?
- What are Signals?
- What are Slots?
- How do Signals and Slots interconnect?
- When to use Signals and Slots?
- When to not use Signals and Slots?
- What is the observer pattern?
- What are many-to-many observer connections?
- What is the meta-object compiler (moc)?) (E: This was already mentioned in 1.03?)
- What kind of connection variants exist? (lambdas)
http://doc.qt.io/qt-5/signalsandslots.html
https://wiki.qt.io/Qt_for_Beginners#Signals_and_slots
https://wiki.qt.io/Qt_for_Beginners#Features_of_signals_and_slots
Now we're getting somewhere! Signals and slots are the key of Qt and object communication within. They are in a sense comparable to callbacks, but the difference is that they are type-safe where as callbacks typically are not. One of the benefits we could mention before starting, is that signals and slots allow you to build many-to-many connections, where as typically virtual methods are one-to-one or one-to-many, if several virtual functions are used.
We discussed the Q_OBJECT macro in 1.03, and it will find some relevance throughout this topic as well.
- https://github.com/TestMyQt/material-outline/wiki/1.05#signals (needs editing)
- https://github.com/TestMyQt/material-outline/wiki/1.05#slots (needs editing)
- https://github.com/TestMyQt/material-outline/wiki/1.05#interconnecting-signals-and-slots (needs all)
- https://github.com/TestMyQt/material-outline/wiki/1.05#when-to-use-signals-and-slots (needs all)
- https://github.com/TestMyQt/material-outline/wiki/1.05#the-observer-pattern (needs edit, may need more)
http://doc.qt.io/qt-5/signalsandslots.html#signals
Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.
When a signal is emitted, the slots connected to it are usually executed immediately, just like a normal function call. When this happens, the signals and slots mechanism is totally independent of any GUI event loop. Execution of the code following the emit statement will occur once all slots have returned. The situation is slightly different when using queued connections; in such a case, the code following the emit keyword will continue immediately, and the slots will be executed later.
Here are some examples of signals from the 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.
Signals are automatically generated by the moc and must not be implemented in the .cpp file. They can never have return types (i.e. use void).
A note about arguments: Our experience shows that signals and slots are more reusable if they do not use special types. If QScrollBar::valueChanged() were to use a special type such as the hypothetical QScrollBar::Range, it could only be connected to slots designed specifically for QScrollBar. Connecting different input widgets together would be impossible.
http://doc.qt.io/qt-5/signalsandslots.html#slots
A slot is called when a signal connected to it is emitted. Slots are normal C++ functions and can be called normally; their only special feature is that signals can be connected to them.
Here are some slots, from different classes:
- QApplication::quit
- QWidget::setEnabled
- QPushButton::setText
If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted.
Since slots are normal member functions, they follow the normal C++ rules when called directly. However, as slots, they can be invoked by any component, regardless of its access level, via a signal-slot connection. This means that a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class.
You can also define slots to be virtual, which we have found quite useful in practice.
Compared to callbacks, signals and slots are slightly slower because of the increased flexibility they provide, although the difference for real applications is insignificant. In general, emitting a signal that is connected to some slots, is approximately ten times slower than calling the receivers directly, with non-virtual function calls. This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission), and to marshall any parameters in a generic fashion. While ten non-virtual function calls may sound like a lot, it's much less overhead than any new or delete operation, for example. As soon as you perform a string, vector or list operation that behind the scene requires new or delete, the signals and slots overhead is only responsible for a very small proportion of the complete function call costs. The same is true whenever you do a system call in a slot; or indirectly call more than ten functions. The simplicity and flexibility of the signals and slots mechanism is well worth the overhead, which your users won't even notice.
Note that other libraries that define variables called signals or slots may cause compiler warnings and errors when compiled alongside a Qt-based application. To solve this problem, #undef the offending preprocessor symbol.
http://doc.qt.io/qt-5/signalsandslots.html#a-small-example
http://wiki.qt.io/How_to_Use_Signals_and_Slots
https://wiki.qt.io/Qt_for_Beginners#Transmitting_information
https://wiki.qt.io/Qt_for_Beginners#Creating_custom_signals_and_slots
- Always
https://wiki.qt.io/Qt_for_Beginners#The_observer_pattern
https://en.wikipedia.org/wiki/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.
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 applications, and often leads to some boilerplate 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.
Create classes Sender and Receiver.
On Sender create functions sendText(QString)
and sendNumbers(int one, int two)
and define signals textSignal(QString)
and numberSignal(int f, int s)
.
On Receiver create the corresponding slots textSlot()
that uses QDebug to print out the message numberSlot()
that uses QDebug to print out the sum.
Perhaps we'll just give the students the connect()s on the template? Point of the exercise is to learn the QObject declaration for the classes that use signal/slots.