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

Signals and Slots
Program Execution

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.