【问题标题】:Real time plotting with Matplotlib, PyQt and Threading ends to python crash使用 Matplotlib、PyQt 和 Threading 实时绘图以 python 崩溃结束
【发布时间】:2013-10-23 19:29:40
【问题描述】:

我一直在努力使用我的 Python 应用程序,但找不到任何答案。

我有使用 Matplotlib 小部件的 PyQT GUI 应用程序。 GUI 启动一个新线程来处理对 mpl 小部件的绘图。恐怕我现在通过从另一个线程访问 matplotlib 绘图组件会导致崩溃。

这基本上就是我的代码的样子:

class Analyzer(QMainWindow, Ui_MainWindow):
  def __init__(self, parent=None):
    self.timer = QTimer()
    super(Analyzer, self).__init__(parent)
    self.setupUi(self)

    self.background = self.mpl.canvas.copy_from_bbox(self.mpl.canvas.ax.bbox)

    self.plotQueue = Queue.Queue()
    self.plotterStarted = False

    self.plotter = Plotter(self.mpl, self.plotQueue)
    self.cam = Cam(self.plotQueue, self.textEdit)
    ...

class Ui_MainWindow(object):
  def setupUi(self, MainWindow):
    ...
    self.mpl = MplWidget(self.centralWidget)
    ...

class MplWidget(QtGui.QWidget):
"""Widget defined in Qt Designer"""
  def __init__(self, parent = None):
    QtGui.QWidget.__init__(self, parent)
    self.canvas = MplCanvas()
    ...

class MplCanvas(FigureCanvas):
"""Class to represent the FigureCanvas widget"""
  def __init__(self):        
    # setup Matplotlib Figure and Axis
    self.fig = Figure()
    self.ax = self.fig.add_subplot(111)

    # initialization of the canvas
    FigureCanvas.__init__(self, self.fig)

    FigureCanvas.updateGeometry(self)

和绘图仪类:

class Plotter():
  def __init__(self, mpl="", plotQueue=""):
    self.mpl = mpl
    self.background = self.mpl.canvas.copy_from_bbox(self.mpl.canvas.ax.bbox)
    self.plotQueue = plotQueue
    ...
  def start(self):
    threading.Thread(target=self.run).start()

  ''' Real time plotting '''
  def run(self):
    while True:
      try:
        inputData = self.plotQueue.get(timeout=1)

        # Go through samples
        for samples in inputData:
            self.line, = self.mpl.canvas.ax.plot(x, y, animated=True, label='Jee')

            for sample in samples:
                x.append(sample['tick'])
                y.append(sample['linear'])

            self.line.set_data(x,y)
            self.mpl.canvas.ax.draw_artist(self.line)
            self.mpl.canvas.blit(self.mpl.canvas.ax.bbox)
         ...

所以我将 mpl 和 plotQueue 传递给 Plotter 类对象。 PlotQueue 填充在 Cam 类中,该类处理来自外部硬件的传入数据。 Plotter 读取 plotQueue,对其进行处理并为 mpl 调用绘图。

但是这是访问 mpl 的线程安全方法吗?如果没有,我该怎么做?对此的任何提示表示赞赏。


编辑 1。

按照 cmets 中的建议,我在主线程中添加了 QTimer 来处理绘图。稍作调整后,我得到了它的运行良好。

class Analyzer(...):
  def __init__(self, parent=None):
    QObject.connect(self.timer, SIGNAL("timeout()"), self.periodicCall)

  def periodicCall(self):
    self.plotter.draw()

  def startButton(self):
    self.timer.start(10)

非常感谢有用的 cmets。

【问题讨论】:

  • 我记得读过一些关于 matplotlib 线程问题的文章。 大多数绘图命令应该非常快(并且您可以使用 blitting 来绕过大多数缓慢的情况),在您的主线程中执行它们。
  • 谢谢,这也是我一直在想的,但在这种情况下,我的主线程在实现 GUI 的 Analyzer 类中。分析器类有按钮等,所以如果我在那里创建一个读取队列并调用绘图的循环,它会阻塞整个应用程序。我怎样才能在主线程中做一个循环?我相信它一定是另一个线程在做绘图,但是如何做到线程安全呢?我可以使用同步,但是除了我的绘图方法之外,我应该把另一个同步调用放在哪里,它应该在 matplotlib 库中的某个地方。

标签: python multithreading matplotlib pyqt queue


【解决方案1】:

如果您的程序中的 matplotlib 使用 QT 后端(我认为这是因为您将其嵌入到 Qt 应用程序中),那么绘图将在您调用 matplotlib 命令的线程中完成。这将是一个问题,因为 Qt 要求所有绘图都从主线程完成。 所以我相当肯定你不能简单地修复它。(如果你使用 GTK,你可以使用 gtk 锁来防止主进程与 GUI 交互,同时你从您的线程,但 Qt 摆脱了它们在 v4 及更高版本中的类似锁定)。

你有几个选择:

  1. 尝试分离 matplotlib 的绘图部分(可能甚至不可能?)并通过发送 QApplication.postEvent() 事件让它们在主线程中运行

  2. 不使用线程,只需在主线程中使用回调(可能使用 QTimer 定期调用或在程序空闲时调用)。这可能不会影响您的应用程序的性能,因为 Python GIL 无论如何都会阻止真正的多线程行为。

  3. 使用不同的绘图库。前几天我查看了PyQtGraph,似乎进展顺利。从我的简要一瞥,我认为它有能力使用RemoteGraphicsView 为您在幕后处理所有这些事情。这将启动第二个进程来执行 CPU 密集型绘图工作,从而解决上述 Python GIL 问题。如果您有兴趣,请查看他们提供的示例

【讨论】:

  • 非常感谢。选项 1. 是我在之前的帖子评论中试图弄清楚的,但我相信这不是要走的路。选项 2. 可能对我有用。如果图书馆能提供我需要的东西,我也会检查选项 3。
  • 或者,如果我在主线程中有一个回调方法可以进行绘图。我从传递它所需参数的线程调用此方法。所以基本上,我只有线程而不是计时器调用该方法?
  • 那行不通,因为你调用的方法将在线程中执行,而不是在主线程中执行。您需要使用 QApplication.postEvent() 使函数在主线程中运行 这实际上非常复杂,我将在接下来的 24 小时内尝试为您准备一些代码(只需与编写者核实)他很高兴发布它)
  • 不,没必要。我将使用计时器,因为它实际上似乎是做这些事情的非常有效的方法。绘图不像在其他线程中完成的处理数据那样关键。
  • 我添加了计时器,但绘图似乎很慢而且看起来不太好。我不知道为什么。我尝试了非常短的计时器延迟,但即便如此。使用线程模型(偶尔会崩溃),绘图非常流畅。
猜你喜欢
  • 1970-01-01
  • 2016-08-21
  • 2015-01-15
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 2018-05-23
  • 2015-06-23
  • 2011-04-15
相关资源
最近更新 更多