4/7/2022»»Thursday

Qt Signal Slot Thread Context

4/7/2022
Qt Signal Slot Thread Context 9,1/10 5547 reviews
Context

This PySide tutorial shows you how to use native Python threads (non-QThread, i.e. Threading.Thread) to carry out work in the background (e.g. Downloading files).In the special case of downloading, you might want to use QNetworkAccessManager and friends, but in this case, we assume that you can't use it for whatever reason (e.g. Because you want to use Twisted or because you already have your. @sierdzio said in Cannot connect signal and slot from different thread.: Also, remember to use Qt::QueuedConnection for your inter-thread connections - then you don't have to worry about locking any mutexes and such. Thank you sierdzio for your help!

Envision you've decided to go solo and start an indie game studio to fulfil your lifelong dream - to design and create a turn-based strategy game. You begin your endeavour by outlining the different components and sketch on the overall architecture of the game. The game will have some units which can be moved and controlled by using the mouse. There will be an AI component which will perhaps kick in after the units have completed their movements. The AI will then decide how the enemies should respond.

In other words, a sequence of actions need to happen: the mouse click, then the movement of units and then the response of the enemies. However, you probably don't want the mouse logic to directly depend on the unit-classes; it will be used for much more. For the same reason the unit-classes shouldn't need to rely on the AI nor the enemy logic; possibly the same AI will be used for many different enemies. I'm sure you're a great developer so you're aiming to decouple these different components. Nonetheless, the components need to communicate with each other and you need some kind of callback-mechanism. And this, ladies and gentlemen, this is where Qt's signals and slots comes to the rescue.

  • This PySide tutorial shows you how to use native Python threads (non-QThread, i.e. Threading.Thread) to carry out work in the background (e.g. Downloading files).In the special case of downloading, you might want to use QNetworkAccessManager and friends, but in this case, we assume that you can't use it for whatever reason (e.g. Because you want to use Twisted or because you already have your.
  • Nd the index of the signal and of the slot Keep in an internal map which signal is connected to what slots When emitting a signal, QMetaObject::activate is called. It calls qt metacall (generated by moc) with the slot index which call the actual slot.
  • How Qt Signals and Slots Work - Part 3 - Queued and Inter Thread Connections. Since the thread is blocked, the event will never be processed 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.

This is the third post in the series 'Crash course in Qt for C++ developers' covering the signals and slots mechanism. The other topics are listed below.

  1. Signals and slots - communication between objects
  2. Qt Quick/QML example
  3. Qt Widgets example
  4. Tooling, e.g. Qt Creator
  5. Remaining good-to-know topics
  6. Where to go from here?

Qt Signal Slot Parameter

The most common usage of the signals and slots mechanism is for inter-object communication and has been around since the birth of Qt. It's a system where an object broadcasts a signal with data and any listeners, including the sender itself, can subscribe to that signal. The listeners then redirect the data to a member function, or what's so called the slot, where it's then processed.

You might wonder how this system is different from using a standard callback-mechanism used in many other GUI tools. One benefit of Qt's solution is that the sender is not dependent on the listener, it's just firing off the signal. In a callback mechanism the sender usually needs to know which listeners to notify. Another benefit, in contrast to many other callback implementations, is that the signals and slots functions are type safe.

OK - enough text, let's look at some code.

The basics

Signals

Let's start with signals. In order to enable signals for a class, it has to inherit from QObject and use the Q_OBJECT macro. We'll define a signal called signalName with three arguments. The arguments are used to pass in data, which will later be available in the slots.

The signals keyword is essentially a simple macro defined to public but is also used by the MOC (remember from the previous post?) to generate the implementation of the member function signalName(). The implementation can be found in the generated file moc_mysender.cpp. The signal is declared similarly to a regular function, except for a constraint on the return value - it has to be void. To emit the signal, we'll only call the function with the addition of appending emit, i.e.:

emit is a macro defined to do... nothing. It's only used for documentation and readability purposes. You could omit it: the code would compile fine and the outcome would be the same, but again, we want to be explicit that it's a signal. Also, notice that we don't need to depend on any of the listeners, we're only so far just emitting a signal. So how would an object listen to it?

Slots

A listener object will redirect the signal to one of its slots. However, slots are not limited to member functions but can also consist of lambdas, non-member functions and functors. The arguably most common use is to define the slot within an object, so let's start by creating a new class MyReceiver which has a slot called slotName():

Similarly to the signal object, the receiver class needs to inherit from QObject and also use the Q_OBJECT macro. Furthermore, slots is another macro declared to do nothing, but in contrast to emit it's used by the MOC to generate introspection code. Slots-function can be specified as public, protected and private; Signals are always public.

Did I mention that the system is type safe? The signal's and slot's signature must find a match in order to link them: the arguments must be of the same types and qualifiers and declared in the same order. So how do we connect our signal to our slot?

Connect

We'll use the static function QObject::connect():

