データポイントへのスナップ
前のステップで作成したカーソルは、マウスの各移動時にカーソル位置を更新するため、やはり少し遅いです。このステップでは、Line2D
オブジェクトのデータポイントにスナップするカーソルを作成します。
class SnappingCursor:
"""
線のデータポイントにスナップする十字マーカーカーソルで、
このカーソルの*x*位置に最も近いものです。
簡単のため、これはデータの*x*値がソートされていることを前提としています。
"""
def __init__(self, ax, line):
self.ax = ax
self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
self.x, self.y = line.get_data()
self._last_index = None
## 軸座標におけるテキストの位置
self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
def set_cross_hair_visible(self, visible):
need_redraw = self.horizontal_line.get_visible()!= visible
self.horizontal_line.set_visible(visible)
self.vertical_line.set_visible(visible)
self.text.set_visible(visible)
return need_redraw
def on_mouse_move(self, event):
if not event.inaxes:
self._last_index = None
need_redraw = self.set_cross_hair_visible(False)
if need_redraw:
self.ax.figure.canvas.draw()
else:
self.set_cross_hair_visible(True)
x, y = event.xdata, event.ydata
index = min(np.searchsorted(self.x, x), len(self.x) - 1)
if index == self._last_index:
return ## まだ同じデータポイント上です。何もする必要はありません。
self._last_index = index
x = self.x[index]
y = self.y[index]
## 線の位置を更新
self.horizontal_line.set_ydata([y])
self.vertical_line.set_xdata([x])
self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
self.ax.figure.canvas.draw()
x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)
fig, ax = plt.subplots()
ax.set_title('Snapping cursor')
line, = ax.plot(x, y, 'o')
snap_cursor = SnappingCursor(ax, line)
fig.canvas.mpl_connect('motion_notify_event', snap_cursor.on_mouse_move)
plt.show()