Robomatt
Robomatt

Reputation: 45

PyQt5 OpenCV WebCam Using QThread

I wrote a webcam app using PyQt5 and OpenCV. It works fine, however I would like to improve it more. I have couple of questions:

1) At line 24 of WebCam.py, I would like to break the while loop properly, when I click the quit push button. I mean I need to define and pass a "running" boolean value somehow from "Ui_MainWindow" class to FrameGrabber class to break the while loop, such as while cap.isOpened() & running: Once the loop is broken, I can properly release the video capture.

2) Ui_MainWindow is mostly generated by the Qt Designer. I had to add code inside it to utilize Object Oriented Encapsulation rules, such as not defining a global variable. However, if I needed to make a change in the UI, do I need to write code all the time? What is the proper way of not changing the code, once the UI is updated?

3) Please feel free to make comments if you think that the code would be better for any section of it, so that I can also improve my skills.

Thank you in advance!

Here is WebCam.py:

from PyQt5 import QtCore, QtGui, QtWidgets
import cv2


class FrameGrabber(QtCore.QThread):
    def __init__(self, parent=None):
        super(FrameGrabber, self).__init__(parent)

    signal = QtCore.pyqtSignal(QtGui.QImage)

    def run(self):
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
        while cap.isOpened():
            success, frame = cap.read()
            if success:
                image = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_BGR888)
                self.signal.emit(image)

class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self, MainWindow):
        super().__init__()
        self.MainWindow = MainWindow
        self.setupUi(self.MainWindow)
        self.grabber = FrameGrabber()
        self.grabber.signal.connect(self.updateFrame)
        self.grabber.start()

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setWindowModality(QtCore.Qt.NonModal)
        MainWindow.resize(1300, 799)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
        MainWindow.setSizePolicy(sizePolicy)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(12)
        font.setBold(True)
        font.setUnderline(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setFrameShape(QtWidgets.QFrame.Box)
        self.label.setTextFormat(QtCore.Qt.RichText)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.webCamDisplay = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.webCamDisplay.sizePolicy().hasHeightForWidth())
        self.webCamDisplay.setSizePolicy(sizePolicy)
        self.webCamDisplay.setFrameShape(QtWidgets.QFrame.Box)
        self.webCamDisplay.setText("")
        self.webCamDisplay.setObjectName("webCamDisplay")
        self.verticalLayout.addWidget(self.webCamDisplay)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem = QtWidgets.QSpacerItem(500, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.quitPushButton = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.quitPushButton.sizePolicy().hasHeightForWidth())
        self.quitPushButton.setSizePolicy(sizePolicy)
        self.quitPushButton.setObjectName("quitPushButton")
        self.horizontalLayout.addWidget(self.quitPushButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        MainWindow.setFixedSize(MainWindow.width(), MainWindow.height())
        self.quitPushButton.clicked.connect(self.quitApp)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    @QtCore.pyqtSlot(QtGui.QImage)
    def updateFrame(self, image):
        self.webCamDisplay.setPixmap(QtGui.QPixmap.fromImage(image))

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "WebCam"))
        self.label.setText(_translate("MainWindow", "WebCam Application"))
        self.quitPushButton.setText(_translate("MainWindow", "Quit"))

    def quitApp(self):
        QtWidgets.QApplication.quit()

main.py:

from WebCam import Ui_MainWindow
from PyQt5.QtWidgets import QApplication, QMainWindow

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

Upvotes: 3

Views: 2799

Answers (1)

Yunus Temurlenk
Yunus Temurlenk

Reputation: 4367

Here are something you should know:

  • In Qt UI thread can only be controlled and update via on a function or anything which is again connected to the same thread.
  • When you didn't create any thread then all stuff in the code connected to ui thread.
  • Especially while working on cameras and making some process on it there can be some shaking problems in the interface because ui thread is waiting to update itself until the process finished in background. I think you also using another thread to solve this issue.
  • Its not possible emit something for ui on a new thread because its not ui thread. To emit for something from another thread you may use global values. You change global values in thread and you can update in another function which is connected to ui thread. You should use mutexes in this situation.
  • When you checked the documentation about QThread class, you have limited functionalities to control threads and its normal. Basically you can stop thread or start thread, there is no pause. When you wanna close the camera running in a while loop in a thread you may close inside the thread but thread ll still be running or you can close the thread.
  • As a result if you ll make some video processing, using threads needed. For simple processes no need.

Upvotes: 2

Related Questions