Answer the question
In order to leave comments, you need to log in
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?)
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)
Answer the question
In order to leave comments, you need to log in
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()
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question