C++ Qt开发:如何使用信号与槽
在Qt中,信号与槽(Signal and Slot)是一种用于对象之间通信的机制。是Qt框架引以为傲的一项机制,它带来了许多优势,使得Qt成为一个强大且灵活的开发框架之一。信号与槽的关联通过QObject::connect函数完成。这样的机制使得对象能够以一种灵活而松散耦合的方式进行通信,使得组件之间的交互更加灵活和可维护。
信号(Signal)是一种特殊的成员函数,用于表示某个事件的发生。当特定的事件发生时,对象会发射(emit)相应的信号。例如,按钮被点击、定时器时间到达等都可以是信号。
槽(Slot)是用于处理信号的成员函数。槽函数定义了在特定信号发生时执行的操作。一个槽可以与一个或多个信号关联,当信号被发射时,与之关联的槽函数将被调用。
在早期,对象间的通信采用回调实现。回调实际上是利用函数指针来实现,当我们希望某件事发生时处理函数能够获得通知,就需要将回调函数的指针传递给处理函数,这样处理函数就会在合适的时候调用回调函数。回调有两个明显的缺点:
- 它们不是类型安全的,无法保证处理函数传递给回调函数的参数都是正确的。
- 回调函数和处理函数紧密耦合,源于处理函数必须知道哪一个函数被回调。
而信号与槽机制则可以更好的比秒上述问题的产生,以下是信号与槽机制的一些优势:
- 松散耦合(Loose Coupling): 信号与槽机制实现了松散耦合,使得对象之间的连接更加灵活。对象不需要知道彼此的具体实现,只需通过信号与槽进行通信。这降低了组件之间的依赖关系,提高了代码的可维护性。
- 事件驱动(Event-Driven): 信号与槽机制使得Qt应用程序能够轻松地处理事件。例如,按钮的点击、定时器的超时等都可以通过信号与槽来处理,使得应用程序能够响应用户交互和外部事件。
- 模块化设计: 通过信号与槽,不同模块之间可以通过事件进行通信,这样可以更容易地设计和维护模块化的代码。一个模块的改变不太可能影响到其他模块,从而提高了代码的可维护性。
- 异步通信: 信号与槽机制支持跨线程的异步通信。当信号与槽连接在不同线程的对象上时,Qt会自动进行线程间的通信,使得开发者能够更方便地处理多线程应用。
- 灵活的连接方式: Qt支持多种连接方式,包括在代码中使用
QObject::connect连接,也可以使用Qt Creator等工具在图形界面上进行可视化的信号与槽关联。这种灵活性使得开发者可以选择最适合他们需求的连接方式。 - 类型安全的连接(Qt5新增特性): 在Qt5中引入了新的connect语法,不再需要使用SIGNAL()和SLOT()宏,而是使用函数指针直接进行连接,从而在编译时进行类型检查,减少了潜在的运行时错误。
总体而言,这些优势使得Qt成为构建各种类型应用程序的理想选择。
1.1 信号与槽函数
1.1.1 Connect
信号和槽进行关联使用的是QObject类的connect()函数,QObject::connect 是用于建立信号与槽连接的Qt框架函数。它有几个不同的重载形式,但最常用的形式是:
static QMetaObject::Connection QObject::connect(
const QObject *sender,
const char *signal,
const QObject *receiver,
const char *slot,
Qt::ConnectionType type = Qt::AutoConnection
);
参数解释如下:
sender:发出信号的对象指针。signal:信号的签名,使用SIGNAL宏包装,指定了发出的信号。receiver:接收信号的对象指针。slot:槽函数的签名,使用SLOT宏包装,指定了接收到信号时要调用的函数。type:连接的类型,是一个枚举值,可以是Qt::AutoConnection、Qt::DirectConnection、Qt::QueuedConnection或Qt::BlockingQueuedConnection。
在函数定义中,第一个参数sender为发送信号的对象,第二个参数signal为要发送的信号,第三个参数receiver为接收信号的对象,第4个参数slot为接收对象在接收到信号之后所需要调用的槽函数。该函数的最后一个参数表明了关联的方式,默认值是Qt::AutoConnection方式,函数最终返回值是一个 QMetaObject::Connection 对象,可以用于断开连接时使用。
这个函数的作用是将 sender 对象的 signal 与 receiver 对象的 slot 进行连接。当 sender 发出信号时,receiver 对象的 slot 函数将被调用。
1.1.2 Disconnect
QObject::disconnect 是 Qt 框架用于断开信号与槽连接的函数。它有几个不同的重载形式,但最常用的形式是:
static bool QObject::disconnect(
const QObject *sender,
const char *signal,
const QObject *receiver,
const char *slot
);
参数解释如下:
sender:发出信号的对象指针。signal:信号的签名,使用SIGNAL宏包装,指定了发出的信号。receiver:接收信号的对象指针。slot:槽函数的签名,使用SLOT宏包装,指定了接收到信号时要调用的函数。
这个函数的作用是断开 sender 对象的 signal 与 receiver 对象的 slot 之间的连接。如果连接存在,那么它将被断开,不再触发。该函数返回值是一个 bool 类型,表示是否成功断开连接。
1.2 应用信号与槽
1.2.1 信号与槽绑定
信号与槽函数的使用非常容易理解,笔者将以最简单的案例来告诉大家该如何灵活的运用这两者,首先新建一个Qt Widgets Application项目,如下图所示第一个则是该项目的选项卡,其他参数保持默认即可;

当项目被创建好之后读者应该能构建看到如下图所示的页面提示信息,其中的untitled.pro是项目的主配置文件该配置文件一般有Qt自动维护,文件夹Headers则是项目的头文件包含路径,Sources则是代码的实现路径,最后一个Forms是用于图形化设计的UI模板。

首先双击mainwindow.ui进入到UI设计模式,接着拖拽一个PushButton按钮组件,与两个lineEdit组件到右侧的窗体画布上,并按下Ctrl+S保存该画布,刷新配置文件,如下图所示;

此时回到编辑菜单,并点击mainwindow.h头文件部分,并在头文件mainwindow.h的类MainWindow的定义中声明槽函数,代码如下,其含义是定义一个按钮点击槽:
public slots:
void on_pushButton_clicked();

接着我们就需要点击mainwindow.cpp文件,并在头文件中实现这个槽函数的具体功能,此处我们就实现设置两个lineEdit组件分别用于显示两串字符串,代码如下;
void MainWindow::on_pushButton_clicked()
{
ui->lineEdit->setText("hello lyshark");
ui->lineEdit_2->setText("www.lyshark.com");
}

最后一步则是建立映射关系,在类MainWindow的构造函数中添加如下语句,以便将信号和槽函数进行连接:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 建立关联当点击pushButton时信号clicked 发送给槽on_pushButton_clicked
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
ui->lineEdit->setText("hello lyshark");
ui->lineEdit_2->setText("www.lyshark.com");
}
此时运行程序,当读者点击按钮时,则会自动触发on_pushButton_clicked()所关联的代码,将两个lineEdit设置为不同的内容,如下图;

当然了,上述过程都是需要我们手动的去关联信号与槽,在开发中其实可以直接在PushButton组件上邮件,选中转到槽选项,此时则会弹出关于该组件所支持的所有槽函数,读者只需要选中并双击,即可自动实现槽函数的创建与管理,这对于高效率开发是至关重要的。

