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文件直接进行拉控件搭界面简单易行一些
- 我用的是VS中的qt插件 qt tools 来进行界面开发,如果没有在
-
基类可以在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();
}
- 运行如下: