如何在 Pyqt5 中使用 multiprocessing.Queue 和 pyqtgraph?
How to use multiprocessing.Queue in Pyqt5 with pyqtgraph?
我正在使用 pygtgraph 和 pyqt5 用十字准线画线,然后根据算法用十种不同的颜色给它们上色。我还使用滑块来选择我想要着色的第一行的数量。
每次滑块值更改时,GUI 都会冻结以计算我不想发生的颜色。
我试图将计算放在不同的线程中并且它有点工作,但现在我想使用进程。
我想做的是使用多处理创建一个线程池来处理队列中的算法调用,并使用一个线程为绘制的线条重新着色。
self.threads = max(1, multiprocessing.cpu_count() - 3)
self.queue_in = multiprocessing.Queue()
self.queue_out = multiprocessing.Queue()
self.the_pool = multiprocessing.Pool(self.threads, algorithm_worker, (self.queue_in, self.queue_out))
self.recolor_thread = multiprocessing.Process(target=assign_and_recolor_worker, args=(
self.queue_out, self.color_numbers, self.canvas.getPlotItem(), self.pens))
self.recolor_thread.start()
这些是函数:
def algorithm_worker(queue_in, queue_out):
print(os.getpid(), "working")
while True:
lines = queue_in.get(block=True)
result = list(get_color_nums_algo(lines)) if lines else []
print(result)
queue_out.put(result)
def assign_and_recolor_worker(queue_in, color_nums, plot_item, pens):
print(os.getpid(), "working")
while True:
color_nums = queue_in.get(block=True)
for plotdataitem, pen_i in zip(plot_item.items, color_nums):
plotdataitem.setPen(pens[pen_i % len(pens)])
第一部分似乎工作正常,但由于我是多处理的新手,所以我对第二部分有点费劲。
- 我不知道如何从
self.recolor_thread
中更改主 window 中的变量,主要是:分配的颜色编号列表 self.color_numbers
。
- 我也不知道如何访问
self.canvas.getPlotItem().items
1 - 我尝试使用 manager 但它没有更新值
self.manager = multiprocessing.Manager()
self.color_numbers = self.manager.list()
2 - 我通过了 PlotItem 和 mkPen,但出现了类似
的错误
TypeError: cannot pickle 'PlotItem' object
如何解决或规避这些问题?这里是link到必要的代码https://pastebin.com/RyvNpP67
首先,您需要保持 GUI 事件循环 运行ning。 GUI 运行 是一个事件循环,当您长时间处于 event/signal 中时,它会 freezes/hangs。如果您单击一个按钮并停留在按钮单击信号中,则当您处于该功能时,GUI 无法处理其他事件。
线程仅在线程正在等待 I/O 或 time.sleep 被调用时对此有所帮助。 I/O 是当您调用 socket.recv() 或 queue.get() 之类的东西时。这些操作等待数据。当线程等待数据时,主线程可以 运行 并在 Qt 事件循环中处理事件。仅运行计算的线程不让主线程处理事件。
Multiprocessing 用于将数据发送到单独的进程并等待结果。在等待结果时,如果等待在单独的线程中,您的 Qt 事件循环可以处理事件。将数据发送到单独的进程并从该进程接收数据可能需要很长时间。除此之外,有些项目不能轻松地发送到单独的流程。可以将 QT GUI 项目发送到单独的进程,但这很困难并且可能必须使用操作系统的 window 句柄。
多处理
def __init__(self):
...
self.queue_in = multiprocessing.Queue()
self.queue_out = multiprocessing.Queue()
# Create thread that waits for data and plots the data
self.recolor_thread = threading.Thread(target=assign_and_recolor_worker, args=(
self.queue_out, self.color_numbers, self.canvas.getPlotItem(), self.pens))
self.recolor_thread.start() # comment this line
self.alg_proc = multiprocessing.Process(target=algorithm_worker, args=(self.queue_in, self.queue_out))
self.alg_proc.start()
def calc_color(self):
lines_to_recog = self.current_lines[:self.color_first_n]
self.queue_in.put(lines_to_recog) # Send to process to calculate
处理事件
如果您只想响应式 GUI 调用 QtWidgets.QApplication.processEvents()。尽管并不总是推荐“processEvents”,但它在某些情况下非常有用。
def get_color_nums_algo(lines: list, proc_events=None) -> list:
li = []
for _ in lines:
li.append(np.random.randint(0, 10))
try:
proc_events()
except (TypeError, Exception):
pass
return li
class MainWindow(QtWidgets.QMainWindow):
....
def calc_color(self):
lines_to_recog = self.current_lines[:self.color_first_n]
# Color alg
proc_events= QtWidgets.QApplication.processEvents
color_nums = list(get_color_nums_algo(lines_to_recog, proc_events)) if lines_to_recog else []
# Plot data
for plotdataitem, pen_i in zip(self.canvas.getPlotItem().items, color_nums):
plotdataitem.setPen(self.pens[pen_i % len(self.pens)])
QtWidgets.QApplication.processEvents()
鼠标移动
此外,mouseMoved 事件发生的速度非常快。如果 update_drawing
花费的时间太长,您可能需要使用计时器定期调用 update_drawing
,因此当可能发生 10 个事件时调用 1 次。
我正在使用 pygtgraph 和 pyqt5 用十字准线画线,然后根据算法用十种不同的颜色给它们上色。我还使用滑块来选择我想要着色的第一行的数量。 每次滑块值更改时,GUI 都会冻结以计算我不想发生的颜色。
我试图将计算放在不同的线程中并且它有点工作,但现在我想使用进程。 我想做的是使用多处理创建一个线程池来处理队列中的算法调用,并使用一个线程为绘制的线条重新着色。
self.threads = max(1, multiprocessing.cpu_count() - 3)
self.queue_in = multiprocessing.Queue()
self.queue_out = multiprocessing.Queue()
self.the_pool = multiprocessing.Pool(self.threads, algorithm_worker, (self.queue_in, self.queue_out))
self.recolor_thread = multiprocessing.Process(target=assign_and_recolor_worker, args=(
self.queue_out, self.color_numbers, self.canvas.getPlotItem(), self.pens))
self.recolor_thread.start()
这些是函数:
def algorithm_worker(queue_in, queue_out):
print(os.getpid(), "working")
while True:
lines = queue_in.get(block=True)
result = list(get_color_nums_algo(lines)) if lines else []
print(result)
queue_out.put(result)
def assign_and_recolor_worker(queue_in, color_nums, plot_item, pens):
print(os.getpid(), "working")
while True:
color_nums = queue_in.get(block=True)
for plotdataitem, pen_i in zip(plot_item.items, color_nums):
plotdataitem.setPen(pens[pen_i % len(pens)])
第一部分似乎工作正常,但由于我是多处理的新手,所以我对第二部分有点费劲。
- 我不知道如何从
self.recolor_thread
中更改主 window 中的变量,主要是:分配的颜色编号列表self.color_numbers
。 - 我也不知道如何访问
self.canvas.getPlotItem().items
1 - 我尝试使用 manager 但它没有更新值
self.manager = multiprocessing.Manager()
self.color_numbers = self.manager.list()
2 - 我通过了 PlotItem 和 mkPen,但出现了类似
的错误TypeError: cannot pickle 'PlotItem' object
如何解决或规避这些问题?这里是link到必要的代码https://pastebin.com/RyvNpP67
首先,您需要保持 GUI 事件循环 运行ning。 GUI 运行 是一个事件循环,当您长时间处于 event/signal 中时,它会 freezes/hangs。如果您单击一个按钮并停留在按钮单击信号中,则当您处于该功能时,GUI 无法处理其他事件。
线程仅在线程正在等待 I/O 或 time.sleep 被调用时对此有所帮助。 I/O 是当您调用 socket.recv() 或 queue.get() 之类的东西时。这些操作等待数据。当线程等待数据时,主线程可以 运行 并在 Qt 事件循环中处理事件。仅运行计算的线程不让主线程处理事件。
Multiprocessing 用于将数据发送到单独的进程并等待结果。在等待结果时,如果等待在单独的线程中,您的 Qt 事件循环可以处理事件。将数据发送到单独的进程并从该进程接收数据可能需要很长时间。除此之外,有些项目不能轻松地发送到单独的流程。可以将 QT GUI 项目发送到单独的进程,但这很困难并且可能必须使用操作系统的 window 句柄。
多处理
def __init__(self):
...
self.queue_in = multiprocessing.Queue()
self.queue_out = multiprocessing.Queue()
# Create thread that waits for data and plots the data
self.recolor_thread = threading.Thread(target=assign_and_recolor_worker, args=(
self.queue_out, self.color_numbers, self.canvas.getPlotItem(), self.pens))
self.recolor_thread.start() # comment this line
self.alg_proc = multiprocessing.Process(target=algorithm_worker, args=(self.queue_in, self.queue_out))
self.alg_proc.start()
def calc_color(self):
lines_to_recog = self.current_lines[:self.color_first_n]
self.queue_in.put(lines_to_recog) # Send to process to calculate
处理事件
如果您只想响应式 GUI 调用 QtWidgets.QApplication.processEvents()。尽管并不总是推荐“processEvents”,但它在某些情况下非常有用。
def get_color_nums_algo(lines: list, proc_events=None) -> list:
li = []
for _ in lines:
li.append(np.random.randint(0, 10))
try:
proc_events()
except (TypeError, Exception):
pass
return li
class MainWindow(QtWidgets.QMainWindow):
....
def calc_color(self):
lines_to_recog = self.current_lines[:self.color_first_n]
# Color alg
proc_events= QtWidgets.QApplication.processEvents
color_nums = list(get_color_nums_algo(lines_to_recog, proc_events)) if lines_to_recog else []
# Plot data
for plotdataitem, pen_i in zip(self.canvas.getPlotItem().items, color_nums):
plotdataitem.setPen(self.pens[pen_i % len(self.pens)])
QtWidgets.QApplication.processEvents()
鼠标移动
此外,mouseMoved 事件发生的速度非常快。如果 update_drawing
花费的时间太长,您可能需要使用计时器定期调用 update_drawing
,因此当可能发生 10 个事件时调用 1 次。