Qt基础之九:子线程和GUI交互
创始人
2024-04-09 21:36:02
0

首先要强调的是,子线程是不能直接操作GUI的,关于原因,详见:
C++面试题之为什么不能多线程直接操作GUI状态  
Qt提供了三种方式来实现异步操作GUI
1.postEvent
2.信号和槽
3.InvokeMethod

一.postEvent

[static] void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
:postEvent函数将事件添加到事件队列后立即返回,receiver是该事件的接收者。事件必须分配到堆上,因为事件队列将获得事件的所有权,并在发布后将其删除。在事件发布后访问它是不安全的。
事件按优先级降序排序,即优先级高的事件在优先级低的事件之前排队。优先级可以是任何整数值,即INT_MAX和INT_MIN之间,包括INT_MAX;有关详细信息,请参阅Qt::EventPriority。
许多第三方库通过回调函数的方式将数据传给上层处理,比如将解码后的图像数据交给Qt渲染显示。但回调函数不一定运行在主线程中,因此我们需要将数据封装成事件,然后用postEvent添加到事件列表,接着就可以在主线程重写的event方法中处理该事件了。

struct Frame
{uint8_t *buffer;int width;int height;
};
// 回调函数.
void FrameNotifyHandler(void *userData, Frame *frame)
{QHMediaPlayerGL *self = (QHMediaPlayerGL *)userData;QHFrameEvent::postEvent(self, frame);
}
// 主线程中处理事件.
bool QHMediaPlayerGL::event(QEvent *e)
{QHFrameEvent *ev = QHFrameEvent::event(e);    if(ev){if(hasPlayMedia()){m_videoBuffer=ev->frame()->buffer;m_videoWidth=ev->frame()->width;m_videoHeight=ev->frame()->height;update();}return true;}return QWidget::event(e);
}

二.信号和槽

常用于将子线程执行结果更新到GUI。下面的例子中将子线程中的累加值更新到按钮上

class MyThread: public QThread
{Q_OBJECTpublic:explicit MyThread(QObject *parent = nullptr): QThread(parent){}~MyThread(){requestInterruption();quit();wait();}void run(){qDebug() <<"worker thread id=" << QThread::currentThreadId();static int i = 0;while(!isInterruptionRequested()){sleep(1);emit update(i++);}}signals:void update(int value);
};
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);qDebug() <<"main thread id=" << QThread::currentThreadId();MyThread *thread = new MyThread(this);connect(thread, &MyThread::update, this, [=](int value){ui->pushButton->setText(QString::number(value));});// 处理子线程的finished信号.connect(thread, &MyThread::finished, []{qDebug() << "worker thread exit";});thread->start();
}

三.InvokeMethod

关于InvokeMethod详见:
Qt基础之五:使用invokeMethod异步调用函数 

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include #include QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;bool m_running;QFuture m_future;
};
#endif // MAINWINDOW_H

mainwindow.cpp 

#include "mainwindow.h"
#include "ui_mainwindow.h"#include MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), m_running(true)
{ui->setupUi(this);qDebug()<<"main thread id="<pushButton->setText(QString::number(i++));qDebug()<<"invokeMethod thread id="<

由于使用了并发模块QtConcurrent,需要在 .pro 中添加: QT += concurrent

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...