项目中有一个需求就是,因为需要请求服务端数据,因为网络的读取会阻塞,所以该过程不能放在Qt中的UI主线程当中,需要用一个后台线程来读取数据,数据准备完毕后

在通过Qt5中的信号槽机制来跨线程的传递数据。之前的博文使用过moveToThread的方式来讲解创建后台线程,但是现在后台线程需要与前台UI线程数据互交,然而,最悲剧的就是信号发出去了,

但是前台的UI线程对象收不到信号,也就是相应的槽函数没被调用。之前博文后台线程是没有与前台UI线程互交的,因为它是采集数据的线程,只管往目标地址发送数据就可以了。但是接收线程就不一样了,

它需要把后台接收到的网络数据放到前台GUI中展现出来。这不可避免的产生互交和数据的传递。

前台的UI线程创建后台线程的代码大概如下:

  RecvDataObject *recv_obj = new RecvDataObject;

     QThread* backgroundRecvThread = new QThread;

      recv_obj->moveToThread(backgroundRecvThread);

     connect(recv_obj, &RecvDataObject::dataRecved,
this, &TerminalStatusWidget::slotDataRecved,Qt::QueuedConnection); backgroundRecvThread->start();

注意,多线程间的信号槽传递,在connect的时候需要以Qt::QueuedConnection的方式,不然以Qt::DirectConnection的方式接收者UI线程会很长时间收不到后台线程发出的信号,或者信号直接丢失都是有可能的。参考

http://www.qtcentre.org/threads/17764-emit-qt-signal-is-very-slow-how-it-can-be-optimized

RecvDataObect是用来接收后台数据的对象被move到了backgroundRecvThread线程中去执行了。其声明是这样的:

 class RecvDataObject : public QObject
{
Q_OBJECT public:
RecvDataObject();
~RecvDataObject();
signals:
void dataRecved(std::vector<RunTimeInfo> list);
public slots:
void slotRecvTask();
private:
QTimer m_RecvTask; };

该类的构造函数我采用了一个Timer来循环执行slotRecvTask()的任务,专门创建网络连接,接收网络数据。然后数据接收完毕后,通过发送dataRecved的信号传递到UI主线程中的slot函数中,但是不能正常工作。槽函数一直不能调用。

上网查了原因才知道,原来Qt的信号槽函数只默认支持Qt的类型和C++提供的内建的基本类型,比如int double float啥的,根本不支持C++的std::string std::vector 自定义的struct类型。所以需要用Qt提供的Q_DECLARE_METATYPE和

qRegisterMetaType来声明和注册自定义的类型和C++的其他类型。  所以以上的C++类RecvDataObject应该变成以下:

 Q_DECLARE_METATYPE(RunTimeInfo)
Q_DECLARE_METATYPE(std::vector<RunTimeInfo>) class RecvDataObject : public QObject
{
Q_OBJECT public:
RecvDataObject()
{
qRegisterMetaType<RunTimeInfo>("RunTimeInfo");
qRegisterMetaType<std::vector<RunTimeInfo>>("std::vector<RunTimeInfo>"); m_RecvTask.setInterval();
connect(&m_RecvTask, SIGNAL(timeout()), this, SLOT(slotRecvTask()));
m_RecvTask.start();
}
~RecvDataObject();
signals:
void dataRecved(std::vector<RunTimeInfo> list);
public slots:
void slotRecvTask();
private:
QTimer m_RecvTask; };

然后主线程的Widget类的构造函数里面还必须加入:

 qRegisterMetaType<RunTimeInfo>("RunTimeInfo");
qRegisterMetaType<std::vector<RunTimeInfo>>("std::vector<RunTimeInfo>");

这样信号槽函数才能正确工作,通过信号槽机制跨线程的数据传递完成了,完美运行。

references:

https://stackoverflow.com/questions/638251/how-to-emit-cross-thread-signal-in-qt

http://www.qtcentre.org/threads/54409-signal-slot-with-std-string-How

https://stackoverflow.com/questions/14083599/signals-and-slots-passing-data

