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.