我正在尝试在使用 wxPython 的“生产”应用程序中实时绘制 matplotlib 中的数据。我一直为此目的使用 Chaco,但出于多种原因,我试图在未来避免使用 Chaco,其中一个原因是,由于它没有很好的文档记录,因此我经常必须在需要时花很长时间阅读 Chaco 源代码甚至将最小的特征添加到我的一个情节中。Chaco 胜过 matplotlib 的一个方面是速度,所以我正在探索从 matplotlib 获得可接受性能的方法。
我在 matplotlib 中看到广泛用于快速绘图的一种技术是设置animated
为True
您希望经常更新的绘图元素,然后只绘制一次背景(轴、刻度线等),并使用该canvas.copy_from_bbox()
方法保存背景。然后,在绘制新foreground
的(绘图轨迹等)时,您canvas.restore_region()
只需将预渲染的背景复制到屏幕上,然后将新的前景与axis.draw_artist()
它一起绘制canvas.blit()
到屏幕上。
我编写了一个相当简单的示例,将 FigureCanvasWxAgg 嵌入到 wxPython 框架中,并尝试以 45 FPS 的速度显示随机数据的单个跟踪。当程序以默认大小的帧(在我的源代码中硬编码)运行时,它在我的机器上达到每秒约 13-14 帧。当我最大化窗口时,刷新率下降到 5.5 FPS 左右。我认为这对我的应用程序来说不够快,尤其是当我开始添加要实时渲染的其他元素时。
我的代码发布在这里:basic_fastplot.py
我想知道这是否可以更快,所以我分析了代码,发现到目前为止,处理时间的最大消耗者是对canvas.blit()
第 99 行和第 109 行的调用。我进一步挖掘,检测 matplotlib 代码本身以发现最这段时间花在了对MemoryDC.SelectObject()
. 在周围的代码中有几个调用SelectObject
,但只有下面标记的一个需要任何可观的时间。
来自 matplotlib 源代码 backend_wxagg.py:
class FigureCanvasWxAgg(FigureCanvasAgg, FigureCanvasWx):
# ...
def blit(self, bbox=None):
"""
Transfer the region of the agg buffer defined by bbox to the display.
If bbox is None, the entire buffer is transferred.
"""
if bbox is None:
self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
self.gui_repaint()
return
l, b, w, h = bbox.bounds
r = l + w
t = b + h
x = int(l)
y = int(self.bitmap.GetHeight() - t)
srcBmp = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
srcDC = wx.MemoryDC()
srcDC.SelectObject(srcBmp) # <<<< Most time is spent here, 30milliseconds or more!
destDC = wx.MemoryDC()
destDC.SelectObject(self.bitmap)
destDC.BeginDrawing()
destDC.Blit(x, y, int(w), int(h), srcDC, x, y)
destDC.EndDrawing()
destDC.SelectObject(wx.NullBitmap)
srcDC.SelectObject(wx.NullBitmap)
self.gui_repaint()
我的问题:
- SelectObject() 会花这么长时间做什么?我有点假设它只是设置指针等,而不是做太多的复制或计算。
- 有什么办法可以加快速度(全屏时可能达到 10 FPS)?