0

I am trying to design a gui which is related to my computer vision project. In that, the video I want to stop the web camera feed and I want to resume it by pressing a button. I managed to stop the feed, but I cannot resume it. The camera gets turned on but it is not working. This is the code for the program.

from PyQt5 import uic
from PyQt5 import QtCore, QtWidgets, QtGui
import cv2
import sys

class opencv_feed(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = uic.loadUi('../designs/design5_flexible_opencv_window2.ui', self)    #change this whenever u want... keep the ui file with you
        self.resize(900,600)

        self.worker1 = worker1()                                #creating an instance
        self.worker1.start()
        self.worker1.ImgUpdate.connect(self.ImageUpdateSlot)
        self.but_stop.clicked.connect(self.cancel_feed)
        self.but_resume.clicked.connect(self.resume_feed)
        
   
    def ImageUpdateSlot(self, Image):
        self.label.setPixmap(QtGui.QPixmap.fromImage(Image))
        
    def cancel_feed(self):
        self.worker1.stop()
    
    def resume_feed(self):
        self.__init__()
        #self.worker1.ImgUpdate.connect(self.ImageUpdateSlot)

class worker1(QtCore.QThread):
    ImgUpdate = QtCore.pyqtSignal(QtGui.QImage)
    
    @QtCore.pyqtSlot()
    def run(self):  #put self in every variable to stop crashing the gui, when we interact with gui
        self.ThreadActive = True
        self.feed = cv2.VideoCapture(0)
        while self.ThreadActive:
            self.ret, self.frm = self.feed.read()
            if self.ret:
                self.img = cv2.cvtColor(self.frm, cv2.COLOR_BGR2RGB)
                #print(img1.shape)
                self.img = cv2.flip(self.img,1)
                self.qtformat_conv_img = QtGui.QImage(self.img.data, self.img.shape[1], self.img.shape[0], QtGui.QImage.Format_RGB888)
                #print(self.img.shape)
                self.pic = self.qtformat_conv_img.scaled(self.img.shape[1],self.img.shape[0],QtCore.Qt.KeepAspectRatio)    #keep this as an attribute, else when resizing the app stops                                              
                self.ImgUpdate.emit(self.pic)
    def stop(self):
        self.ThreadActive = False
        self.feed.release()
        self.quit()
        #os._exit(0)  
        
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    wind = opencv_feed()
    wind.show()
    sys.exit(app.exec_())

Can someone explain me what am I doing wrong.

Link to the UI file.. https://drive.google.com/file/d/1UP8RjQML1GzFA75eGURgWt4Y0o_Ip3sU/view?usp=sharing

tad
  • 117
  • 7
  • You should certainly *not* recall `__init__`, and reconnecting the signal is pointless. Instead, have you tried a simple `self.worker1.start()`? – musicamante Jun 15 '22 at 14:24
  • yeah, I tried that too, but same result, camera turns on but video doesn't continue. – tad Jun 15 '22 at 17:52

1 Answers1

1

You can only start a thread once. Once it finishes you need to create another thread object to actually run. I would add another flag after self.ThreadActive called something like "pause" to keep the thread alive, just without doing anything.

@QtCore.pyqtSlot()
def run(self):  #put self in every variable to stop crashing the gui, when we interact with gui
    self.ThreadActive = True
    self.paused = False
    self.feed = cv2.VideoCapture(0)
    while self.ThreadActive:
        if not self.paused:
            self.ret, self.frm = self.feed.read()
            if self.ret:
                self.img = cv2.cvtColor(self.frm, cv2.COLOR_BGR2RGB)
                #print(img1.shape)
                self.img = cv2.flip(self.img,1)
                self.qtformat_conv_img = QtGui.QImage(self.img.data, 
                                                      self.img.shape[1], 
                                                      self.img.shape[0], 
                                                      QtGui.QImage.Format_RGB888)
                #print(self.img.shape)
                self.pic = self.qtformat_conv_img.scaled(self.img.shape[1],self.img.shape[0],QtCore.Qt.KeepAspectRatio)    #keep this as an attribute, else when resizing the app stops                                              
                self.ImgUpdate.emit(self.pic)

this way when you want to pause the thread you can pause and unpause using that flag

Either that or you need to always create another instance of the worker. Does it work if you create the instance outside of the init ? I'm unsure what happens to the GUI if the init is called twice.

EDIT: You'll also have to change the way you pause and start again

def cancel_feed(self):
    self.worker1.paused = True

def resume_feed(self):
    self.worker1.paused = False
Andew
  • 321
  • 1
  • 9
  • "You can only start a thread once": that's *not* true. A QThread can be restarted as many times as needed, the only requirement is that the thread *has* to be finished (otherwise `start()` won't do nothing) before that. This is achieved through the proper usage of `quit()` *and* `wait()`. – musicamante Jun 15 '22 at 23:26
  • I tried it, there is no attribute called paused for worker1 object. – tad Jun 16 '22 at 04:57
  • @musicamante oh this is a QThread sorry. I was thinking of the threading library, which I thought can't restart threads: https://stackoverflow.com/questions/29692250/restarting-a-thread-in-python – Andew Jun 16 '22 at 13:36
  • @tad sorry I didn't initialize the self.paused variable as I didn't run the code I was just showing what I meant. I edited my comment to initialize that variable for you – Andew Jun 16 '22 at 13:37
  • And I am just curious about the method that @musicamante use, can you show how it is done? – tad Jun 18 '22 at 04:04
  • tad maybe this link will help: https://stackoverflow.com/questions/44006024/restart-qthread-with-gui – Andew Jun 21 '22 at 14:03