Qt主线程和子线程协作更新UI
一、概述
现有一个需求:Qt+OpenCV执行角点检测。使用Qt当做UI界面进行角点检测。我们知道像角点检测这种算法需要大量的计算,是比较耗时的一个操作。如果把计算+UI显示全放入主线程中计算,那么
UI界面有可能就会卡主,进而出现应用程序无响应的情况。
要求:
使用QtThread进行角点检测,让Qt的主线程更新UI,即子线程角点检测完成后主线程(UI线程)再更新UI。这样可以避免主线程由于计算量打被卡死的情况。
解决步骤:
1.使用Qt的信号槽来解决问题
2.在线程中发射一个信号,主线程的UI界面去接收这个信号。(emit 信号方法)
3.UI界面更新UI即可
二、代码示例
1.自定义一个QtThread。UpdateImageThread.h/.cpp
#pragma once #include <QThread> #include <QPixmap> #include <QImage> #include "../base/UIInterface.h" #include <QDebug> class UpdateImageThread : public QThread { Q_OBJECT public: UpdateImageThread(QObject *parent, UIInterface *base); UpdateImageThread( UIInterface* base); ~UpdateImageThread(); protected: void run(); signals: void updateUi(QPixmap pixmap); private: UIInterface* base; };
#include "UpdateImageThread.h" UpdateImageThread::UpdateImageThread(QObject* parent, UIInterface* base) : QThread(parent) { this->base = base; } UpdateImageThread::UpdateImageThread(UIInterface* base) { this->base = base; } //程序执行的run方法 void UpdateImageThread::run() { QPixmap pixmap = base->generate(); qDebug() << "QThread 已执行完成,发送信号更新UI"; emit updateUi(pixmap); } UpdateImageThread::~UpdateImageThread() { }
这里面有一个自定义的UIInterface.h的头文件,里面定义了一个UIInterface的类,这个类被需要更新UI的界面继承,即CornerHarrisWindow类继承。类里定义了一个generate的方法,用于生成一个QPixmap。从外部传递这个地对象给当前线程,当前线程拿到这个对象后在run方法中调用generate方法。类定义如下:
#pragma once #include <QPixmap> class UIInterface { public: virtual QPixmap generate() { return NULL; } };
2.定义CornerHarrisWindow.h/.cpp,其中CornerHarrisWindow类继承IInterface类,并重写generate方法
#pragma once #include <QWidget> #include "CornerHarris.h" #include "../../common/CommonGraphicsView.h" #include <QPixmap> #include <QLineEdit> #include <QPushButton> #include <QHBoxLayout> #include <QLabel> #include <thread> #include "../../common/base/UIInterface.h" #include "../../common/thread/UpdateImageThread.h" using namespace std; class CornerHarrisWindow : public CommonGraphicsView, UIInterface { Q_OBJECT public: CornerHarrisWindow(QWidget* parent = nullptr); ~CornerHarrisWindow(); protected: QPixmap generate(); private: Mat src, gray, resultImage; CornerHarris* cornerHarris; int thresh = 130; QLineEdit* edit_blockSize; QLineEdit* edit_kSize; QLineEdit* edit_k; QLineEdit* edit_thresh; QPushButton* btn_submit; UpdateImageThread* mThread; public: QPixmap showCornerHarris(); void dropEvent(QDropEvent* event) override; public slots: void updateImage(QPixmap pixmap); };
#include "CornerHarrisWindow.h" void runFun(CornerHarrisWindow* window) { qDebug() << "显示角点"; window->showCornerHarris(); } CornerHarrisWindow::CornerHarrisWindow(QWidget* parent) : CommonGraphicsView(parent) { this->setWindowTitle("角点检测(cornerHarris)"); cornerHarris = new CornerHarris(); QLabel* blockSizeLabel = new QLabel(this); blockSizeLabel->setText("block_size:"); blockSizeLabel->setFixedSize(QSize(80, 30)); //创建一个输入框 edit_blockSize = new QLineEdit(this); edit_blockSize->setPlaceholderText("请输入block_size"); edit_blockSize->setText("2"); edit_blockSize->setFixedSize(QSize(50, 30)); edit_blockSize->move(blockSizeLabel->x() + blockSizeLabel->width() + 10, 0); QLabel* kLabel = new QLabel(this); kLabel->setText("k"); kLabel->setFixedSize(QSize(80, 30)); kLabel->move(0, edit_blockSize->y() + edit_blockSize->height() + 10); edit_k = new QLineEdit(this); edit_k->setPlaceholderText("请输入k"); edit_k->setText("0.04"); edit_k->setFixedSize(QSize(50, 30)); edit_k->move(kLabel->x() + kLabel->width() + 10, edit_blockSize->y() + edit_blockSize->height() + 10); QLabel* kSizeLabel = new QLabel(this); kSizeLabel->setText("ksize:"); kSizeLabel->setFixedSize(QSize(80, 30)); kSizeLabel->move(0, edit_k->y() + edit_k->height() + 10); edit_kSize = new QLineEdit(this); edit_kSize->setText("3"); edit_kSize->setFixedSize(QSize(50, 30)); edit_kSize->move(kSizeLabel->x() + kSizeLabel->width() + 10, edit_k->y() + edit_k->height() + 10); QLabel* threshLabel = new QLabel(this); threshLabel->setText("thresh:"); threshLabel->setFixedSize(QSize(80, 30)); threshLabel->move(0, edit_kSize->y() + edit_kSize->height() + 10); edit_thresh = new QLineEdit(this); edit_thresh->setText("130"); edit_thresh->setFixedSize(QSize(50, 30)); edit_thresh->move(threshLabel->x() + threshLabel->width() + 10, edit_kSize->y() + edit_thresh->height() + 10); btn_submit = new QPushButton(this); btn_submit->setText("开始检测"); btn_submit->move(0, edit_thresh->y() + edit_thresh->height() + 10); mThread = new UpdateImageThread(this); connect(btn_submit, &QPushButton::clicked, [=]() { qDebug() << "您点击了检测按钮:"; qDebug() << "thresh:" << edit_thresh->text().toInt(); qDebug() << "block_size:" << edit_blockSize->text().toInt(); qDebug() << "ksize:" << edit_kSize->text().toInt(); qDebug() << "k:" << edit_k->text().toDouble(); //showCornerHarris(); //std::thread myThread; //myThread = std::thread(runFun, this); mThread->start(); }); connect(mThread, &UpdateImageThread::updateUi, this, [=](QPixmap pixmap) { updateImage(pixmap); }); } void CornerHarrisWindow::dropEvent(QDropEvent* event) { QString mPath = event->mimeData()->urls().at(0).toLocalFile(); qDebug() << mPath; QFileInfo file(mPath); filePath = file.absoluteFilePath(); //Mat src = imread(filePath.toStdString()); //imshow("src", src); /* showCornerHarris();*/ QPixmap pixmap = generate(); updateImage(pixmap); } /** * 角点检测 * @brief CornerHarrisWindow::showCornerHarris */ QPixmap CornerHarrisWindow::showCornerHarris() { cornerHarris->updateParams(edit_thresh->text().toInt(), edit_blockSize->text().toInt(), edit_kSize->text().toInt(), edit_k->text().toDouble()); cornerHarris->showCornerHarris(filePath.toStdString().c_str(), resultImage); QImage image = ImageUtils::matToQImage(resultImage); QPixmap pixmap = QPixmap::fromImage(image); return pixmap; /*QGraphicsPixmapItem* item = new QGraphicsPixmapItem(pixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); scene.addItem(item);*/ } QPixmap CornerHarrisWindow::generate() { return showCornerHarris(); } //更新UI void CornerHarrisWindow::updateImage(QPixmap pixmap) { qDebug() << "收到信号开始更新UI"; QGraphicsPixmapItem* item = new QGraphicsPixmapItem(pixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); scene.addItem(item); } CornerHarrisWindow::~CornerHarrisWindow() { }
1.在CornerHarrisWindow.h中定义了一个槽函数updateImage,这个槽函数就是用于接收UI的。
public slots: void updateImage(QPixmap pixmap);
2.信号是在UpdateImageThread.h中定义的updateUi
signals: void updateUi(QPixmap pixmap);
3.发射信号是在UpdateImageThread中的run方法,使用emit 信号发出的
void UpdateImageThread::run() { QPixmap pixmap = base->generate(); qDebug() << "QThread 已执行完成,发送信号更新UI"; emit updateUi(pixmap); }
4.关联信号和槽函数
mThread = new UpdateImageThread(this); connect(btn_submit, &QPushButton::clicked, [=]() { //myThread = std::thread(runFun, this); mThread->start(); }); connect(mThread, &UpdateImageThread::updateUi, this, [=](QPixmap pixmap) { updateImage(pixmap); });
所以信号是由QThread发出,并由CornerHarrisWindow类接收,最终触发槽函数updateImage,然后更新UI
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库