方案起源
最近做了一个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模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