如何在Shiny for Python交互式绘图中识别选中/悬停的Ax?
Shiny for Python多子图交互:识别选中/悬停的Ax
1. 判断哪个Ax被选中(点击交互)
可以通过两种直接的方式实现:
- 给Ax添加唯一标识:创建子图时为每个Ax设置自定义ID,比如
ax.set_gid(f"subplot_{i}"),在点击回调中通过事件对象获取点击元素所属的Ax,进而拿到对应ID。 - 坐标范围匹配:调用
plt.gcf().get_axes()获取所有子图,通过每个Ax的get_window_extent()方法得到其像素范围,再结合点击事件的coords_css坐标,判断该坐标落在哪个Ax的范围内。
示例代码片段:
from matplotlib.pyplot import gcf def on_click(event): for ax in gcf().get_axes(): bbox = ax.get_window_extent() if bbox.contains(event.x, event.y): print(f"选中的子图:{ax.get_gid()}") break
2. 识别多子图中被悬停的Ax
针对你提到的悬停事件返回JSON缺少子图信息的问题,推荐两种简便方案:
方案一:为Ax添加唯一标识并关联交互元素
创建子图时给每个Ax分配唯一ID,同时给子图内的所有可交互元素(如线条、散点)设置gid属性关联到对应Ax的ID。在悬停回调中,通过事件数据中的元素gid直接匹配到对应的Ax。
示例代码:
fig, axes = plt.subplots(2, 2) for i, ax in enumerate(axes.flat): ax_id = f"ax_{i}" ax.set_gid(ax_id) # 给子图内的绘图元素绑定相同ID line = ax.plot([1,2,3])[0] line.set_gid(ax_id)
方案二:预计算Ax像素范围并实时匹配
绘图完成后,获取每个Ax的get_window_extent()(包含相对于整个画布的像素坐标)并存储这些范围信息。在悬停回调中,用事件的coords_css.x和coords_css.y匹配到对应的Ax。
若窗口resize,需重新计算所有Ax的范围,可以监听窗口resize事件触发更新逻辑。
示例逻辑:
# 初始化时存储每个Ax的像素范围 ax_bboxes = [] fig, axes = plt.subplots(2, 2) for ax in axes.flat: bbox = ax.get_window_extent() ax_bboxes.append({ "ax": ax, "left": bbox.x0, "right": bbox.x1, "top": bbox.y0, "bottom": bbox.y1 }) # 悬停回调中匹配子图 def on_hover(hover_data): css_x = hover_data["coords_css"]["x"] css_y = hover_data["coords_css"]["y"] for bbox_info in ax_bboxes: if (bbox_info["left"] <= css_x <= bbox_info["right"] and bbox_info["top"] <= css_y <= bbox_info["bottom"]): print(f"悬停的子图:{bbox_info['ax'].get_gid()}") break
补充说明
你提供的悬停JSON中的range字段是子图自身的数据坐标范围,而coords_css是相对于图表容器的像素坐标,用coords_css匹配预存的Ax像素范围是更直接的方式。
内容的提问来源于stack exchange,提问作者davipatti
相关产品推荐
相关产品推荐

