Qt结合QThread创建一个QWidget基类,用于方便的在QWidget中执行耗时任务

发布时间 2023-12-01 13:33:22作者: 飘杨......

一、概述

  背景:Qt+OpenCV项目改造,之前项目中的OpenCV算法都是在主线程中执行,导致部分操作或者重复点击的时候界面卡顿。现在想对这块进行改造。

      集合QThread和QWidget设计一个QWidget基类用于方便的执行耗时任务并显示执行结果。子类只需要继承这个QWidget基类,只需要重写一个handle方法用于执行耗时任务(在子线程执行),调用execute()方法开启耗时任务的执行即可。

      现成切换全封装到基类中。

  设计需要的类:

    1.Handler.h定义一个handle接口,子类需要实现它。作用是通过继承关系父类调用子类方法

    2.HandlerThread.h/.cpp 继承QThread,并把Handler类传入,在run方法中调用Handler执行耗时操作,执行完成会通过emit updateUI发送信号

    3.BaseSceneView.h/.cpp此类是基类,此类中会导入HandlerThread类,并继承Handler类。此类会捕获HandlerThread的run方法发出的信号,然后通过updateImage更新UI

    4.子类实现BaseSenceView,重写handle方法,如果有耗时任务则放入handle方法即可。耗时任务是通过父类的execute()方法执行的

二、示例代码

  1.Handler.h

#pragma once

#include <QPixmap>
class Handler
{

public:
    virtual QPixmap handle() {
        return NULL;
    }
};

 

  2.HandlerThread.h/.cpp

#pragma once

#include <QThread>
#include <QPixmap>
#include "Handler.h"

class HandlerThread : public QThread
{
    Q_OBJECT

public:
    HandlerThread(QObject* parent, Handler* handler);
    ~HandlerThread();

private:
    Handler* handler;

protected:
    void run();//线程执行体

signals:
    void updateUI(QPixmap pixmap);//发送更新UI的信号

};
#include "HandlerThread.h"

HandlerThread::HandlerThread(QObject* parent, Handler* handler)
    : QThread(parent)
{
    this->handler = handler;
}

void HandlerThread::run() {
    QPixmap pixmap = handler->handle();
    emit updateUI(pixmap);
}

HandlerThread::~HandlerThread()
{

}

 

  3.BaseSenceView.h/.cpp

#pragma once

#include <stdint.h>
#include <QWidget>
#include <QGraphicsScene>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QResizeEvent>
#include <QSize>
#include <QGraphicsView>
#include <QMessageBox>
#include <QMimeData>
#include <QFileInfo>
#include <QStringList>
#include <QGraphicsPixmapItem>
#include <QDebug>
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>
#include <QGraphicsRectItem>
#include <QGraphicsPolygonItem>
#include <QSlider>
#include <QDebug>
#include <QPixmap>
#include <QImage>
#include <opencv2/opencv.hpp>
#include "../utils/ImageUtils.h"
#include "HandlerThread.h"

class BaseSceneView : public QWidget,public Handler
{
    Q_OBJECT

public:
    BaseSceneView(QWidget* parent = nullptr);
    ~BaseSceneView();

private:
    HandlerThread* mThread;
    QGraphicsView* view;
    QGraphicsScene scene;
    

protected:
    void dragEnterEvent(QDragEnterEvent* event);
    void dropEvent(QDropEvent* event);
    void resizeEvent(QResizeEvent* event);
    
    
public:
    //调用此方法开始执行耗时任务,此方法必须调用否则耗时任务不执行,
    //此处的耗时任务就是,handle()方法中的代码块
    void execute();//执行耗时任务
    //此方法必须要重写,需要把耗时任务放进去
    virtual QPixmap handle() {
        return NULL;
    }
    

private:
    //向场景中添加Bitmap
    void addPixmap(QPixmap pixmap);
    void addImage(QImage image);
    void addMat(cv::Mat mat);
    //更新UI4
    void updateImage(QPixmap pixmap);

public slots:
    void onHSliderValueChanged(int value);



private:
};
#include "BaseSceneView.h"

BaseSceneView::BaseSceneView(QWidget* parent)
    : QWidget(parent)
{
    this->setAcceptDrops(true);//允许往窗口拖放
    this->setFixedSize(QSize(320, 480));
    view = new QGraphicsView(this);//实例化View
    view->setMouseTracking(true);
    view->setFixedSize(this->size());
    view->setAcceptDrops(false);
    view->setScene(&scene);//给View设置场景
    mThread = new HandlerThread(this, this);//子线程,用于处理耗时任务,处理完成后发送信号
    //接收上面线程执行完成后发出的信号
    connect(mThread, &HandlerThread::updateUI, this, [=](QPixmap pixmap) {
        updateImage(pixmap);
        });
}
void BaseSceneView::dragEnterEvent(QDragEnterEvent* event) {
    QStringList acceptedFileTypes;
    acceptedFileTypes.append("jpeg");
    acceptedFileTypes.append("png");
    acceptedFileTypes.append("bmp");
    acceptedFileTypes.append("jpg");//设置可以接受的文件格式类型
    if (event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) {
        QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
        if (acceptedFileTypes.contains(file.suffix().toLower())) {
            event->acceptProposedAction();
        }
    }
    else {
        QMessageBox::critical(this, "Error", "count not load mulit files");
    }
}

//拖动内容到窗口
void BaseSceneView::dropEvent(QDropEvent* event) {
    scene.clear();
    QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
}

void BaseSceneView::resizeEvent(QResizeEvent* event) {

}

void BaseSceneView::onHSliderValueChanged(int value) {


}
void BaseSceneView::addPixmap(QPixmap pixmap) {
    scene.clear();
    QGraphicsPixmapItem* item = new QGraphicsPixmapItem(pixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    scene.addItem(item);//给场景添加内容
}

void BaseSceneView::addImage(QImage image) {
    scene.clear();
    QPixmap pixmap = QPixmap::fromImage(image);
    QGraphicsPixmapItem* item = new QGraphicsPixmapItem(pixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    scene.addItem(item);//给场景添加内容
}

void BaseSceneView::addMat(cv::Mat mat) {
    scene.clear();
    QImage image = ImageUtils::matToQImage(mat);
    QPixmap pixmap = QPixmap::fromImage(image);
    QGraphicsPixmapItem* item = new QGraphicsPixmapItem(pixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    scene.addItem(item);//给场景添加内容
}

void BaseSceneView::updateImage(QPixmap pixmap) {
    addPixmap(pixmap);
}


//执行耗时任务(执行耗时任务前需要调用)
void BaseSceneView::execute() {
    mThread->start();
}

BaseSceneView::~BaseSceneView()
{
}