本文介绍了使用Matplotlib,PyQt和Threading进行实时绘图会导致python崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've been struggling with my Python application and I can't find any answers.

我有使用Matplotlib小部件的PyQT GUI应用程序. GUI启动一个新线程,该线程处理对mpl小部件的绘制.恐怕我现在正在通过从另一个线程访问导致崩溃的线程访问matplotlib绘图组件而进入竞争状态.

I'm having PyQT GUI application which uses Matplotlib widget. GUI starts a new thread which handles plotting to mpl widget. I'm afraid I run now to a race condition by accessing matplotlib drawing components from another thread which leads to crash.

这基本上就是我的代码:

This is basically, what my code looks like:

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的绘图.

So I pass mpl and plotQueue to Plotter class object. PlotQueue is populated in Cam class which processes incoming data from external hw. Plotter reads the plotQueue, processes it and calls drawing for mpl.

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

But is this a thread safe method to access mpl? If not, how should I do it? Any tips on this are appreciated.

编辑1.

我在注释中建议在主线程中添加QTimer来处理图形.稍作调整后,我就可以正常工作了.

I added QTimer in main thread to handle drawing, as suggested in comments. After a small tweaking, I got it working fairly well.

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)

非常感谢您提供有用的评论.

Thanks a lot for useful comments.

推荐答案

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

If matplotlib in your program is using the QT backend (which I assume it is since you are embedding it in a Qt application), then the drawing is going to be done in thread you call the matplotlib commands from. This is going to be a problem because Qt requires that all drawing is done from the main thread. So I'm fairly certain you can't fix it simply. (if you were using GTK you could use the gtk lock to prevent the main process from interacting with the GUI while you did GUI related things from your thread, but Qt got rid of their similar lock in v4 and above).

您有一些选择:

  1. 尝试并分离出matplotlib的绘图部分(可能甚至无法?),并通过使用QApplication.postEvent()

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

Instead of using a thread, just use callbacks in the main thread (maybe called periodically using a QTimer or when the program is idle). This probbaly won't impact the performance of your application since the Python GIL prevents true multi-threading behaviour anyway.

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

Use a different plotting library. I had a look over PyQtGraph the other day, and it seems to be coming along nicely. From my brief glance I think it has the ability to handle all of this behind the scenes for you, using a RemoteGraphicsView. This would launch a second process for doing the CPU intensive plotting stuff, which gets around the aforementioned Python GIL issue. Check out the examples they provide if you are interested

这篇关于使用Matplotlib,PyQt和Threading进行实时绘图会导致python崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 19:40