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_OBJECTmacro must appear in the private section of the class declaration. - Slots must be declared under a
slotsaccess 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_OBJECTmacro, - Incorrect inheritance from
QObject, - Typos in slot names,
- Mismatched signal/slot signatures,
- Failure to re-run
qmakeafter 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.