事件处理及拾取
matplotlib 使用了许多用户界面工具包(wxpython,tkinter,qt4,gtk 和 macosx),为了支持交互式平移和缩放图形等功能,拥有一套 API 通过按键和鼠标移动与图形交互,并且『GUI中立』,对开发人员十分有帮助,所以我们不必重复大量的代码来跨不同的用户界面。虽然事件处理 API 是 GUI 中立的,但它是基于 GTK 模型,这是 matplotlib 支持的第一个用户界面。与标准 GUI 事件相比,被触发的事件也比 matplotlib 丰富一些,例如包括发生事件的 事件连接要接收事件,你需要编写一个回调函数,然后将你的函数连接到事件管理器,它是 fig = plt.figure()ax = fig.add_subplot(111)ax.plot(np.random.rand(10))def onclick(event): print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (event.button, event.x, event.y, event.xdata, event.ydata))cid = fig.canvas.mpl_connect('button_press_event', onclick)
fig.canvas.mpl_disconnect(cid)
以下是可以连接到的事件,在事件发生时发回给你的类实例以及事件描述:
事件属性所有 matplotlib 事件继承自基类
事件名称
生成事件的
触发 matplotlib 事件的 GUI 事件 最常见的事件是按键按下/释放事件、鼠标按下/释放和移动事件。 处理这些事件的
x 位置,距离画布左端的像素
y 位置,距离画布底端的像素
如果鼠标经过轴域,则为
鼠标的
鼠标的 但我们看一看画布的简单示例,其中每次按下鼠标时都会创建一条线段。 from matplotlib import pyplot as pltclass LineBuilder: def __init__(self, line): self.line = line self.xs = list(line.get_xdata()) self.ys = list(line.get_ydata()) self.cid = line.figure.canvas.mpl_connect('button_press_event', self) def __call__(self, event): print('click', event) if event.inaxes!=self.line.axes: return self.xs.append(event.xdata) self.ys.append(event.ydata) self.line.set_data(self.xs, self.ys) self.line.figure.canvas.draw()fig = plt.figure()ax = fig.add_subplot(111)ax.set_title('click to build line segments')line, = ax.plot([0], [0]) # empty linelinebuilder = LineBuilder(line)plt.show()
我们刚刚使用的
按下的按钮,
按下的键, 可拖拽的矩形练习编写使用 这里是解决方案: import numpy as npimport matplotlib.pyplot as pltclass DraggableRectangle: def __init__(self, rect): self.rect = rect self.press = None def connect(self): 'connect to all the events we need' self.cidpress = self.rect.figure.canvas.mpl_connect( 'button_press_event', self.on_press) self.cidrelease = self.rect.figure.canvas.mpl_connect( 'button_release_event', self.on_release) self.cidmotion = self.rect.figure.canvas.mpl_connect( 'motion_notify_event', self.on_motion) def on_press(self, event): 'on button press we will see if the mouse is over us and store some data' if event.inaxes != self.rect.axes: return contains, attrd = self.rect.contains(event) if not contains: return print('event contains', self.rect.xy) x0, y0 = self.rect.xy self.press = x0, y0, event.xdata, event.ydata def on_motion(self, event): 'on motion we will move the rect if the mouse is over us' if self.press is None: return if event.inaxes != self.rect.axes: return x0, y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress #print('x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f' % # (x0, xpress, event.xdata, dx, x0+dx)) self.rect.set_x(x0+dx) self.rect.set_y(y0+dy) self.rect.figure.canvas.draw() def on_release(self, event): 'on release we reset the press data' self.press = None self.rect.figure.canvas.draw() def disconnect(self): 'disconnect all the stored connection ids' self.rect.figure.canvas.mpl_disconnect(self.cidpress) self.rect.figure.canvas.mpl_disconnect(self.cidrelease) self.rect.figure.canvas.mpl_disconnect(self.cidmotion)fig = plt.figure()ax = fig.add_subplot(111)rects = ax.bar(range(10), 20*np.random.rand(10))drs = []for rect in rects: dr = DraggableRectangle(rect) dr.connect() drs.append(dr)plt.show()
附加题:使用动画秘籍中讨论的动画 blit 技术,使动画绘制更快更流畅。 附加题解决方案: # draggable rectangle with the animation blit techniques; see# http://www./Cookbook/Matplotlib/Animationsimport numpy as npimport matplotlib.pyplot as pltclass DraggableRectangle: lock = None # only one can be animated at a time def __init__(self, rect): self.rect = rect self.press = None self.background = None def connect(self): 'connect to all the events we need' self.cidpress = self.rect.figure.canvas.mpl_connect( 'button_press_event', self.on_press) self.cidrelease = self.rect.figure.canvas.mpl_connect( 'button_release_event', self.on_release) self.cidmotion = self.rect.figure.canvas.mpl_connect( 'motion_notify_event', self.on_motion) def on_press(self, event): 'on button press we will see if the mouse is over us and store some data' if event.inaxes != self.rect.axes: return if DraggableRectangle.lock is not None: return contains, attrd = self.rect.contains(event) if not contains: return print('event contains', self.rect.xy) x0, y0 = self.rect.xy self.press = x0, y0, event.xdata, event.ydata DraggableRectangle.lock = self # draw everything but the selected rectangle and store the pixel buffer canvas = self.rect.figure.canvas axes = self.rect.axes self.rect.set_animated(True) canvas.draw() self.background = canvas.copy_from_bbox(self.rect.axes.bbox) # now redraw just the rectangle axes.draw_artist(self.rect) # and blit just the redrawn area canvas.blit(axes.bbox) def on_motion(self, event): 'on motion we will move the rect if the mouse is over us' if DraggableRectangle.lock is not self: return if event.inaxes != self.rect.axes: return x0, y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress self.rect.set_x(x0+dx) self.rect.set_y(y0+dy) canvas = self.rect.figure.canvas axes = self.rect.axes # restore the background region canvas.restore_region(self.background) # redraw just the current rectangle axes.draw_artist(self.rect) # blit just the redrawn area canvas.blit(axes.bbox) def on_release(self, event): 'on release we reset the press data' if DraggableRectangle.lock is not self: return self.press = None DraggableRectangle.lock = None # turn off the rect animation property and reset the background self.rect.set_animated(False) self.background = None # redraw the full figure self.rect.figure.canvas.draw() def disconnect(self): 'disconnect all the stored connection ids' self.rect.figure.canvas.mpl_disconnect(self.cidpress) self.rect.figure.canvas.mpl_disconnect(self.cidrelease) self.rect.figure.canvas.mpl_disconnect(self.cidmotion)fig = plt.figure()ax = fig.add_subplot(111)rects = ax.bar(range(10), 20*np.random.rand(10))drs = []for rect in rects: dr = DraggableRectangle(rect) dr.connect() drs.append(dr)plt.show()
鼠标进入和离开如果希望在鼠标进入或离开图形时通知你,你可以连接到图形/轴域进入/离开事件。 下面是一个简单的例子,它改变了鼠标所在的轴域和图形的背景颜色: '''Illustrate the figure and axes enter and leave events by changing theframe colors on enter and leave'''import matplotlib.pyplot as pltdef enter_axes(event): print('enter_axes', event.inaxes) event.inaxes.patch.set_facecolor('yellow') event.canvas.draw()def leave_axes(event): print('leave_axes', event.inaxes) event.inaxes.patch.set_facecolor('white') event.canvas.draw()def enter_figure(event): print('enter_figure', event.canvas.figure) event.canvas.figure.patch.set_facecolor('red') event.canvas.draw()def leave_figure(event): print('leave_figure', event.canvas.figure) event.canvas.figure.patch.set_facecolor('grey') event.canvas.draw()fig1 = plt.figure()fig1.suptitle('mouse hover over figure or axes to trigger events')ax1 = fig1.add_subplot(211)ax2 = fig1.add_subplot(212)fig1.canvas.mpl_connect('figure_enter_event', enter_figure)fig1.canvas.mpl_connect('figure_leave_event', leave_figure)fig1.canvas.mpl_connect('axes_enter_event', enter_axes)fig1.canvas.mpl_connect('axes_leave_event', leave_axes)fig2 = plt.figure()fig2.suptitle('mouse hover over figure or axes to trigger events')ax1 = fig2.add_subplot(211)ax2 = fig2.add_subplot(212)fig2.canvas.mpl_connect('figure_enter_event', enter_figure)fig2.canvas.mpl_connect('figure_leave_event', leave_figure)fig2.canvas.mpl_connect('axes_enter_event', enter_axes)fig2.canvas.mpl_connect('axes_leave_event', leave_axes)plt.show()
对象拾取你可以通过设置艺术家的
选择对于该艺术家已禁用(默认)
如果为
如果选择器是数字,则将其解释为点的 ε 公差,并且如果其数据在鼠标事件的 ε 内,则艺术家将触发事件。 对于像线条和补丁集合的一些艺术家,艺术家可以向生成的选择事件提供附加数据,例如,在选择事件的 ε 内的数据的索引。 函数 如果拾取器是可调用的,则它是用户提供的函数,用于确定艺术家是否被鼠标事件击中。 签名为 通过设置 def pick_handler(event): mouseevent = event.mouseevent artist = event.artist # now do something with this...
传给你的回调的
是生成拾取事件的鼠标事件。鼠标事件具有像
生成拾取事件的 另外,像 简单拾取示例在下面的示例中,我们将行选择器属性设置为标量,因此它表示以点为单位的容差(72 点/英寸)。 当拾取事件位于距离线条的容差范围时,将调用 import numpy as npimport matplotlib.pyplot as pltfig = plt.figure()ax = fig.add_subplot(111)ax.set_title('click on points')line, = ax.plot(np.random.rand(100), 'o', picker=5) # 5 points tolerancedef onpick(event): thisline = event.artist xdata = thisline.get_xdata() ydata = thisline.get_ydata() ind = event.ind points = tuple(zip(xdata[ind], ydata[ind])) print('onpick points:', points)fig.canvas.mpl_connect('pick_event', onpick)plt.show()
拾取练习创建含有 100 个数组的数据集,包含 1000 个高斯随机数,并计算每个数组的样本平均值和标准差(提示: 练习的解决方案: '''compute the mean and stddev of 100 data sets and plot mean vs stddev.When you click on one of the mu, sigma points, plot the raw data fromthe dataset that generated the mean and stddev'''import numpy as npimport matplotlib.pyplot as pltX = np.random.rand(100, 1000)xs = np.mean(X, axis=1)ys = np.std(X, axis=1)fig = plt.figure()ax = fig.add_subplot(111)ax.set_title('click on point to plot time series')line, = ax.plot(xs, ys, 'o', picker=5) # 5 points tolerancedef onpick(event): if event.artist!=line: return True N = len(event.ind) if not N: return True figi = plt.figure() for subplotnum, dataind in enumerate(event.ind): ax = figi.add_subplot(N,1,subplotnum+1) ax.plot(X[dataind]) ax.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f'%(xs[dataind], ys[dataind]), transform=ax.transAxes, va='top') ax.set_ylim(-0.5, 1.5) figi.show() return Truefig.canvas.mpl_connect('pick_event', onpick)plt.show()
|
|