Qt Signal And Slots Callback

  1. Qt Signal Slot Callback
  2. Qt Signal Slots Vs Callbacks

Signals and Slots In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. Secondly, the callback is strongly coupled to the processing function since the processing function must know which callback to call. Signals and Slots. In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs.

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.

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

  • 2New syntax: Signal() and Slot()
Qt Signal And Slots Callback

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.

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 Signal And Slots Callback

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:
  • 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'

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 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?

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:

Qt Signal Slot Callback

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.

Qt signal slots vs callbacks

Tips and tricks

  • 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.

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.

Qt Signal Slots Vs Callbacks

See you next time!