Signals and Slots#
Due to the nature of Qt, QObject s require a way to communicate, and that’s the reason for this mechanism to be a central feature of Qt.
In simple terms, you can understand Signal and Slots in the same way you interact with the lights in your house. When you move the light switch (signal) you get a result which may be that your light bulbs are switched on/off (slot).
While developing interfaces, you can get a real example by the effect of clicking a button: the ‘click’ will be the signal, and the slot will be what happens when that button is clicked, like closing a window, saving a document, etc.
If you have experience with other frameworks or toolkits, it’s likely that you read a concept called ‘callback’. Leaving the implementation details aside, a callback will be related to a notification function, passing a pointer to a function in case it’s required due to the events that happen in your program. This approach might sound similar, but there are essential differences that make it an unintuitive approach, like ensuring the type correctness of callback arguments, and some others.
All classes that inherit from QObject or one of its subclasses, like QWidget can contain signals and slots. Signals are emitted by objects when they change their state in a way that may be interesting to other objects. This is all the object does to communicate. It does not know or care whether anything is receiving the signals it emits. This is true information encapsulation, and ensures that the object can be used as a software component.
Slots can be used for receiving signals, but they are also normal member functions. Just as an object does not know if anything receives its signals, a slot does not know if it has any signals connected to it. This ensures that truly independent components can be created with Qt.
You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)
Qt’s widgets have many predefined signals and slots. For example, QAbstractButton (base class of buttons in Qt) has a clicked() signal and QLineEdit (single line input field) has a slot named ‘clear()`. So, a text input field with a button to clear the text could be implemented by placing a QToolButton to the right of the QLineEdit and connecting its clicked() signal to the slot ‘clear()`. This is done using the connect() method of the signal:
button = QToolButton() line_edit = QLineEdit() button.clicked.connect(line_edit.clear)
connect() returns a QMetaObject.Connection object, which can be used with the disconnect() method to sever the connection.
Signals can also be connected to free functions:
import sys from PySide6.QtWidgets import QApplication, QPushButton def function(): print("The 'function' has been called!") app = QApplication() button = QPushButton("Call function") button.clicked.connect(func) button.show() sys.exit(app.exec())
Connections can be spelled out in code or, for widget forms, designed in the Signal-Slot Editor of Qt Designer.
The Signal Class#
When writing classes in Python, signals are declared as class level variables of the class QtCore.Signal() . A QWidget-based button that emits a clicked() signal could look as follows:
from PySide6.QtCore import Qt, Signal from PySide6.QtWidgets import QWidget class Button(QWidget): clicked = Signal(Qt.MouseButton) . def mousePressEvent(self, event): self.clicked.emit(event.button())
The constructor of Signal takes a tuple or a list of Python types and C types:
signal1 = Signal(int) # Python types signal2 = Signal(QUrl) # Qt Types signal3 = Signal(int, str, int) # more than one type signal4 = Signal((float,), (QDate,)) # optional types
In addition to that, it can receive also a named argument name that defines the signal name. If nothing is passed, the new signal will have the same name as the variable that it is being assigned to.
# TODO signal5 = Signal(int, name='rangeChanged') # . rangeChanged.emit(. )
Another useful option of Signal is the arguments name, useful for QML applications to refer to the emitted values by name:
sumResult = Signal(int, arguments=['sum'])
Connections target: . function onSumResult(sum) // do something with 'sum' >
The Slot Class#
Slots in QObject-derived classes should be indicated by the decorator @QtCore.Slot() . Again, to define a signature just pass the types similar to the QtCore.Signal() class.
@Slot(str) def slot_function(self, s): .
Slot() also accepts a name and a result keyword. The result keyword defines the type that will be returned and can be a C or Python type. The name keyword 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.
Overloading Signals and Slots with Different Types#
It is actually possible to use signals and slots of the same name with different parameter type lists. This is legacy from Qt 5 and not recommended for new code. In Qt 6, signals have distinct names for different types.
The following example uses two handlers for a Signal and a Slot to showcase the different functionality.
import sys from PySide6.QtWidgets import QApplication, QPushButton from PySide6.QtCore import QObject, Signal, Slot class Communicate(QObject): # create two new signals on the fly: one will handle # int type, the other will handle strings speak = Signal((int,), (str,)) def __init__(self, parent=None): super().__init__(parent) self.speak[int].connect(self.say_something) self.speak[str].connect(self.say_something) # define a new slot that receives a C 'int' or a 'str' # and has 'say_something' as its name @Slot(int) @Slot(str) def say_something(self, arg): if isinstance(arg, int): print("This is a number:", arg) elif isinstance(arg, str): print("This is a string:", arg) if __name__ == "__main__": app = QApplication(sys.argv) someone = Communicate() # emit 'speak' signal with different arguments. # we have to specify the str as int is the default someone.speak.emit(10) someone.speak[str].emit("Hello everybody!")
Specifying Signals and Slots by Method Signature Strings#
Signals and slots can also be specified as C++ method signature strings passed through the SIGNAL() and/or SLOT() functions:
from PySide6.QtCore import SIGNAL, SLOT button.connect(SIGNAL("clicked(Qt::MouseButton)"), action_handler, SLOT("action1(Qt::MouseButton)"))
This is not recommended for connecting signals, it is mostly used to specify signals for methods like QWizardPage::registerField() :
wizard.registerField("text", line_edit, "text", SIGNAL("textChanged(QString)"))
Copyright © 2023 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.
A Simple Button Tutorial¶
In this tutorial, we’ll show you how to handle signals and slots using Qt for Python. Signals and slots is a Qt feature that lets your graphical widgets communicate with other graphical widgets or your python code. Our application creates a button that logs the Button clicked, Hello! message to the python console each time you click it.
Let’s start by importing the necessary PySide2 classes and python sys module:
import sys from PySide2.QtWidgets import QApplication, QPushButton from PySide2.QtCore import Slot
Let’s also create a python function that logs the message to the console:
# Greetings @Slot() def say_hello(): print("Button clicked, Hello!")
The @Slot() is a decorator that identifies a function as a slot. It is not important to understand why for now, but use it always to avoid unexpected behavior.
Now, as mentioned in previous examples you must create the QApplication to run your PySide2 code:
# Create the Qt Application app = QApplication(sys.argv)
Let’s create the clickable button, which is a QPushButton instance. To label the button, we pass a python string to the constructor:
# Create a button button = QPushButton("Click me")
Before we show the button, we must connect it to the say_hello() function that we defined earlier. There are two ways of doing this; using the old style or the new style, which is more pythonic. Let’s use the new style in this case. You can find more information about both these styles in the Signals and Slots in PySide2 wiki page.
The QPushButton has a predefined signal called clicked, which is triggered every time the button is clicked. We’ll connect this signal to the say_hello() function:
# Connect the button to the function button.clicked.connect(say_hello)
Finally, we show the button and start the Qt main loop:
# Show the button button.show() # Run the main Qt loop app.exec_()
Here is the complete code for this example:
#!/usr/bin/python import sys from PySide2.QtWidgets import QApplication, QPushButton from PySide2.QtCore import Slot @Slot() def say_hello(): print("Button clicked, Hello!") # Create the Qt Application app = QApplication(sys.argv) # Create a button, connect it and show it button = QPushButton("Click me") button.clicked.connect(say_hello) button.show() # Run the main Qt loop app.exec_()
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.