QThread创建多线程之子类化QThread方法

参考《qt5与opencv3计算机视觉应用开发》

通过创建一个示例项目来说明如何使用QThread及其附属类创建多线程应用程序。qt里创建多线程有两种方法:一是子类化 QThread 并重写run方法,另一种是使用moveToThread函数。

1 子类化QThread

1.1 创建应用程序

创建名为 MultithreadedCV 的Qt 控件应用程序,注意在 .pro 文件中包含 opencv。在 ui 界面上添加两个 QLbel 类标签,网格布局,左边标签为 inVideo,右边标签为 outVideo。将两者的 alignment/Horizontal 属性设置为 AlignHCenter。如下图:

在这里插入图片描述

1.2 创建 QThread 子类

右键项目,从菜单中选择 " Add new",创建名为 VideoProcessorThread 的新类。确保在新的类向导中的界面与下图一致:
在这里插入图片描述

1.3 更新代码

首先在新创建的 .h 头文件中更新包含文件 #include,添加

#include <QThread>
#include "opencv2/opencv.hpp"
#include <QPixmap>

更新类名,将头文件中的继承自 QObject 改成继承自 QThread,如下:

class VideoProcessorThread : public QThread

更新新创建的 .cpp 文件中的构造函数:

VideoProcessorThread::VideoProcessorThread(QObject *parent) : QThread(parent)
{

}

然后就可以编辑函数定义和实现了。
向新添加的头文件中添加一些必要的声明,将下列代码添加至类的私有成员区域:

void run() override;

然后将下列代码添加至 signals 部分:

void inDisplay(QPixmap pixmap);
void outDisplay(QPixmap pixmap);

将 run 函数的实现添加到 .cpp 文件:

 void VideoProcessorThread::run()
{
    using namespace cv;

    VideoCapture camera(0);
    Mat inFrame, outFrame;
    while(camera.isOpened() && !isInterruptionRequested())
    {
        camera >> inFrame;
        if(inFrame.empty())
            continue;
        bitwise_not(inFrame, outFrame);

        emit inDisplay(
                    QPixmap::fromImage(
                        QImage(
                            inFrame.data,
                            inFrame.cols,
                            inFrame.rows,
                            inFrame.step,
                            QImage::Format_RGB888)
                        .rgbSwapped()));

        emit outDisplay(
                    QPixmap::fromImage(
                        QImage(
                            outFrame.data,
                            outFrame.cols,
                            outFrame.rows,
                            outFrame.step,
                            QImage::Format_RGB888)
                        .rgbSwapped()));
    }
}

1.4 启动程序

为了控制线程及其执行行为,需要使用以下函数:

  • start: 用于启动一个尚未启动的线程,该函数通过调用所实现的 run 函数来启动执行。在这里就是我们实现的子类 run()。
  • terminate: 用于强制终止线程。
  • setTerminationEnabled: 用于允许或禁止 terminater 函数。
  • wait: 用于阻塞线程(强制等待),直至线程完成或者达到超时值。
  • requestInterruption 和 isRequestInterrupted: 用于设置和获取中断请求状态。
  • isRunning 和 isFinished: 用于请求线程的执行状态。
    在 MainWindow 中使用线程(调用线程),将其头文件包含进 mainwindow.h:
#include "videoprocessorthread.h"

然后,将下面的代码行添加到 MainWindow 的私有成员部分:

VideoProcessorThread processor;

使用 connect 将连接信号和槽,在 MainWindow 构造函数中添加如下代码:

connect(&processor,
        SIGNAL(inDisplay(QPixmap)),
        ui -> inVideo,
        SLOT(setPixmap(QPixmap)));

connect(&processor,
        SIGNAL(outDisplay(QPixmap)),
        ui -> outVideo,
        SLOT(setPixmap(QPixmap)));

processor.start();

将下列代码添加到 MainWindow 析构函数中,放在 delete ui; 代码行之前:

processor.requestInterruption();
processor.wait();

在析构函数中调用 requestInterruption() 函数,能够保证一旦 MainWindow 关闭并且删除 GUI 之前请求线程停止,通过调用 wait 函数,能够确保等待线程完成清理并安全执行完毕之后再继续执行 delete 指令。

1.5 流程总结

  1. 创建应用程序,一定要包含 QObject,修改 ui 界面,修改 ObjectName 属性,修改布局等;

  2. 创建新的子类,在头文件中添加要使用的文件,修改相关类和构造函数; 在子类中添加函数声明和实现;

  3. 在主程序中添加包含文件,并在头文件中的私有成员中使用 VideoProccessorThread 类的构造函数创建线程指针processer,然后添加信号和槽的连接;

  4. 调用 start() 函数启动线程,在析构函数中添加 requestInterruption 和 wait 函数用于保证安全。

可以在一个进程中创建多个线程,方法就是创建多个 QThread 的实例(即为创建多个类),然后在 MainWindow 的头文件中创建一个 VideoProcessorThread(创建的类名) 的指针,然后使用 start() 函数即可启动多个线程。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值