Qt 子线程中使用UI线程

方案起源

最近做了一个Excel保存图表的项目,因为不能直接用Excel的图表(会直接暴露计算数据),所以采用的是QCharts生成的表格,但是QCharts的问题是 调用QChartView::setChart接口之后,会出现不在同一个线程的问题,简单来说就是必须在主(UI)线程才能进行setChart操作。

如何方便的在子线程中调用主线程,就成为了要解决的主要问题。

搜寻了很多方案,对MainWindow的耦合很严重,况且我也不需要访问MainWindow的UI控件,我只是借用主线程保存一下图表而已。

找了很久,找到一个Stack Overflow上面的方案。

Stack Overflow 方案

#include <functional>

void dispatchToMainThread(std::function<void()> callback)
{
    QTimer* timer = new QTimer();
    timer->moveToThread(qApp->thread());
    timer->setSingleShot(true);
    QObject::connect(timer, &QTimer::timeout, [=]()
    {        
        callback();
        timer->deleteLater();
    });
    QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓ 

采用了这个方案,貌似很完美的解决了问题,只需要将此方案,新建一个类,放置dispatchToMainThread接口,然后利用lambda函数,传递callback即可。如下所示:

A a;
a.dispatchToMainThread( [&]{
    func(x);
});

但是采用这种方案最大的问题在于,timer->deletelater()是让系统去管理timer何时释放资源,但是如果线程比较多的话,timer根本就不会delete,一直处在activate状态,在 上面调用dispatchToMainThread之后,如果函数体结束了,就会造成资源释放的问题,导致软件崩溃。

想到了一个方法,就是在invokemethod后面加Qt::BlockingQueuedConnection,然后直接调用QTimer::destroy,但是这样也会崩溃,因为系统会判断,QTimer未运行结束,就强制结束。

索性最后想到一个完美的解决方案,一定程度上减少了对MainWindow的依赖。

我的解决方案

  • 在MainWindow中

建立MainWindow的单例模式,然后添加如下方法

void MainWindow::dispatch2MainThread(std::function<void ()> callback)
{
    callback();
}
  • 新建DispatchMainTherad类,代码如下

头文件

#include <functional>
#include <QObject>

class DispatchMainThread
        : public QObject
{
    Q_OBJECT
public:
    static DispatchMainThread *instance();
protected:
    DispatchMainThread();
public:
    ~DispatchMainThread();
signals:
    void dispatch2MainThread(std::function<void()> callback);

public:
    void dispatchToMainThread(std::function<void()> callback);

private:
    static DispatchMainThread *self;
};

源文件

#include "dispatchmainthread.h"
#include "mainwindow.h"
#include "mutex.h"

DispatchMainThread *DispatchMainThread::self = nullptr;
DispatchMainThread *DispatchMainThread::instance()
{
    static std::once_flag onceFlag;

    std::call_once(onceFlag, [&]
    {
        self = new DispatchMainThread();
        QObject::connect(self, &DispatchMainThread::dispatch2MainThread, MainWindow::instance(), &MainWindow::dispatch2MainThread,
                         Qt::BlockingQueuedConnection);
    });

    return self;
}

DispatchMainThread::DispatchMainThread()
    : QObject()
{

}

DispatchMainThread::~DispatchMainThread()
{
    if(!self)
    {
      delete self;
    }
}

void DispatchMainThread::dispatchToMainThread(std::function<void ()> callback)
{
    emit dispatch2MainThread(callback);
}
  • 使用部分代码
auto dis = DispatchMainThread::instance;
dis->dispatchToMainThread([&](){yourFunction();});

说明

因为是粗略的代码,所以删除了部分细节,优化就看读者自己的操作了,代码直接使用的问题,请自己Debug,思路已经很详细了。

文章转自博客园(ligiggy):Qt子线程中使用UI线程 - ligiggy - 博客园

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt线程不能直接访问UI控件,因为UI控件只能在主线程访问。但是可以通过信号和槽机制来实现线程UI控件的交互。具体步骤如下: 1.在主线程定义一个槽函数,用于更新UI控件的状态。 2.在线程定义一个信号,用于触发主线程的槽函数。 3.在主线程创建线程对象,并将线程的信号连接到主线程的槽函数。 4.在线程通过信号触发主线程的槽函数,从而更新UI控件的状态。 下面是两个例: 引用: ``` MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //关联信号 connect(this,&MainWindow::setui,this,&MainWindow::SetUI); mythread = new MyThread(this); mythread->start();//启动线程 } void MainWindow::SetUI() { this->ui->pushButton->setText("开始"); } ``` 在主线程定义了一个槽函数SetUI(),用于更新UI控件pushButton的文本。在构造函数,将主线程的信号setui连接到槽函数SetUI()。在线程,通过emit关键字触发setui信号,从而更新UI控件的状态。 引用: ``` Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); sonThread *sonthread = new sonThread; //创建线程对象 sonthread->label=ui->label; //将主界面UI指针赋给线程的指针对象 sonthread->start(); //启动线程 qDebug()<<"Dialog()"<<QThread::currentThreadId(); } ``` 在主线程创建了一个线程sonthread,并将主界面UI指针赋给线程的指针对象label。在线程,通过访问label指针来更新UI控件label的状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值