简体   繁体   English

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

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

I've been struggling with my Python application and I can't find any answers. 我一直在努力处理我的Python应用程序,但找不到任何答案。

I'm having PyQT GUI application which uses Matplotlib widget. 我有使用Matplotlib小部件的PyQT GUI应用程序。 GUI starts a new thread which handles plotting to mpl widget. GUI启动一个新线程,该线程处理对mpl小部件的绘制。 I'm afraid I run now to a race condition by accessing matplotlib drawing components from another thread which leads to crash. 恐怕我现在通过从另一个线程访问matplotlib绘图组件导致崩溃而运行到一种竞争状态。

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)

And plotter class: 和绘图仪类:

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)
         ...

So I pass mpl and plotQueue to Plotter class object. 因此,我将mpl和plotQueue传递给Plotter类对象。 PlotQueue is populated in Cam class which processes incoming data from external hw. PlotQueue填充在Cam类中,该类处理来自外部硬件的传入数据。 Plotter reads the plotQueue, processes it and calls drawing for mpl. 绘图仪读取plotQueue,对其进行处理并为mpl调用绘图。

But is this a thread safe method to access mpl? 但这是访问mpl的线程安全方法吗? If not, how should I do it? 如果没有,我该怎么办? Any tips on this are appreciated. 任何提示对此表示赞赏。


Edit 1. 编辑1。

I added QTimer in main thread to handle drawing, as suggested in comments. 我在注释中建议在主线程中添加QTimer以处理绘图。 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. 非常感谢您的有用评论。

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. 如果您程序中的matplotlib使用的是QT后端(我认为是因为您将其嵌入到Qt应用程序中),那么绘图将在您从中调用matplotlib命令的线程中完成。 This is going to be a problem because Qt requires that all drawing is done from the main thread. 这将是一个问题,因为Qt要求所有绘图都是从主线程完成的。 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). (如果您使用的是GTK,则可以在线程中执行与GUI相关的操作时使用gtk锁来防止主进程与GUI交互,但是Qt在v4及更高版本中摆脱了它们的类似锁)。

You have a few options: 您有几种选择:

  1. Try and separate out the drawing parts of matplotlib (may not even be possible?) and have them run in the main thread by sending events with QApplication.postEvent() 尝试通过使用QApplication.postEvent()发送事件来分离出matplotlib的绘图部分(可能甚至无法实现?),并使它们在主线程中运行。

  2. Instead of using a thread, just use callbacks in the main thread (maybe called periodically using a QTimer or when the program is idle). 除了使用线程外,还可以在主线程中使用回调(可以使用QTimer或在程序空闲时定期调用)。 This probbaly won't impact the performance of your application since the Python GIL prevents true multi-threading behaviour anyway. 由于Python GIL仍然会阻止真正的多线程行为,因此这种可能性不会影响应用程序的性能。

  3. Use a different plotting library. 使用其他绘图库。 I had a look over PyQtGraph the other day, and it seems to be coming along nicely. 几天我看了看PyQtGraph ,看来进展顺利。 From my brief glance I think it has the ability to handle all of this behind the scenes for you, using a RemoteGraphicsView . 从我的简要信息看,我认为它可以使用RemoteGraphicsView为您在后台处理所有这些问题。 This would launch a second process for doing the CPU intensive plotting stuff, which gets around the aforementioned Python GIL issue. 这将启动第二个过程来执行CPU密集型绘图工作,从而绕过上述Python GIL问题。 Check out the examples they provide if you are interested 如果您有兴趣,请查看他们提供的示例

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

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