幼儿园扛把子95 发表于 2023-2-28 11:27:27

求助pyqt多线程卡死的问题

各位大佬,我在主程序中调用另一个模块的摄像头录制,卡死在webcam.py的start_capture的while循环中了,主程序中的停止线程(StopCapturingThread)无法触发,需要如何修改呢?
相关代码如下,感谢各位大佬的帮助!:

-------------main.py-------------
from UIs.mainwindow import Ui_Form
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from Module.VideoRecording.webcam import WebCam
from Module.SmartEyePro.external_interfaces import ExternalInterface
import os
import time


class InteractiveFeatures(WebCam, ExternalInterface):
    def __init__(self):
      self.webcam = None
      self.sep = None
      self.biosignalsplux = None
      self.brainproducts = None
      self.connected_dict = {'webcam': False, 'sep': False, 'biosignalsplux': False, 'brainproducts': False}
      self.init_list = []

      self.sep_logname = None
      self.sep_last_ip = None

    # 设置摄像头交互功能
    def webcam_init(self):
      VideoPath = r'D:\PycharmProjects\IntelligentAssessment\inset'
      PNGPath = r'D:\PycharmProjects\IntelligentAssessment\inset'
      LogPath = r'D:\PycharmProjects\IntelligentAssessment\inset'
      CSVPath = r'D:\PycharmProjects\IntelligentAssessment\inset'
      IP = '192.168.1.2'
      self.webcam = WebCam(VideoPath, PNGPath, CSVPath, IP)
      self.connected_dict['webcam'] = True

    def webcam_disconnect(self):
      self.webcam = None
      self.connected_dict['webcam'] = False

    def _webcam_start(self):
      self.webcam.start_capture()

    def _webcam_stop(self):
      self.webcam.stop_capture()

    # 选中设备开始采集
    def start_capture(self):
      for progress, state in self.connected_dict.items():
            if state:
                self.init_list.append(progress)
      init_number = len(self.init_list)
      for i in range(init_number):
            code = "t{} = Thread(target=self._{}_start)".format(i + 1, self.init_list)
            exec(code)
      for i in range(init_number):
            code = "t{}.start()".format(i + 1)
            eval(code)
      for i in range(init_number):
            code = "t{}.join()".format(i + 1)
            eval(code)

    # 选中设备结束采集
    def stop_capture(self):
      init_number = len(self.init_list)
      for i in range(init_number):
            code = "self._{}_stop()".format(self.init_list)
            eval(code)
      for i in range(init_number):
            code = "self.{}_init()".format(self.init_list)
            eval(code)
      self.init_list = []

    # 生成当前时间
    def get_now(self):
      now = time.localtime()
      nowt = time.strftime("%Y-%m-%d-%H-%M-%S", now)
      return nowt


# 开始采集线程
class StartCapturingThread(QThread):

    startCapturingSignal = Signal()

    def __init__(self, parent=None):
      super(StartCapturingThread, self).__init__(parent)

    def run(self):
      self.startCapturingSignal.emit()


# 结束采集线程
class StopCapturingThread(QThread):

    stopCapturingSignal = Signal()

    def __init__(self, parent=None):
      super(StopCapturingThread, self).__init__(parent)

    def run(self):
      self.stopCapturingSignal.emit()


class MyMainForm(QMainWindow, Ui_Form, InteractiveFeatures, StartCapturingThread, StopCapturingThread):
    def __init__(self, parent=None):
      super(MyMainForm, self).__init__(parent)
      self.start_capturing_thread = StartCapturingThread()
      self.stop_capturing_thread = StopCapturingThread()
      self.interactive_features = InteractiveFeatures()
      self.setupUi(self)
      self.pushButton.clicked.connect(self.btn1clicked)
      self.pushButton_2.clicked.connect(self.btn2clicked)
      self.pushButton_5.clicked.connect(self.btn5clicked)
      self.pushButton_6.clicked.connect(self.btn6clicked)
      self.pushButton_15.clicked.connect(self.Start)
      self.pushButton_16.clicked.connect(self.Stop)
      self.pushButton_11.clicked.connect(self.btn11clicked)

    def btn1clicked(self):
      IP = self.sep_ip.text()
      self.interactive_features.sep_logname = self.sep_logfile.text()
      ans = self.interactive_features.sep_init(IP)
      if ans:
            self.label_9.setPixmap(QPixmap(u"SrcPics/connect_green.svg"))
            self.label_10.setPixmap(QPixmap(u"SrcPics/disconnect_black.svg"))
            self.sep_ip.setEnabled(False)
            self.sep_logfile.setEnabled(False)
      else:
            QMessageBox.information(self, "连接失败", "连接失败,请检查设备后重试!")

    def btn5clicked(self):
      self.label_21.setPixmap(QPixmap(u"SrcPics/connect_green.svg"))
      self.label_22.setPixmap(QPixmap(u"SrcPics/disconnect_black.svg"))
      self.camera_ip.setEnabled(False)
      self.camera_frequency.setEnabled(False)
      self.interactive_features.webcam_init()

    def btn2clicked(self):
      self.label_9.setPixmap(QPixmap(u"SrcPics/connect_black.svg"))
      self.label_10.setPixmap(QPixmap(u"SrcPics/disconnect_red.svg"))
      self.sep_ip.setEnabled(True)
      self.sep_logfile.setEnabled(True)
      self.interactive_features.sep_disconnect()

    def btn6clicked(self):
      self.label_21.setPixmap(QPixmap(u"SrcPics/connect_black.svg"))
      self.label_22.setPixmap(QPixmap(u"SrcPics/disconnect_red.svg"))
      self.camera_ip.setEnabled(True)
      self.camera_frequency.setEnabled(True)
      self.interactive_features.webcam_disconnect()

    def btn11clicked(self):
      self.sep_logfile.setText(self.interactive_features.get_now())

    def Start(self):
      # self.start_capturing_thread = StartCapturingThread()
      self.start_capturing_thread.startCapturingSignal.connect(self.interactive_features.start_capture)
      self.start_capturing_thread.start()
      self.pushButton_15.setEnabled(False)
      self.pushButton_17.setEnabled(False)

    def Stop(self):
      # self.stop_capturing_thread = StopCapturingThread()
      self.stop_capturing_thread.stopCapturingSignal.connect(self.interactive_features.stop_capture)
      self.stop_capturing_thread.start()
      self.pushButton_15.setEnabled(True)
      self.pushButton_17.setEnabled(True)



-------------webcam.py-------------
"""
下列代码包括如下功能:
    1.start_capture(video_path):在video_path目录下录制网络摄像头拍摄的视频
    2.analysis2csv(video_path):分析录制视频表情,将其输出给out.csv
    3.screenshot_emotion(joy, surprise, fear, video_path, png_path):将video_path下的视频中joy、surprise、fear三种情绪最大的
                                                                  帧输出至png_path
    4.web_video(video_path, png_path):上述功能总函数,需输入video_path、png_path两个参数
"""
import sys
import os
cwd = os.getcwd()
cac_path = os.path.join(cwd, 'Module', 'CacEmotions')
sys.path.append(cac_path)
from Module.CacEmotions.cac_emotion import getEmotioner, emotion_flag, express_flag
import cv2
import pandas as pd
from Module import CAPTUREFLAG

class WebCam:
    def __init__(self, video_path, png_path, csv_path, IP):
      self.video_path = video_path
      self.png_path = png_path
      self.csv_path = csv_path
      self.ip = IP
      self.capture = True
      self.is_connected = False

    def start_capture(self):
      # 初始化摄像头
      cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
      fourcc = cv2.VideoWriter_fourcc(*'XVID')
      size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
      num = 0
      print('Starting capture video!')
      while cap.isOpened():
            isSuccess, frame = cap.read()
            if isSuccess and num == 0:
                filename = self.video_path + '\\capture.avi'
                video_writer = cv2.VideoWriter(filename, fourcc, 10, size)
            video_writer.write(frame)
            num = num + 1
            if not CAPTUREFLAG.captureflag:
                break
      cap.release()
      cv2.destroyAllWindows()

    # TODO: 运行start_capture时,stop_capture函数无法触发,建立新的线程解决该问题
    def stop_capture(self):
      self.capture = False
      joy_max, surprise_max, fear_max = self.analysis2csv()
      self.screenshot_emotion(joy_max, surprise_max, fear_max)

isdkz 发表于 2023-2-28 15:04:40

依赖的库过多的时候最好放一个依赖库的版本(requirements.txt)上来,别人才好帮你 debug,

不然光靠读代码是很费时间的,没多少人愿意这样去做

幼儿园扛把子95 发表于 2023-3-1 10:39:27

isdkz 发表于 2023-2-28 15:04
依赖的库过多的时候最好放一个依赖库的版本(requirements.txt)上来,别人才好帮你 debug,

不然光靠读 ...

我想问的是程序卡死在我调用的模块的while循环当中了,我的主程序中有开始线程和结束线程,但结束线程无法触发,该如何解决这个问题

hrpzcf 发表于 2023-3-1 12:09:48

看你代码start_capture方法里退出循环的标志位是CAPTUREFLAG.captureflag,但是stop_capture方法里却把self.capture设置为False,这么做能结束循环吗???
要么 start_capture 里if not self.capture ,要么 stop_capture 里 CAPTUREFLAG.captureflag = False

还有你贴出来的程序都不能运行,目录结构都不知道是啥,自写模块也不完整,CAPTUREFLAG.captureflag 都不知道到底是个啥,别人怎么帮你解决问题?

我想问的是程序卡死在我调用的模块的while循环当中了,我的主程序中有开始线程和结束线程,但结束线程无法触发,该如何解决这个问题
你说的是主界面卡住无响应还是单纯地结束线程的时候没反应?如果是前者,你应该用新线程执行你的另一个模块,在主线程执行结束方法,而不是反过来;如果是后者,请参考这个回答的前面两行

最后想说的是程序不要用 exec 和 eval 执行代码,这么写调试困难,也是十分不优雅不Pythonic的写法,除非你想执行用户从外部输入的代码。我是看到这里不想再往下看了,追溯困难

幼儿园扛把子95 发表于 2023-3-1 13:45:49

hrpzcf 发表于 2023-3-1 12:09
看你代码start_capture方法里退出循环的标志位是CAPTUREFLAG.captureflag,但是stop_capture方法里却把self ...

感谢回复与帮助!
1是贴错代码了 CAPTUREFLAG.captureflag = False 是已经删掉的
2“你说的是主界面卡住无响应还是单纯地结束线程的时候没反应?如果是前者,你应该用新线程执行你的另一个模块,在主线程执行结束方法,而不是反过来”应该是正确的解决方法

幼儿园扛把子95 发表于 2023-3-1 14:04:21

hrpzcf 发表于 2023-3-1 12:09
看你代码start_capture方法里退出循环的标志位是CAPTUREFLAG.captureflag,但是stop_capture方法里却把self ...

十分感谢!已解决

歌者文明清理员 发表于 2023-3-2 09:10:19

幼儿园扛把子95 发表于 2023-3-1 14:04
十分感谢!已解决

请将解决问题的楼层设为最佳答案
页: [1]
查看完整版本: 求助pyqt多线程卡死的问题