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

news/2024/12/24 1:41:30 标签: qt, 开发语言, 计算机视觉, 人工智能, opencv

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.niftyadmin.cn/n/5797204.html

相关文章

C#经典算法面试题

网络上收集的一些C#经典算法面试题&#xff0c;分享给大家 # 递归算法 ## C#递归算法计算阶乘的方法 > 一个正整数的阶乘&#xff08;factorial&#xff09;是所有小于及等于该数的正整数的积&#xff0c;并且0的阶乘为1。自然数n的阶乘写作n!。1808年&#xff0c;基斯顿…

MySQL InnoDB 存储引擎详解

InnoDB 是 MySQL 中最常用、最强大的存储引擎之一&#xff0c;其支持事务、外键、行级锁等特性&#xff0c;非常适合对可靠性、并发性要求较高的场景。本文将详细解析 InnoDB 的核心特性、内部机制以及使用场景&#xff0c;帮助你更好地理解和优化 MySQL 数据库。 1. 为什么选择…

【C#】WebSoket 演示(使用websocket-sharp库)

Example 3服务器 Example1 客户端 示例3 此源代码片段包含了如何使用WebSocketSharp库来创建一个HTTP服务器&#xff0c;并提供WebSocket服务。 分析整个代码&#xff0c;我们可以归纳出以下关键信息&#xff1a; 导入了一系列重要的命名空间&#xff0c;包括系统配置、加密库…

【数据安全】如何保证其安全

数据安全风险 数字经济时代&#xff0c;数据已成为重要的生产要素。智慧城市、智慧政务的建设&#xff0c;正以数据为核心&#xff0c;推动城市管理的智能化和公共服务的优化。然而&#xff0c;公共数据开放共享与隐私保护之间的矛盾日益凸显&#xff0c;如何在确保数据安全的…

HTTPS如何通过CA证书实现安全通信,以及HTTPS的局限性

关键概念&#xff1a; HTTPS&#xff1a; 超文本传输安全协议&#xff0c;是HTTP的安全版本&#xff0c;通过SSL/TLS协议对数据进行加密&#xff0c;提供身份验证、数据加密和数据完整性。CA&#xff08;证书颁发机构&#xff09;&#xff1a; 受信任的第三方机构&#xff0c;…

类OCSP靶场-Kioptrix系列-Kioptrix Level 5(2014)

一、前情提要 二、实战打靶 1. 信息收集 1.1. 主机发现 1.2. 端口扫描 1.3.目录遍历 1.4. 敏感信息 2.漏洞发现 2.1. 任意文件访问 2.2. web服务8080端口访问限制绕过 2.3. 远程命令执行 2.4. webshell进行getshell 2.5. 提权 一、前情提要 kali黑客-利用searchsp…

Vue3之状态管理Vuex

Vuex作为Vue.js的官方状态管理库&#xff0c;在大型或复杂的前端项目中扮演着至关重要的角色。本文将从Vuex的原理、特点、应用场景等多个方面进行深入解析&#xff0c;并通过代码示例展示如何在Vuex中实现特定功能。 一、Vuex原理 Vuex是一个专为Vue.js应用程序开发的状态管…

【Java基础面试题027】Java的StringBuilder是怎么实现的?

回答重点 StringBuilder主要是为了解决String对象的不可变性问题&#xff0c;还有单线程环境下StringBuffer的性能问题。提供了高效动态的字符串拼接和修改操作。大致需要实现append、insert等功能 大致核心实现如下&#xff1a; 内部使用字符数组&#xff08;char[] value&…