A
A
Anastasia Taube2020-05-05 01:10:47
Python
Anastasia Taube, 2020-05-05 01:10:47

How to terminate the thread, change the values ​​for it to run, and start it again?

Good evening.

I create a widget that plays this or that video depending on the pressed button (open or open_from_camera) in the parent widget: either from a file or broadcast a stream from a webcam.
When you first download the video, there are no problems: everything shows as it should. When reloading from another source (from another file or switching to a webcam), the original video continues to play, although the path to the new file changes.
I suspect that I am interrupting/stopping the thread incorrectly.
I tried terminate(), but, according to the documentation (and my mediocre English), after that the memory is not cleared, and in general everything is bad and it’s better not to use this function in a good way, because anything can happen to the stream at any point in the code .
Therefore, the question is: how to properly terminate a thread, change variables for its operation, and immediately start it again?
(ps To be honest, I don’t even remember why I chose the stream (I think I read it under some question on stackoverflow or peeped in some example). If you can do without it, and this seems to be quite possible, because before worked without it and, moreover, the picture loaded much faster, how to do it right?)

spoiler
Actually, the code (I will select the necessary and comment):
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak
from dialog import *
     
class VideoWidget(QWidget):
    
    def __init__(self, root, **kwargs):
        super().__init__(root, **kwargs)
        self.main=root
        self.dialogOpenFromCamera = Dialog(self)
        self.initUI()

    @Slot(QImage)
    def setImage (self, image):
        self.label.setPixmap(QPixmap.fromImage(image))
    
    def initUI(self):
        self.setFixedSize(640,480)
        self.label = QLabel(self)
        self.label.setText("Загрузите видео")
        self.label.setAlignment(Qt.AlignCenter)
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.layout.addWidget(self.label)
        self.setLayout(self.layout)
        self.th = Thread(self)
        self.th.changePixmap.connect(self.setImage)

    def open(self):
        fileDialog = QFileDialog(self)
        supportedMimeTypes = ["video", "*.*"]
        fileDialog.setMimeTypeFilters(supportedMimeTypes)
        moviesLocation = QStandardPaths.writableLocation(QStandardPaths.MoviesLocation)
        fileDialog.setDirectory(moviesLocation)
        if fileDialog.exec_() == QDialog.Accepted: 
            if (self.th.way != fileDialog.selectedUrls()[0].toDisplayString()): # если выбран не тот же файл
                print ("\nВыбран новый файл: ", fileDialog.selectedUrls()) # проверка — выведем название выбранного файла
                if (self.th.way != None): #и если это не первый запуск (кажется, это избыточное условие, но пока проверяю всё) 
                    self.th.exit(0) #!!!прерывание потока
                    print ("Старый th.way: ", self.th.way) # проверка старого пути
                self.th.way = fileDialog.selectedUrls()[0].toDisplayString() #меняем путь в классе потока, чтобы следующий поток запустился с новым источником видео
                print ("Теперь th.way =", self.th.way) #проверка нынешнего пути в потоке
                self.th.start() # запуск потока снова

    def open_from_camera(self):
        self.dialogOpenFromCamera.exec_() # запуск формочки для ввода порта камеры (позже будет получение адреса ip-камеры). Сейчас self.dialogOpenFromCamera.way = 0
        if (self.th.way != self.dialogOpenFromCamera.way): # если путь потока и путь формы не совпадают
            if (self.th.way != None): #и если это не первый запуск потока
                self.th.exit(0) #!!!завершить поток
            self.th.way = self.dialogOpenFromCamera.way #изменить его путь
            self.th.start() #и запустить
            

class Thread (QThread):
    changePixmap = Signal(QImage)

    def __init__(self, root, **kwargs):
        super().__init__(root, **kwargs)
        self.main=root
        self.way=None
        
    def run(self):
        cap = cv2.VideoCapture(self.way)#"video_file.mp4")#0)
        while True:
            face_cascade= cv2.CascadeClassifier(r'face_cascade.xml')
            ret, frame = cap.read()
            if (ret == True):
                cap.set(3,640)
                cap.set(4, 480)
                frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB)
                gray = cv2.cvtColor (frame, cv2.COLOR_BGR2GRAY)
                faces = face_cascade.detectMultiScale(gray, 1.3, 5)
                for (x,y,w,h) in faces:
                    frame = cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
                    roi_gray = gray[y:y+h, x:x+w]
                    roi_color = frame[y:y+h, x:x+w]
                image = qimage2ndarray.array2qimage(frame)
                self.changePixmap.emit(image)


Вероятность в 99,9%, что вместоself.th.exit(0) должно быть что-то более адекватное и рабочее, но пока не пойму что.
Если нужно, прикреплю остальной код (main.py, dialog.py и face_cascade.xml).


Thanks in advance for your reply! :)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Alexander, 2020-05-05
@Tayaki

That's how it's an option

import sys
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak


class ThreadCamera(QThread):
    status = True
    changePixmap = Signal(QImage)

    def __init__(self):
        super().__init__()
        self.capture = cv2.VideoCapture(0)

    def setStatus(self, status):
        self.status = status
    def captureRelease(self):
        self.capture.release()

    def run(self):

        while self.status:
            face_cascade = cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml')
            ret, frame = self.capture.read()
            if (ret == True):
                self.capture.set(3,640)
                self.capture.set(4, 480)
                frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB)
                gray = cv2.cvtColor (frame, cv2.COLOR_BGR2GRAY)
                faces = face_cascade.detectMultiScale(gray, 1.3, 5)
                for (x,y,w,h) in faces:
                    frame = cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
                    roi_gray = gray[y:y+h, x:x+w]
                    roi_color = frame[y:y+h, x:x+w]
                image = qimage2ndarray.array2qimage(frame)
                self.changePixmap.emit(image)


class ThreadVideoFile(QThread):
    status = True
    changePixmap = Signal(QImage)

    def __init__(self, path):
        super().__init__()
        self.capture = cv2.VideoCapture(path)
    def set_status(self, status):
        self.status = status
    def captureRelease(self):
        self.capture.release()

    def run(self):

        while self.status:
            face_cascade = cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml')
            ret, frame = self.capture.read()
            if (ret == True):
                self.capture.set(3,640)
                self.capture.set(4, 480)
                frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB)
                gray = cv2.cvtColor (frame, cv2.COLOR_BGR2GRAY)
                faces = face_cascade.detectMultiScale(gray, 1.3, 5)
                for (x,y,w,h) in faces:
                    frame = cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
                    roi_gray = gray[y:y+h, x:x+w]
                    roi_color = frame[y:y+h, x:x+w]
                image = qimage2ndarray.array2qimage(frame)
                self.changePixmap.emit(image)



class Dialog(QDialog):
    def __init__(self, parent):
        super().__init__()
        self.resize(400, 100)
        self.label = QLabel()
        self.label.setText("<center><h1>Выберите действие</h1></center>")

        self.button_open_camera = QPushButton('Камера')
        self.button_open_camera.clicked.connect(parent.open_camera)
        self.button_open_file = QPushButton('Видео файл')
        self.button_open_file.clicked.connect(parent.open_file)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.button_open_camera)
        layout.addWidget(self.button_open_file)
        self.setLayout(layout)



class VideoWidget(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

        self.thread_camera = ThreadCamera()
        self.thread_video = ThreadVideoFile("videoplayback.mp4")



    @Slot(QImage)
    def setImage (self, image):
        self.label.setPixmap(QPixmap.fromImage(image))

    def initUI(self):
        self.setFixedSize(640, 480)
        self.label = QLabel(self)
        self.label.setText("Загрузите видео")
        self.label.setAlignment(Qt.AlignCenter)
        self.label.installEventFilter(self)
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.addWidget(self.label)
        self.setLayout(self.layout)


    def eventFilter(self, obj, event):
        # Только фильтровать событие label, переписать его поведение,
        # другие события будут проигнорированы
        if obj == self.label:
            # здесь отфильтруйте событие mouse и перепишите его поведение
            if event.type() == QEvent.MouseButtonPress:

                if event.buttons() == Qt.LeftButton:
                    self.dialog = Dialog(self)
                    self.dialog.exec_()
        return False
    def open_file(self):
        if self.thread_video.isRunning() is True:
            self.thread_video.set_status(False)
            self.thread_video.captureRelease()

        self.dialog.hide()
        self.thread_camera.setStatus(False)
        self.thread_camera.captureRelease()

        self.label.setText("Загрузите видео")

        fileDialog = QFileDialog(self)
        supportedMimeTypes = ["video", "*.*"]
        fileDialog.setMimeTypeFilters(supportedMimeTypes)
        moviesLocation = QStandardPaths.writableLocation(QStandardPaths.MoviesLocation)
        fileDialog.setDirectory(moviesLocation)

        if fileDialog.exec_() == QDialog.Accepted:
            self.file = fileDialog.selectedUrls()[0].toDisplayString()
            self.thread_video = ThreadVideoFile(self.file)
        self.thread_video.changePixmap.connect(self.setImage)
        self.thread_video.start()


    def open_camera(self):
        self.dialog.hide()

        self.thread_video.set_status(False)
        self.thread_video.captureRelease()
        self.label.setText("Загрузите видео")

        self.thread_camera = ThreadCamera()
        self.thread_camera.changePixmap.connect(self.setImage)
        self.thread_camera.start()


def main():
    app = QApplication([])
    video_widget = VideoWidget()
    video_widget.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Further on your own)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question