簡體   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