簡體   English   中英

PyQt5 線程退出時關閉 GUI

[英]PyQt5 Thread shuts down GUI when quitted

我正在嘗試從 opencv 視頻 stream 在 pyqt5 應用程序中運行視頻,我可以在其中使用 2 個按鈕啟動和停止視頻。

這個問題與This高度相關。 問題是,如果我想取消視頻,主 Window 也正在關閉。 然而,我不確定為什么會這樣。

編輯:此問題出現在 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())

為了解決這個問題,它像PyQt 一樣工作,顯示來自 opencv 線程的視頻 stream 重要的步驟是在發出信號之前而不是在主線程中調整線程的 run() function 中的 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())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM