Qt 开发:深入详解 Qt 的信号与槽机制——彻底搞懂QT信号与槽

devtools/2024/12/23 1:27:41/

一、概念

Qt 的信号与槽(Signals and Slots)机制是一个用于对象间通信的核心特性。这个机制使得对象能以松散耦合的方式进行通信,从而提升了代码的模块化和可维护性。

  • 信号(Signal):对象状态的变化或事件发生时发出的通知。
  • 槽(Slot):信号发出时调用的函数,用于处理信号。
  • 连接(Connect):将信号和槽关联起来,使得信号发出时自动调用对应的槽。

二、原理机制

        信号与槽机制依赖于 Qt 的元对象系统(Meta-Object System),该系统通过 Q_OBJECT 宏和 MOC(Meta-Object Compiler)生成额外的代码来支持信号与槽功能。

三、工作流程

  1. 信号声明:在类中使用 signals 关键字声明信号。
  2. 槽声明和定义:在类中用 public slotsprivate slots 声明槽,并实现槽函数。
  3. 连接信号和槽:使用 QObject::connect() 函数将信号和槽连接起来。
  4. 发射信号:在适当时机调用信号,触发相应的槽函数。

四、实际项目示例

我们将通过一个简单的 Qt 应用程序来演示信号与槽机制。这个程序包含一个按钮和一个标签,当点击按钮时,标签显示的文字会改变。

步骤 1:创建 Qt 项目
  1. 打开 Qt Creator,选择 "File" -> "New File or Project"。
  2. 选择 "Application" -> "Qt Widgets Application"。
  3. 输入项目名称(例如 "SignalSlotExample")。
  4. 选择项目路径并点击 "Next"。
  5. 选择适合的平台和 Qt 版本,点击 "Next"。
  6. 点击 "Finish" 创建项目。
步骤 2:设计用户界面
  1. 在项目树中,打开 mainwindow.ui 文件。
  2. 拖动一个 QPushButton 和一个 QLabel 到窗口中。
  3. 设置按钮和标签的文本为 "Click Me!" 和 "Hello, Qt!"。

步骤 3:添加按钮功能
1. 使用“转到槽”功能

“转到槽”功能允许你快速为UI控件添加槽函数,并自动生成必要的代码。

步骤

  1. 打开 UI 设计器:在项目树中,双击打开 mainwindow.ui 文件。
  2. 选择控件:在 UI 设计器中,选择想要添加槽函数的控件,比如 QPushButton
  3. 右键菜单:右键点击选择的控件,并在弹出的右键菜单中选择“转到槽...”(Go to Slot...)。
  4. 选择信号:在弹出的对话框中,选择你希望处理的信号,例如 clicked(),然后点击“确定”或“添加”(Add)。

Qt Creator 将自动生成槽函数的声明和实现代码,并将你带到实现代码的位置。

执行上述步骤后,mainwindow.h 文件将自动添加新槽函数的声明,如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

对应的槽函数实现也会自动添加到 mainwindow.cpp 文件中,修改 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 修改标签的文本ui->label->setText("Button Clicked!");
}
2. 自定义“添加信号”到槽功能

虽然“自定义添加信号”功能没有像“转到槽”那样的快捷选项,但你可以手动添加信号声明,并在代码中进行连接。

手动添加信号

  1. 修改 mainwindow.h 文件:在类中添加新的信号声明。
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACEclass MainWindow : public QMainWindow
    {Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void customSignal(); // 新的信号声明private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui;
    };#endif // MAINWINDOW_H
    

  2. 发射信号:在 mainwindow.cpp 文件中,发射自定义信号。我们可以在按钮点击槽函数中发射这个信号:
    #include "mainwindow.h"
    #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
    {ui->setupUi(this);// 连接自定义信号到某个槽connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);
    }MainWindow::~MainWindow()
    {delete ui;
    }void MainWindow::on_pushButton_clicked()
    {// 修改标签的文本ui->label->setText("Button Clicked!");// 发射自定义信号emit customSignal();
    }void MainWindow::handleCustomSignal()
    {// 处理自定义信号ui->label->setText("Custom Signal Emitted!");
    }
    

    自定义添加信号到槽关键步骤

    自定义槽 handleCustomSignal,并在 mainwindow.cpp 中实现:

    1、声明信号:

       在 mainwindow.h 中声明新的信号

    signals:void customSignal(); // 新的信号声明

    2、声明槽:

     在 mainwindow.h 中声明新的槽

    private slots:void on_pushButton_clicked();void handleCustomSignal(); // 新的槽声明
    

    3、处理自定义信号(槽的实现)

    void MainWindow::handleCustomSignal()
    {// 处理自定义信号ui->label->setText("Custom Signal Emitted!");
    }
    

    4、连接自定义信号到指定槽:

    // 连接自定义信号到指定槽connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);

    5、发射信号

     // 发射自定义信号emit customSignal();
