我一直在努力與我的Python應用程序,我找不到任何答案。實時繪製Matplotlib,PyQt和線程結束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繪圖儀類對象。 PlotQueue填充在處理來自外部hw的傳入數據的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有線程問題。_most_繪圖命令應該非常快(並且可以使用blitting來解決大部分緩慢的情況),請在主線程中執行它們。 – tacaswell
謝謝,這正是我一直在想的,但在這種情況下,我的主線程是在實現GUI的Analyzer類中。分析器類有按鈕等,所以如果我有一個循環讀取隊列和調用繪圖,它會阻止整個應用程序。 我怎麼能夠在主線程中做循環? 我相信它必須是另一個線程做繪圖,但如何做到線程安全?我可以使用同步,但除了我的plot方法之外,我應該在哪裏放置另一個同步調用,它應該位於matplotlib庫中的某個位置。 – PyTechnic