Qt5中运行后台网络读取线程与主UI线程互交的更多相关文章

  1. Qt中运行后台线程不阻塞UI线程的方案

    有一个想法,一个客户端,有GUI界面的同时也要向网络服务器发送本地采集的数据,通过网络发送数据的接口是同步阻塞的,需要等待服务器响应数据. 如果不采用后台线程的方案,用主UI线程关联一个定时器QTim ...

  2. Android中后台线程如何与UI线程交互

    我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须 ...

  3. 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)

    [分析]浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang) 今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间 ...

  4. Android UI线程和非UI线程

    Android UI线程和非UI线程 UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这 ...

  5. android脚步---如何看log之程序停止运行,和UI线程和非UI线程之间切换

    经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 ...

  6. 关于 SWT 的UI线程和非UI线程

    要理解UI线程,先要了解一下“消息循环”这个概念.链接是百度百科上的条目,简单地说,操作系统把用户界面上的每个操作都转化成为对应的消息,加入消息队列.然后把消息转发给对应的应用程序(一般来说,就是活动 ...

  7. Android线程---UI线程和非UI线程之间通信

        近期自学到了线程这一块,用了一上午的时间终于搞出来了主.子线程间的相互通信.当主线程sendMessage后,子线程便会调用handleMessage来获取你所发送的Message.我的主线程 ...

  8. 跟着拉大锯大神学Android——网络编程中运行后台服务器端口占用问题

    拉大锯网页地址:https://www.sunofbeach.net/u/1153952789488054272 跟着拉大锯大神学Android,在学到网络编程时,使用了大神搭建的用于学习的后台服务器 ...

  9. jenkins在pipline中运行后台命令

    需求 在jenkin中启动java程序或者python程序的时候,希望程序在后台执行,并在jenkins构建完成之后继续执行.在工作中有两个地方我碰到了这种需求 在shell script输入框中pi ...

随机推荐

  1. 还是畅通工程(hdu1233)并查集应用

    还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  2. linux_shell_字符串

    字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号. 但是单引号和双引号是有区别的: 单引号: 单引号里的任何 ...

  3. Python标准库--UUID

    UUID(Universally Unique Identifier)是128位通用唯一识别码,通常由32字节的字符串表示.它可以保证时间和空间的唯一性,也称为GUID,全称为:UUID —— Uni ...

  4. vue中prop传值时加不加v-bind(冒号:)

    前言:有关Vue中父组件通过prop传值给子组件时,是否加v-bind的问题,没弄清楚时感觉很乱,弄清楚之后很简单. 由于结果记起来很容易,所以先给出结果: 只有传递字符串常量时,不采用v-bind形 ...

  5. SpringCloud 组件Eureka参数配置项详解

    Eureka涉及到的参数配置项数量众多,它的很多功能都是通过参数配置来实现的,了解这些参数的含义有助于我们更好的应用Eureka的各种功能,下面对Eureka的配置项做具体介绍,供大家参考. Eure ...

  6. JS 解决 IOS 中拍照图片预览旋转 90度 BUG

    上篇博文[ Js利用Canvas实现图片压缩 ]中做了图片压缩上传,但是在IOS真机测试的时候,发现图片预览的时候自动逆时针旋转了90度.对于这个bug,我完全不知道问题出在哪里,接下来就是面向百度编 ...

  7. 数据表自增Id获取时IDENTITY的正确使用方式

    在SQLServer中很多表主键会设置为自增列,有的业务需求需要知道新插入的自增Id是多少,一般我们会用SELECT @@IDENTITY来获取,可由于@@IDENTITY是个全局变量作用据较大,所以 ...

  8. Android 借助Stetho在Chrome上调试Android网络、数据库、Sharedpreferences

    Android 借助Stetho在Chrome上调试Android网络.数据库.Sharedpreferences 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/a ...

  9. LeetCode题解之Unique Email Addresses

    1.题目描述 2.问题分析 将字符串中的 ‘.’ 去掉,将 ‘+’后面直到‘@’的字符串去掉,然后利用set的特性. 3.代码 int numUniqueEmails(vector<string ...

  10. 一致性Hash(Consistent Hashing)原理剖析

    引入 在业务开发中,我们常把数据持久化到数据库中.如果需要读取这些数据,除了直接从数据库中读取外,为了减轻数据库的访问压力以及提高访问速度,我们更多地引入缓存来对数据进行存取.读取数据的过程一般为: ...