【发布时间】:2013-10-23 19:29:40
【问题描述】:
我一直在努力使用我的 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 类中,该类处理来自外部硬件的传入数据。 Plotter 读取 plotQueue,对其进行处理并为 mpl 调用绘图。
但是这是访问 mpl 的线程安全方法吗?如果没有,我该怎么做?对此的任何提示表示赞赏。
编辑 1。
按照 cmets 中的建议,我在主线程中添加了 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)
非常感谢有用的 cmets。
【问题讨论】:
-
我记得读过一些关于 matplotlib 线程问题的文章。 大多数绘图命令应该非常快(并且您可以使用 blitting 来绕过大多数缓慢的情况),在您的主线程中执行它们。
-
谢谢,这也是我一直在想的,但在这种情况下,我的主线程在实现 GUI 的 Analyzer 类中。分析器类有按钮等,所以如果我在那里创建一个读取队列并调用绘图的循环,它会阻塞整个应用程序。我怎样才能在主线程中做一个循环?我相信它一定是另一个线程在做绘图,但是如何做到线程安全呢?我可以使用同步,但是除了我的绘图方法之外,我应该把另一个同步调用放在哪里,它应该在 matplotlib 库中的某个地方。
标签: python multithreading matplotlib pyqt queue