Qt Multi Thread Signal Slot

admin

This page describes the use of signals and slots in Qt for Python.The emphasis is on illustrating the use of so-called new-style signals and slots, although the traditional syntax is also given as a reference.

  1. Qt Multi Thread Signal Slot Machine
  2. Qt Signals And Slots Tutorial

Signals & Slots, Signals and slots are made possible by Qt's meta-object system. To one signal, the slots will be executed one after the other, in the order they have valueChanged, and it has a slot which other objects can send signals to. The context object provides information about in which thread the receiver should be executed.

The main goal of this new-style is to provide a more Pythonic syntax to Python programmers.

  • 2New syntax: Signal() and Slot()

Traditional syntax: SIGNAL () and SLOT()

QtCore.SIGNAL() and QtCore.SLOT() macros allow Python to interface with Qt signal and slot delivery mechanisms.This is the old way of using signals and slots.

Connect(pRtrThread, SIGNAL(displayInfo(QString)), pFacade-muiMainWindow.outputPane, SLOT(append(QString)), Qt::QueuedConnection); connect(pRtrThread, SIGNAL(taskOver), CFacadeHandler::getUniqueInstance(pFacade-muiMainWindow), SLOT(changeCursorShape), Qt::QueuedConnection). This wrapper provides the signals, slots and methods to easily use the thread object within a Qt project. To use it, prepare a QObject subclass with all your desired functionality in it. Then create a new QThread instance, push the QObject onto it using moveToThread(QThread.) of the QObject instance and call start on the QThread instance. QML Beginners: Core Beginners: https://www.udemy.com/course/qt-core-for-b. Multi-threading behavior of signals and slots. Suppose I have an object A living in thread a and object C living in thread c. Object A has a signal sig1 which is connected to a particular slot of object C (default connection, which means in this case queued connection since it is cross-thread).

The example below uses the well known clicked signal from a QPushButton.The connect method has a non python-friendly syntax.It is necessary to inform the object, its signal (via macro) and a slot to be connected to.

New syntax: Signal() and Slot()

The new-style uses a different syntax to create and to connect signals and slots.The previous example could be rewritten as:

Using QtCore.Signal()

Signals can be defined using the QtCore.Signal() class.Python types and C types can be passed as parameters to it.If you need to overload it just pass the types as tuples or lists.

In addition to that, it can receive also a named argument name that defines the signal name.If nothing is passed as name then the new signal will have the same name as the variable that it is being assigned to.

Qt Multi Thread Signal Slot Machine

The Examples section below has a collection of examples on the use of QtCore.Signal().

Note: Signals should be defined only within classes inheriting from QObject.This way the signal information is added to the class QMetaObject structure.

Using QtCore.Slot()

Slots are assigned and overloaded using the decorator QtCore.Slot().Again, to define a signature just pass the types like the QtCore.Signal() class.Unlike the Signal() class, to overload a function, you don't pass every variation as tuple or list.Instead, you have to define a new decorator for every different signature.The examples section below will make it clearer.

Another difference is about its keywords.Slot() accepts a name and a result.The result keyword defines the type that will be returned and can be a C or Python type.name behaves the same way as in Signal().If nothing is passed as name then the new slot will have the same name as the function that is being decorated.

Examples

The examples below illustrate how to define and connect signals and slots in PySide2.Both basic connections and more complex examples are given.

  • Hello World example: the basic example, showing how to connect a signal to a slot without any parameters.
  • Next, some arguments are added. This is a modified Hello World version. Some arguments are added to the slot and a new signal is created.
  • Add some overloads. A small modification of the previous example, now with overloaded decorators.
  • An example with slot overloads and more complicated signal connections and emissions (note that when passing arguments to a signal you use '[]'):
  • An example of an object method emitting a signal:
  • An example of a signal emitted from another QThread:

Qt Signals And Slots Tutorial

  • Signals are runtime objects owned by instances, they are not class attributes:
Retrieved from 'https://wiki.qt.io/index.php?title=Qt_for_Python_Signals_and_Slots&oldid=35927'

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.

Summary from Part 1

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::activatewill 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_activateand other parts that were skipped for the BlockingQueuedConnection

Qt Event Loop

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.

Copying the parameters

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.

queued_activate

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

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.

The dangers of BlockingQueuedConnection

You must be careful in order to avoid deadlocks.

Qt signals and slots tutorial

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.

Conclusion

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.