直接进入主题,据我所知,方法有3种:

1、自定义signal、slot,这个就不多说了,标准方法

2、使用QmetaMethod::invoke,这是可以直接在子线程调用的,看起来比较另类

3、从5.4开始,有了另一种新方法:Qtimer::singleShot(0),这玩意儿可不是定时启动那么简单,你给他0的时间他就是GUI线程安全的,完整例子可以测试:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

#include <QCoreApplication>#include <QTimer>

#include <QDebug>

#include <thread>

#include <QThread>

#include <QEventLoop>

#include <QThread>

using std::thread;

class TestObj

        :public QObject

{

// Used new connect syntax no need for Q_OBJECT define

// you SHOULD use it. I used just to upload one file

//Q_OBJECT

public slots:

    void doWork()

    {

        qDebug() << "QThread id" << QThread::currentThreadId();

        QTimer::singleShot(0, qApp, []()

        {

            qDebug() << "singleShot from QThread" << QThread::currentThreadId();

        });

    }

};

int main(int argc, char *argv[])

{

    QCoreApplication a(argc, argv);

    qDebug() << "main thread id" << QThread::currentThreadId();

    thread testThread([]()

    {

        QEventLoop loop;

        Q_UNUSED(loop)

        qDebug() << "std::thread id" << QThread::currentThreadId();

        QTimer::singleShot(0, qApp, []()

        {

            qDebug() << "singleShot from std thread with qApp" << QThread::currentThreadId();

        });

        qDebug() << "std::thread finished";

    });

    testThread.detach();

    QThread testQThread;

    TestObj testObj;

    testObj.moveToThread(&testQThread);

    QObject::connect(&testQThread, &QThread::started, &testObj, &TestObj::doWork);

    testQThread.start();

    // 下列方法2选1,都可以退出app    

    //QMetaObject::invokeMethod(&a, "quit", Qt::QueuedConnection);

    QTimer::singleShot(0, &a, [&a]()

    {

        a.quit();

    });

    // 直接调用a.quit();是无法退出的,因为没有到eventloop

    // a.quit();

    a.exec();

    testQThread.quit();

    testQThread.wait();

    return 0;

}

输出:

1

2

3

4

5

6

main thread id 0x1bb4

std::thread id 0x294c

std::thread finished

QThread id 0x1bd4

singleShot from std thread with qApp 0x1bb4

singleShot from QThread 0x1bb4

可以看到,qtimer执行的一直是主线程,也就是GUI线程。

