【发布时间】:2013-05-25 08:27:50
【问题描述】:
我有很大的时间痕迹,必须用肉眼检查,所以我需要一个快速滚动工具。
如何实现最快的 Maplotlib/Pyside 滚动?
没错,我在 MPL 图上添加了一个 PySide 滚动条,并使用 set_xlim() 方法更新绘图的 x 范围。这还不够快,尤其是因为在最终应用程序中,我在不同的子图中至少有 8 个时间轨迹,它们必须一起滚动。 A figure of the plot is attached.
还有改进的余地吗?
这里我附上演示相对低滚动的演示代码。它很长,但几乎都是样板代码。有趣的一点(需要改进)在 xpos_changed() 方法中,其中 plot xlimits 发生了变化。
编辑:下面我结合了tcaswell建议的一些微优化,但更新速度并没有提高。
from PySide import QtGui, QtCore
import pylab as plt
import numpy as np
N_SAMPLES = 1e6
def test_plot():
time = np.arange(N_SAMPLES)*1e-3
sample = np.random.randn(N_SAMPLES)
plt.plot(time, sample, label="Gaussian noise")
plt.title("1000s Timetrace \n (use the slider to scroll and the spin-box to set the width)")
plt.xlabel('Time (s)')
plt.legend(fancybox=True)
q = ScrollingToolQT(plt.gcf(), scroll_step=10)
return q # WARNING: it's important to return this object otherwise
# python will delete the reference and the GUI will not respond!
class ScrollingToolQT(object):
def __init__(self, fig, scroll_step=10):
# Setup data range variables for scrolling
self.fig = fig
self.scroll_step = scroll_step
self.xmin, self.xmax = fig.axes[0].get_xlim()
self.width = 1 # axis units
self.pos = 0 # axis units
self.scale = 1e3 # conversion betweeen scrolling units and axis units
# Save some MPL shortcuts
self.ax = self.fig.axes[0]
self.draw = self.fig.canvas.draw
#self.draw_idle = self.fig.canvas.draw_idle
# Retrive the QMainWindow used by current figure and add a toolbar
# to host the new widgets
QMainWin = fig.canvas.parent()
toolbar = QtGui.QToolBar(QMainWin)
QMainWin.addToolBar(QtCore.Qt.BottomToolBarArea, toolbar)
# Create the slider and spinbox for x-axis scrolling in toolbar
self.set_slider(toolbar)
self.set_spinbox(toolbar)
# Set the initial xlimits coherently with values in slider and spinbox
self.ax.set_xlim(self.pos,self.pos+self.width)
self.draw()
def set_slider(self, parent):
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, parent=parent)
self.slider.setTickPosition(QtGui.QSlider.TicksAbove)
self.slider.setTickInterval((self.xmax-self.xmin)/10.*self.scale)
self.slider.setMinimum(self.xmin*self.scale)
self.slider.setMaximum((self.xmax-self.width)*self.scale)
self.slider.setSingleStep(self.width*self.scale/4.)
self.slider.setPageStep(self.scroll_step*self.width*self.scale)
self.slider.setValue(self.pos*self.scale) # set the initial position
self.slider.valueChanged.connect(self.xpos_changed)
parent.addWidget(self.slider)
def set_spinbox(self, parent):
self.spinb = QtGui.QDoubleSpinBox(parent=parent)
self.spinb.setDecimals(3)
self.spinb.setRange(0.001,3600.)
self.spinb.setSuffix(" s")
self.spinb.setValue(self.width) # set the initial width
self.spinb.valueChanged.connect(self.xwidth_changed)
parent.addWidget(self.spinb)
def xpos_changed(self, pos):
#pprint("Position (in scroll units) %f\n" %pos)
pos /= self.scale
self.ax.set_xlim(pos, pos+self.width)
self.draw()
def xwidth_changed(self, width):
#pprint("Width (axis units) %f\n" % step)
if width <= 0: return
self.width = width
self.slider.setSingleStep(self.width*self.scale/5.)
self.slider.setPageStep(self.scroll_step*self.width*self.scale)
old_xlim = self.ax.get_xlim()
self.xpos_changed(old_xlim[0]*self.scale)
if __name__ == "__main__":
q = test_plot()
plt.show()
【问题讨论】:
-
你应该看看
pan代码是如何工作的。我认为问题在于您正在生成太多事件,因为跟踪已开启。如果您生成了一小部分事件,它看起来会更平滑。 -
更改
draw->draw_idle让它看起来好一点。鉴于pan的工作原理完全一样,我认为减速是在 QT 样板中,而不是在matplotlib中。 github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/… -
PySide/Qt 代码只是添加了一些其他小部件并调用用户交互的方法。这怎么可能是减速的原因?还要查看
pan代码,它使用相同的set_xlim()方法。 -
从信号/槽处理、主循环中获得的所有开销......堆栈中有很多层的函数调用。
pan是平滑的这一事实表明问题不在set_xlim中,这使得 QT 成为时间接收器所在的位置。您应该分析此代码。 -
Matplotlib 用于出版质量图,不太注重绘图速度/交互性。我建议使用 [pyqtgraph][1],这是一个与 pyside 兼容的极速绘图库。 [1]:pyqtgraph.org
标签: python animation matplotlib pyside