步骤 4:运行程序
  1. 点击 Qt Creator 工具栏上的 "Run" 按钮。
  2. 在弹出的窗口中,点击按钮,观察标签的文本变化。

详细解释

  • 信号声明:在 Qt 中,信号是通过在类中使用 signals 关键字声明的。在这个例子中,QPushButton 已经有了一个名为 clicked() 的信号。
  • 槽声明和定义:槽是一个普通的成员函数,可以在类中用 public slotsprivate slots 声明。在这个例子中,我们在 MainWindow 类中声明并定义了一个槽 handleButtonClick()
  • 连接信号和槽:使用 QObject::connect() 函数将信号和槽连接起来。参数包括发出信号的对象、信号名称、接收信号的对象和槽名称。
  • 发射信号:当按钮被点击时,QPushButton 内部自动发射 clicked() 信号,触发 handleButtonClick() 槽。

五、常见问题和调试

  • 未使用 Q_OBJECT 宏:信号与槽机制依赖于 Qt 的元对象系统,必须在类中加入 Q_OBJECT 宏。
  • 信号未正确连接:确保 connect() 函数的参数类型匹配。
  • 对象未正确管理:确保信号发出者和槽接收者对象的生命周期被正确管理,避免提前销毁。

        通过以上详细步骤和解释,我们实现了一个简单的 Qt 应用程序,并深入理解了信号与槽机制的工作原理和应用方式。希望这能帮助你更好地掌握 Qt 开发。

六、带参数的信号和槽

        上面的实列中信号和槽是没有带参数传递的,那么信号和槽可以带参数传递吗?那是肯定的,在 Qt 的信号与槽机制中,信号和槽可以带参数。这种功能非常强大,可以让你在发射信号时传递数据到槽函数中进行处理。接下来,我们将演示如何为信号和槽添加参数。

修改示例带参数

        假设我们要修改前面的示例,使得自定义信号 customSignal 可以传递一个字符串参数,并在槽函数中处理这个参数。

修改 mainwindow.h

首先,我们需要修改信号和槽的声明,使其带有参数:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void customSignal(const QString &message); // 带参数的信号声明private slots:void on_pushButton_clicked();void handleCustomSignal(const QString &message); // 带参数的槽声明private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

在这里信号和槽的声明都已经带了参数,如下

signals:void customSignal(const QString &message); // 带参数的信号声明private slots:void handleCustomSignal(const QString &message); // 带参数的槽声明

修改 mainwindow.cpp

接下来,我们需要在合适的位置发射信号,并在槽函数中处理传递的参数。

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 连接自定义信号到槽,注意这里的参数类型需要匹配connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 修改标签的文本ui->label->setText("Button Clicked!");// 发射自定义信号,传递参数emit customSignal("Custom Signal Emitted with Parameter!");
}void MainWindow::handleCustomSignal(const QString &message)
{// 处理自定义信号,接收参数ui->label->setText(message);
}

运行程序
  1. 点击 Qt Creator 工具栏上的 "Run" 按钮。
  2. 在弹出的窗口中,点击按钮,观察标签的文本变化。

详细解释

  • 信号声明:在类中使用 signals 关键字声明带参数的信号。例:void customSignal(const QString &message);
  • 槽声明和定义:在类中用 private slots 或 public slots 声明带参数的槽,并实现槽函数。例:void handleCustomSignal(const QString &message);
  • 连接信号和槽:使用 QObject::connect() 函数将带参数的信号和槽连接起来。注意信号和槽的参数类型必须匹配。
  • 发射信号:在适当时机调用信号,并传递参数。例:emit customSignal("Custom Signal Emitted with Parameter!");
  • 处理参数:在槽函数中接收并处理传递的参数。

        通过以上步骤和详细解释,我们实现了一个带参数的信号与槽示例,并深入理解了 Qt 信号与槽机制中带参数的用法。

七、信号(signals)的 访问权限

              上面的例子中,我们看到信号的声明

signals:void customSignal(const QString &message); // 带参数的信号声明

并没有带publicprotectedprivate关键字,那么它到底是什么权限呢?下面我们进行讲解。

        在 Qt 中,信号(signals)在类中是默认的 protected 访问权限,而不是 private。这意味着信号通常只能从类内部或其派生类中发射(emit)。然而,在实际使用中,你可以将信号声明为 publicprotectedprivate,以控制其访问权限。

默认情况

如果你不显式声明信号的访问权限,信号默认是 protected

signals:void customSignal(const QString &message); // 带参数的信号,默认是protected
显式声明访问权限

