Signals and slots are Qt's event handling mechanism for communication between objects. For example, when a button is clicked, it emits a "clicked" signal that executes a pre-connected slot function.
Basic Concepts
- Signal: Notification of an event (e.g., "button clicked", "window resized")
- Slot: Function that responds to signals (e.g., "quit program", "save file")
- Connect: Pairing signals with slots
Connecting Signals and Slots

Example code for connecting signals and slots:
import sys
from PySide6.QtCore import Slot
from PySide6.QtWidgets import (
QApplication, QWidget, QTextEdit, QPushButton, QVBoxLayout, QHBoxLayout
)
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Signals and Slots Example")
# Create widgets
self.input = QTextEdit()
self.btn_clear = QPushButton("Clear")
self.btn_add = QPushButton("Add Text")
self.btn_exit = QPushButton("Exit")
# Layout setup
layout = QVBoxLayout()
layout.addWidget(self.input)
btn_layout = QHBoxLayout()
btn_layout.addWidget(self.btn_clear)
btn_layout.addWidget(self.btn_add)
btn_layout.addWidget(self.btn_exit)
layout.addLayout(btn_layout)
self.setLayout(layout)
# Connect signals and slots
self.btn_clear.clicked.connect(self.input.clear)
self.btn_add.clicked.connect(self.add_text)
self.btn_exit.clicked.connect(QApplication.quit)
@Slot()
def add_text(self):
self.input.append("hello world!")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec())
In this example, button btn_clear
clears the text box, btn_add
appends text, and btn_exit
quits the program.
Lines 32-34 connect button signals to slot functions. clicked
is a predefined signal for button clicks, connect()
pairs signals with slots, self.input.clear()
is a built-in slot, and self.add_text()
is a custom slot decorated with @Slot()
for better safety.
What happens without @Slot?
PySide6 will treat it as a regular Python function that can still be connected, but using @Slot:
- Improves performance
- Adds type safety
- Prevents memory leaks
connect() Function
Establishes a connection between signal and receiver:
connect(receiver[, type=Qt.AutoConnection])
- receiver: Callable, can be a slot or signal
- type: Connection type determining immediate or queued delivery
Connection Type | Description |
---|---|
Qt.AutoConnection | Default. Uses Qt.DirectConnection if receiver is in signal thread, otherwise Qt.QueuedConnection |
Qt.DirectConnection | Slot executes immediately in signal thread |
Qt.QueuedConnection | Slot executes when control returns to receiver's event loop |
Qt.BlockingQueuedConnection | Similar to QueuedConnection but blocks signal thread until slot returns |
Disconnecting
Disconnect unused connections to prevent memory leaks:
# Disconnect specific slot
button.clicked.disconnect(function)
# Disconnect all slots
button.clicked.disconnect()
One Signal, Multiple Slots
A signal can connect to multiple slots:
A slot can also be connected by multiple signals
def log():
print("[LOG] clicked.")
button.clicked.connect(log)
button.clicked.connect(lambda: print("Do something!"))
Signals with Parameters
Some built-in signals carry parameters for passing information between components.
Example with QLineEdit
's textChanged(str)
signal:
self.input = QLineEdit(self)
self.label = QLabel("Current input:", self)
self.input.textChanged.connect(self.on_text_changed)
@Slot(str)
def on_text_changed(self, text: str):
self.label.setText(f"Current input: {text}")
Custom Signals
Define custom signals using Signal
class:
from PySide6.QtCore import QObject, Signal, Slot
class A(QObject):
message = Signal(str)
class B(QObject):
@Slot(str)
def print_message(self, msg):
print("Received:", msg)
a = A()
b = B()
a.message.connect(b.print_message)
a.message.emit("Hello!")
Multiple parameter signals:
message = Signal(str,int)
...
@Slot(str,int)
def print_message(self, msg, value):
print("Received:",msg,value)
...
a.message.emit("hello",123)
Overloading with @Slot
Use multiple @Slot
decorators for function overloading:
class Receiver(QObject):
@Slot(str)
@Slot(int)
def handle(self, value):
print("Received:", value)
This allows handle
to accept either str
or int
parameters, with Qt automatically matching the appropriate version.