QT的信号槽机制和线程的启动方式已经在前面的文章中写过了,本文主要是对信号槽的连接方式进行解读,信号槽的连接方式一共有5种:

1.Qt::DirectConnection

发出信号后立即调用槽函数。 该槽函数在信号函数所在的线程中执行。

示例

//头文件
#ifndef SIGNALSLOTTEST_H
#define SIGNALSLOTTEST_H#include <QObject>
#include <QThread>
#include <iostream>
#include <cassert>class signalslottest : public QObject
{Q_OBJECT
public:explicit signalslottest(QObject *parent = nullptr);virtual ~signalslottest();signals:void emitsignal();
private:QThread workthread_;};class work:public QObject
{Q_OBJECT
public:explicit work(QObject *parent = nullptr);virtual ~work();void dowork();
};#endif // SIGNALSLOTTEST_H
//源文件
#include "signalslottest.h"using namespace std;signalslottest::signalslottest(QObject *parent) : QObject(parent)
{cout<<QThread::currentThreadId()<<endl;work *pw=new work();pw->moveToThread(&workthread_);connect(this, &signalslottest::emitsignal, pw, &work::dowork, Qt::DirectConnection);workthread_.start();emit emitsignal();cout<<__func__<<endl;
}signalslottest::~signalslottest()
{cout<<__func__<<endl;
}work::work(QObject *parent) : QObject(parent)
{cout<<__func__<<endl;
}work::~work()
{cout<<__func__<<endl;
}void work::dowork()
{cout<<__func__<<endl;QThread::sleep(2);cout<<QThread::currentThreadId()<<endl;
}
//主函数
#include <QCoreApplication>
#include "signalslottest.h"using namespace std;int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);signalslottest t;cout<<__func__<<endl;cout<<QThread::currentThreadId()<<endl;return a.exec();
}

上述代码采用https://blog.csdn.net/Master_Cui/article/details/109209175中的第一种方式启动线程,输出结果如下

上述代码首先创建一个signalslottest的对象,在signalslottest的构造函数中,启动线程并将work对象放在分线程中执行,然后发射信号并调用void work::dowork()

由于信号槽的连接方式是DirectConnection,所以,信号发射后,马上执行槽函数void work::dowork(),当槽函数执行完,退出后,接着执行信号函数后面的代码

此外,即使槽函数被移动到其他线程,可是由于信号槽的连接方式是DirectConnection,所以,槽函数最终也会在信号函数所在的线程(主线程)中执行

2.Qt::QueuedConnection

当程序切换到接收者所在线程的事件循环时,将调用槽函数。 该槽函数在接收者的线程中执行。

示例

依然采用Qt::DirectConnection的示例代码,并将信号槽的连接方式改为Qt::QueuedConnection

connect(this, &signalslottest::emitsignal, pw, &work::dowork, Qt::QueuedConnection);

执行结果如下

因为信号槽分别处在两个线程,而且信号槽的连接方式是QueuedConnection。所以,当信号发出后,并不会马上执行槽函数,而是将信号所在线程的代码执行完后,再去执行槽函数所在线程的代码

3.Qt::AutoConnection

如果在调用connect时,如果不指定连接方式,则Qt::AutoConnection是默认的连接方式。该连接方式表示:如果槽函数和信号函数位于同一线程,那么,连接方式是Qt :: DirectConnection。 否则,连接方式是Qt :: QueuedConnection。

示例

依然采用Qt::DirectConnection的示例代码,并将信号槽的连接方式改为Qt::AutoConnection

connect(this, &signalslottest::emitsignal, pw, &work::dowork, Qt::AutoConnection);

执行结果如下

因为信号槽分别处在两个线程,而且信号槽的连接方式是AutoConnection。所以,连接方式实际上是QueuedConnection,所以,时序和QueuedConnection一致,如果将void work::dowork()放到主线程中,那么连接方式实际上是DirectConnection

4.Qt::BlockingQueuedConnection

与Qt :: QueuedConnection相同,只不过信号线程将一直阻塞直到槽函数执行结束。 如果接收者位于信号线程中,则一定不要使用此连接方式,否则将造成死锁。

