Trong lập trình Qt, Signal và Slot là cơ chế cốt lõi để các đối tượng giao tiếp với nhau. Ngoài các tín hiệu có sẵn của các widget, lập trình viên có thể tự định nghĩa các Signal và Slot riêng để phù hợp với logic nghiệp vụ của ứng dụng.
1. Định nghĩa lớp phát tín hiệu (Signal)
Giả sử chúng ta có một lớp Academy (Học viện). Khi học viện phát một thông báo, nó sẽ kích hoạt một Signal. Trong Qt, Signal chỉ cần khai báo trong phần signals: và không cần triển khai mã nguồn (implementation) trong file .cpp.
File: academy.h
#ifndef ACADEMY_H
#define ACADEMY_H
#include <QObject>
class Academy : public QObject
{
Q_OBJECT
public:
explicit Academy(QObject *parent = nullptr);
signals:
// Signal tùy chỉnh, không cần định nghĩa trong file .cpp
void broadcastNotice();
};
#endif // ACADEMY_H
File: academy.cpp
#include "academy.h"
Academy::Academy(QObject *parent) : QObject(parent)
{
}
2. Định nghĩa lớp nhận tín hiệu (Slot)
Lớp Trainee (Học viên) sẽ sở hữu các Slot để xử lý khi nhận được tín hiệu từ Academy. Slot thực chất là các hàm thành viên bình thường nhưng được đặt dưới từ khóa public slots: hoặc đơn giản là public trong các phiên bản Qt mới.
File: trainee.h
#ifndef TRAINEE_H
#define TRAINEE_H
#include <QObject>
class Trainee : public QObject
{
Q_OBJECT
public:
explicit Trainee(QObject *parent = nullptr);
public slots:
// Slot xử lý khi nhận tín hiệu
void handleNotice();
};
#endif // TRAINEE_H
File: trainee.cpp
#include "trainee.h"
#include <QDebug>
Trainee::Trainee(QObject *parent) : QObject(parent)
{
}
void Trainee::handleNotice()
{
qDebug() << "Hoc vien da nhan duoc thong bao va dang chuan bi len lop.";
}
3. Kết nối và kích hoạt Signal
Để hệ thống hoạt động, chúng ta cần kết nối Signal của đối tượng phát với Slot của đối tượng nhận thông qua hàm connect(). Sau đó, sử dụng từ khóa emit để phát tín hiệu.
File: mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "academy.h"
#include "trainee.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Academy *myAcademy;
Trainee *myTrainee;
};
#endif // MAINWINDOW_H
File: mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Khỏi tạo đối tượng
myAcademy = new Academy(this);
myTrainee = new Trainee(this);
// Kết nối Signal và Slot theo cú pháp hiện đại (Function Pointer)
connect(myAcademy, &Academy::broadcastNotice, myTrainee, &Trainee::handleNotice);
// Kích hoạt tín hiệu
emit myAcademy->broadcastNotice();
}
MainWindow::~MainWindow()
{
delete ui;
}
4. Những lưu ý quan trọng
- Mọi lớp sử dụng Signal/Slot phải kế thừa từ
QObjectvà chứa macroQ_OBJECTở đầu khai báo lớp. - Signal không có thân hàm (body) và luôn trả về kiểu
void. - Từ khóa
emitgiúp mã nguồn rõ ràng hơn về mục đích phát tín hiệu, dù về mặt kỹ thuật nó là một macro rỗng. - Cú pháp
connectdựa trên con trỏ hàm (như ví dụ trên) được khuyến khích sử dụng thay cho macroSIGNAL()vàSLOT()cũ để kiểm tra lỗi ngay tại thời điểm biên dịch.