PySide2 и Matplotlib: как заставить MatPlotLib работать в отдельном процессе?..как не может работать в отдельном потоке - PullRequest
0 голосов
/ 24 сентября 2019

Я не опытный программист, и я пытаюсь создать своего рода программу регистрации данных на python, используя Qt для python (PySide2) для построения GUI.Я смог создать графический интерфейс с помощью Designer и загрузить его в Python.Графический интерфейс пока только пустое окно.Затем я создал функцию, которая запускает MatplotLib в окне с графиком, и я обновляю данные в каждом цикле основной программы, используя таймер Qt.

Everithing работает, но время перерисовки MatPlotLib слишком сильно замедляет обновление графического интерфейса.Поэтому я попытался поместить MatPlotLib в отдельный поток, и после многих испытаний я понял, что он не может работать в отдельном потоке. В конце я решил попробовать Multiprocessing.Теперь MatPlotLib работает нормально в отдельном процессе (я использую очередь для отправки данных в MatPlotLib) и корректно завершаю работу после завершения процесса, но когда я закрываю главное окно, программа закрывается полностью, а также нажимая Ctrl + C, появляется приглашениезаблокирован.

Это мой код:

#!/usr/bin/env python3
import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile, QTimer

import matplotlib.pyplot as plt
from multiprocessing import Process, Queue, freeze_support
import random


class DSL(QWidget):
    def __init__(self):
        # LOAD HMI
        QWidget.__init__(self)
        designer_file = QFile('userInterface.ui')
        designer_file.open(QFile.ReadOnly)
        loader = QUiLoader()
        self.ui = loader.load(designer_file, self)
        designer_file.close()
        self.ui.show()

        # Data to be visualized
        self.data = []

    def mainLoop(self):
        self.data = []
        for i in range(10):
            self.data.append(random.randint(0, 10))

        # Send data to graph process
        queue.put(self.data)

        # LOOP repeater
        QTimer.singleShot(10, self.mainLoop)


def graphProcess(queue):
    for i in range(10):
        # Get data
        data = queue.get()

        # MatPlotLib
        plt.ion()
        plt.clf()
        plt.plot(data)
        plt.show()
        plt.pause(0.1)

    print('process end')


if __name__ == '__main__':
    # MatPlotLib Process
    queue = Queue()
    freeze_support()
    p = Process(target=graphProcess, args=(queue,))
    p.daemon = True
    p.start()

    # PySide2 Process
    app = QApplication(sys.argv)
    dsl = DSL()
    dsl.mainLoop()
    sys.exit(app.exec_())

1 Ответ

0 голосов
/ 24 сентября 2019

Вместо использования matplotlib во вторичном процессе лучше встроить холст в QWidget, который позволяет ему запускаться в тех же процессах PySide2:

#!/usr/bin/env python3
import sys

from PySide2.QtCore import QFile, QObject, Signal, Slot, QTimer
from PySide2.QtWidgets import QApplication, QVBoxLayout, QWidget
from PySide2.QtUiTools import QUiLoader

import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

import random


class DSL(QObject):
    dataChanged = Signal(list)

    def __init__(self, parent=None):
        # LOAD HMI
        super().__init__(parent)
        designer_file = QFile("userInterface.ui")
        if designer_file.open(QFile.ReadOnly):
            loader = QUiLoader()
            self.ui = loader.load(designer_file)
            designer_file.close()
            self.ui.show()
        # Data to be visualized
        self.data = []

    def mainLoop(self):
        self.data = []
        for i in range(10):
            self.data.append(random.randint(0, 10))
        # Send data to graph
        self.dataChanged.emit(self.data)
        # LOOP repeater
        QTimer.singleShot(10, self.mainLoop)


class MatplotlibWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        fig = Figure(figsize=(7, 5), dpi=65, facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
        self.canvas = FigureCanvas(fig)
        self.toolbar = NavigationToolbar(self.canvas, self)
        lay = QVBoxLayout(self)
        lay.addWidget(self.toolbar)
        lay.addWidget(self.canvas)

        self.ax = fig.add_subplot(111)
        self.line, *_ = self.ax.plot([])

    @Slot(list)
    def update_plot(self, data):
        self.line.set_data(range(len(data)), data)

        self.ax.set_xlim(0, len(data))
        self.ax.set_ylim(min(data), max(data))
        self.canvas.draw()


if __name__ == "__main__":

    app = QApplication(sys.argv)
    dsl = DSL()
    dsl.mainLoop()

    matplotlib_widget = MatplotlibWidget()
    matplotlib_widget.show()

    dsl.dataChanged.connect(matplotlib_widget.update_plot)
    sys.exit(app.exec_())
...