功能:
使用多线程采集视频刷新主窗口,完成跨线程通信。
采用队列传图

说明:
1为什么不用qt本身的信号和槽函数实现或者Qthread?
这个也是可以实现的,但是考虑学习的通用性,例如将来不用qt写界面或者其他嵌入式设备部署(树莓派+英伟达开发板),这套机制也是可以复用的。这样将会很灵活不用局限于QT本身拥有的东西,使用队列可以自己灵活增加数据类型。2线程配合队列完成跨线程通信。
2注意线程锁的使用。
3尝试使用进程代替线程,失败了,入口错误?
为什么要用进程,像树莓派好像使用多线程是无效的,实际还是在一个线程跑,使用多进程才是分开了加速。
进程也会带来问题,队列跨进程之后里所表示的数据将无法识别问题。
实现过程
1首先搭建环境
https://www.cnblogs.com/kekeoutlook/p/13964523.html
2创建工程
目录文件只有3个

2-1激活环境
activate py37_tfgpu1131_keras215_opencv341
2-2创建界面
运行
designer
生成一个新界面.ui

一个 lable显示视频和两个按钮
布局 整体栅格布局 两个局部水平布局
默认布局-是初始化给多大就是多大,不会随着窗口自适应变大

保存成 .ui文件
转换成py文件
python -m PyQt5.uic.pyuic .输入名字.ui -o .输出名字.py
生成的界面布局结果
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '.UI_vedio.ui'
#
# Created by: PyQt5 UI code generator 5.15.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(50, 20, 691, 441))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(50, 470, 691, 80))
self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.pushButton_1 = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
self.pushButton_1.setObjectName("pushButton_1")
self.horizontalLayout_2.addWidget(self.pushButton_1)
self.pushButton_2 = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout_2.addWidget(self.pushButton_2)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "视频流"))
self.pushButton_1.setText(_translate("MainWindow", "开始播放"))
self.pushButton_2.setText(_translate("MainWindow", "结束播放"))
主程序
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QFileDialog
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets, QtCore, QtGui
#导入图像库
import cv2
#进程测试
#import multiprocessing
#线程测试
import threading
from queue import Queue
#导入自己创建的图形类
from UI_vedio import *
#一个是这个类本身的,一个是这个类继承
class Example(QMainWindow,Ui_MainWindow):
def __init__(self):
#类的初始化
super(Example, self).__init__()
self.setupUi(self)#界面控件初始化
#相机线程初始化
self.CAM_Int()
#按钮绑定函数
self.pushButton_1.clicked.connect(self.on_video) #开始按键
#按键函数
def on_video(self):
if self.open_flag:
self.pushButton_1.setText('关闭视频')
self.open_flag=bool(0)
else:
self.pushButton_1.setText('开始播放')
self.open_flag=bool(1)
#self.open_flag = bool(1-self.open_flag)#
self.lock.acquire()#获得锁
if self.qcontrol.full():
self.qcontrol.get()
self.qcontrol.put(self.open_flag)
self.lock.release()#释放锁
def CAM_Int(self):
#控制信号量
self.open_flag=1
self.qcontrol = Queue(1) #用于主线程控制播放线程控制信号
self.qcontrol.put(self.open_flag)
self.qframe =Queue(2) #用于主线程读取次线程图像数据
#创建播放线程
self.CAM_Play()
def CAM_Play(self):
#开启线程
self.lock = threading.Lock()
self.t1_video = threading.Thread(target=self.videogo, args=(self.qcontrol,self.qframe,self.lock))
self.t1_video.start()
#多进程报错
'''
self.lock=multiprocessing.Lock()
p1=multiprocessing.Process(target=self.videogo,args=(self.qcontrol,self.qframe,self.lock,))
p1.daemon = True
p1.start()
'''
#定时器20毫秒更新画板
self.timer = QTimer(self)
self.timer.timeout.connect(self.CAM_SetImage)
self.timer.start(20) #单位为毫秒
#配合定时器刷新图像显示
def CAM_SetImage(self):
if self.qframe.empty():
pass
else:
image=self.qframe.get()
self.label.setPixmap(QPixmap.fromImage(image))
def videogo(self,qcontrol,qframe,lock):
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,800)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,600)
cap.set (cv2.CAP_PROP_FPS,25)
while (cap.isOpened()==True):
#判断是否需要开启或者关闭
if bool(1-qcontrol.empty()):
go=qcontrol.get()
print(go)
if go:
pass
else:
break
pass
ret, frame = cap.read()
if ret:
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QImage.Format_RGB888)#在这里可以对每帧图像进行处理,
qtimg = convertToQtFormat.scaled(600, 600, Qt.KeepAspectRatio)
lock.acquire()#获取锁
if qframe.full():#如果满了就清空一帧
qframe.get()
qframe.put(qtimg)
lock.release()# 释放锁
else:
continue
if __name__ == '__main__':
app = QApplication(sys.argv)
ui=Example()
ui.show()
sys.exit(app.exec_())
运行程序
python .4UI_Video_thread_queue.py
