增加挪动鼠标时的交互之后,相比于各个券商 APP 上的 K 线,还差拖动和放大、放大、十字光标的交互性能。
鼠标拖动
拖动 K 线时要做的操作是按下鼠标,而后平行挪动,最初松开鼠标,因而须要绑定针对这些事件的解决。
fig.canvas.mpl_connect('button_press_event', button_press)
fig.canvas.mpl_connect('button_release_event', button_release)
按下鼠标
因为是要在松开鼠标后才真正挪动 K 线,因而须要记录鼠标按下时的坐标,并标记目前鼠标已按下。
def button_press(event):
global current_x, button_flag
idx = int(event.xdata)
current_x = idx
button_flag = True
再次挪动时,各个指标的数据能够不必追随鼠标挪动而动态显示对应的数值。
def on_move(event):
if button_flag:
return
松开鼠标
松开鼠标时,依据拖动到的新地位与之前鼠标按下时的地位做差求得间隔,而后在默认显示 90 天的根底上批改起止日期,从新获取数据,而后对子图和画布都做清理后从新显示。
def button_release(event):
global current_x, button_flag
button_flag = False
idx = int(event.xdata)
diff = idx - current_x
global s_idx, e_idx, show_data
s_idx -= diff
e_idx -= diff
if e_idx > 0:
e_idx = -1
s_idx = -90
show_data = data[s_idx: e_idx]
for ax in axes_data.values():
ax.cla()
plt.clf()
show_chart()
current_x = -1
放大放大
要实现放大放大实际上就是对鼠标的滚轮事件做解决。
fig.canvas.mpl_connect('scroll_event', on_scroll)
设置默认显示 90 天的数据,当放大放大时,每次在以后根底上变动 10%,而后用新的起止日期获取数据,清理以后画布后从新显示新数据。
def on_scroll(event):
ax = event.inaxes
if ax is None:
return
global s_idx, e_idx, show_data
diff = int(data_size * 0.1 / 2)
if event.button == "down":
s_idx += diff
e_idx -= diff
elif event.button == "up":
s_idx -= diff
e_idx += diff
if e_idx > 0:
e_idx = -1
show_data = data[s_idx: e_idx]
for ax in axes_data.values():
ax.cla()
plt.clf()
show_chart()
十字光标
matplotlib
自带有两种光标,一种是在单子图中显示,另外一种是在多子图中显示,但多子图显示时十字会呈现在所有的子图中,因而这里仿照 MultiCursor
的实现形式对 motion_notify_event
的事件处理函数进行革新。
应用 dict
构造存储垂直线和水平线,它们的 key
为子图的名字,value
即为 matplotlib
中的 line
对象
vlines = {}
hlines = {}
在图表显示时默认在所有子图中显示垂直线,只在 K 线所在的子图中显示水平线。其中垂直线的 X 轴坐标即为以后显示数据大小的一半,水平线的 Y 轴坐标即为以后显示数据中最高价的最高值与最低价的最低值做差,获取显示区域 Y 轴的间隔,再用最低价的最低值加上其一半的值即为水平线的 Y 轴坐标。
def show_chart():
xmid = data_size/2
ymid = min(show_data["Low"]) + (max(show_data["High"]) - min(show_data["Low"]))/2
vlines = dict([(key, ax.axvline(xmid, color="r", lw=1, ls="dashdot"))
for key, ax in axes_data.items()])
hlines = {"main": ax_main.axhline(ymid, visible=True, color="r", lw=1, ls="dashdot")}
当鼠标挪动时,须要动态显示垂直线和水平线。
fig.canvas.mpl_connect('motion_notify_event', on_move)
fig.canvas.mpl_connect('draw_event', clear)
在 draw_event
的处理函数中获取 background
以减速动态显示,并将垂直线和水平线都设为不可见。
def clear(event):
global background
background = (fig.canvas.copy_from_bbox(fig.bbox))
for line in vlines.values():
line.set_visible(False)
for line in hlines.values():
line.set_visible(False)
在鼠标挪动时垂直线能够间接显示,但对于水平线,要看鼠标以后挪动到了哪个子图上,只显示鼠标所在子图上的水平线。
def on_move(event):
# 垂直线间接显示
for line in vlines.values():
line.set_xdata(idx)
line.set_visible(True)
# 标记是否找到了鼠标所在的子图
find = False
# 初始化时只在 K 线所在的子图创立了水平线, 因而须要先判断鼠标是否在具备水平线的子图上
for key, line in hlines.items():
ax = axes_data.get(key)
if ax is not None and event.inaxes == ax:
line.set_ydata(event.ydata)
line.set_visible(True)
find = True
else:
line.set_visible(False)
# 如果没有找到阐明鼠标挪动到了尚未创立水平线的子图上, 须要新建水平线
if not find:
for key, ax in axes_data.items():
if event.inaxes != ax:
continue
hlines[key] = ax.axhline(event.ydata, visible=True, color="r", lw=1, ls="dashdot")
# 应用 background, draw_artist, blit 等减速动态显示
if background is not None:
fig.canvas.restore_region(background)
for key, ax in axes_data.items():
line = vlines.get(key)
if line is not None:
ax.draw_artist(line)
line = hlines.get(key)
if line is not None:
ax.draw_artist(line)
fig.canvas.blit()
最终成果
残缺代码参见:https://github.com/just4alpha/GreedyAlpha