QT: Qt Signals and Slots — Inter-Object Communication

Signals and Slots is Qt's fundamental inter-object communication mechanism — an implementation of the observer pattern that enables loosely coupled, type-safe event-driven programming. It forms the backbone of how Qt objects communicate and how UIs react to events.

Every interactive application needs a way for objects to communicate — a button needs to tell something that it was clicked, a sensor needs to report that a value changed, a timer needs to notify its owner that it fired. Qt's answer to this problem is the Signals and Slots mechanism: a clean, type-safe, loosely coupled approach to event-driven communication that is central to everything Qt does.


The Observer Pattern

Signals and Slots is Qt's implementation of the Observer Pattern — a software design pattern where:

  • An object (the subject/sender) maintains a list of observers
  • When the subject's state changes, it notifies all registered observers automatically
  • Observers do not need to be known to the subject at the time the subject is written

In Qt's implementation:

  • Signals are the notification mechanism (what the subject emits)
  • Slots are the observer functions (what the observer executes in response)
  • connect() is how subjects and observers are linked

Core Concepts

Signal

A signal is a notification emitted by an object when something happens. Signals are declared in the class using the signals: keyword (processed by MOC — see the MOC article for details).

cpp
class Sensor : public QObject {
    Q_OBJECT

signals:
    void temperatureChanged(double newTemperature);  // Signal declaration
    void thresholdExceeded();                         // Signal with no data
};

Key properties of signals:

  • Signals are declared, not implemented — MOC generates the implementation
  • A signal can carry parameters that provide data to connected slots
  • Emitting a signal is done with the emit keyword:

cpp
// Inside the Sensor class implementation:
void Sensor::readTemperature() {
    double temp = readFromHardware();
    emit temperatureChanged(temp);  // Notify all connected slots
}

Slot

A slot is a function that can be connected to a signal. When the signal is emitted, the slot is called. Slots are declared with the public slots: (or private slots:, protected slots:) keyword.

cpp
class Display : public QObject {
    Q_OBJECT

public slots:
    void onTemperatureChanged(double temperature) {
        qDebug() << "Temperature updated:" << temperature;
        updateDisplay(temperature);
    }
};

Slots are ordinary C++ functions that can also be called directly — the slots: keyword just marks them as connectable targets for MOC.

connect()

The connect() function links a signal to a slot. When the signal is emitted, the connected slot is automatically called.

cpp
connect(sender, signal, receiver, slot);


Syntax — Modern Qt 5/6 Style

Qt 5 introduced a new, type-safe connect syntax using function pointers. This is the recommended modern approach:

cpp
Sensor sensor;
Display display;

// Modern syntax: compile-time type checking
QObject::connect(&sensor, &Sensor::temperatureChanged,
                 &display, &Display::onTemperatureChanged);

Advantages of the modern syntax:

  • Compile-time type safety — mismatched signal/slot parameter types are caught at compile time, not runtime
  • Refactoring safe — renaming a signal or slot is caught by the compiler immediately
  • IDE support — autocompletion works for signal and slot names

Syntax — Qt 4 Legacy Style (String-Based)

The original Qt 4 syntax used string macros:

cpp
// Legacy syntax (still valid but not recommended for new code)
QObject::connect(sender, SIGNAL(temperatureChanged(double)),
                 receiver, SLOT(onTemperatureChanged(double)));

This syntax still works, but errors in signal/slot names are only caught at runtime (printed to the debug output), not at compile time. Avoid it in new code.


Lambda Slots

Qt 5+ supports connecting signals to lambda functions — convenient for simple, inline responses:

cpp
QObject::connect(&sensor, &Sensor::temperatureChanged, [](double temp) {
    qDebug() << "Lambda received temperature:" << temp;
});

Lambda slots are ideal when:

  • The response is simple (a few lines of code)
  • No separate slot method declaration is needed
  • The slot logic is closely tied to the connect call and benefits from being inline

Many-to-Many Relationships

