当前位置: 首页 > article >正文

QT_Demo(1)之实现多线程实现简单的电脑摄像头视频流开关

QT_Demo(1)之实现多线程实现简单的电脑摄像头视频流开关

  • 使用qt中的多线程进行功能控制:继承QThread
  • 直接通过代码进行UI搭建
  • 简单示例使用信号与槽

1. 功能介绍

  • 首先想搭一个界面可以交互,从而实现手动开关笔记本摄像头的目的

  • 想通过多线程进行功能实现,从而提升稳定性和运行速度

  • 基础界面设置为QWidget,图像通过QLabel 显示

  • 界面一览:
    在这里插入图片描述

  • 创建项目

    • 我用的是VS中的qt插件 qt tools 来进行界面开发,如果没有在扩展->管理扩展->联网下载即可
    • 下方给出了两种工程种类,1是一个全空的qt 工程,2比1多了个UI文件,并在创建项目的时候可以选择UI文件的基类,在此我选择2进行开发吧
    • 工程2自带了UI文件,我目前对纯代码界面开发还不太熟练,稍微复杂的话还是直接通过QDesigner打开UI文件直接进行拉控件搭界面简单易行一些
      在这里插入图片描述
  • 基类可以在QMainWindow 、QWidget、QDialog中选择,这里随便选一个,先进入工程
    在这里插入图片描述

  • 选择完成后工程文件列表如下:
    在这里插入图片描述

2. 代码实现

  • 本文中主要通过纯代码进行UI搭建

  • 本次使用了opencv,需要在项目属性中配置opencv

  • 右键项目添加新建项 -> 添加类
    在这里插入图片描述

  • 代码如下:

  • VideoDisplayWidget.h

#include <QApplication>
#include <QWidget>
#include <QImage>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QThread>
#include <opencv2/opencv.hpp>

#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>

// 视频采集线程
class VideoCaptureThread : public QThread
{
    Q_OBJECT
public:
    VideoCaptureThread(QObject* parent = nullptr);
    ~VideoCaptureThread();

    void startCapture();

    void stopCapture();

    void run() override;


public:
    cv::VideoCapture cap;

signals:
    void newFrame(const QImage& frame); // 向主线程发送新帧

private:
    bool running;
};

// 主窗口类
class VideoDisplayWidget : public QWidget
{
    Q_OBJECT
public:
    VideoDisplayWidget(QWidget* parent = nullptr);

    ~VideoDisplayWidget();

public slots:
    void onNewFrame(const QImage& frame);

    // 启动视频采集线程
    void startCapture();

    // 停止视频采集线程
    void stopCapture();

private:
    QLabel* label;
    VideoCaptureThread* captureThread;

};

  • VideoDisplayWidget.cpp
#include"VideoDisplayWidget.h"

// 视频采集线程
VideoCaptureThread::VideoCaptureThread(QObject* parent) : QThread(parent), running(false) {}
VideoCaptureThread::~VideoCaptureThread() {}

void VideoCaptureThread::startCapture()
{
    cap.open(0); // 打开默认摄像头
    if (!cap.isOpened()) {
        qWarning("Failed to open the camera");
        return;
    }
    running = true;
    start();
}

void VideoCaptureThread::stopCapture()
{
    running = false;
}

void VideoCaptureThread::run()
{
    while (running) {
        cv::Mat frame;
        if (cap.read(frame)) {
            cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB); // 转换为 RGB 格式
            QImage image(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
            emit newFrame(image);
        }
        msleep(30); // 控制帧率
    }
}

// -----------------------VideoDisplayWidget------------------------------------------

// 主窗口类
VideoDisplayWidget::VideoDisplayWidget(QWidget* parent) : QWidget(parent), label(new QLabel(this)), captureThread(new VideoCaptureThread())
{
    // 设置界面布局
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(label);
    QPushButton* startButton = new QPushButton("Start Capture", this);
    QPushButton* stopButton = new QPushButton("Stop Capture", this);
    layout->addWidget(startButton);
    layout->addWidget(stopButton);

    // 设置窗口大小
    setFixedSize(640, 480);
    this->setWindowTitle(QString::fromLocal8Bit("VideoManager"));

    // 初始化线程和控件
    label->setFixedSize(640, 480);
    label->setAlignment(Qt::AlignCenter);
    connect(captureThread, &VideoCaptureThread::newFrame, this, &VideoDisplayWidget::onNewFrame);
    connect(startButton, &QPushButton::clicked, this, &VideoDisplayWidget::startCapture);
    connect(stopButton, &QPushButton::clicked, this, &VideoDisplayWidget::stopCapture);
}

VideoDisplayWidget::~VideoDisplayWidget()
{
    captureThread->stopCapture();
    captureThread->wait();
}

void VideoDisplayWidget::onNewFrame(const QImage& frame)
{
    label->setPixmap(QPixmap::fromImage(frame));
}

void VideoDisplayWidget::startCapture()
{
    captureThread->startCapture();
}

void VideoDisplayWidget::stopCapture()
{
    captureThread->stopCapture();
}
  • main.cpp 更改如下:
//#include "videocam1.h"
#include <QtWidgets/QApplication>
#include"VideoDisplayWidget.h"

int main(int argc, char *argv[])
{
    //基类时QMainWindow
    QApplication a(argc, argv);
    //videocam1 w;

    //基类时QWidget
    VideoDisplayWidget w;
    w.show();
    return a.exec();
}

