Understanding Qt's Signal and Slot Mechanism

Qt abstracts the underlying operating system’s event handling into a consistent, cross-platform mesaging model based on signals and slots. This mechanism enables communication between objects without requiring them to be tightly coupled.

A signal is emitted when a particular event occurs—such as a button click—while a slot is a function that responds to such a signal. The connection between them is established using QObject::connect(), which links a signal from one object to a slot in another (or the same) object.

Key requirements for using signals and slots include:

  • The class must inherit (directly or indirectly) from QObject.
  • The Q_OBJECT macro must appear in the private section of the class declaration.
  • Slots must be declared under a slots access specifier.
  • The signal and slot signatures must match exactly in terms of parameter types; paramter names are not part of the signature.

The following example creates a push button that quits the application when clicked:

#include <QApplication>
#include <QPushButton>

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

    QPushButton button;
    button.setText("Click to quit");
    button.show();

    QObject::connect(&button, SIGNAL(clicked()), &app, SLOT(quit()));

    return app.exec();
}

To define custom slots, declare them in a QObject-derived class with the Q_OBJECT macro and the slots keyword. For instance:

// QCalculatorUI.h
#ifndef QCALCULATORUI_H
#define QCALCULATORUI_H

#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QDebug>

class QCalculatorUI : public QWidget
{
    Q_OBJECT

    QLineEdit* m_edit;
    QPushButton* m_buttons[20];

    QCalculatorUI();
    bool construct();

private slots:
    void onButtonClicked();

public:
    static QCalculatorUI* NewInstance();
    void show();
    ~QCalculatorUI();
};

#endif // QCALCULATORUI_H
// QCalculatorUI.cpp
#include "QCalculatorUI.h"

QCalculatorUI::QCalculatorUI() : QWidget(nullptr, Qt::WindowCloseButtonHint)
{
}

bool QCalculatorUI::construct()
{
    bool success = true;

    m_edit = new QLineEdit(this);  // Fixed: assign to member, not local variable
    if (m_edit)
    {
        m_edit->resize(240, 30);
        m_edit->move(10, 10);
        m_edit->setReadOnly(true);
    }
    else
    {
        return false;
    }

    const char* labels[20] = {
        "7", "8", "9", "+", "(",
        "4", "5", "6", "-", ")",
        "1", "2", "3", "*", "<-",
        "0", ".", "=", "/", "C"
    };

    for (int i = 0; i < 4 && success; ++i)
    {
        for (int j = 0; j < 5 && success; ++j)
        {
            int index = i * 5 + j;
            m_buttons[index] = new QPushButton(this);
            if (m_buttons[index])
            {
                m_buttons[index]->resize(40, 40);
                m_buttons[index]->move(10 + (10 + 40) * j, 50 + (10 + 40) * i);
                m_buttons[index]->setText(labels[index]);
                connect(m_buttons[index], SIGNAL(clicked()), this, SLOT(onButtonClicked()));
            }
            else
            {
                success = false;
            }
        }
    }

    return success;
}

QCalculatorUI* QCalculatorUI::NewInstance()
{
    QCalculatorUI* instance = new QCalculatorUI();
    if (!(instance && instance->construct()))
    {
        delete instance;
        instance = nullptr;
    }
    return instance;
}

void QCalculatorUI::show()
{
    QWidget::show();
    setFixedSize(size());
}

void QCalculatorUI::onButtonClicked()
{
    QPushButton* source = qobject_cast<QPushButton*>(sender());
    if (source)
    {
        qDebug() << "Button pressed:" << source->text();
    }
}

QCalculatorUI::~QCalculatorUI() = default;
// main.cpp
#include <QApplication>
#include "QCalculatorUI.h"

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

    QCalculatorUI* ui = QCalculatorUI::NewInstance();
    if (ui)
    {
        ui->show();
        int result = app.exec();
        delete ui;
        return result;
    }

    return -1;
}

A common error—"No such slot"—typically arises from:

  • Missing Q_OBJECT macro,
  • Incorrect inheritance from QObject,
  • Typos in slot names,
  • Mismatched signal/slot signatures,
  • Failure to re-run qmake after modifying header files.

In summary, the signal-slot mechanism is central to Qt’s event-driven architecture. It requires adherence to specific syntactic and structural rules but provides a powerful, type-safe way for objects to communicate.

Tags: Qt signals and slots Event Handling GUI Programming C++

Posted on Fri, 12 Jun 2026 18:19:10 +0000 by acac