「C++ Qt6 开发」信号与槽

1 信号与槽简介

1.1 信号与槽

信号与槽是 Qt 的核心机制。当某个事件发生时(如按钮点击),对象会发出一个信号;接收方通过槽函数处理该信号。

信号(signal)是在特定情况下被发射的通知,例如 QPushButton 比较常见的信号就是点击鼠标时的 clicked() 信号。

(slot)是对信号进行响应的函数。槽就是函数,所以也称为槽函数。槽函数与一般的函数不同的是,槽函数可以与信号相关联,当 信号被发射时,关联的槽函数被自动执行。

1.2 QObject::connect() 函数

信号与槽 的关联是用函数 QObject::connect() 实现的。connect() 函数的基本格式主要有两种:

  • Qt 4 传统风格(字符串匹配,不推荐)

    1
    connect(sender, SIGNAL(signalName(args)), receiver, SLOT(slotName(args)));

    SIGNAL()SLOT() 将函数签名转为字符串,例如 SIGNAL(clicked(bool))

    如果信号和槽函数带有参数,需要参数列表完全匹配。

  • Qt 5/6 推荐风格(类型安全连接)

    1
    2
    3
    connect(sender, &SenderClass::signalName, 
    receiver, &ReceiverClass::slotName,
    connectionType = Qt::AutoConnection);
    • sender:发出信号的对象指针(如 button)

    • &SenderClass::signalName:指向信号成员函数的指针(如 &QPushButton::clicked)

    • receiver:接收信号的对象指针(如 this)

    • &ReceiverClass::slotName:指向槽成员函数的指针(如 &MainWindow::handleClick)

    • connectionType(可选):连接类型(默认为 Qt::AutoConnection)

    Qt 5/6 推荐风格(类型安全连接)能够在编译时进行类型检查,而 Qt4 字符串匹配方式只会在运行时报错,连接失败不会编译报错。 Qt 5/6 推荐风格能够支持重载信号的选择,也避免字符串匹配错误。

一个信号可以连接多个槽函数多个信号可以连接同一个槽函数;一个信号也可以连接到另一个信号上。

1.3 QObject::connect() 连接类型