示例

依然采用Qt::DirectConnection的示例代码,并将信号槽的连接方式改为Qt::BlockingQueuedConnection

connect(this, &signalslottest::emitsignal, pw, &work::dowork, Qt::BlockingQueuedConnection);

执行结果如下

时序看起来和DirectConnection的时序相同。原因是因为信号发出后,信号所在的线程(主线程)会阻塞,当槽函数执行结束后,又回到主线程继续执行

使用BlockingQueuedConnection时,信号和槽函数一定不能在同一线程

示例

依然采用Qt::DirectConnection的示例代码,在类signalslottest中添加一个槽函数slotfunc(),将信号槽的连接方式改为Qt::BlockingQueuedConnection

class signalslottest : public QObject
{Q_OBJECT
public:explicit signalslottest(QObject *parent = nullptr);virtual ~signalslottest();
public slots:void slotfunc() {std::cout<<__func__<<std::endl;}
signals:void emitsignal();
private:QThread workthread_;
};signalslottest::signalslottest(QObject *parent) : QObject(parent)
{cout<<QThread::currentThreadId()<<endl;work *pw=new work();pw->moveToThread(&workthread_);connect(this, &signalslottest::emitsignal, pw, &work::dowork, Qt::BlockingQueuedConnection);connect(this, &signalslottest::emitsignal, this, &signalslottest::slotfunc, Qt::BlockingQueuedConnection);workthread_.start();emit emitsignal();cout<<__func__<<endl;
}

执行结果如下

因为信号和槽函数都在同一个线程执行,并且连接方式是BlockingQueuedConnection,所以,当执行完dowork后,程序出现死锁,而且,上述代码也说明,当一个信号连接多个槽函数时,槽函数的执行顺序和connect的顺序一致

5.Qt::UniqueConnection

该标志可以使用按位或操作将其与上述任何一种连接类型组合在一起。 设置Qt::UniqueConnection时,如果连接已经存在,则QObject :: connect()将失败。(即,如果同一信号已经连接到同一对象的同一槽函数两次,那么,第二次connect时,连接会失败)

其实UniqueConnection并不算是一种连接方式,只是配合其他连接方式一起使用,防止重复连接而已

示例

signalslottest::signalslottest(QObject *parent) : QObject(parent)
{cout<<QThread::currentThreadId()<<endl;work *pw=new work();pw->moveToThread(&workthread_);connect(this, &signalslottest::emitsignal, pw, &work::dowork, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));connect(this, &signalslottest::emitsignal, pw, &work::dowork, Qt::QueuedConnection);workthread_.start();emit emitsignal();cout<<__func__<<endl;
}

通过上述代码的执行结果可见,即使使用了UniqueConnection,并且同一信号槽函数连接两次,信号槽函数的连接依然能正常建立,槽函数依旧执行了两次,所以,UniqueConnection并不像官网说的能防止信号槽重复连接

此外,当使用UniqueConnection时,要么单独使用,如果想和其他方式做按位与运算,那么与运算的结果是一个int数据,不是枚举ConnectionType,会提示如下错误

所以要用static_cast做类型转换,将int转换成一个枚举

参考

https://doc.qt.io/qt-5/qt.html#ConnectionType-enum

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

