如何在Python Tkinter环境下实现无鼠标操作的Matplotlib光标控制
实现方案
核心实现逻辑
- 自动定位初始光标:调用mplcursors的
select()方法手动指定首个数据点,通过Tkinter的after()方法延迟调用,保证画布渲染完成后再执行光标定位,避免坐标计算异常 - 同步控制黄色光标:自定义全局索引记录当前光标所在横轴位置,绑定键盘左右方向键事件,修改索引后同时更新所有子图的mplcursors选中位置
- 同步绿色MultiCursor:每次更新黄色光标位置时,获取当前点的像素坐标,手动修改MultiCursor的横竖线位置并重绘画布,实现对齐效果
修改后完整可运行代码
from matplotlib.widgets import MultiCursor import numpy as np from tkinter import * from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import mplcursors root = Tk() # 初始化绘图数据 x = np.linspace(-np.pi, np.pi, 256, endpoint=True) y = np.sin(x) z = np.cos(x) # 创建子图 fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True, figsize=(8,6)) ax1.plot(x, y, label="sin function") ax1.legend(loc="upper right") ax2.plot(x, z, label="cos function") ax2.legend(loc="upper left") canvas = FigureCanvasTkAgg(fig,root) # 初始化mplcursors,移除默认左右键绑定,改为自定义全局控制 c1 = mplcursors.cursor(ax1, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e"}) c2 = mplcursors.cursor(ax2, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e"}) # 存储所有光标实例方便批量操作,多绘图场景仅需新增实例到该列表即可 cursor_list = [c1, c2] # 全局索引记录当前选中的数据点位置 current_idx = 0 # 数据点总数量 total_points = len(x) # 初始化绿色多光标 multi = MultiCursor(fig.canvas, (ax1, ax2), color='g', lw=0.5, horizOn=True, vertOn=True) # 更新所有光标位置的通用方法 def update_cursor(): global current_idx # 边界控制,避免索引超出数据范围 current_idx = max(0, min(current_idx, total_points-1)) # 同步更新所有黄色mplcursors for cursor in cursor_list: cursor.select(cursor.artists[0], current_idx) # 获取当前点坐标,更新绿色多光标位置 current_x = x[current_idx] # 转换x坐标为像素值,更新垂直光标 x_pixel = ax1.transData.transform((current_x, 0))[0] multi.vline.set_xdata([x_pixel, x_pixel]) # 转换两个子图的y坐标为像素值,更新水平光标 y1_pixel = ax1.transData.transform((0, y[current_idx]))[1] y2_pixel = ax2.transData.transform((0, z[current_idx]))[1] multi.hline[0].set_ydata([y1_pixel, y1_pixel]) multi.hline[1].set_ydata([y2_pixel, y2_pixel]) # 重绘画布 canvas.draw_idle() # 绑定键盘方向键事件 def on_key_press(event): global current_idx if event.keysym == "Left": current_idx -= 1 update_cursor() elif event.keysym == "Right": current_idx += 1 update_cursor() fig.canvas.mpl_connect('key_press_event', on_key_press) # 窗口加载完成后自动定位到第一个数据点,延迟100ms保证画布渲染完成 root.after(100, update_cursor) # 布局渲染 canvas.draw() canvas.get_tk_widget().pack(side = BOTTOM, fill=BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, root) canvas._tkcanvas.pack(side = TOP, fill=BOTH, expand=True) root.mainloop()
适配说明
如果是多页面、多绘图场景,仅需要将对应子图的mplcursors实例加入cursor_list列表,同时在update_cursor方法中补充对应子图的y坐标更新逻辑即可快速适配。
内容的提问来源于stack exchange,提问作者Nedi
相关产品推荐
相关产品推荐

