【问题标题】:PyQt5 Thread shuts down GUI when quittedPyQt5 Thread 退出时关闭 GUI
【发布时间】:2021-08-06 19:37:25
【问题描述】:

我正在尝试通过 opencv 视频流在 pyqt5 应用程序中运行视频,我可以在其中使用 2 个按钮开始和停止视频。

这个问题与This 高度相关。问题是,如果我想取消视频,主窗口也会关闭。但是,我不确定为什么会这样。

编辑:此问题出现在 Windows 10 上,而线程在 Mac 上运行时没有任何问题。

from PyQt5.QtGui import * 
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
import cv2 

class MainWindow(QWidget):
    def __init__(self) -> None:
        super(MainWindow, self).__init__()
        self.VBL = QVBoxLayout()
        self.FeedLabel = QLabel()
        self.VBL.addWidget(self.FeedLabel)
        self.playBTN = QPushButton('Play')
        self.playBTN.clicked.connect(self.play)
        self.VBL.addWidget(self.playBTN)
        self.cancelBTN.clicked.connect(self.Cancel)
        self.VBL.addWidget(self.cancelBTN)
        self.setLayout(self.VBL)
    
    def ImageUpdateSlot(self, Image):
        Image = Image.scaled(640, 480, Qt.KeepAspectRatio)
        self.FeedLabel.setPixmap(QPixmap.fromImage(Image))
    
    def Cancel(self):
        self.worker1.stop()
    
    def play(self):
        self.worker1 = CompleteVideoRunner("Path/to/some/video")
        self.worker1.start()
        self.worker1.sigImage.connect(self.ImageUpdateSlot)
        self.cancelBTN.clicked.connect(self.Cancel)

class VideoSignal(QObject):
    Image = pyqtSignal(QImage)
    imageCount = pyqtSignal(int)

class CompleteVideoRunner(QThread):
    #signal = VideoSignal() 
    sigImage = pyqtSignal(QImage)
    sigCount =pyqtSignal(int)
    def __init__(self, path, curFrame = 0):
        super(CompleteVideoRunner, self).__init__()
        self.cap = cv2.VideoCapture(path)
        #self.signal = VideoSignal()
        self.curFrame = curFrame
        self.is_killed = False

    def run(self):
        self.is_killed = False
        j = self.curFrame
        self.cap.set(1, j)
        while (self.cap.isOpened() and not self.is_killed):
            rep, frame = self.cap.read()
            self.curFrame = j
            if not rep:
                break
            height, width, channel = frame.shape
            bytesPerLine = 3 * width
            jpg = frame.tobytes()
            jpg = QByteArray(jpg)
            QImg= QImage(frame.data, width, height, bytesPerLine,
                         QImage.Format_BGR888)
            self.sigImage.emit(QImg)
            self.sigCount.emit(j)
            j+=1
    

    def stop(self):
        self.is_killed = True
        print('finished thread')
        self.quit()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    Root = MainWindow()
    Root.show()
    sys.exit(App.exec())

【问题讨论】:

    标签: python pyqt pyqt5 opencv-python


    【解决方案1】:

    为了解决这个问题,它就像在PyQt showing video stream from opencv 线程中一样工作。重要的一步是在发出信号之前在线程的 run() 函数中调整 QImage 的大小,而不是在主线程中。

    一个后续问题是:为什么我需要在发出信号之前重新调整 QImage?

    对于重现能力,请考虑以下代码。

    from PyQt5.QtGui import * 
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    import sys
    import cv2 
    
    class MainWindow(QWidget):
        def __init__(self) -> None:
            super(MainWindow, self).__init__()
            self.VBL = QVBoxLayout()
            self.FeedLabel = QLabel()
            self.VBL.addWidget(self.FeedLabel)
            self.playBTN = QPushButton('Play')
            self.playBTN.clicked.connect(self.play)
            self.VBL.addWidget(self.playBTN)
            self.cancelBTN.clicked.connect(self.Cancel)
            self.VBL.addWidget(self.cancelBTN)
            self.setLayout(self.VBL)
        
        def ImageUpdateSlot(self, Image):
            self.FeedLabel.setPixmap(QPixmap.fromImage(Image))
        
        def Cancel(self):
            self.worker1.stop()
        
        def play(self):
            self.worker1 = CompleteVideoRunner("Path/to/some/video")
            self.worker1.start()
            self.worker1.sigImage.connect(self.ImageUpdateSlot)
            self.cancelBTN.clicked.connect(self.Cancel)
    
    class VideoSignal(QObject):
        Image = pyqtSignal(QImage)
        imageCount = pyqtSignal(int)
    
    class CompleteVideoRunner(QThread):
        #signal = VideoSignal() 
        sigImage = pyqtSignal(QImage)
        sigCount =pyqtSignal(int)
        def __init__(self, path, curFrame = 0):
            super(CompleteVideoRunner, self).__init__()
            self.cap = cv2.VideoCapture(path)
            #self.signal = VideoSignal()
            self.curFrame = curFrame
            self.is_killed = False
    
        def run(self):
            self.is_killed = False
            j = self.curFrame
            self.cap.set(1, j)
            while (self.cap.isOpened() and not self.is_killed):
                rep, frame = self.cap.read()
                self.curFrame = j
                if not rep:
                    break
                height, width, channel = frame.shape
                bytesPerLine = 3 * width
                QImg= QImage(frame.data, width, height, bytesPerLine,
                             QImage.Format_BGR888)
                QImg = QImg.scaled(640, 480, Qt.KeepAspectRatio)
                self.sigImage.emit(QImg)
                self.sigCount.emit(j)
                j+=1
        
    
        def stop(self):
            self.is_killed = True
            print('finished thread')
            self.quit()
    
    if __name__ == "__main__":
        App = QApplication(sys.argv)
        Root = MainWindow()
        Root.show()
        sys.exit(App.exec())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-23
      • 1970-01-01
      • 1970-01-01
      • 2017-10-06
      • 2018-11-02
      • 2019-10-05
      • 2011-05-06
      相关资源
      最近更新 更多