两种不同功能的 QTreading 实现 - PyQT5

QTreading implementation for two different functions - PyQT5

我一直在尝试使用 QThreads 在 PyQT5 上实现多线程。我想同时显示视频和更新一组标签(我以计数器为例)。根据我的研究,我发现了实现 QThreads 的不同方法,recommended 使用以下方法而不是实例化 QTread 和修改 运行 方法,所以我听从了那个建议。这是我的简化代码:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QPoint, QRect, QSize, Qt
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

import sys

import os

import cv2

import numpy as np
import time

############################# Telemtry widgets update ##########################
class DISPLAY(QObject):
    acc1_val = pyqtSignal(int)
    finished = pyqtSignal()

    def run(self):
        global cntr
        cntr = 0
        while 1:
            cntr = cntr +1
            time.sleep(1)
            self.acc1_val.emit(cntr)
            if(cntr >50):
                cntr = 0 

        self.finished.emit()



######################################################################################

class VideoThread(QObject):
    ImageUpdate = pyqtSignal(QImage)
    frame_cv = pyqtSignal(np.ndarray)
    started = pyqtSignal()
    finished = pyqtSignal()
    frame=[]
    def run(self):
        self.display_width= 1280
        self.display_height = 720
        self.ThreadActive = True
        Capture = cv2.VideoCapture('sample.mp4')
        while Capture.isOpened():
            ret, self.frame = Capture.read()
            if ret:
                Image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
                ConvertToQtFormat = QImage(Image.data, Image.shape[1], Image.shape[0], QImage.Format_RGB888)
                Pic = ConvertToQtFormat.scaled(self.display_width, self.display_height, Qt.KeepAspectRatio)
                self.ImageUpdate.emit(Pic)
                time.sleep(30/1000)  #use it just when playing from a file to memic FPS delay
        Capture.release()

        self.finished.emit()
    def sel(self):
        self.frame_cv.emit(self.frame)


############################ GUI #######################################


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1920, 1080)
        MainWindow.setFocusPolicy(QtCore.Qt.NoFocus)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.vid = QtWidgets.QLabel(self.centralwidget)

        self.vid.setGeometry(QtCore.QRect(550, 70, 1280, 720))
        self.vid.setFrameShape(QtWidgets.QFrame.Box)
        self.vid.setText("")
        self.vid.setObjectName("vid")
        self.vid.setFocusPolicy(Qt.StrongFocus) #important to be able to listen to a keypress

        self.Title = QtWidgets.QLabel(self.centralwidget)
        self.Title.setGeometry(QtCore.QRect(30, 10, 421, 71))
        font = QtGui.QFont()
        font.setPointSize(24)
        font.setBold(True)
        font.setWeight(75)
        self.Title.setFont(font)
        self.Title.setObjectName("Title")
        
        self.ACC1_X = QtWidgets.QLineEdit(self.centralwidget)
        self.ACC1_X.setGeometry(QtCore.QRect(220, 420, 41, 31))
        self.ACC1_X.setObjectName("ACC1_X")

        
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1920, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


        ###################### Display thread ######################
    
        self.thread1 = QThread()       
        self.worker1 = DISPLAY()
        self.worker1.moveToThread(self.thread1)
        self.thread1.started.connect(self.worker1.run)
        self.worker1.acc1_val.connect(self.Label_update)

        self.worker1.finished.connect(self.thread1.quit)
        self.worker1.finished.connect(self.worker1.deleteLater)          

        self.thread1.finished.connect(self.thread1.deleteLater)

        self.thread1.start()




    def Label_update(self,acc_val):

        self.ACC1_X.setText(str(acc_val))
        print(str(acc_val))

        ############### video thread ############################3
        self.thread2 = QThread()
        self.worker2 = VideoThread()
        self.worker2.moveToThread(self.thread2)
        self.thread2.started.connect(self.worker2.run)
        self.worker2.finished.connect(self.thread2.quit)
        self.worker2.finished.connect(self.worker2.deleteLater)          

        self.thread2.finished.connect(self.thread2.deleteLater)

        
        # connect its signal to the update_image slot
        self.worker2.ImageUpdate.connect(self.ImageUpdateSlot)


        # start the thread
        self.thread2.start()



    def ImageUpdateSlot(self, Image):
        self.vid.setPixmap(QPixmap.fromImage(Image))



    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Title.setText(_translate("MainWindow", "TEST"))

    

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