你可以显式地声明信号为 publicprotectedprivate

如 public 信号


### 示例如果我们希望信号能够从类的外部发射,可以将信号声明为 `public`:#### 修改 `mainwindow.h````cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();public signals: // 显式声明信号为publicvoid customSignal(const QString &message); // 带参数的信号声明private slots:void on_pushButton_clicked();void handleCustomSignal(const QString &message); // 带参数的槽声明private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
修改 mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 连接自定义信号到槽,注意这里的参数类型需要匹配connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 修改标签的文本ui->label->setText("Button Clicked!");// 发射自定义信号,传递参数emit customSignal("Custom Signal Emitted with Parameter!");
}void MainWindow::handleCustomSignal(const QString &message)
{// 处理自定义信号,接收参数ui->label->setText(message);
}
  • 关键说明
  • 默认情况下,signals 关键字下声明的信号是 protected
  • 你可以根据需要显式地将信号声明为 publicprotected 或 private
  • 选择合适的访问权限取决于你希望信号从哪里发射和使用。

八、信号和槽的优点

  • 松耦合:信号和槽使对象之间的通信更加松散,无需对象彼此了解。
  • 类型安全:编译时检查信号和槽的签名是否匹配,避免了运行时错误。
  • 灵活性:可以动态连接和断开信号和槽,支持多种连接模式(例如,一个信号连接多个槽,或多个信号连接一个槽)。

总结

Qt 的信号和槽机制为开发者提供了一种优雅、灵活且类型安全的方式来处理对象间的通信。通过理解和利用这一机制,可以显著提高应用程序的模块化、可维护性和可扩展性。


http://www.ppmy.cn/devtools/111845.html

相关文章

面向对象程序设计之模板进阶(C++)

在之前我出过一篇博客介绍了模版的初阶:面向对象程序设计(C)模版初阶&#xff0c;接下来我们将进行模版的进阶学习&#xff0c;介绍关于更多模版的知识 1.非类型模版参数 模板参数分类类型形参与非类型形参 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或…

memset函数的使用

目录 1.头文件 2.memset函数讲解 小心&#xff01;VS2022不可直接接触&#xff0c;否则&#xff01;没这个必要&#xff0c;方源面色淡然一把抓住&#xff01;顷刻炼化&#xff01; 1.头文件 memset函数的使用需要包括头文件 #include<string.h> 2.memset函数讲解 简述…

QT day3

代码 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QLabel> #include <QIcon> #include <QPixmap> #include<QLineEdit> #include<QCheckBox> #include <QPushButton> #include <QFrame> #in…

RFID读写器:零部件加工中的高效识别与管理利器

RFID读写器&#xff1a;零部件加工中的高效识别与管理利器 在传统零部件加工行业&#xff0c;面临着提高生产效率、保证生产计划执行、系统化管控产品质量以及有效管理库存等多方面的挑战&#xff0c;而 RFID 读写器在应对这些挑战的过程中扮演着至关重要的角色。 传统识别方式…

JavaScript 基础语法

1. 初识 JavaScript JavaScript是一种流行的、功能齐全的脚本语言&#xff0c;主要用于网页开发。HTML构建结构&#xff0c;CSS美化外观&#xff0c;JavaScript增加动态交互&#xff0c;三者结合可以创建丰富、动态和交互式的Web页面。JavaScript可以操作网页的内容和样式&am…

HarmonyOS开发实战( Beta5.0)自定义装饰器实践规范

介绍 本示例介绍通过自定义装饰器在自定义组件中自动添加inspector (布局回调)方法并进行调用。 效果图预览 不涉及 使用说明 在自定义组件上添加自定义装饰器CallbackObserver&#xff0c;并根据参数设置对应的方法名和需要绑定的组件的ID。编译工程&#xff0c;可以根据…

代码随想录训练营 Day56打卡 图论part06 108. 冗余连接 109. 冗余连接II

代码随想录训练营 Day56打卡 图论part06 一、卡码108. 冗余连接 题目描述 有一个图&#xff0c;它是一棵树&#xff0c;他是拥有 n 个节点&#xff08;节点编号1到n&#xff09;和 n - 1 条边的连通无环无向图&#xff08;其实就是一个线形图&#xff09;&#xff0c;如图&…

《深度学习》OpenCV 高阶 图像直方图、掩码图像 参数解析及案例实现

目录 一、图像直方图 1、什么是图像直方图 2、作用 1&#xff09;分析图像的亮度分布 2&#xff09;判断图像的对比度 3&#xff09;检测图像的亮度和色彩偏移 4&#xff09;图像增强和调整 5&#xff09;阈值分割 3、举例 二、直方图用法 1、函数用法 2、参数解析…