功能:
使用多线程采集视频刷新主窗口,完成跨线程通信。
采用队列传图
说明:
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