[QT入门篇]信号槽机制

2023-11-14

一、信号与槽的引入

信号与槽(Signal & Slot)是 Qt 编程的基础。信号槽,实际是观察者模式 (发布 - 订阅模式)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。当信号发出时,被连接的槽函数会自动被回调。类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时候Qt 对应的窗口类会发出某个信号,以此对用户的挑选做出反应。

信号的本质就是事件:

1.按钮单击、双击

2.窗口刷新

3.鼠标移动、鼠标按下、鼠标释放

4.键盘输入....

对某一个窗口进行操作,该窗口就可以捕捉到这些被触发的事件。对于使用者来说触发了一个事件我们就可以得到 Qt 框架给我们发出的某个特定信号。信号的呈现形式就是函数, 也就是说某个事件产生, Qt 框架就会调用某个对应的信号函数, 通知使用者。在QT中信号的发出者是某个实例化的类对象,对象内部可以进行相关事件的检测。

槽(Slot)是对信号响应的函数。槽是一个函数,可以定义在类的任何部分(public、 private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行

信号与槽两者本身是相对独立的个体,没有任何联系,在 Qt 中需要使用 QOjbect类中的 connect 函数进二者的关联。这是手动关联的,也可以通过ui设计界面自动关联。

QMetaObject::Connection QObject::connect(
        const QObject *sender, PointerToMemberFunction signal, 
        const QObject *receiver, PointerToMemberFunction method, 
        Qt::ConnectionType type = Qt::AutoConnection);
参数:
  - sender:   发出信号的对象
  - signal:   属于sender对象, 信号是一个函数, 这个参数的类型是函数
              指针, 信号函数地址
  - receiver: 信号接收者
  - method:   属于receiver对象, 当检测到sender发出了signal信号, 
              receiver对象调用method方法,信号发出之后的处理动作
 
//参数 signal 和 method 都是函数地址
connect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::method);

//另外一种写法

connect(A,SIGNAL(B),C,SLOT(D));

当对象A发出B信号时候,就会触发对象C的槽函数D

使用connect()进行信号槽连接的注意事项:

1.connect函数相对于做了信号处理动作的注册
2.调用conenct函数的sender对象的信号并没有产生, 因此receiver对象的 槽函数 也不会被调用
3.槽函数本质是一个回调函数, 调用是在信号产生之后, 调用是Qt框架来执行的
4.connect中的sender和recever两个指针必须被实例化, 否则conenct不会成功

connect()操作一般写在窗口的构造函数中, 相当于在事件产生之前在qt框架中先进行注册, 这样在程序运行过程中假设产生了按钮的点击事件, 框架就会调用信号接收者对象对应的槽函数了, 如果信号不产生, 槽函数也就一直不会被调用。

二、信号/槽使用

在 Qt 提供的标准类中可以对用户触发的某些特定事件进行检测,当用户做了这些操作之后,事件被触发类的内部就会产生对应的信号,这些信号都是 Qt 类内部自带的,因此称之为标准信号。

在 Qt 的很多类内部提供了很多功能函数,并且这些函数也可以作为触发的信号的处理动作,该函数在 Qt 中称之为标准槽函数。

两者之间通过connect()函数连接。

//功能:点击按钮窗口关闭

// 单击按钮发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close();

//连接
// 单击按钮关闭窗口
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);


如果想要在QT类中自定义信号槽, 需要满足以下条件:

1.新的子类必须从QObject类或者是QObject子类进行派生
2.在定义类的头文件中加入 Q_OBJECT 宏

在 Qt 中信号的本质是事件,但是在框架中也是以函数的形式存在的,该函数只有声明,没有定义。当自定义信号对应的事件产生之后,将这个信号发射出去即可,其实就是调用一下这个信号函数。

自定义信号的要求:

