Reputation: 11
I'm developing an application with PyQt5 and QtDesigner. For one of the pages (page 2), I'm trying to embed a live video stream from a camera with OpenCV. The code has a thread running and I confirmed that it is sending good frames. The problem I'm facing is dynamically updating a QLabel with the OpenCV frame.
The program currently crashes when this line of code (in the MainWindow class) is uncommented: Why?
self.ui.Worker1.ImageUpdate.connect(self.ui.ImageUpdateSlot)
Below is the main code
# by: reevve
# Import Modules
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import cv2
# Import UI files
from ui_main import Ui_MainWindow
from ui_splashscreen import Ui_SplashScreen
# Global Variables
counter = 0
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# add page btn click functionality
self.ui.btn_page_1.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_1))
self.ui.btn_page_2.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_2))
self.ui.btn_page_3.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_3))
# set up the video feed
self.ui.CancelBTN.clicked.connect(lambda: self.ui.CancelFeed)
self.ui.Worker1 = Worker1()
self.ui.Worker1.start()
# the line below is causing the program to crash
#self.ui.Worker1.ImageUpdate.connect(self.ui.ImageUpdateSlot)
def ImageUpdateSlot(self, Image):
print('recieve frames')
self.ui.FeedLabel.setPixmap(QPixmap.fromImage(Image))
def CancelFeed(self):
print('cancel feed')
self.ui.Worker1.stop()
class SplashScreen(QMainWindow):
def __init__(self):
super(SplashScreen,self).__init__()
self.ui = Ui_SplashScreen()
self.ui.setupUi(self)
# remove title bar
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
# drop shadow effect
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(20)
self.shadow.setXOffset(0)
self.shadow.setYOffset(0)
self.shadow.setColor(QColor(0, 0, 0, 60))
self.ui.dropShadowFrame.setGraphicsEffect(self.shadow)
# start timer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.progress)
# specify duration of launcher
self.timer.start(15)
# initial text
self.ui.label_description.setText("<strong>UD ASAE</strong> Ground Station GUI")
# change texts during loading process
QtCore.QTimer.singleShot(1500, lambda: self.ui.label_description.setText("<strong>LOADING</strong> the good stuff"))
QtCore.QTimer.singleShot(3000, lambda: self.ui.label_description.setText("<strong>GATHERING</strong> remaining braincells"))
# show main window
self.show()
def progress(self):
global counter
self.ui.progressBar.setValue(counter)
# close splash screen and open main gui
if counter > 100:
self.timer.stop()
self.main = MainWindow()
self.main.show()
self.close()
counter += 1
# FPV thread
class Worker1(QThread):
ImageUpdate = pyqtSignal(QImage)
def run(self):
print('\nrun feed')
self.ThreadActive = True
Capture = cv2.VideoCapture(0)
while self.ThreadActive:
ret, frame = Capture.read()
if ret:
Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
ConvertToQtFormat = QImage(Image.data, Image.shape[1], Image.shape[0], QImage.Format_RGB888)
Pic = ConvertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.ImageUpdate.emit(Pic)
print('send good frames')
def stop(self):
print('stop feed')
self.ThreadActive = False
self.quit()
def window():
app = QApplication(sys.argv)
win = SplashScreen()
sys.exit(app.exec_())
window()
Again, the Worker1 thread seems to be sending good frames (confirmed with print statement), but I'm having trouble updating my QLabel (called FeedLabel) as the frames come in.
I did not attach the supporting .ui files to this post.
Upvotes: 0
Views: 2772
Reputation: 4629
I changed a bunch of things in your code, indicated in the comments. Essentially the methods of your program were defined in a strange way and you stored many things in self.ui
instead of self
.
I made myself a minimal UI to be able to test the changes and it works. Below you can see the back of the sticky note I put on my laptop's camera:
Here is your modified code:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__() # (optional) removed the args of `super`
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# add page btn click functionality
...
# set up the video feed
self.ui.CancelBTN.clicked.connect(self.CancelFeed) # removed `lambda` and `.ui`
self.Worker1 = Worker1() # (optional) removed `.ui` because your thread should be an attr of the program, not of the ui. This is a matter of preference though.
self.Worker1.start() # (optional) removed `.ui`
self.Worker1.ImageUpdate.connect(self.ImageUpdateSlot) # removed `.ui`
@pyqtSlot(QImage) # (optional) decorator to indicate what object the signal will provide.
def ImageUpdateSlot(self, Image): # Unindented by 4 spaces.
print('recieve frames')
self.ui.FeedLabel.setPixmap(QPixmap.fromImage(Image))
def CancelFeed(self): # Unindented by 4 spaces.
print('cancel feed')
self.Worker1.stop() # (optional) removed `.ui`
Upvotes: 1