我一直在努力使用我的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类中,该类处理来自外部hw的传入数据。绘图仪读取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)
非常感谢您提供有用的评论。
答案 0 :(得分:1)
如果你的程序中的matplotlib正在使用QT后端(我认为它是因为你将它嵌入到Qt应用程序中),那么绘图将在你调用matplotlib命令的线程中完成。这将是一个问题,因为Qt要求所有绘图都是从主线程完成的。 所以我很确定你不能简单地修复它。(如果你使用的是GTK,你可以使用gtk锁来防止主进程与GUI进行交互,同时你从GUI做了相关的事情你的线程,但Qt摆脱了他们在v4及以上的类似锁定。)
您有几个选择:
尝试并分离出matplotlib的绘图部分(可能甚至不可能?)并通过发送QApplication.postEvent()
不使用线程,只需在主线程中使用回调(可能使用QTimer定期调用或在程序空闲时调用)。这可能不会影响应用程序的性能,因为Python GIL无论如何都会阻止真正的多线程行为。
使用其他绘图库。前几天我看了PyQtGraph,看起来好像很顺利。从我的简短一瞥看,我认为它有能力使用RemoteGraphicsView
在幕后处理所有这些。这将启动第二个进程,用于执行CPU密集型绘图工作,这可以解决前面提到的Python GIL问题。如果您有兴趣,请查看他们提供的示例