/******************************

之前有篇文章说,是传界面指针到线程中去,从而在线程中操作主界面中控件。
今天发现,这种方法是极其错误的,文章我已经删掉,希望没有误人子弟。
前面转的两篇文章中对于为什么不能在线程中操纵界面控件指针有了很好的解释。下面在做下解释:
尽管QObject是可重入的,但GUI类,特别是QWidget与它的所有子类都是不可重入的。它们仅用于主线程。正如前面提到过 的,QCoreApplication::exec() 也必须从那个线程中被调用。实践上,不会在别的线程中使用GUI类,它们工作在主线程上,把一些耗时的操作放入独立的工作线程中,当工作线程运行完成,把 结果在主线程所拥有的屏幕上显示。

下面的几个帖子是我在解决问题的过程中看到的几个很有帮助的帖子,一并贴下:
http://www.qtcn.org/bbs/read.php?tid=12121&keyword=%CF%DF%B3%CC
http://www.qtcn.org/bbs/read.php?tid=13508&keyword=%CF%DF%B3%CC
QT里面可以在非GUI线程里发个事件,让GUI线程执行完后再返回呢?老问题 - QTCN开发网 - Powered by phpwind

下面截取里面比较有价值的回复:

线程里面直接操作界面元素是肯定不行的,即使不总是出现错误,偶尔也会出莫明其妙,在 文档里面已经有说了,你再找找看,
你需要使用比如postEvent之类的异步处理方式.
至于进程中的那个错误,你设断点调试下吧.
不可以在非界面的进程中直接操作界面的
Qt的文档有这么说
据说4之前版本可以,没有试过

signal/slot目前有三种调用方式
1.DirectConnection
和以前一样,在emit处直接invoke你的slot 函数,一般情况是sender,receiver在同一线程

2.QueuedConnection
将 发送Event给你的receiver所在的线程
postEvent(QEvent::MetaCall,...)
slot 函数会在receiver所在的线程的event loop中进行处理,一般情况是sender,receiver不在同一线程

3.BlockingQueuedConnection
调 用sendEvent(QEvent::MetaCall,...),在receiver所在的线程处 理完成后才会返回;只能当sender,receiver不在同一线程时才可以

好了,上面的是说为什么不行的,那如果非要在非GUI线程里操作GUI线程里的控件,该怎么做呢?
答案是使用signal/slot。
在线程里的run()里定期emit signal,GUI线程里建立连接,写槽函数,注意connect的第五个参数应该使用Queued方式。

下面列个从QT论坛上找的例子,专门用来解释这个问题的

thread.h:

#ifndef THREAD_H
#define THREAD_H
#include <QThread>

class Thread : public QThread
{
Q_OBJECT
public:
Thread();
signals:
void sendString(QString);
protected:
void run();
};

#endif // THREAD_H

widget.h:

#ifndef WIDGET_H
#define WIDGET_H
#include <QtGui/QWidget>
#include <QtGui/QTextEdit>

class Thread;

class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
QTextEdit *m_textEdit;
Thread *m_thread;
};
#endif // WIDGET_H

thread.cpp:

#include "thread.h"
#include <QtCore/QTime>
Thread::Thread()
{
}
void Thread::run()
{
while(1)
{
emit sendString(QTime::currentTime().toString("hh:mm:ss.zzz"));
msleep(200);
}
}

widget.cpp:

#include <QtGui/QHBoxLayout>
#include "widget.h"
#include "thread.h"

Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_textEdit = new QTextEdit(this);
QHBoxLayout * layout = new QHBoxLayout(this);
layout->addWidget(m_textEdit);
setLayout(layout);
m_thread = new Thread();
connect(m_thread, SIGNAL(sendString(QString)), m_textEdit, SLOT(append(QString)));
m_thread->start();
}
Widget::~Widget()
{
}

main.cpp:

#include <QtGui/QApplication>
#include "widget.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}

/************************************

线程里面直接操作界面元素是肯定不行的,即使不总是出现错误,偶尔也会出莫明其妙,在文档里面已经有说了,你再找找看,
你需要使用比如postEvent之类的异步处理方式.
至于进程中的那个错误,你设断点调试下吧.

/******************************************

1. 线程与界面组件需要注意的地方

  • 在QThread线程中不能直接创建QWidget之类的界面组件.
  • 因为在QT中,所有界面组件相关的操作都必须在主线程中(也就是GUI thread)
  • 所以, QThread线程不能直接操作界面组件.

2.QThread线程如何操作界面组件-方法1

  • 将多线程类对象封装为GUI界面类的类成员
  • 然后在子线程定义信号函数,通过信号槽机制,向界面组件emit发射信号,从而实现间接操作.

3.QThread线程如何操作界面组件-方法2

  • 使用QApplication::postEvent()实现向界面发送事件,从而能够封装一个自定义类

4.使用Invokes()函数来调用界面组件的信号槽-方法3

一般使用该函数(用来调用对方的私有信号或槽):

该函数的连接方式默认使用的是Qt::AutoConnection

  • 表示如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。

比如,当我们想调用一个obj下的compute(QString, int, double)槽函数时:

则只需要写入:

QMetaObject::invokeMethod(obj, "compute",Q_ARG(QString, "sqrt"),                        Q_ARG(int, 42),Q_ARG(double, 9.7));

示例如下所示:

在Testtherd线程类里通过invokeMethod向父界面类的paintMsg槽函数发送信息

void Testtherd::run()
{int count=0;while(1){QString str="请稍等,正在验证用户,登录中";for(int i =0;i<count;i++)str.append('.');                   //循环添加小数点count=(count+1)%7;QMetaObject::invokeMethod(this->parent(), "paintMsg",Q_ARG(QString, str));msleep(500);}
}

父界面类的paintMsg槽函数如下所示:

void loginwindow:: paintMsg(QString msg) {
this->LineHint->setText(msg);
}

运行效果如下:

qt 子线程(多线程)更新gui的几种方法相关推荐

  1. Android子线程中更新UI的4种方法

    方法一:用Handler 1.主线程中定义Handler: Handler mHandler = new Handler() { @Override public void handleMessage ...

  2. c# ThreadPool 判断子线程全部执行完毕的四种方法

    1.先来看看这个 多线程编程 多线程用于数据采集时,速度明显很快,下面是基本方法,把那个auto写成采集数据方法即可. using System; using System.Collections.G ...

  3. QT子线程实现串口通信_学习记录

    QT子线程实现串口通信_学习记录 1 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一个表格 设定内容 ...

  4. android 不能在子线程中更新ui的讨论和分析

    问题描述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是只能在主线程中更改 ui,子线程要修改 ...

  5. 为什么不能在子线程中更新UI

    首先声明一点:子线程里面是可以更新UI的--创建一个空白的Activity,在其xml文件中放一个空白TextView,Java代码如下: @Override protected void onCre ...

  6. C#子线程中更新ui

    本文实例总结了C#子线程更新UI控件的方法,对于桌面应用程序设计的UI界面控制来说非常有实用价值.分享给大家供大家参考之用.具体分析如下: 一般在winform C/S程序中经常会在子线程中更新控件的 ...

  7. android 线程 界面,android开发教程之子线程中更新界面

    每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联. Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间 ...

  8. Android中Handler的使用方法——在子线程中更新界面

    本文主要介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Ha ...

  9. Android--Handler的使用方法:在子线程中更新界面

    版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/chaoyu168/article/details/50914021 本文主要介绍Android的Handl ...

  10. 【源码】让源码告诉你:为什么在子线程无法更新 UI 操作?

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

最新文章

  1. [笔记]React+Cordova踩坑
  2. lnmp搭建的常见错误
  3. com/mysql/jdbc/sqlerror_com.mysql.jdbc.sqlerror
  4. 实时监听input输入框value的变化:
  5. 【前端工程师手册】JavaScript作用域拾遗
  6. Openshift 4.4 静态 IP 离线安装系列:初始安装
  7. 中国哌唑替芬行业市场供需与战略研究报告
  8. html5 Canvas画图3:1像素线条模糊问题
  9. flashtorch:卷积神经网络的可视化
  10. STL模板之vector与sort的使用
  11. Acrobat Pro DC 教程,如何使用密码保护 PDF 文件?
  12. Java自学!java题库网站
  13. oracle同音字模糊查询,已释甲骨文会意字研究.pdf
  14. 百度AI攻略:iOCR自定义模板功能
  15. 关于代付和分账系统的区别
  16. 关于springboot自动注入出现Consider defining a bean of type 'xxx' in your configuration问题解决方案...
  17. 网络虚拟化中的 offload 技术:LSO/LRO、GSO/GRO、TSO/UFO、VXLAN
  18. 鱼塘钓鱼(贪心算法)--算法设计
  19. vue + echarts 省份地图 以及打包后地图加载不出来(比较详细)
  20. SAP内部培训效果考察表存档

热门文章

  1. C语言循环语句中 i++, ++i, i--, --i的使用
  2. [CVPR2021]pi-GAN: Periodic Implicit Generative Adversarial Networks for 3D-Aware Image Synthesis
  3. Docker —— 从入门到实践
  4. 百度云智峰会 百度天像打造多媒体新生态
  5. 《21天学通HTML+CSS+JavaScript Web开发(第7版)》——2.8 作业
  6. 虚拟opc服务器软件,几个OPC服务器开发工具比较
  7. python爬虫(20)获取酷我音乐排行榜榜单作品
  8. spring mvc +maven 集成 quartz实现定时任务
  9. (转)一些jbx的配置
  10. 涉密计算机怎么更新补丁,当需要将病毒库、系统补丁程序等导入到涉密信息系统时采用什么方式...