3. designer 搭建UI

  • 使用qdesigner打开ui文件如下:起始UI主要包含右上角4项:中心主控件、菜单栏、工具栏和状态栏
  • 然后简单拉两个按钮+个label进行搭建
  • 窗体->查看代码->保存 会生成ui_videocam1.h,它包含了界面的相关内容:控件及尺寸、位置等属性
    在这里插入图片描述
  • 添加布局,并将所有控件的策略改为preferred,最后将总布局设为网格布局,UI就支持整体缩放了,具体实现可参考:https://blog.csdn.net/yohnyang/article/details/128469084
    在这里插入图片描述
  • 然后在项目中的VideoDisplayWidget.h,VideoDisplayWidget.cpp 移除项目,videocam1.h,videocam1.cpp做出如下更改:
#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_videocam1.h"
#include <QApplication>
#include <QWidget>
#include <QImage>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QThread>
#include <opencv2/opencv.hpp>

#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>


// 视频采集线程
class VideoCaptureThread : public QThread
{
    Q_OBJECT
public:
    VideoCaptureThread(QObject* parent = nullptr);
    ~VideoCaptureThread();

    void startCapture();

    void stopCapture();

    void run() override;


public:
    cv::VideoCapture cap;

signals:
    void newFrame(const QImage& frame); // 向主线程发送新帧

private:
    bool running;
};



class videocam1 : public QMainWindow
{
    Q_OBJECT

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

public slots:
    void onNewFrame(const QImage& frame);

    // 启动视频采集线程
    void startCapture();

    // 停止视频采集线程
    void stopCapture();
    
private:
    Ui::videocam1Class ui;

    VideoCaptureThread* captureThread;
};

#include "videocam1.h"


// 视频采集线程
VideoCaptureThread::VideoCaptureThread(QObject* parent) : QThread(parent), running(false) {}
VideoCaptureThread::~VideoCaptureThread() {}

void VideoCaptureThread::startCapture()
{
    cap.open(0); // 打开默认摄像头
    if (!cap.isOpened()) {
        qWarning("Failed to open the camera");
        return;
    }
    running = true;
    start();
}

void VideoCaptureThread::stopCapture()
{
    running = false;
}

void VideoCaptureThread::run()
{
    while (running) {
        cv::Mat frame;
        if (cap.read(frame)) {
            cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB); // 转换为 RGB 格式
            QImage image(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
            emit newFrame(image);
        }
        msleep(30); // 控制帧率
    }
}



videocam1::videocam1(QWidget *parent)
{
    ui.setupUi(this);
    
    this->setWindowTitle(QString::fromLocal8Bit("VideoManager"));
    captureThread = new VideoCaptureThread();
    connect(captureThread, &VideoCaptureThread::newFrame, this, &videocam1::onNewFrame);
    connect(ui.pushButton, &QPushButton::clicked, this, &videocam1::startCapture);
    connect(ui.pushButton_2, &QPushButton::clicked, this, &videocam1::stopCapture);
}

videocam1::~videocam1()
{}

void videocam1::onNewFrame(const QImage& frame)
{
    ui.label->setPixmap(QPixmap::fromImage(frame));
    ui.label->setScaledContents(true);
}


void videocam1::startCapture()
{
    captureThread->startCapture();
}

void videocam1::stopCapture()
{
    captureThread->stopCapture();
}
  • main()更改如下:
#include "videocam1.h"
#include <QtWidgets/QApplication>
//#include"VideoDisplayWidget.h"

int main(int argc, char *argv[])
{
    //基类时QMainWindow
    QApplication a(argc, argv);
    videocam1 w;

    //基类时QWidget
    //VideoDisplayWidget w;
    w.show();
    return a.exec();
}

  • 运行如下:
    在这里插入图片描述

http://www.kler.cn/a/448947.html

相关文章:

  • Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
  • PingCAP TiDB数据库专员PCTA认证笔记
  • leetcode hot100除自身以外的数组的乘积
  • 问题解决:发现Excel中的部分内容有问题。是否让我们尽量尝试恢复? 如果您信任此工作簿的源,请单击“是”。
  • [Unity Shader][图形渲染] Shader数学基础11 - 复合变换详解
  • 2025系统架构师(一考就过):案例题之一:嵌入式架构、大数据架构、ISA
  • 叉车作业如何确认安全距离——UWB测距防撞系统的应用
  • Kubernetes APF(API 优先级和公平调度)简介
  • guava本地缓存+自定义线程工厂和线程池
  • Day 15:Spring 框架基础
  • Sass变量的妙用:提升CSS开发效率与可维护性
  • Web安全攻防入门教程——hvv行动详解
  • 深入理解 OpenCV 的距离变换(cv2.distanceTransform)及其应用
  • 生鲜电商新篇章:在线销售系统的创新设计
  • 二叉树总结
  • 【IMU:视觉惯性SLAM系统】
  • Redis分布式锁释放锁是否必须用lua脚本?
  • 聊一聊性能测试是如何开展的?
  • Unittest框架及自动化测试实现流程
  • Blender 中投影仪的配置与使用
  • RT-DETR融合[ECCV2024]FADformer中的FFCM模块
  • 【1 day】OtterRoot:Netfilter 通用 Root
  • 第22天:信息收集-Web应用各语言框架安全组件联动系统数据特征人工分析识别项目
  • SecureCRT汉化版
  • 3D架构图软件 iCraft Editor 正式发布 @icraftplayer-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
  • 关于JavaScript中的this-笔记