Hands down the best – I buy tons of T slot extrusions and it is absolutely absurd what every other company charges for this stuff – Like, it actually makes me sick thinking about it. I go through tons of T slot extrusions and fasteners every week and I’m so glad I recently found these guys. Code for this videothis video we will learn How Qt Signals and Slots Wor. DetailsDialog Definition. The DetailsDialog class is a subclass of QDialog, implementing a slot verify to allow contents of the DetailsDialog to be verified later. This is further explained in DetailsDialog Implementation. Signals and slots are used for communication between objects. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks. The minimal example requires a class with one signal, one slot and one connection: counter.h.
This blog is part of a series of blogs explaining the internals of signals and slots.
In this article, we will explore the mechanisms powering the Qt queued connections.
In the first part, we saw that signalsare just simple functions, whose body is generated by moc. They are just calling QMetaObject::activate
, with an array of pointers to arguments on the stack.Here is the code of a signal, as generated by moc: (from part 1)
QMetaObject::activate
will then look in internal data structures to find out what are the slots connected to that signal.As seen in part 1, for each slot, the following code will be executed:
So in this blog post we will see what exactly happens in queued_activate
and other parts that were skipped for the BlockingQueuedConnection
A QueuedConnection
will post an event to the event loop to eventually be handled.
When posting an event (in QCoreApplication::postEvent
),the event will be pushed in a per-thread queue(QThreadData::postEventList
).The event queued is protected by a mutex, so there is no race conditions when threadspush events to another thread's event queue.
Once the event has been added to the queue, and if the receiver is living in another thread,we notify the event dispatcher of that thread by calling QAbstractEventDispatcher::wakeUp
.This will wake up the dispatcher if it was sleeping while waiting for more events.If the receiver is in the same thread, the event will be processed later, as the event loop iterates.
The event will be deleted right after being processed in the thread that processes it.
An event posted using a QueuedConnection is a QMetaCallEvent
. When processed, that event will call the slot the same way we call them for direct connections.All the information (slot to call, parameter values, ...) are stored inside the event.
The argv
coming from the signal is an array of pointers to the arguments. The problem is that these pointers point to the stack of the signal where the arguments are. Once the signal returns, they will not be valid anymore. So we'll have to copy the parameter values of the function on the heap. In order to do that, we just ask QMetaType. We have seen in the QMetaType article that QMetaType::create
has the ability to copy any type knowing it's QMetaType ID and a pointer to the type.
To know the QMetaType ID of a particular parameter, we will look in the QMetaObject, which contains the name of all the types. We will then be able to look up the particular type in the QMetaType database.
We can now put it all together and read through the code ofqueued_activate, which is called by QMetaObject::activate
to prepare a Qt::QueuedConnection
slot call.The code showed here has been slightly simplified and commented:
Upon reception of this event, QObject::event
will set the sender and call QMetaCallEvent::placeMetaCall
. That later function will dispatch just the same way asQMetaObject::activate
would do it for direct connections, as seen in Part 1
BlockingQueuedConnection
is a mix between DirectConnection
and QueuedConnection
. Like with aDirectConnection
, the arguments can stay on the stack since the stack is on the thread thatis blocked. No need to copy the arguments.Like with a QueuedConnection
, an event is posted to the other thread's event loop. The event also containsa pointer to a QSemaphore
. The thread that delivers the event will release thesemaphore right after the slot has been called. Meanwhile, the thread that called the signal will acquirethe semaphore in order to wait until the event is processed.
It is the destructor of QMetaCallEvent which will release the semaphore. This is good becausethe event will be deleted right after it is delivered (i.e. the slot has been called) but also whenthe event is not delivered (e.g. because the receiving object was deleted).
A BlockingQueuedConnection
can be useful to do thread communication when you want to invoke afunction in another thread and wait for the answer before it is finished. However, it must be donewith care.
You must be careful in order to avoid deadlocks.
Obviously, if you connect two objects using BlockingQueuedConnection
living on the same thread,you will deadlock immediately. You are sending an event to the sender's own thread and then are locking thethread waiting for the event to be processed. Since the thread is blocked, the event will never beprocessed and the thread will be blocked forever. Qt detects this at run time and prints a warning,but does not attempt to fix the problem for you.It has been suggested that Qt could then just do a normal DirectConnection
if both objects are inthe same thread. But we choose not to because BlockingQueuedConnection
is something that can only beused if you know what you are doing: You must know from which thread to what other thread theevent will be sent.
The real danger is that you must keep your design such that if in your application, you do aBlockingQueuedConnection
from thread A to thread B, thread B must never wait for thread A, or you willhave a deadlock again.
When emitting the signal or calling QMetaObject::invokeMethod()
, you must not have any mutex lockedthat thread B might also try locking.
A problem will typically appear when you need to terminate a thread using a BlockingQueuedConnection
, for example in thispseudo code:
You cannot just call wait here because the child thread might have already emitted, or is about to emitthe signal that will wait for the parent thread, which won't go back to its event loop. All the thread cleanup information transfer must only happen withevents posted between threads, without using wait()
. A better way to do it would be:
The downside is that MyOperation::cleanup()
is now called asynchronously, which may complicate the design.
This article should conclude the series. I hope these articles have demystified signals and slots,and that knowing a bit how this works under the hood will help you make better use of them in yourapplications.
Official documentation on this topic can be found here.
Signals and slots are used for communication between objects. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks.
The minimal example requires a class with one signal, one slot and one connection:
counter.h
The main
sets a new value. We can check how the slot is called, printing the value.
Finally, our project file:
The conventional connect
syntax that uses SIGNAL
and SLOT
macros works entirely at runtime, which has two drawbacks: it has some runtime overhead (resulting also in binary size overhead), and there's no compile-time correctness checking. The new syntax addresses both issues. Before checking the syntax in an example, we'd better know what happens in particular.
Let's say we are building a house and we want to connect the cables. This is exactly what connect function does. Signals and slots are the ones needing this connection. The point is if you do one connection, you need to be careful about the further overlaping connections. Whenever you connect a signal to a slot, you are trying to tell the compiler that whenever the signal was emitted, simply invoke the slot function. This is what exactly happens.
Here's a sample main.cpp:
Hint: the old syntax (SIGNAL
/SLOT
macros) requires that the Qt metacompiler (MOC) is run for any class that has either slots or signals. From the coding standpoint that means that such classes need to have the Q_OBJECT
macro (which indicates the necessity to run MOC on this class).
The new syntax, on the other hand, still requires MOC for signals to work, but not for slots. If a class only has slots and no signals, it need not have the Q_OBJECT
macro and hence may not invoke the MOC, which not only reduces the final binary size but also reduces compilation time (no MOC call and no subsequent compiler call for the generated *_moc.cpp
file).
While being better in many regards, the new connection syntax in Qt5 has one big weakness: Connecting overloaded signals and slots. In order to let the compiler resolve the overloads we need to use static_cast
s to member function pointers, or (starting in Qt 5.7) qOverload
and friends:
A simple multiwindow example using signals and slots.
There is a MainWindow class that controls the Main Window view. A second window controlled by Website class.
The two classes are connected so that when you click a button on the Website window something happens in the MainWindow (a text label is changed).
I made a simple example that is also on GitHub:
mainwindow.h
mainwindow.cpp
website.h
website.cpp
Project composition:
Consider the Uis to be composed:
So the keypoints are the connections between signals and slots and the management of windows pointers or references.