Qt 的信号槽机制是 Qt 框架的核心特性之一,它是一种用于对象间通信的机制。信号槽机制取代了传统的回调函数,提供了更灵活、更安全的方式来处理事件和对象之间的交互。
一、 信号槽机制的基本概念
语法:connect(sender, signal, receiver, slot);
- sender:信号发送者
- signal:信号
- receiver:信号接收者
- slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
1. 信号(Signal)
- 信号是 Qt 中对象发出的一种通知,用于表示某种事件的发生。
- 信号是一个函数声明,但没有实现(类似于接口)。
- 例如,按钮被点击时,
QPushButton会发出clicked()信号,也可以手动触发。
2. 槽(Slot)
- 槽是一个普通的成员函数,用于响应信号。
- 槽可以有参数,也可以没有参数。
- 槽可以被直接调用,也可以通过信号触发。
3. 连接(Connect)
-
使用
QObject::connect()函数将信号与槽连接起来。 -
当信号发出时,连接的槽函数会被自动调用。
-
语法:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));或者使用 Qt5 的新语法:
QObject::connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot);
一句话概括就是:谁的什么东西作用在谁的哪里,比如:你用手打他的脸就是 QObject::connect(你,手,他,脸);
二、 声明与使用
示例代码:
#include <QApplication>
#include <QPushButton>
#include <QObject>
#include <QDebug>
class Example : public QObject {
Q_OBJECT
public slots:
void handleClick() {
qDebug() << "Button clicked!";
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QPushButton button("Click Me");
Example example;
QObject::connect(&button, &QPushButton::clicked, &example, &Example::handleClick);
button.show();
return app.exec();
}
上面代码示例作用是点击按钮会触发Example 类中的handleClick方法,执行并打印 “Button clicked!”
Qt中的信号槽分为系统控件自带的和我们自定义的,一般都是使用系统控件自带的,如果我们想要看一个控件他自带的信号槽有哪些,该怎么办呢,可以通过以下几种方法查找:
1. 系统自带的信号槽
1.1 在代码中选中控件类名(如 QPushButton),按 F1 键即可打开帮助文档。


1.2 或者选中类直接点击去,找到Q_SLOTS和 Q_SIGNALS 两部分

1.3 使用 Qt Designer,将控件拖入设计器。右键控件,选择 “转到槽…” 来查看常见槽。

2. 自定义信号槽
2.1 自定义信号
自定义信号需要使用 signals 关键字声明。注意:信号只需声明,不需要实现。
头文件声明
class CustomWidget : public QWidget {
Q_OBJECT
public:
explicit CustomWidget(QWidget *parent = nullptr);
signals:
void customSignal(int value);
};
2.2 自定义槽
槽函数是普通成员函数,需要用 slots 关键字声明。
头文件
class CustomWidget : public QWidget {
Q_OBJECT
public slots:
void handleCustomSignal(int value);
};
实现文件
#include <QDebug>
void CustomWidget::handleCustomSignal(int value) {
qDebug() << "收到有值的自定义信号:" << value;
}
2.3 连接自定义信号与槽
CustomWidget *widget = new CustomWidget();
connect(widget, &CustomWidget::customSignal, widget, &CustomWidget::handleCustomSignal);
// 手动发出信号
emit widget->customSignal(42);
2.4 总结
| 特性 | 系统信号槽 | 自定义信号槽 |
|---|---|---|
| 声明 | 不需额外定义 | signals 声明 |
| 连接方式 | 直接使用 | 自定义 connect() |
| 触发方式 | 内部事件触发 | 手动 emit |
| 常见用途 | UI 交互 | 自定义模块通信 |
三、 信号槽机制的特点
1. 松耦合
- 信号和槽之间不需要知道对方的具体实现,只需要知道接口。
- 发送信号的对象和接收槽的对象可以是完全独立的。
2. 类型安全
- Qt5 的新语法在编译时会检查信号和槽的参数类型是否匹配。
- 如果类型不匹配,编译会报错。
3. 多对多支持
- 一个信号可以连接多个槽。
- 一个槽可以响应多个信号。
4. 跨线程支持
- 信号槽机制支持跨线程通信。
- 通过
Qt::QueuedConnection连接方式,信号可以在不同线程之间传递。
四、 信号槽在不同界面之间的交互
在 Qt 中,不同界面(窗口或对话框)之间的交互可以通过信号槽机制实现。下面是一个典型的使用场景:
1. 主窗口与子窗口的交互
- 主窗口打开子窗口,并通过信号槽与子窗口通信。
- 子窗口完成任务后,通过信号通知主窗口。
mainwindow.h (主窗口头文件)
创建主窗口类的头文件,定义类和槽:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QString>
#include "subwindow.h"
class QPushButton;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
private slots:
void openSubWindow();
void handleData(const QString &data);
private:
QPushButton *button;
};
#endif // MAINWINDOW_H
mainwindow.cpp (主窗口源文件)
主窗口类的实现,包含与子窗口的交互逻辑:
#include "mainwindow.h"
#include <QPushButton>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
button = new QPushButton("Open SubWindow", this);
setCentralWidget(button);
connect(button, &QPushButton::clicked, this, &MainWindow::openSubWindow);
}
void MainWindow::openSubWindow() {
SubWindow *subWindow = new SubWindow(this);
connect(subWindow, &SubWindow::dataSent, this, &MainWindow::handleData);
subWindow->exec();
}
void MainWindow::handleData(const QString &data) {
qDebug() << "Received data:" << data;
}
subwindow.h (子窗口头文件)
子窗口类的头文件,包含信号和槽的定义:
#ifndef SUBWINDOW_H
#define SUBWINDOW_H
#include <QDialog>
class QPushButton;
class SubWindow : public QDialog {
Q_OBJECT
public:
explicit SubWindow(QWidget *parent = nullptr);
signals:
void dataSent(const QString &data);
private slots:
void sendData();
private:
QPushButton *button;
};
#endif // SUBWINDOW_H
subwindow.cpp (子窗口源文件)
子窗口类的实现,包括信号的发射和按钮的动作:
#include "subwindow.h"
#include <QPushButton>
SubWindow::SubWindow(QWidget *parent) : QDialog(parent) {
button = new QPushButton("Send Data", this);
connect(button, &QPushButton::clicked, this, &SubWindow::sendData);
}
void SubWindow::sendData() {
emit dataSent("Hello from SubWindow!");
close();
}
main.cpp (主程序入口)
主程序入口,初始化Qt应用程序并显示主窗口:
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
运行结果:

