QThread不是运行并行
QThread not running in parallel
我有一个函数 "preprocess" 可能需要很长时间才能执行。因此,我希望它 运行 与我的 UI 同时进行。我试着用 QThread 做这个:
class Preprocessor(QThread):
done = pyqtSignal(ndarray)
def __init__(self, image):
super(Preprocessor, self).__init__()
self.image = image
def run(self) -> None:
result = preprocess(self.image)
self.done.emit(result)
然后 运行将其与:
preprocessor = Preprocessor(image)
preprocessor.start()
preprocessor.done.connect(self.handler_function)
但现在当我按下 UI 中的按钮时,它仍然只是 运行 我的预处理器并阻止 UI 响应。我无法更改有关预处理器代码的任何内容。
最小可重现示例:
import sys
import cv2
from PyQt5.Qt import *
from numpy.core.multiarray import ndarray
import dlib
class Preprocessor(QThread):
done = pyqtSignal(ndarray) # ndarray
def __init__(self, image):
super(Preprocessor, self).__init__()
self.detector = dlib.get_frontal_face_detector()
self.image = image
def run(self) -> None:
gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
rects = self.detector(gray, 2)
self.done.emit(self.image)
class ExampleApp(QMainWindow):
def __init__(self):
super().__init__()
test_button = QPushButton(self)
test_button.clicked.connect(self.start)
self.preprocessor = None
def handler_function(self, image):
cv2.imshow('test', image)
def start(self):
image = cv2.imread("../Resources/snowIceLakeMountains.jpg")
self.preprocessor = Preprocessor(image)
self.preprocessor.done.connect(self.handler_function)
self.preprocessor.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = ExampleApp()
window.show()
app.exec_()
好的,我想我知道为什么它不起作用以及如何解决它。
它不起作用,因为调用 rects = self.detector(gray, 2) 可能调用一些预编译的 C 代码,这是 Python 解释器的原子操作,因此它不能让其他线程执行操作之间的东西。 (我真的不知道,但这是一些 GIL 的东西)
我通过使用 multiprocessing.Process 和 multiprocessing.Queue
修复了它
import os
import queue
import sys
import cv2
import dlib
from multiprocessing import Queue, Process
from PyQt5.Qt import *
from numpy.core.multiarray import ndarray
class CheckDone(QThread):
done = pyqtSignal(ndarray) # ndarray
def __init__(self, proces_queue):
super(CheckDone, self).__init__()
self.process_queue = proces_queue
def run(self) -> None:
while True:
try:
image = self.process_queue.get(block=False)
self.done.emit(image)
return
except queue.Empty:
pass
def proc(q):
path = os.path.dirname(os.path.abspath(__file__))
image = cv2.imread(os.path.join(path, '..', 'Resources', 'snowIceLakeMountains.jpg'))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
detector = dlib.get_frontal_face_detector()
done = detector(gray, 2)
q.put(image)
class ExampleApp(QMainWindow):
def __init__(self, q):
super().__init__()
self.q = q
self.test_button = QPushButton(self)
self.test_button.clicked.connect(self.start)
self.checker = None
def handler_function(self, image):
cv2.imshow('test', image)
def start(self):
p = Process(target=proc, args=(self.q, ))
self.checker = CheckDone(self.q)
self.checker.done.connect(self.handler_function)
self.checker.start()
p.start()
if __name__ == '__main__':
pq = Queue()
app = QApplication(sys.argv)
window = ExampleApp(pq)
window.show()
app.exec_()
我有一个函数 "preprocess" 可能需要很长时间才能执行。因此,我希望它 运行 与我的 UI 同时进行。我试着用 QThread 做这个:
class Preprocessor(QThread):
done = pyqtSignal(ndarray)
def __init__(self, image):
super(Preprocessor, self).__init__()
self.image = image
def run(self) -> None:
result = preprocess(self.image)
self.done.emit(result)
然后 运行将其与:
preprocessor = Preprocessor(image)
preprocessor.start()
preprocessor.done.connect(self.handler_function)
但现在当我按下 UI 中的按钮时,它仍然只是 运行 我的预处理器并阻止 UI 响应。我无法更改有关预处理器代码的任何内容。
最小可重现示例:
import sys
import cv2
from PyQt5.Qt import *
from numpy.core.multiarray import ndarray
import dlib
class Preprocessor(QThread):
done = pyqtSignal(ndarray) # ndarray
def __init__(self, image):
super(Preprocessor, self).__init__()
self.detector = dlib.get_frontal_face_detector()
self.image = image
def run(self) -> None:
gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
rects = self.detector(gray, 2)
self.done.emit(self.image)
class ExampleApp(QMainWindow):
def __init__(self):
super().__init__()
test_button = QPushButton(self)
test_button.clicked.connect(self.start)
self.preprocessor = None
def handler_function(self, image):
cv2.imshow('test', image)
def start(self):
image = cv2.imread("../Resources/snowIceLakeMountains.jpg")
self.preprocessor = Preprocessor(image)
self.preprocessor.done.connect(self.handler_function)
self.preprocessor.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = ExampleApp()
window.show()
app.exec_()
好的,我想我知道为什么它不起作用以及如何解决它。 它不起作用,因为调用 rects = self.detector(gray, 2) 可能调用一些预编译的 C 代码,这是 Python 解释器的原子操作,因此它不能让其他线程执行操作之间的东西。 (我真的不知道,但这是一些 GIL 的东西)
我通过使用 multiprocessing.Process 和 multiprocessing.Queue
修复了它import os
import queue
import sys
import cv2
import dlib
from multiprocessing import Queue, Process
from PyQt5.Qt import *
from numpy.core.multiarray import ndarray
class CheckDone(QThread):
done = pyqtSignal(ndarray) # ndarray
def __init__(self, proces_queue):
super(CheckDone, self).__init__()
self.process_queue = proces_queue
def run(self) -> None:
while True:
try:
image = self.process_queue.get(block=False)
self.done.emit(image)
return
except queue.Empty:
pass
def proc(q):
path = os.path.dirname(os.path.abspath(__file__))
image = cv2.imread(os.path.join(path, '..', 'Resources', 'snowIceLakeMountains.jpg'))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
detector = dlib.get_frontal_face_detector()
done = detector(gray, 2)
q.put(image)
class ExampleApp(QMainWindow):
def __init__(self, q):
super().__init__()
self.q = q
self.test_button = QPushButton(self)
self.test_button.clicked.connect(self.start)
self.checker = None
def handler_function(self, image):
cv2.imshow('test', image)
def start(self):
p = Process(target=proc, args=(self.q, ))
self.checker = CheckDone(self.q)
self.checker.done.connect(self.handler_function)
self.checker.start()
p.start()
if __name__ == '__main__':
pq = Queue()
app = QApplication(sys.argv)
window = ExampleApp(pq)
window.show()
app.exec_()