1.前言
在QT开发时,对于耗时的操作经常会出现窗口阻塞的情况,这个时候如果再频繁操作窗口,可能会导致窗口崩溃。造成这个问题的关键原因是在QT中GUI是作为主线程存在的,比如我们执行一个点击按钮的操作,主线程需要等待槽函数执行结束后才能继续响应,这就导致了窗口的阻塞。搜索相关的内容大多的解决方法都提到使用QThread多线程的方法,这种方法我也测试过的确可以解决,但是使用起来比较复杂,往往需要重写类。尽管moveToThread方法能够灵活一些,也需要将耗时的操作另写入一个类中,还是比较复杂。经实践发现使用QtConcurrent::run能够更简单一些(如果需求仅仅是将GUI线程与数据操作线程分离开)。
2.QtConcurrent
Qt Concurrent
是QT中用于并发编程的框架,可以简化多线程编程,特别是对于简单的并行任务。Qt Concurrent
提供了一些类和函数,例如 QtConcurrent::run
,可以方便地在后台线程中执行函数。如果使用camke构建的项目,在cmakelist文件中只需添加如下代码就可引用这个框架。
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Core Concurrent)
……
target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::Concurrent)
在使用Concurrent
实现将 GUI 和数据计算过程分离时,不需要另外再写一个类,只需要将数据操作的耗时任务单独写入一个函数中,然使用 Qt Concurrent 在后台线程中执行该任务就可以。废话不多说直接上示例代码:
mainwindows.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void worker_pushButton_clicked( int N); // 触发任务的信号,带参数
public slots:
void worker_pushButton_finished(int N); // 耗时任务
private slots:
void on_actionStart_clicked(); // 按钮点击的槽函数(看具体需求,也可以直接通过按钮信号触发耗时任务)
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "utils.h"
#include <matio.h>
#include <iostream>
#include <fstream>
#include <QThread>
#include <QtConcurrent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
// 按钮点击槽函数
void MainWindow::on_actionStart_clicked()
{
connect(this, &MainWindow::worker_pushButton_clicked, [&](int N) {
QtConcurrent::run(this, &MainWindow::worker_pushButton_finished, N);
});
emit worker_pushButton_clicked(100);
}
//耗时任务槽函数
void MainWindow::worker_pushButton_finished(int n)
{
int Sum = 0;
for (int i = 0; i < n; i++)
{
Sum += i;
QThread::sleep(1);
ui->lineEdit->setText(QString::number(Sum));
}
}
在上面的代码中我是通过qtdesigner设计了窗口,添加了一个pushbutton和一个lineEdit,在点击按钮后将触发任务的信号与任务的槽函数绑定并使用QtConrurrent::run将任务放入线程中并行,而后触发信号。