I've been trying to implement multithreading on PyQT5 using QThreads. I want to display a video and update a set of labels at the same time(I used a counter as an example). From my research I found different ways to implement QThreads, it was recommended to use the following method instead of instantiating QTread and modifying run method, so I followed that advice. Here is my reduced code:
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_())
When I run it I get the following error:
1
2
QThread: Destroyed while thread is still running
Aborted (core dumped)
WHat's the best way to implement it, and can normal threads be used instead of QThreads in this case?
Thank you
I figured it out, for some reason I defined worker2 inside of Label_update by mistake, I just moved Label_update method down and followed the comment of musicanante and now its working. here is the full working code if anyone is interested.
############################# 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.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_())