当我 运行 它时,我收到以下错误:

1
2
QThread: Destroyed while thread is still running
Aborted (core dumped)

什么是最好的实现方式,在这种情况下可以使用普通线程代替 QThreads 吗?

谢谢

我想通了,出于某种原因我错误地在 Label_update 中定义了 worker2,我只是将 Label_update 方法向下移动并按照 musicanante 的评论进行操作,现在它开始工作了。如果有人感兴趣,这里是完整的工作代码。

############################# Telemtry 小部件更新

##########################
class DISPLAY(QObject):
    acc1_val = pyqtSignal(int)
    finished = pyqtSignal()

    def run(self):
        global cntr
        cntr = 0
        while 1:
            cntr = cntr +1
            time.sleep(1)
            self.acc1_val.emit(cntr)
            if(cntr >50):
                cntr = 0 

        self.finished.emit()



######################################################################################

class VideoThread(QObject):
    ImageUpdate = pyqtSignal(QImage)
    frame_cv = pyqtSignal(np.ndarray)
    started = pyqtSignal()
    finished = pyqtSignal()
    frame=[]
    def run(self):
        self.display_width= 1280
        self.display_height = 720
        self.ThreadActive = True
        Capture = cv2.VideoCapture('sample.mp4')
        while Capture.isOpened():
            ret, self.frame = Capture.read()
            if ret:
                Image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
                ConvertToQtFormat = QImage(Image.data, Image.shape[1], Image.shape[0], QImage.Format_RGB888)
                Pic = ConvertToQtFormat.scaled(self.display_width, self.display_height, Qt.KeepAspectRatio)
                self.ImageUpdate.emit(Pic)
                time.sleep(30/1000)  #use it just when playing from a file to memic FPS delay
        Capture.release()

        self.finished.emit()
    def sel(self):
        self.frame_cv.emit(self.frame)


############################ GUI #######################################


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1920, 1080)
        MainWindow.setFocusPolicy(QtCore.Qt.NoFocus)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.vid = QtWidgets.QLabel(self.centralwidget)

        self.vid.setGeometry(QtCore.QRect(550, 70, 1280, 720))
        self.vid.setFrameShape(QtWidgets.QFrame.Box)
        self.vid.setText("")
        self.vid.setObjectName("vid")
        self.vid.setFocusPolicy(Qt.StrongFocus) #important to be able to listen to a keypress

        self.Title = QtWidgets.QLabel(self.centralwidget)
        self.Title.setGeometry(QtCore.QRect(30, 10, 421, 71))
        font = QtGui.QFont()
        font.setPointSize(24)
        font.setBold(True)
        font.setWeight(75)
        self.Title.setFont(font)
        self.Title.setObjectName("Title")
        
        self.ACC1_X = QtWidgets.QLineEdit(self.centralwidget)
        self.ACC1_X.setGeometry(QtCore.QRect(220, 420, 41, 31))
        self.ACC1_X.setObjectName("ACC1_X")

        
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1920, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


        ###################### Display thread ######################
    
        self.thread1 = QThread()       
        self.worker1 = DISPLAY()
        self.worker1.moveToThread(self.thread1)
        self.thread1.started.connect(self.worker1.run)
        self.worker1.acc1_val.connect(self.Label_update)

        #self.worker1.finished.connect(self.thread1.quit)
        #self.worker1.finished.connect(self.worker1.deleteLater)             


        self.thread1.finished.connect(self.worker1.deleteLater)

        self.thread1.start()





        ############### video thread ############################3
        self.thread2 = QThread()
        self.worker2 = VideoThread()
        self.worker2.moveToThread(self.thread2)
        self.thread2.started.connect(self.worker2.run)

        #self.worker2.finished.connect(self.thread2.quit)

        #self.worker2.finished.connect(self.worker2.deleteLater)             


        self.thread2.finished.connect(self.worker2.deleteLater)
        
        # connect its signal to the update_image slot
        self.worker2.ImageUpdate.connect(self.ImageUpdateSlot)


        # start the thread
        self.thread2.start()


    def Label_update(self,acc_val):

        self.ACC1_X.setText(str(acc_val))
        print(str(acc_val))


    def ImageUpdateSlot(self, Image):
        self.vid.setPixmap(QPixmap.fromImage(Image))



    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Title.setText(_translate("MainWindow", "TEST"))

    

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())