当然在槽函数使用结束后我们需要断开,在断开时直接使用disconnect并传入需要断开的绑定sender信号即可,如下所示;
void MainWindow::on_pushButton_2_clicked()
{
disconnect(ui->pushButton,SIGNAL(clicked()),nullptr,nullptr);
}
1.2.2 匿名函数绑定
你是否感觉使用代码创建信号与槽很麻烦呢,其实通过使用Lambda表达式我们可以与Connect完美的结合在一起使用,者能够让信号与槽的使用更加的得心应手。
Lambda表达式是一种匿名函数的表示方式,引入C++11标准,用于创建内联函数或闭包。Lambda表达式可以在需要函数对象的地方提供一种更为简洁和灵活的语法。
它的基本形式如下:
[capture](parameters) -> return_type {
// 函数体
}
capture:用于捕获外部变量的列表。可以捕获外部变量的值或引用,也可以省略不捕获任何变量。捕获列表是Lambda表达式的一部分。parameters:参数列表,类似于普通函数的参数。return_type:返回类型,指定Lambda表达式的返回类型。可以省略,由编译器自动推断。{}:Lambda表达式的函数体。
使用Lambda表达式与Qt的connect函数结合实现匿名槽函数。具体概述如下:
Lambda表达式的初始化
[=]() {
this->setWindowTitle("初始化..");
}();
这里使用Lambda表达式对 this->setWindowTitle("初始化.."); 进行了初始化,Lambda表达式中的 [=] 表示捕获外部变量并通过值传递,其中的 () 表示Lambda表达式立即执行,实现对窗口标题的初始化。
Lambda表达式作为槽函数
connect(btn_ptr1, &QPushButton::clicked, this, [=]() mutable {
number = number + 100;
std::cout << "inner: " << number << std::endl;
});
这里使用Lambda表达式作为 btn_ptr1 按钮的槽函数。在Lambda表达式中,使用了 mutable 关键字,允许修改通过值传递的变量 number。当按钮 btn_ptr1 被点击时,Lambda表达式内部修改了 number 的值,并输出修改后的值。
Lambda表达式中的返回值
int ref = []() -> int {
return 1000;
}();
std::cout << "Return = " << ref << std::endl;
这里的Lambda表达式中带有返回值的情况。Lambda表达式通过 -> int 指定返回类型,然后在大括号中返回了一个整数值。该Lambda表达式被立即执行,返回值被赋给变量 ref,并输出到控制台。
如下,我们就来演示一个简单的直接使用匿名函数实现功能的案例,当使用匿名函数时,只需要在Connect时将功能一并写到链接函数的底部即可,此时的效果等同于上述功能,因为没有函数名所以显得更加简单,如下图;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 匿名函数
connect(ui->pushButton,&QPushButton::clicked,this,[=](){
std::cout << "hello lyshark" << std::endl;
ui->lineEdit->setText("www.lyshark.com");
});
}

总体来说,匿名函数(Lambda表达式)在Qt中与connect函数一起使用,提供了一种方便的方式来定义简短的槽函数,使得代码更加紧凑和可读。
C++ Qt开发:如何使用信号与槽的更多相关文章
- Qt Quick 事件处理之信号与槽
前面两篇文章<QML 语言基础>和<Qt Quick 简单教程>中我们介绍了 QML 语言的基本的语法和 Qt Quick 的常见元素,亲们,通过这两篇文章,您应该已经能够完毕 ...
- Qt对象模型之一:信号和槽
一.信号和槽机制概述 信号槽是 Qt 框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式.当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal).这种发出是没有目 ...
- Qt Quick 事件处理之信号与槽(foruok的博客)
前面两篇文章<QML 语言基础>和<Qt Quick 简单教程>中我们介绍了 QML 语言的基本语法和 Qt Quick 的常见元素,亲们,通过这两篇文章,您应该已经可以完成简 ...
- 第七章 探秘Qt的核心机制-信号与槽
第七章 探秘Qt的核心机制-信号与槽 注:要想使用Qt的核心机制信号与槽,就必须在类的私有数据区声明Q_OBJECT宏,然后会有moc编译器负责读取这个宏进行代码转化,从而使Qt这个特有的机制得到使用 ...
- 2.QT-窗口组件(QWidget),QT坐标系统,初探消息处理(信号与槽)
本章主要内容如下: 1) 窗口组件(QWidget) 2) QT坐标系统 3) 消息处理(信号与槽) 窗口组件(QWidget) 介绍 Qt以组件对象的方式构建图形用户界面 Qt中没有父组件的顶级组件 ...
- Qt 和 Boost关于信号和槽的对比说明
对比 无论是 Qt 的实现方式还是 Boost 的实现方式,除了必须的定义信号和槽的类之外,都不需要额外的类. 两种实现都解决了类爆炸的问题.下面让我们对照着来看一下我们前面的分析. 两个不同的术语以 ...
- Qt 编程指南 3 信号和槽沟通
https://qtguide.ustclug.org/ 1 信号和槽 所谓信号槽,简单来说,就像是插销一样:一个插头和一个插座.怎么说呢?当某种事件发生之后,比如,点击了一下鼠标,或者按了某个按键, ...
- C/C++ -- Gui编程 -- Qt库的使用 -- 信号与槽的关联
Qt信号与槽的三种关联方法:1.设计界面关联,编辑信号/槽,自动关联 2.手动关联(1).头文件中定义槽 -----mywidget.h----- #ifndef MYWIDGET_H #define ...
- Qt Quick中的信号与槽
在QML中,在Qt Quick中,要想妥善地处理各种事件,肯定离不开信号与槽,本博的主要内容就是整理Qt 中的信号与槽的内容. 1. 链接QML类型的已知信号 QML中已有类型定义的信号分为两类:一类 ...
- QT(4)信号与槽
mainWidget.h #ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QPushBu ...
随机推荐
- 使用 AutoGPTQ 和 transformers 让大语言模型更轻量化
大语言模型在理解和生成人类水平的文字方面所展现出的非凡能力,正在许多领域带来应用上的革新.然而,在消费级硬件上训练和部署大语言模型的需求也变得越来越难以满足. Hugging Face 的核心使命是 ...
- 两种方式,轻松实现ChatGPT联网
两种方式效果: 方式一:浏览器搜索内嵌插件 方式二:官方聊天页内嵌插件 首先,要有一个谷歌浏览器,然后再安装一个叫ChatGPT for Google,直接在谷歌里搜一下就能找,也可以Chrome应用 ...
- OptiX8入门(一)optixHello
本人初学者,如有错误和更好的表述,请指出 环境:CLion+VS2022+CUDA Toolkit 12.0.1+OptiX8 下载好后打开SDK就可以看到OptiX官方提供的许多例子,CMake配置 ...
- Maven安装与配置教程
一.安装前检查 检查电脑上是否安装JDK,如果没有安装,请查看JDK安装教程:点我查看 如果电脑上已经安装JDK,按Win 和R键,输入cmd,然后点击确定 输入java -version,点击回车, ...
- SQL注入简介
SQL注入(SQL Injection)是一种计算机安全漏洞,它允许攻击者通过操纵应用程序的输入来执行恶意的SQL查询,从而访问.修改或删除数据库中的数据.这种攻击通常发生在应用程序未正确验证.过滤或 ...
- 噢耶!字节后端Offer,拿到了
很多同学反馈多搞点面经,说来就来! 今天分享一位拿到字节跳动实习Offer的面经,没错,Java转Go. 别问我选Java还是选Go,成年人不做选择题.先搞定一个语言,再学第二语言从来不是难事. 无论 ...
- 两种方式,创建有返回值的DB2函数
函数场景:路径信息由若干个机构编码组成,且一个机构编码是9位字符. 要求:获取路径信息,并且删除路径中包含'99'开头的机构编码. 从客户端及服务器端分别创建ignore99(pathinfo var ...
- SQL函数Intersect,except整理
1. 集合函数使用的规则 ① 每个集合要求列数量相同,列顺序相同. ② 每个集合显示的列,要求数据类型一致或者可隐式转换成同一数据类型 ③ 最终集合列名称与第一个集合的列名称一致 2. ...
- linux日常维护(二)
linux启动流程 BIOS自检 启动GRUB 2 加载内核 执行systemd进程 初始化系统环境 执行/bin/login程序 (一)BIOS自检 加电POST自检(对硬件进行检测) 进行本地设备 ...
- 栈和堆的区别、FreeRTOS 中的任务栈
栈和堆的区别.FreeRTOS 中的任务栈 01 堆和栈的概念 堆 功能 堆是一块用于动态分配内存的区域,用于存储程序运行时动态创建的对象.堆的大小可以在程序运行时动态调整. 特点 堆的分配和释放是由 ...