类型 描述
Qt::AutoConnection (默认) 自动选择直接连接(同线程)或队列连接(跨线程)
Qt::DirectConnection 信号发出后立即调用槽(在发送者线程执行)
Qt::QueuedConnection 槽在接收者线程的事件循环中异步执行(跨线程安全)
Qt::BlockingQueuedConnection 同步执行槽,发送者线程阻塞直到槽完成(跨线程,需避免死锁)
Qt::UniqueConnection 确保同一信号槽不会重复连接(可与其他类型组合使用,如 `Qt::QueuedConnection

  1. Qt::AutoConnection (默认)

    自动检测发送者和接收者是否在同一线程

    • 如果同线程 → 使用 DirectConnection(立即执行
    • 如果跨线程 → 使用 QueuedConnection(异步执行
    1
    2
    3
    connect(sender, &Sender::signal, receiver, &Receiver::slot);
    // 等同于
    connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::AutoConnection);

    当对象在不同线程间移动时,连接行为可能意外改变。

  2. Qt::DirectConnection

    信号发出后 立即在发送者线程中调用槽函数

    1
    2
    3
    connect(button, &QPushButton::clicked, 
    this, &MainWindow::handleClick,
    Qt::DirectConnection);

    适用于 发送者和接收者一定在同一线程 的情形。如果发送者和接收者不在同一线程,会导致程序崩溃(数据竞争、死锁、随机崩溃等)。

    若多个线程同时发射同一信号,槽函数会被多个线程同时调用。如果槽函数访问或修改共享数据(如全局变量、类成员变量),且没有同步机制(如互斥锁),就会发生数据竞争,最终可能出现错误的结果。

    当信号发射线程和槽函数执行线程持有不同的锁,线程 A 持有锁 L1 , 发射信号并等待槽函数返回;而 线程 B 持有锁 L2 ,正在执行槽函数,但槽函数需要锁 L1 才能继续执行,此时线程 A 等待 B 执行完槽函数释放 L2,而 B 等待 A 释放 L1,双方永久阻塞,形成了死锁。

  3. Qt::QueuedConnection

    信号发出后,槽函数不会立即执行。将调用请求 放入接收者线程的事件队列,接收者线程处理事件时 异步执行槽函数

    这样保证线程安全,槽函数在接收者线程上下文执行,避免了数据竞争。

  4. Qt::BlockingQueuedConnection

    槽函数在接收者线程执行,与 Qt::QueuedConnection 的区别在于 发送者线程会阻塞直到槽函数执行完成需要严格避免死锁

    适用于 需要同步结果的跨线程调用 的情形,例如 从工作线程获取实时计算结果。

  5. Qt::UniqueConnection

    不是独立连接类型,需与其他类型组合使用。确保同一信号槽对不会重复连接。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 防止多次连接同一信号槽
    connect(button, &QPushButton::clicked,
    this, &MainWindow::processClick,
    Qt::UniqueConnection); // 自动包含 Qt::AutoConnection

    // 显式组合其他类型
    connect(worker, &Worker::finished,
    this, &MainWindow::cleanup,
    Qt::QueuedConnection | Qt::UniqueConnection);

2 信号与槽的使用

2.1 信号与槽编辑器的使用(Qt Designer)

Qt 界面组件都是从 QWidget 类继承而来的,都支持信号与槽的功能。每个类都有一些内建的信号与槽函数。例如,QPushButton 类的常用信号是 clicked(),当按钮被点击时信号被发射。此处以一个 QDialog 项目为例,在 QDialog 中有以下几个公有的槽函数:

  • accept(): 会关闭对话框,表示肯定的选择。
  • reject(): 会关闭对话框,表示否定的选择。
  • close(): 关闭对话框。

可以在 Qt Designer 中的 Signal and Slot 编辑器中设置组件的内建信号与其他组件的公有槽函数的关联:


2.2 Go to slot 生成槽函数

在 Qt Designer 中,可以选择要设置信号与槽函数关联的组件,右键找到 Go to slot 来生成槽函数原型。例如,选择一个复选框 chkBoxBold,点击 Go to slot,会出现如下对话框:

这个对话框显示了当前组件 所有可用的信号

复选框 chkBoxBold 被勾选时会发出 clicked() 和 clicked(bool) 信号,其中带有 bool 参数的 clicked(bool) 以复选框的勾选状态作为参数,因此此处选择 clicked(bool) 信号。

之后 Dialog 类的 private slot 中会 自动生成槽函数的声明,函数名是根据发射信号的对象名和信号名自动命名的:

同时,在 dialog.cpp 中会自动生成槽函数的框架。我们可以在槽函数框架下添加代码,实现对文本框文字字体的控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Go to slot 构建链接,会自动生成该函数的定义
void Dialog::on_chkBoxUnder_clicked(bool checked)
{
QFont font = ui -> plainTextEdit -> font();
font.setUnderline(checked);
ui -> plainTextEdit -> setFont(font);
}


void Dialog::on_chkBoxItalic_clicked(bool checked)
{
QFont font = ui -> plainTextEdit -> font();
font.setItalic(checked);
ui -> plainTextEdit -> setFont(font);
}


void Dialog::on_chkBoxBold_clicked(bool checked)
{
QFont font = ui -> plainTextEdit -> font();
font.setBold(checked);
ui -> plainTextEdit -> setFont(font);
}

void Dialog::on_btnClear_clicked()
{
ui -> plainTextEdit -> clear();
}

在编译生成的 ui_dialog.h 文件中,有自动生成的如下连接语句:

1
2
3
4
QObject::connect(btnOK, &QPushButton::clicked, Dialog, qOverload<>(&QDialog::accept));
QObject::connect(btnExit, &QPushButton::clicked, Dialog, qOverload<>(&QDialog::close));

QMetaObject::connectSlotsByName(Dialog);

其中前两句是通过 Signal and Slot 编辑器生成的构建关联的代码。

QMetaObject::connectSlotsByName(Dialog); 的功能是搜索 Dialog 界面中的所有组件,将名称匹配的信号与槽关联起来。例如有槽函数的名称为 on_chkBoxBold_clicked(bool),这正好是 chkBoxBold 的 clicked(bool) 信号的槽函数,connectSlotsByName 会将他们关联起来。相当于执行如下语句(字符串匹配):

1
connect(chkBoxBold, SIGNAL(clicked(bool)), this, SLOT(on_chkBoxBold_clicked(bool)));

2.3 使用自定义槽函数

  1. 在 dialog.h 类头文件中声明槽函数(需在 public slots 或 private slots 区域内)。
  2. 在 dialog.cpp 源文件中实现槽函数逻辑。
  3. 在 Dialog 类构造函数中使用 connect() 绑定信号与槽。

例如本示例中的字体颜色函数。在 private slots: 中声明颜色槽函数 void do_setFontColor();

在 dialog.cpp 源文件中实现 do_setFontColor() 的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 自定义实现字体颜色函数,需手动构建链接
void Dialog::do_setFontColor()
{
QPalette plet = ui -> plainTextEdit -> palette();
if(ui -> radioButtonBlue -> isChecked())
plet.setColor(QPalette::Text, Qt::blue);
else if(ui -> radioButtonBlack -> isChecked())
plet.setColor(QPalette::Text, Qt::black);
else if(ui -> radioButtonRed -> isChecked())
plet.setColor(QPalette::Text, Qt::red);
else
plet.setColor(QPalette::Text, Qt::black);
ui -> plainTextEdit -> setPalette(plet);
}

在 Dialog 类构造函数中构建信号与槽函数的关联:

1
2
3
4
5
6
7
8
9
// 使用字符串匹配语法连接信号与槽
connect(ui->radioButtonBlack, SIGNAL(clicked()), this, SLOT(do_setFontColor())); // 手动构建链接
connect(ui->radioButtonBlue, SIGNAL(clicked()), this, SLOT(do_setFontColor()));
connect(ui->radioButtonRed, SIGNAL(clicked()), this, SLOT(do_setFontColor()));

// 或者 使用函数指针语法连接信号和槽
connect(ui->radioButtonBlack, &QRadioButton::clicked, this, &Dialog::do_setFontColor);
connect(ui->radioButtonBlue, &QRadioButton::clicked, this, &Dialog::do_setFontColor);
connect(ui->radioButtonRed, &QRadioButton::clicked, this, &Dialog::do_setFontColor);

完成后的效果如下图所示:


参考

《Qt 6 C++ 开发指南》


「C++ Qt6 开发」信号与槽
https://marisamagic.github.io/2025/07/17/20250717/
作者
MarisaMagic
发布于
2025年7月17日
许可协议