QObject::connect() takes five arguments, the last one has a default value and will be ignored for now. From left to right:

  • Start with the object which emits the signal - the sender.
  • Next we have a pointer to a member function - the signal.
  • Hook it up to the listener - the receiver.
  • Last, we have another pointer to a member function - the slot.

Now, if we emit out signal, the slot will be notified and we get the following output:

Important data that will be sent to all listeners 42 1.618033

You may connect many different signals to the same slot, or use the same signal for many different slots. It's your choice - the system is very flexible. As mentioned, the system even allows to connect functions and lambdas directly, without using a listener, e.g:

The connection is automatically disconnected if either the sender or the receiver becomes deleted. However, as you might have guessed it's also possible to manually disconnect it by using disconnect:

Heads-up when capturing data in a lambda

Be extra careful when capturing data using lambdas or functors as only the sender will automatically control the lifetime of the connection. Consider the following case:

What if data has been deleted prior to emitting the signal? The connection might still be alive (if it hasn't been manually disconnected) and we'll have a naughty bug in our application. However, Qt provides another overload of the connect() function where it's possible to provide a context object. This is used like so:

Notice the third argument. We're now also providing the same captured object as a context object. If the context object is deleted, the connection will automatically disconnect. Note that the context object has to inherit from QObject for this to work.

We've now come to an end of the basics. There should be enough information here to cover the most common cases. But down the development road you might hit some issues and will need some additional information in the future. Perhaps the next section will help you at those times.

Tips and tricks

Qt Signal Slot Thread

  • In case you have multiple overloads of a signals or slots you'll need to perform a cast within the QObject::connect(), e.g.:
  • Although it should be avoided, it's possible to delete a listener (or schedule it for deletion) within its slot by using deletelater().

  • There are several different types of connections. The type is defined by the fifth argument in the connect()-function. The type is an enum called Qt::ConnectionType and is defaulted to Qt::AutoConnection. In a single threaded application, the default behaviour is for the signal to directly notify the listeners. However, in a multi threaded application, usually the connections between the threads are queued up on each respective event loop. Threads are outside the scope of this series but is very well documented by Qt.

  • It's possible to connect a signal directly to another object's signal. This can be used, for example, to forward signals:

  • You might find a different syntax that's used for the connect() function by other tutorials, blogs or even the Qt documentation, see code below. This style was the main syntax used up until Qt5. The syntax shown in this post provides several benefits over the old style. Personally, I believe the best advantage is that it allows compile-time checking of the signals and slots, which wasn't previously possible. However, in some cases the old syntax must be used, for example when connecting C++ functions to QML functions.
  • Do you remember the slots keyword above (when defining the slot-functions)? It's actually only necessary to define when using the old connect() syntax mentioned in the previous point. The type checking for the old syntax is done at run-time by using type introspection. The MOC generates the introspection code, but only if the slots keyword is defined.

  • If you need to know which signals and slots are connected to a specific object at a certain point in time, you'll find QObject::dumpObjectInfo() very helpful. It's especially useful when debugging since it will output all the inbound and outbound signals for the object.

Qt Signal Slot Thread Contexto

Back to the game architecture

You might now have a better understanding on how signals and slots can be used to separate the different game components. For example, let's say that we define four components which should be decoupled: the MouseComponent, the UnitComponent, the AIComponent and lastly the EnemyComponent. Furthermore, we'll use signals in each component to communicate and notify the other components. However to solve the separation, we'll have to introduce another component, the MainController, which will be used to create all the connections and define the game flow. The MainController will obviously need to depend on the different components, however the components are now well isolated from each other and we've achieved our goal. Do you see how this can be used in a GUI application to separate the visuals and user interaction from the actual logic?

Lastly, if you're interested in reading more about the inner-works of the signals and slots mechanism woboq.com has written a great blog series which has got you covered. Let me know if something is unclear or perhaps if I missed something out.

See you next time!

But generaly this is bad idea to use Qt:DirectConnection until you really know what is this and there is no other way. Lets explain it more, Each thread created by Qt (including main thread and new threads created by QThread) have Event loop, the event loop is responsible for receiving signals and call aproporiate slots in its thread. Generaly executing a blocking operation inside an slot is bad practice, because it blocks the event loop of that threads so no other slots would be called.

If you block an event loop (by making very time consuming or blocking operation) you will not receive events on that thread until the event loop will be unblocked. If the blocking operation, blocks the event loop forever (such as busy while), the slots could never be called.

In this situation you may set the connection type in connect to Qt::DirectConnection, now the slots will be called even the event loop is blocked. so how this could make broke everything? In Qt::DirectConnection Slots will be called in emiter threads, and not receiver threads and it can broke data synchronizations and ran into other problems. So never use Qt::DirectConnection unless you know what are you doing. If your problem will be solved by using Qt::DirectConnection, you have to carefull and look at your code and finding out why your event loop is blocked. Its not a good idea to block the event loop and its not recomended in Qt.

Qt Signal Slot Thread Contextual

Here is small example which shows the problem, as you can see the nonBlockingSlot would be called even the blockingSlot blocked event loop with while(1) which indicates bad coding



Qt Connect Signal Slot

Related Tags