【问题标题】:How to use multiprocessing.Queue in Pyqt5 with pyqtgraph?如何在 Pyqt5 中使用 multiprocessing.Queue 和 pyqtgraph?
【发布时间】:2021-06-26 17:00:52
【问题描述】:

我正在使用 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)])

第一部分似乎运行良好,但由于我是多处理新手,所以我在第二部分中遇到了很多困难。

  1. 我不知道如何从self.recolor_thread 中更改主窗口内的变量,主要是:分配的颜色编号列表self.color_numbers
  2. 我也不知道如何访问self.canvas.getPlotItem().items

1 - 我尝试使用manager,但它没有更新值

self.manager = multiprocessing.Manager()
self.color_numbers = self.manager.list()

2 - 我通过了 PlotItem 和 mkPen 但得到类似的错误

TypeError: cannot pickle 'PlotItem' object

如何解决或规避这些问题?这是必要代码的链接https://pastebin.com/RyvNpP67

【问题讨论】:

    标签: python multiprocessing pyqt5 python-multiprocessing pyqtgraph


    【解决方案1】:

    首先,您需要保持 GUI 事件循环运行。 GUI 运行一个事件循环,当您长时间停留在事件/信号中时,该循环会冻结/挂起。如果您单击按钮并停留在按钮单击信号中,则 GUI 无法在您使用该功能时处理其他事件。

    只有在线程等待 I/O 或调用 time.sleep 时,线程才有帮助。 I/O 是当你调用类似 socket.recv() 或 queue.get() 的时候。这些操作等待数据。当线程等待数据时,主线程可以运行并处理 Qt 事件循环上的事件。只运行计算的线程不会让主线程处理事件。

    多处理用于将数据发送到单独的进程并等待结果。在等待结果时,如果等待是在单独的线程中,您的 Qt 事件循环可以处理事件。将数据发送到单独的进程并从该进程接收数据可能需要很长时间。除此之外,有些项目不能轻易地发送到单独的进程。可以将 QT GUI 项发送到单独的进程,但这很困难并且可能必须在操作系统中使用窗口句柄。

    多处理

    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 次。

    【讨论】:

      猜你喜欢
      • 2021-12-27
      • 2019-04-16
      • 1970-01-01
      • 2020-05-18
      • 2021-03-27
      • 2015-03-25
      • 2018-10-08
      • 1970-01-01
      • 2017-09-25
      相关资源
      最近更新 更多