4.2 多个窗口之间的通信
- 使用单例模式或全局对象来管理窗口之间的通信。
- 通过信号槽将数据传递到目标窗口。
五、 信号槽的高级用法
在 Qt 中,Lambda 表达式是一种非常强大的工具,可以简化信号槽的连接代码,尤其是在槽函数逻辑简单时。Lambda 表达式允许你直接在 connect 函数中定义槽函数的行为,而不需要单独定义一个槽函数。
1. Lambda 表达式的基本语法
Lambda 表达式是 C++11 引入的特性,其基本语法如下:
[capture](parameters) -> return_type {
// 函数体
}
capture:捕获列表,用于引入外部变量。parameters:参数列表,与普通函数的参数列表类似。return_type:返回类型(可以省略,编译器会自动推导)。函数体:Lambda 表达式的实现代码。
2. Lambda 表达式在信号槽中的使用
在 Qt 中,Lambda 表达式可以直接作为槽函数使用。例如:
QPushButton *button = new QPushButton("Click Me", this);
connect(button, &QPushButton::clicked, [=]() {
qDebug() << "Button clicked using Lambda!";
});
在这个例子中:
[=]表示以值捕获的方式引入外部变量。()表示 Lambda 表达式没有参数。qDebug() << "Button clicked using Lambda!";是 Lambda 表达式的函数体。
3. 捕获列表的使用
捕获列表用于控制 Lambda 表达式中如何访问外部变量。以下是常见的捕获方式:
(1)值捕获 ([=])
- 以值的方式捕获所有外部变量。
- 示例:
int x = 10; connect(button, &QPushButton::clicked, [=]() { qDebug() << "Value of x:" << x; // x 的值是 10 });
(2)引用捕获 ([&])
- 以引用的方式捕获所有外部变量。
- 示例:
int x = 10; connect(button, &QPushButton::clicked, [&]() { x = 20; // 修改外部变量 x qDebug() << "Value of x:" << x; // x 的值是 20 });
(3)混合捕获 ([=, &x] 或 [&, x])
- 可以混合使用值和引用捕获。
- 示例:
int x = 10; int y = 20; connect(button, &QPushButton::clicked, [=, &x]() { x = 30; // 修改 x(引用捕获) qDebug() << "x:" << x << ", y:" << y; // y 的值是 20(值捕获) });
(4)捕获特定变量 ([x] 或 [&x])
- 只捕获特定的变量。
- 示例:
int x = 10; int y = 20; connect(button, &QPushButton::clicked, [x, &y]() { y = 30; // 修改 y(引用捕获) qDebug() << "x:" << x << ", y:" << y; // x 的值是 10(值捕获) });
4. Lambda 表达式的参数
Lambda 表达式可以接受参数,参数的类型和数量需要与信号的参数匹配。例如:
QSlider *slider = new QSlider(Qt::Horizontal, this);
connect(slider, &QSlider::valueChanged, [=](int value) {
qDebug() << "Slider value:" << value;
});
在这个例子中:
valueChanged(int)信号带有一个int类型的参数。- Lambda 表达式的参数列表
(int value)与信号匹配。
5. Lambda 表达式的返回值
Lambda 表达式可以指定返回类型,但通常在与信号槽连接时不需要返回值。如果需要返回值,可以使用 -> return_type 语法。例如:
auto lambda = [](int a, int b) -> int {
return a + b;
};
int result = lambda(10, 20); // result = 30
6. Lambda 表达式的实际应用
(1)简化槽函数
如果槽函数逻辑简单,可以直接用 Lambda 表达式替代。例如:
connect(button, &QPushButton::clicked, [=]() {
qDebug() << "Button clicked!";
});
(2)访问局部变量
Lambda 表达式可以方便地访问局部变量。例如:
int count = 0;
connect(button, &QPushButton::clicked, [=]() mutable {
count++;
qDebug() << "Button clicked" << count << "times";
});
注意:
- 如果需要在 Lambda 表达式中修改值捕获的变量,需要使用
mutable关键字。
(3)跨线程通信
Lambda 表达式可以与 Qt::QueuedConnection 结合,实现跨线程通信。例如:
QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
connect(worker, &Worker::resultReady, this, [=](const QString &result) {
qDebug() << "Result:" << result;
}, Qt::QueuedConnection);
thread->start();
六、 信号槽的注意事项
- 内存管理:确保连接的对象在信号发出时仍然有效,避免悬空指针。
- 性能:频繁的信号槽调用可能会影响性能,尤其是在主线程中。
- 循环连接:避免信号槽之间的循环连接,否则会导致无限递归。
七、 总结
Qt 的信号槽机制是一种强大且灵活的对象间通信方式,适用于处理事件、界面交互、跨线程通信等场景。
1619

被折叠的 条评论
为什么被折叠?