1.信号是类的成员函数
2.返回值必须是 void 类型
3.参数可以随意指定, 信号也支持重载
4.信号需要使用 signals 关键字进行声明
5.信号函数只需要声明, 不需要实现
6.在程序中发射自定义信号: 发送信号的本质就是调用信号函数,在信号函数前加关键字: emit, 但是可以省略不写,(emit == #define emit)

// Qt中的类想要使用信号槽机制必须要从QObject类派生,直接或间接派生都可以
class Test : public QObject
{
    Q_OBJECT
signals:
    void testsignal();
    void testsignal(int a);
};

在信号中的参数的作用是数据传递, 谁调用信号函数谁就指定实参,实参最终会被传递给槽函数。在调用的时候传递参数进去,进行信号的发送。

槽函数是信号的处理动作,在 Qt 中槽函数可以作为普通的成员函数来使用。

自定义槽的要求:

1.返回值必须是 void 类型

2.槽也支持重载

3.槽函数需要指定多少个参数, 需要看连接的信号的参数个数,槽函数的参数是用来接收信号传递的数据的, 信号传递的数据就是信号的参数.

4.槽函数的参数应该和对应的信号的参数个数,从左到右类型依次对应,信号的参数可以大于等于槽函数的参数个数 

5.Qt 中的槽函数可以是类的成员函数、全局函数、静态函数、Lambda表达式(匿名函数)

6.槽函数可以使用关键字进行声明: slots

三、信号槽使用拓展

一个信号可以连接多个槽函数,发送一个信号有多个处理动作,但需要写多个 connect()连接


在qt5中,槽函数的执行顺序和 connect 函数的调用顺序有关


信号的接收者可以是一个对象,也可以是多个对象


一个槽函数可以连接多个信号,多个不同的信号,处理动作是相同的,需要写多个 connect()连接

信号可以连接信号

信号接收者可以不处理接收的信号,继续发射新的信号,这相当于传递了数据,并没有对数据进行处理。

信号槽可以使用disconnect函数断开。

(qt4)信号槽连接方式:信号槽函数通过宏 SIGNAL 和 SLOT 转换为字符串类型。因为信号槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测, 如果使用者传错了数据,编译器也不会报错,实际上信号槽的连接已经不对了,只有在程序运行起来之后才能发现问题,而且问题不容易被定位。

而用connect方式连接,信号和槽函数第2,4个参数传递的是地址, 编译器在编译过程中会对数据的正确性进行检测。

// Qt4的信号槽连接方式
[static] QMetaObject::Connection QObject::connect(
    const QObject *sender, const char *signal, 
    const QObject *receiver, const char *method, 
    Qt::ConnectionType type = Qt::AutoConnection);

connect(const QObject *sender,SIGNAL(信号函数名(参数1, 参数2, ...)),
        const QObject *receiver,SLOT(槽函数名(参数1, 参数2, ...)));

但是,如果对于重装函数的槽函数来说在使用 Qt5进行槽连接时,由于传递的是函数的地址,信号和槽都是通过函数名去关联函数的地址, 但是这个同名函数对应两块不同的地址, 一个带参, 一个不带参, 因此编译器就不知道去关联哪块地址, 所以如果在这种时候通过以上方式进行信号槽连接, 编译器就会报错

解决办法:可以通过定义函数指针的方式指定出函数的具体参数,这样就可以确定函数的具体地址了。定义函数指针指向重载的某个信号或者槽函数,在 connect()函数中将函数指针名字作为实参就可以了。

// 函数指针
void (Me::*func1)(QString) = &Me::eat;    // func1指向带参的信号
void (Me::*func2)() = &Me::hungury;    // func2指向不带参的槽函数


// 定义函数指针指向重载的某一个具体的信号地址
void (Me::*mysignal)(QString) = &Me::eat;
// 定义函数指针指向重载的某一个具体的槽函数地址
void (Me::*myslot)(QString) = &Me::hungury;
// 使用定义的函数指针完成信号槽的连接
connect(&m, mysignal, &m, myslot);

小结:

1.Qt4的信号槽连接方式使用了宏函数, 宏函数对用户传递的信号槽不会做错误检测, 容易出bug
2.Qt5的信号槽连接方式, 传递的是信号槽函数的地址, 编译器会做错误检测, 减少了bug的产生
3.当信号槽函数被重载之后, Qt4的信号槽连接方式不受影响
4.当信号槽函数被重载之后, Qt5中需要给被重载的信号或者槽定义函数指针

四、Lambda表达式

Lambda 表达式是一个匿名函数, 语法格式如下:

[capture](params) opt -> ret {body;};
         - capture: 捕获列表
         - params: 参数列表
         - opt: 函数选项
         - ret: 返回值类型
         - body: 函数体

1.捕获列表:捕获一定范围内的变量

[ ] - 不捕捉任何变量
[&] - 捕获外部作用域中所有变量,并作为引用在函数体内使用 (按引用捕获)
[=] - 捕获外部作用域中所有变量,并作为副本在函数体内使用 (按值捕获),拷贝的副本在匿名函数体内部是只读的,若需要操作变量,可在opt中传递mutable
[=, &foo] - 按值捕获外部作用域中所有变量,并按照引用捕获外部变量 foo
[bar] - 按值捕获 bar 变量,同时不捕获其他变量
[&bar] - 按引用捕获 bar 变量,同时不捕获其他变量
[this] - 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限

2.参数列表:和普通函数的参数列表一样

3.opt 选项 –> 可以省略
mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();

4.返回值类型:
标识函数返回值的类型,这部分可以省略

5.函数体:
函数的实现

因为 Lambda 表达式是一个匿名函数,因此是没有函数声明的,直接在程序中进行代码的定义即可,但是如果只定义匿名函数在程序执行过程中是不会被调用的。

// 匿名函数的定义, 程序执行这个匿名函数是不会被调用的
[](){
    qDebug() << "我是一个lambda表达式...";
};

// 匿名函数的定义+调用(在花括号后跟一个小括号()):
int ret = [](int a) -> int
{
    return a+1;
}(100);  // 100是传递给匿名函数的参数
  //匿名函数发送信号
     connect(ui->hurry,&QPushButton::clicked,this,[=](){
         emit mygirl->hungry(); // 发送信号
         emit mygirl->hungry("泡面"); // 发送信号
     });

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[QT入门篇]信号槽机制 的相关文章

  • 调整 QML 图像显示尺寸

    我有一个带有嵌套的 QML 窗口RowLayout 在内排我有两个图像 来源 png这些图像的文件 故意 相当大 当我尝试设置height这些图像上的属性使它们变小 但它们仍然被画得很大 Desired Appearance Actual
  • QLineEdit:显示处理后的文本,而不是输入的文本,但保留它(自定义回显模式)

    我想要一个QLineEdit不显示输入的文本 而是显示经过处理的版本 同时保留原始文本并在通过请求时返回它text 就像密码回显模式一样 但我不希望每个字符都被屏蔽 我想虚拟化空间 例如什么时候some text with spaces i
  • QT 中只获取文件而不获取目录?

    当我这样做时 QDir myDir home some location QStringList filesList myDir entryList 它返回该位置内的文件和目录 但我只想要文件 并且这些文件可以具有任意扩展名 有任何想法吗
  • 在 QtCreator 中将 OpenCV 2.3 与 Qt 结合使用

    随着 OpenCV 2 3 版本终于发布 我想在我的系统上编译并安装这个最新版本 由于我经常使用 Qt 和 QtCreator 我当然希望能够在我的 Qt 项目中使用它 我已经尝试了几种方法几个小时 但总是出现错误 第一次尝试 使用WITH
  • QTableView 中的虚拟列?

    我开始学习 Qt4 模型 视图编程 我有初学者问题 我有一个简单的应用程序 其中显示 sqlite 表QTableView class Model QtSql QSqlTableModel def init self parent None
  • 仅将非模态 QDialog 窗口放置在我的应用程序顶部,而不是所有应用程序顶部

    我有一个 QDialog 窗口 它应该始终位于我的应用程序顶部 它不是模态的 用户可以随时与对话框和主应用程序进行交互 使用窗口保持在顶部提示在某种程度上实现了这一点 但是 该对话框仍然位于所有其他正在运行的应用程序 例如记事本 chrom
  • QGraphicsView 和 eventFilter

    这个问题已经困扰我两天多了 所以我想我应该问一下 我在Win7上使用Qt 4 5 3 用VC2008编译 我有 MyGraphicsView 继承 QGraphicsView 和 MyFilter 继承 QObject 类 当我将 MyFi
  • Qt/c++ 随机字符串生成[重复]

    这个问题在这里已经有答案了 我正在创建一个应用程序 需要生成多个随机字符串 几乎就像一个由一定长度的 ASCII 字符组成的唯一 ID 这些字符混合有大写 小写 数字字符 有没有 Qt 库可以实现这一点 如果没有 在纯 C 中生成多个随机字
  • QSpinBox 输入 NaN 作为有效值

    我正在尝试扩展 QSpinBox 以能够输入 NaN 或 nan 作为有效值 根据文档 我应该使用 textFromValue valueFromText 和 validate 函数来完成此操作 但我无法让它工作 因为它仍然不允许我输入除数
  • Qt mouseReleaseEvent() 未触发?

    我有一个显示图片的库 我们称之为 PictureGLWidget 其中 class PictureGLWidget public QGLWidget 所以 PictureGLWidget 扩展了 QGLWidget 在PictureGlWi
  • 如何在带有预编译头的项目中使用google protobuf

    我有一个包含多个项目的解决方案 我的项目 但不是全部 使用预编译头 我决定使用 protobuf 但遇到了一个问题 在 protoc exe 从 proto 生成 pb h 后 我尝试包含标头并收到错误 预编译标头未包含在 pb h 中 我
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • 使用 Visual Studio 2013 构建 Qt 5.2.1 的静态版本

    几天来我一直在尝试使用 Visual Studio 2013 构建 Qt 的静态版本 我就是不明白我做错了什么 System Windows 7 64 位 Visual Studio 2013 仍安装 Visual Studio 2012
  • 如何获取 QTableView 的标题列表?

    我有一个QTableView我的对话框中的对象 我需要访问该表的水平标题并将它们放入QStringList object 尽管进行了大量搜索 但我在 Qt 文档中找不到如何获取此标头列表 编辑 我发现的最接近的地方是this https w
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959
  • 如何使QTableView类的restoreState()和saveState()正常工作?

    首先 我想说 我的问题已经在这里讨论过 并且这里是 https stackoverflow com questions 1163030 qt qtableview and horizontalheader restorestate 但答案并
  • Qt:测量事件处理时间

    我想测量我的应用程序中的哪些事件在主线程中需要很长时间才能执行 阻塞 GUI 或者至少是否有任何事件花费的时间超过 比如说 10 毫秒 显然 我对需要很长时间的任务使用线程和并发 但有时很难在其他线程中放入的内容和可以保留在 GUI 中的内
  • Qt:在多个布局中使用一个小部件

    我有一个 QTabBar 所有选项卡都应该有相同的小部件 layout1 gt addWidget w layout2 gt addWidget w 然而打电话addWidget第二次导致该小部件在第一个布局中消失 有没有办法使用一个小部件
  • Qt 5.6 测试版 Visual Studio 2015

    我已经安装了这个 http download qt io development releases qt 5 6 5 6 0 beta qt opensource windows x86 msvc2015 5 6 0 beta exe mi
  • QSerialPort 中的 readAll() 不包括最后发送的响应

    我正在使用 Qt 来控制串行设备 如果我向串行设备发送命令 我会执行类似的操作serial gt write command r n 我制作了一个按钮 它将纯文本小部件内的文本更改为串行端口的响应 为了获得串口的响应 我使用serial g

随机推荐

  • springmvc源码学习(二十八)解决跨域的几种方式

    目录 前言 一 跨域 二 解决 1 CorsFilter 2 实现 WebMvcConfigurer 3 实现 HandlerInterceptor 前言 一 跨域 是由于浏览器的同源策略限制 同源策略是一个重要的安全策略 会阻止一个域的j
  • 解决Windows update medic service服务禁用不了拒绝访问

    解决Windows update medic service服务禁用不了拒绝访问 解决方法 方法一 CMD命令 1 首先使用 Win R 组合快捷键 打开运行对话框 然后输入命令 cmd 点击下方的 确定 打开服务 如下图所示 复制这条命令
  • 【Java面试】Mysql为什么使用B+Tree作为索引结构

    一个工作8年的粉丝私信了我一个问题 他说这个问题是去阿里面试的时候被问到的 自己查了很多资料也没搞明白 希望我帮他解答 问题是 Mysql为什么使用B Tree作为索引结构 关于这个问题 看看普通人和高手的回答 普通人 B 数它的特征就是相
  • 用VS Code创建ASP.NET Core Web API项目:TodoWebApi。(默认空项目,不与数据库交互)

    目录 一 使用VS Code终端创建一个 Net Core Web API项目 二 使用VS Code添加相关的NuGet程序集 2 1 方式一 使用 VS Code 终端的 dotnet 命令添加相关的NuGet程序集 2 2 方式二 使
  • TRC20和ERC20的区别(trc20怎么换成erc20)

    TRC20和ERC20的区别 trc20怎么换成erc20 TRC20和ERC20是两种不同的区块链协议 分别基于波场 TRON 和以太坊 Ethereum 网络 它们都可以用来发行代币 比如USDT 但是在转账费用 交易速度 智能合约等方
  • Flutter安装部署运行,bug笔记

    1 根据官方文档下载flutterSDK 附带dart AndroidStudio下载flutter插件 附带dart 2 然后在flutter路径的cmd命令框 或在AS新项目目录里的文件pubspec yaml界面 或在AS命令框执行f
  • 设计模式--FlyWeight--结构型

    程序员是沟通人和机器交流 意图 Intent 运用共享技术有效地支持大量细粒度的对象 设计模式 GoF 结构 structure 面向对象的代价面向对象很好地解决了系统抽象性的问题 同时在大多数情况下 也不会损及系统的性能 但是 在某些特殊
  • 微信小程序animation

    wxml
  • LINUX 查询已安装的软件信息

    问题 1 列出当前主机已安装的所有RPM软件 2 查看firefox软件包的安装清单 3 查询ifconfig命令程序是安装哪个软件包后产生的 4 查看firefox软件包的用途 方案 查询所有已安装的rpm包 可以利用命令rpm qa 查
  • CentOS 修改IP地址, DNS, 网关

    一 CentOS 修改IP地址 修改对应网卡的IP地址的配置文件 vi etc sysconfig network scripts ifcfg eth0 修改以下内容 DEVICE eth0 描述网卡对应的设备别名 例如ifcfg eth0
  • 【满分】【华为OD机试真题2023 JAVA&JS】整理扑克牌

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 整理扑克牌 知识点贪心排序 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 给定一组数字 表示扑克牌的牌面数字 忽略扑克牌的花色 请按如下规则对这一组扑克牌进行整
  • 【Linux】远程连接服务

    1 什么是远程连接服务器 远程连接服务器通过文字或图形接口方式来远程登录系统 让你在远程终端前登录linux主机以取得可操作主机接口 shell 而 登录后的操作感觉就像是坐在系统前面一样 2 远程连接服务器的功能 分享主机的运算能力 服务
  • 服务器传感器不显示,服务器传感器不显示

    服务器传感器不显示 内容精选 换一换 不建议将挂载至Linux系统云服务器的云硬盘卸载后 重新挂载至Windows系统云服务器 也不建议将Windows系统云服务器上的云硬盘重新挂载至Linux系统云服务器 在这种情况下 由于文件系统不一致
  • C++ Primer阅读笔记--函数重载和内联函数

    1 函数重载 main 函数不能重载 重载函数在形参数量或形参类型上有所不同 不允许两个函数除了返回类型外其他所有的要素都相同 即函数重载不允许只有返回类型不同 如果形参是某种类型的指针或引用 则通过区分其指向的是常量对象还是非常量对象可以
  • php 日期1900年开始,php日期操作函数

    header content type text html charset utf 8 总结php操作时间的函数 1 cal days in month calendar month year 函数针对指定的年份和日历 返回一个月中的天数
  • Https如何保证了数据的安全?

    Https与Http在数据传输过程的差别 Https与Http都是OSI模型中传输层协议 而唯一不同的就是Https中在Http的应用层和TCP IP增加了一个SSL TLS层 其实也是属于应用层 主要用来对数据进行加解密 保证数据的传输的
  • 服务器系统环境初始化,Centos7系统

    服务器初始化环境 更新yum源 并添加必要系统工具 修改时区 设置系统时间 ntpdate时间同步服务 修改字符集zh CN UTF 8 关闭selinux 内核优化sysctl conf 调整文件描述符ulimit 即单个进程的最大文件打
  • 模拟电路设计的九个级别,你是模电几段?

    众 生 所 搬 周 硬 知 套 模拟电路设计的九个级别 类似下围棋的段位 快来看看自己处于什么水平 感觉九段已经是世外高人了 一段 你刚开始进入这行 对PMOS NMOS BJT什么的只不过有个大概的了解 各种器件的特性你也不太清楚 具体设
  • VSCode——修改VSCode背景图片

    1 以管理员身份运行VS Code 安装background插件 2 打开设置 在搜索框中输入background 选择扩展中的plugin background 选择在setting json中编辑 3 在用户设置中输入以下代码 修改完后
  • [QT入门篇]信号槽机制

    一 信号与槽的引入 信号与槽 Signal Slot 是 Qt 编程的基础 信号槽 实际是观察者模式 发布 订阅模式 当某个事件发生之后 比如 按钮检测到自己被点击了一下 它就会发出一个信号 signal 这种发出是没有目的的 类似广播 如