What does @Slot() do?

Is the Slot decorator even necessary?
Heads up! You've already completed this tutorial.

When working with Qt slots and signals in PySide6 you will discover the @Slot decorator. This decorator is used to mark a Python function or method as a slot to which a Qt signal can be connected. However, as you can see in our signals and slots tutorials you don't have to use this. Any Python function or method can be used, normally, as a slot for a Qt signals. But elsewhere, in our threading tutorials we do use it.

What's going on here?

  • Why do you sometimes use @Slot but usually not?
  • What happens when you omit it?
  • Are there times when it is required?

What does the documentation say?

The PyQt6 documentation has a good explanation:

Although PyQt6 allows any Python callable to be used as a slot when connecting signals, it is sometimes necessary to explicitly mark a Python method as being a Qt slot and to provide a C++ signature for it. PyQt6 provides the pyqtSlot() function decorator to do this.

Connecting a signal to a decorated Python method has the advantage of reducing the amount of memory used and is slightly faster.

In PySide6 the decorator is named simply Slot() but is otherwise functionally compatible:

PySide6 adopts PyQt's new signal and slot syntax as-is. The PySide6 implementation is functionally compatible with the PyQt one [...].

Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

More info Get the book

From the above we see that:

  • Any Python callable can be used as a slot when connecting signals.
  • It is sometimes necessary to explicitly mark a Python method as being a Qt slot and to provide a C++ signature for it.
  • There is a side-benefit in that marking a function or method with Slot() reduces the amount of memory used, and makes the slot faster.

When is it necessary?

Sometimes necessary is a bit vague. In practice the only situation where you need to use Slot decorators is when working with threads. This is because of a difference in how signal connections are handled in decorated vs. undecorated slots.

  1. If you decorate a method with @Slot then that slot is created as a native Qt slot, and behaves identically
  2. If you don't decorate the method then PySide6 will create a "proxy" object wrapper which provides a native slot to Qt

In normal use this is fine, aside from the performance impact (see below). But when working with threads, there is a complication: is the proxy object created on the GUI thread or on the runner thread. If it ends up on the wrong thread, this can lead to segmentation faults. Using the Slot decorator side-steps this issue, because no proxy is created.

When updating my PySide6 book I wondered -- is this still necessary?! -- and tested removing it from the examples. Many examples continue to work, but some failed. To be safe, use Slot decorators on your QRunnable.run methods.

What about performance?

The PyQt6 documentation notes that using native slots "has the advantage of reducing the amount of memory used and is slightly faster". But how much faster is it really, and does decorating slots actually save much memory?

We can test this directly by using this script from Oliver L Schoenborn. Updating for PySide6 (replace PyQt5 with PySide6 and pyqtSlot with Slot and it will work as-is) and running this we get the following results:

See the original results for PyQt5 for comparison.

First the results for the speed of emitting signals when connected to a decorated slot, vs non-decorated.

python
Raw slot mean, stddev:  1.608 0.066
Pyqt slot mean, stddev: 1.587 0.045
Percent gain with Slot: 1 %

The result shows Slot as 1% faster, but this is negligible (the original data on PyQt5 also showed no difference). So, using Slot will have no noticeable impact on the speed of signal handling in your applications.

PyQt/PySide 1:1 Coaching with Martin Fitzpatrick — Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.

Book Now 60 mins ($195)

Next are the results for establishing connections. This shows the speed, and memory usage of connecting to decorated vs. non-decorated slots.

python
Comparing mem and time required to create 10000000 connections, 1000 times

Measuring for 100000 connections
              # connects   mem (bytes)       time (sec)
Raw         :   100000     38670336 (36MB)    0.381
PySide Slot :   100000     17858560 (17MB)    0.426
Ratios      :                     2               1

The results show that decorated slots are marginally faster to connect to, but the difference is negligible. Based on these numbers, when connecting 100 signals the total execution time difference would be 0.03 ms vs 0.04 ms. This is negligible, not to mention imperceptible.

Perhaps more significant is that using raw connections uses 2x the memory of decorated connections. Again though, bear in mind that for a more realistic upper limit of connections (100) the actual difference here is 3.6KB vs 1.7KB.

The bottom line: don't expect any dramatic improvements in performance or memory usage from using slot decorators, unless you're working with insanely large numbers of signals or making regular connections you won't see any difference at all. That said, decorating your slots is an easy win if you need it.

Are there any other reasons to decorate a slot?

In Qt signals can be used to transmit more than one type of data by overloading signals and slots with different types.

For example, with the following code the my_slot_fn will only receive signals which match the signature of two int values.

python
@Slot(int, int)
def my_slot_fn(a, b):
    pass

This is a legacy of Qt5 and not recommended in new code. In Qt6 all of these signals have been replaced with separate signals with distinct names for different types. I recommend you follow the same approach in your own code for the sake of simplicity.

Conclusion

The Slot decorator can be used to mark Python functions or methods as Qt slots. This decorator is only required on slots which may be connected to across threads, for example the run method of QRunnable objects. For all other slots it can be omitted. There is a very small performance benefit to using it, which you may want to consider when your application makes a large number of signal/slot connections.

Over 10,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt6
Take a look

Downloadable ebook (PDF, ePub) & Complete Source code

Also available from Leanpub and Amazon Paperback

[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak
Martin Fitzpatrick

What does @Slot() do? was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.