【QT】2.QT 信号和槽

目录

一、信号与槽概念

[二、connect 函数使用](#二、connect 函数使用)

[1. QT 类继承关系](#1. QT 类继承关系)

[2. connect 构成](#2. connect 构成)

[3. 如何查找信号与槽?](#3. 如何查找信号与槽?)

三、自定义槽函数

[1. 代码中定义](#1. 代码中定义)

[2. 图形化界面定义](#2. 图形化界面定义)

四、自定义信号

[1. 声明信号](#1. 声明信号)

[2. 发射信号](#2. 发射信号)

[3. 图形化界面创建(同上)](#3. 图形化界面创建(同上))

五、信号和槽的参数

[六、Q_OBJECT 宏](#六、Q_OBJECT 宏)

[七、QT 为什么使用信号和槽机制?](#七、QT 为什么使用信号和槽机制?)

[八、Lambda 表达式在槽函数中的应用](#八、Lambda 表达式在槽函数中的应用)


一、信号与槽概念

  1. 信号源:由哪个控件发出信号。

  2. 信号类型:用户的不同操作,如点击按钮、输入光标变化等,都能触发不同类型的信号。

  3. 信号处理方式:即为槽函数(slot),本质上是一种回调函数。

  4. QT 使用 connect 函数将一个信号(signal)和一个槽(slot)关联起来。

二、connect 函数使用

1. QT 类继承关系

  • 大致的继承关系是:QObject -> QWidget -> QPushButton, QLineEdit ......

  • QObject 是 QT 所有内置类的"老祖宗"。

2. connect 构成

  • 构成要素:由哪个控件发出信号,发出什么类型的信号,由哪个对象负责处理,以及如何处理。

  • 示例代码:

    cpp 复制代码
    QPushButton* p1 = new QPushButton(this);
    p1->setText("关闭");
    p1->move(200, 300);
    connect(p1, &QPushButton::clicked, this, &Widget::close);
  • 注意点

    • connect 的第一个参数应为 QWidget 类型(或其父类),而 QPushButton 是它的子类,因此子类对象传给父类引用是允许的(向上转型)。

    • clickclicked 的区别:click 是"点击"动作,是一个槽函数;clicked 是"检测到点击",是一个信号函数。

    • 槽函数和信号函数在 QT Creator 左侧的"对象浏览器"中图标不同。

  • connect 要求前两个参数的类型匹配,即第二个参数(信号)必须是第一个参数(发送者对象)类型或其基类中定义的信号。

  • closeQWidget 类中内置的槽函数(用于关闭窗口),而我们的 Widget 类继承了 QWidget,所以可以直接使用。

3. 如何查找信号与槽?

问题 :怎么知道 QPushButtonclicked 这个信号,以及 QWidgetclose 这个槽?

  • 查阅官方文档

    • QPushButton Class 的文档里,直接列出的信号函数可能没有 clicked

    • 这时需要去查找它的父类。在父类 QAbstractButton Class 的文档中,就能找到对应的 clicked 信号。

    • 因为 QT 有不同类型的按钮(如 QPushButtonQRadioButton 等),它们共有的信号就被抽象到共同的父类 QAbstractButton 中。

三、自定义槽函数

1. 代码中定义

  • widget.hWidget 类中声明槽函数:

    cpp 复制代码
    private slots:
        void handle();
  • widget.cpp 中实现:

    cpp 复制代码
    void Widget::handle() {
        this->setWindowTitle("按钮点击");
    }
  • 在构造函数或其它地方连接信号:

    cpp 复制代码
    QPushButton* p1 = new QPushButton(this);
    connect(p1, &QPushButton::clicked, this, &Widget::handle);

2. 图形化界面定义

  • widget.ui 的图形编辑器界面中,右键点击按钮控件,选择"转到槽..."。​​​​​​​

  • 选择对应的信号(如 clicked()),QT Creator 会自动在 widget.hwidget.cpp 中生成对应格式的槽函数。

    cpp 复制代码
    // widget.h
    private slots:
        void on_pushButton_clicked();
    // widget.cpp
    void Widget::on_pushButton_clicked() {
        this->setWindowTitle("点击按钮");
    }
  • 命名规则on_ + 控件对象名 + _ + 信号名

  • 之后,无需手动编写 connect 语句即可生效。

  • 原理 :在 ui_widget.h 文件中,QT 使用 connectSlotsByName 函数自动将符合此命名规则的槽函数与对应控件的信号连接起来。

  • 这种方法非常适用于图形化界面编辑,因为手动创建的槽函数并没有这个自动连接的特性。

四、自定义信号

  • 相比于槽函数,由于用户在图形界面上的操作是可以穷举的,因此 QT 自带的信号通常就够用了。

  • 自定义信号只需要声明即可,编译器会自动生成其实现代码。

1. 声明信号

  • widget.hWidget 类中:

    cpp 复制代码
    signals:
        void mySignal();
    • 加上 signals 关键字。

2. 发射信号

  • 使用 emit 关键字来发射信号。

    cpp 复制代码
    // 先连接信号与槽
    connect(this, &Widget::mySignal, this, &Widget::changeTitle);
    // 在某个地方发射信号
    emit mySignal();
  • 注意 :信号的处理逻辑是在连接的槽函数中,因此没有 emit 发射信号,对应的槽函数就不会被调用。

3. 图形化界面创建(同上)

  • 同样可以在 .ui 文件中右键控件"转到槽"来关联自定义信号(需要先在代码中定义好)。

五、信号和槽的参数

  • 信号函数和槽函数都可以带有参数。

  • 参数的类型必须匹配,但参数个数可以是:信号的参数个数 >= 槽的参数个数。

    • 多余的参数会被忽略。
  • 这种机制可以让信号与槽的关联更加灵活,确保每个参数都有对应值。

  • 发射信号时,信号函数传递的参数会传递给槽函数,这有助于代码复用。

示例

cpp 复制代码
// widget.h
signals:
    void mySignal(const QString& text);
private slots:
    void mySlot(const QString& text);
// widget.cpp
void Widget::mySlot(const QString& text) {
    this->setWindowTitle(text);
}
void Widget::on_pushButton_clicked() { // 按钮1
    emit mySignal("按钮1按下");
}
void Widget::on_pushButton_2_clicked() { // 按钮2
    emit mySignal("按钮2按下");
}
  • 这样,点击两个不同的按钮,都可以通过发射同一个信号并传递不同的参数,来改变窗口的标题。

六、Q_OBJECT

  • 一个类如果希望使用信号和槽机制(定义信号或槽),就必须在类声明的开头加上 Q_OBJECT 宏。

  • 这个宏会由 MOC(元对象编译器)展开,生成支持信号槽机制和元对象系统所必需的代码。

七、QT 为什么使用信号和槽机制?

  • 其他界面设计框架(如 MFC, WinForms)通常使用回调函数 ,直接将事件和处理函数绑定,不需要单独的 connect 函数。

  • QT 的信号和槽机制可以实现多对多的连接(一个信号可以连接多个槽,一个槽也可以被多个信号连接),就像做"连线题"一样灵活。

    • 但在绝大多数情况下,一对一的连接已经足够,因此这个特性有时不算是 QT 的绝对亮点。
  • 核心优势:解耦合

    • 信号发送者不知道也不关心谁接收信号;槽接收者也不知道信号来自哪里。它们只通过 connect 函数建立关联。

    • 这种松耦合的设计可以减少组件之间的依赖,一个模块出问题,对其他模块的影响较小。

八、Lambda 表达式在槽函数中的应用

  • 可以使用 Lambda 表达式作为槽函数,使代码更简洁。

    cpp 复制代码
    QPushButton* q1 = new QPushButton(this);
    connect(q1, &QPushButton::clicked, this, [q1]() {
        q1->move(200, 300);
    });
  • 这里需要使用捕获列表 [q1] 来捕获按钮指针 q1

  • 也可以使用 [=] 按值捕获所有外部变量(包括 q1)。

    • 由于 QT 中控件通常是动态创建的指针,使用 [=] 捕获是安全的。相比于 [&](按引用捕获),[=] 不用过多考虑对象树的生命周期问题,因为捕获的是指针的副本,而指针指向的对象由 QT 对象树管理。
相关推荐
Gobysec1 小时前
Goby 漏洞安全通告|GNU InetUtils Telnetd USER环境变量注入 权限绕过漏洞(CVE-2026-24061)
数据库·安全·gnu·漏洞分析·漏洞预警
麦兜*1 小时前
SpringBoot Profile多环境配置详解,一套配置应对所有场景
java·数据库·spring boot
javajingling1 小时前
redis命令
数据库·redis·缓存
枷锁—sha2 小时前
【CTF笔记篇】SQL 注入总结
数据库·笔记·sql·安全·网络安全
九章-2 小时前
呼伦贝尔融媒体数据库国产化替换成功案例:筑牢宣传阵地安全底座,金仓KES助力云雀系统高效运转
数据库·安全·媒体
APIshop2 小时前
示例代码解析:使用 item_get_video_pro 获取小红书笔记详情
数据库·笔记
Navicat中国2 小时前
Navicat Premium MacOS:原生或通过 Rosetta 运行教程
数据库·macos·oracle·navicat·rosetta
小魏每天都学习2 小时前
【数据库-范式-ER图-SQL结合】
数据库
阿坤带你走近大数据2 小时前
Oracle存储过程与触发器的详细介绍
数据库·oracle