Signals and Slots support many-to-many connections — this is one of their most powerful characteristics:

md
Many-to-One:
[Signal A] ---|
[Signal B] ---+--> [Slot X]
[Signal C] ---|

One-to-Many:
             |--> [Slot X]
[Signal A] --+--> [Slot Y]
             |--> [Slot Z]

Many-to-Many:
[Signal A] ---|          |--> [Slot X]
[Signal B] ---+--------->+--> [Slot Y]
[Signal C] ---|          |--> [Slot Z]

  • A single signal can be connected to multiple slots — all slots are called when the signal is emitted
  • A single slot can be connected to multiple signals — the slot responds to any of the connected signals
  • Signals can be connected to other signals — chaining signal propagation

Signal-to-Signal Connections

Signals can be connected to other signals, enabling event propagation without intermediate logic:

cpp
QPushButton button;
QDialog dialog;

// When button is clicked, emit dialog's accepted signal
QObject::connect(&button, &QPushButton::clicked,
                 &dialog, &QDialog::accepted);

This is useful for forwarding signals up through a component hierarchy without writing boilerplate forwarding slots.


Connection Types

Qt supports different connection types that control how the slot is called:

Connection TypeDescriptionUse Case
Qt::AutoConnectionDefault; direct if same thread, queued if cross-threadGeneral use
Qt::DirectConnectionSlot called synchronously in the sender's threadSame-thread, immediate response
Qt::QueuedConnectionSlot called asynchronously via the event loopCross-thread communication
Qt::BlockingQueuedConnectionLike queued, but sender blocks until slot completesCross-thread with synchronization

cpp
// Explicit cross-thread connection
QObject::connect(&workerThread, &Worker::resultReady,
                 &mainObject, &MainObject::handleResult,
                 Qt::QueuedConnection);


Disconnecting Signals and Slots

Connections can be severed explicitly with disconnect():

cpp
// Disconnect a specific connection
QObject::disconnect(&sensor, &Sensor::temperatureChanged,
                    &display, &Display::onTemperatureChanged);

// Disconnect all signals from a sender
QObject::disconnect(&sensor, nullptr, nullptr, nullptr);

Alternatively, connections are automatically cleaned up when either the sender or receiver object is destroyed — preventing dangling connections.


Using Qt Designer for Signal/Slot Connections

In Qt Designer (integrated in Qt Creator), signals and slots can be connected visually:

  1. Switch to Signals/Slots Mode in Qt Designer
  2. Drag from a widget (e.g., a button) to another widget (e.g., a dialog)
  3. Qt Designer shows available signals and slots and lets you connect them visually
  4. The connection is generated as C++ code in the .ui file

This approach is useful for standard widget-to-widget connections without writing any code.


Practical Example — Button Click Counter

cpp
#include <QApplication>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>

int clickCount = 0;

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QWidget window;
    QVBoxLayout layout(&window);

    QPushButton button("Click Me");
    QLabel label("Clicks: 0");

    layout.addWidget(&button);
    layout.addWidget(&label);

    // Connect button click signal to a lambda slot
    QObject::connect(&button, &QPushButton::clicked, [&label]() {
        clickCount++;
        label.setText("Clicks: " + QString::number(clickCount));
    });

    window.show();
    return app.exec();
}


Summary

ConceptDescription
SignalA notification emitted when something happens (emit signalName())
SlotA function that responds to a connected signal
connect()Links a signal to a slot (compile-time safe with modern syntax)
Many-to-manyOne signal can connect to many slots; one slot can connect to many signals
Signal-to-signalSignals can be chained together
Connection typesAuto, Direct, Queued — control synchronous vs. asynchronous execution
Auto-disconnectConnections are removed automatically when objects are destroyed

Signals and Slots is the heartbeat of Qt applications. Once you understand this mechanism, the architecture of every Qt application — from simple widgets to complex multi-threaded systems — becomes clear. It is the foundation for event handling, UI updates, inter-component communication, and thread-safe cross-object messaging.