如何在单个 PyQt GUI 实例中多处理多个图

How to multiprocess multiple plots in a single PyQt GUI instance

我有一个名为 CrosshairPlotWidget 的绘图对象。每个绘图对象都会生成一个更新其数据的线程,但这些线程仍在同一个主 GUI 进程中。这是我目前拥有的和插图:

1 个主 GUI 进程,2 个线程

我想 运行 这两个图各自在一个单独的进程中,但是 都在同一个 GUI 实例中 (相同 window)。本质上,我试图将每个情节放入其自己单独的子进程中以实现真正的并发,因为我遇到了 CPU 瓶颈。通过让每个更新线程在一个单独的进程中,它将绕过 Python 的全局解释器锁。这是预期目标的说明:

1 个主 GUI 进程和 2 个子进程,每个子进程都有自己的线程

我看过

但是 none 对我的情况很有帮助。

我还发现 this 执行多处理但不在同一个 GUI window 中。

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
from threading import Thread
import pyqtgraph as pg
import numpy as np
import sys
import random
import time

class CrosshairPlotWidget(QtGui.QWidget):
    """Scrolling plot with crosshair"""

    def __init__(self, parent=None):
        super(CrosshairPlotWidget, self).__init__(parent)

        # Use for time.sleep (s)
        self.FREQUENCY = .025
        # Use for timer.timer (ms)
        self.TIMER_FREQUENCY = self.FREQUENCY * 1000

        self.LEFT_X = -10
        self.RIGHT_X = 0
        self.x_axis = np.arange(self.LEFT_X, self.RIGHT_X, self.FREQUENCY)
        self.buffer = int((abs(self.LEFT_X) + abs(self.RIGHT_X))/self.FREQUENCY)
        self.data = []

        self.crosshair_plot_widget = pg.PlotWidget()
        self.crosshair_plot_widget.setXRange(self.LEFT_X, self.RIGHT_X)
        self.crosshair_plot_widget.setLabel('left', 'Value')
        self.crosshair_plot_widget.setLabel('bottom', 'Time (s)')
        self.crosshair_color = (101,255,183)

        self.crosshair_plot = self.crosshair_plot_widget.plot()

        self.layout = QtGui.QGridLayout()
        self.layout.addWidget(self.crosshair_plot_widget)

        self.crosshair_plot_widget.plotItem.setAutoVisible(y=True)
        self.vertical_line = pg.InfiniteLine(angle=90)
        self.horizontal_line = pg.InfiniteLine(angle=0, movable=False)
        self.vertical_line.setPen(self.crosshair_color)
        self.horizontal_line.setPen(self.crosshair_color)
        self.crosshair_plot_widget.setAutoVisible(y=True)
        self.crosshair_plot_widget.addItem(self.vertical_line, ignoreBounds=True)
        self.crosshair_plot_widget.addItem(self.horizontal_line, ignoreBounds=True)

        self.crosshair_update = pg.SignalProxy(self.crosshair_plot_widget.scene().sigMouseMoved, rateLimit=60, slot=self.update_crosshair)

        self.update_data_thread = Thread(target=self.plot_updater, args=())
        self.update_data_thread.daemon = True
        self.update_data_thread.start()

    def plot_updater(self):
        """Updates data buffer with data value"""

        while True:
            self.data_point = random.randint(1,101)
            if len(self.data) >= self.buffer:
                del self.data[:1]
            self.data.append(float(self.data_point))
            self.crosshair_plot.setData(self.x_axis[len(self.x_axis) - len(self.data):], self.data)
            time.sleep(self.FREQUENCY)

    def update_crosshair(self, event):
        """Paint crosshair on mouse"""

        coordinates = event[0]  
        if self.crosshair_plot_widget.sceneBoundingRect().contains(coordinates):
            mouse_point = self.crosshair_plot_widget.plotItem.vb.mapSceneToView(coordinates)
            index = mouse_point.x()
            if index > self.LEFT_X and index <= self.RIGHT_X:
                self.crosshair_plot_widget.setTitle("<span style='font-size: 12pt'>x=%0.1f,   <span style='color: red'>y=%0.1f</span>" % (mouse_point.x(), mouse_point.y()))
            self.vertical_line.setPos(mouse_point.x())
            self.horizontal_line.setPos(mouse_point.y())

    def get_crosshair_plot_layout(self):
        return self.layout

if __name__ == '__main__':
    # Create main application window
    app = QtGui.QApplication([])
    app.setStyleSheet("""
        QWidget {
            background-color: #19232D;
            border: 0px solid #32414B;
            padding: 0px;
            color: #F0F0F0;
            selection-background-color: #1464A0;
            selection-color: #F0F0F0;
        }""")
    app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
    mw = QtGui.QMainWindow()
    mw.setWindowTitle('Crosshair Plot')

    # Create and set widget layout
    # Main widget container
    cw = QtGui.QWidget()
    ml = QtGui.QGridLayout()
    cw.setLayout(ml)
    mw.setCentralWidget(cw)

    # Create crosshair plot
    crosshair_plot1 = CrosshairPlotWidget()
    crosshair_plot2 = CrosshairPlotWidget()

    ml.addLayout(crosshair_plot1.get_crosshair_plot_layout(),0,0,1,1)
    ml.addLayout(crosshair_plot2.get_crosshair_plot_layout(),0,1,1,1)
    mw.show()

    ## Start Qt event loop unless running in interactive mode or using pyside.
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

GUI只能存在于主进程的主线程中,所以你要求的是不可能的。正如您在其他示例中看到的,最接近您想要的是在另一个进程中生成实时数据的代码

         Child Process 1               Child Process 2
        ┌----------------┐           ┌----------------┐  
        | ┌-----------┐  |           | ┌-----------┐  |
        | | Producer1 |  |           | | Producer2 |  |
        | └-----------┘  |           | └-----------┘  |
        └-------┬--------┘           └-------┬--------┘   
                |                            |
                └---------------┬------------┘
                                |
                       ┌--------┴--------┐ 
                       |  Main Thread    |
                       |  ┌-----------┐  | 
                       |  |    GUI    |  |
                       |  └-----------┘  | 
                       └-----------------┘
                          Main Process