13.QT信号槽的连接方式相关推荐

  1. Qt信号槽之槽函数中获取发送信号对象——sender()

    QObject::sender() 我们如何在槽函数中获取到信号的发送对象呢,使用sender()方法即可获取.但是使用此方法我们需要注意几点: QObject *QObject::sender() ...

  2. QT信号槽的5种连接方式

    在面试中,这是一个经常被问到的问题点,也是刚刚上qt的工程师不会去注意的一个点. qt源代码定义的连接方式如下: 1.Qt::AutoConnection 一般信号槽不会写第五个参数,其实使用的默认值 ...

  3. Qt 信号-槽的同步与异步处理

    通常使用的connect,实际上最后一个參数使用的是Qt::AutoConnection类型:Qt支持6种连接方式.当中3中最主要: 1.Qt::DirectConnection(直连方式)(信号与槽 ...

  4. (3)Qt——信号槽

    目录 1.信号槽的概念** 2.信号槽的连接*** 2.1自带信号 → 自带槽 2.2 自带信号 → 自定义槽 2.3 自定义信号 3. 参数传递** 3.1 全局变量 3.2 信号槽传参 4. 对应 ...

  5. Hello Qt——Qt信号槽机制源码解析

    基于Qt4.8.6版本 一.信号槽机制的原理 1.信号槽简介 信号槽是观察者模式的一种实现,特性如下: A.一个信号就是一个能够被观察的事件,或者至少是事件已经发生的一种通知: B.一个槽就是一个观察 ...

  6. 6.QT信号槽的时序分析

    前面已经分析了元对象系统.MOC文件和信号槽的连接,本文分析下信号槽的时序 信号的触发通过emit关键字触发,以sigf1为例,通常是这样的 emit sigf1(t1) emit就是个空宏,在qob ...

  7. Qt信号槽如何传递参数

    Qt信号槽如何传递参数 利用Qt进行程序开发时,有时需要信号-槽来完成参数传递.带参数的信号-槽在使用时,有几点需要注意的地方,下面结合实例进行介绍. 1. 当信号与槽函数的参数数量相同时,它们参数类 ...

  8. Qt信号槽机制-传递自定义数据类型(qRegisterMetaType)

    Qt信号槽机制-传递自定义数据类型qRegisterMetaType 前言 前言 通过Qt内置的数据类型进行信号与槽参数传递很方便:如果是自己定义的类型如果想使用signal/slot来传递的话,则没 ...

  9. 【Qt开发经验】Qt信号槽连接不成功问题原因汇总

    以下几种情况会导致信号槽连接不成功,下面分别描述. 1. 拥有信号槽的类,必须继承QObject,声明Q_OBJECT宏. Qt帮助手册里搜索 "Signals & Slots&qu ...

最新文章

  1. 3检测人头_基于人头检测技术客流统计摄像头及管理平台
  2. 比较好用的python编译器_10个最好用的在线编译工具
  3. Python for Data Analysis
  4. opencv实现对象跟踪_如何使用opencv跟踪对象的距离和角度
  5. python virtualenv_python开发之virtualenv与virtualenvwrapper讲解
  6. Java-杂项:Java数组Array和集合List、Set、Map
  7. XSS_伪协议与编码绕过
  8. 苹果手机编辑word_苹果手机自带了三个PDF功能!每一个都这么好用!你知道几个呢?...
  9. js实现复制input隐藏域的取巧做法
  10. 厦门大学计算机考研复试线2021,【厦门大学】2021考研复试分数线3月13日已公布!速看!...
  11. Windows邮箱登录QQ邮箱
  12. WordPress重新安装的几种方法(2022年新版教程)
  13. java 加密并打包_java实现将多个文件打包成zip压缩文件以及对压缩文件的加密
  14. java+桌球小游戏图片_Java桌球小游戏
  15. mysql容灾方案_mysql 架构 ~异地容灾
  16. SNES 与 NES 游戏模拟机区别与 SNES Classic界面实现,
  17. 《道德经》新解(上篇)
  18. 抖音爆火李峋同款爱心代码,简单附带教程,还有烟花代码,手残党也能学会!!
  19. 使用Ajax传用户信息数据到后端
  20. 计算机英语 美剧,给力美剧口语IT篇(83):冒充电脑高手

热门文章

  1. DOS批处理高级教程精选(七)
  2. 期未课程设计:基于SSM的产品销售管理系统
  3. 连续变量的转换:ECDF、Box-Cox、Yeo-Johnson
  4. spring + mybatis
  5. CF183D T-shirt
  6. elastic date时区问题解决办法
  7. 《Java基础入门》课后习题答案 资源分享
  8. Linux常用下载软件
  9. 以太坊开发(二)使用Ganache CLI在私有链上搭建智能合约
  10. Fiddler抓包使用教程-会话图标