繁体   English   中英

使用Matplotlib,PyQt和Threading进行实时绘图会导致python崩溃

[英]Real time plotting with Matplotlib, PyQt and Threading ends to python crash

我一直在努力处理我的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类中,该类处理来自外部硬件的传入数据。 绘图仪读取plotQueue,对其进行处理并为mpl调用绘图。

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


编辑1。

我在注释中建议在主线程中添加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)

非常感谢您的有用评论。

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

您有几种选择:

  1. 尝试通过使用QApplication.postEvent()发送事件来分离出matplotlib的绘图部分(可能甚至无法实现?),并使它们在主线程中运行。

  2. 除了使用线程外,还可以在主线程中使用回调(可以使用QTimer或在程序空闲时定期调用)。 由于Python GIL仍然会阻止真正的多线程行为,因此这种可能性不会影响应用程序的性能。

  3. 使用其他绘图库。 几天我看了看PyQtGraph ,看来进展顺利。 从我的简要信息看,我认为它可以使用RemoteGraphicsView为您在后台处理所有这些问题。 这将启动第二个过程来执行CPU密集型绘图工作,从而绕过上述Python GIL问题。 如果您有兴趣,请查看他们提供的示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM