You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Matplotlib中点击交互图将坐标存入对应多列表

问题描述

将三组数据绘制在同一张Matplotlib图中,希望通过交互式鼠标点击图上的点,将这些点的坐标存入各自对应的列表zradius、zangle、zamp中。目前可以点击图上的点并显示坐标,但无法将值存入定义的列表中,请问需修改哪些内容以适配多列表存储需求?

原代码

import matplotlib.pyplot as plt
import numpy as np
from mpldatacursor import datacursor

# 假设实际数据定义
actual_x = np.random.rand(10)*100
actual_y = np.random.rand(10)*100
actual_z = np.linspace(100,700,10)
amp0 = np.random.rand(10)*100

fig1, ax = plt.subplots()
fig1 = plt.figure(figsize= (10,10))
radius  = np.sqrt(actual_x**2 + actual_y**2)
angle = np.arctan2(actual_y,actual_x)
angle = angle/0.01745  # 弧度转角度
amplitudez = amp0/10.

for i in range(len(actual_x)):
    ax.scatter([actual_z],[radius], color = 'black', 
               label='ID: Z: {:.2f},Radius: {:.2f}'.format(actual_z[i],radius[i]),marker = 
               '*',s = 20)
    ax.scatter([actual_z],[angle], color = 'green', 
               label='ID: Z: {:.2f},Angle: {:.2f}'.format(actual_z[i],angle[i]),marker = '*',s 
               = 20)
    ax.scatter([actual_z],[amplitudez], color = 'brown', 
               label='ID: Z: {:.2f},AmpZ: {:.2f}'.format(actual_z[i],amplitudez[i]),marker = 
               'x',s = 20)

ax.set_title('Experimental Data event:')
ax.set_xlabel('Z values')
ax.set_ylabel('Y/X values')
ax.set_xlim(100,700)
ax.grid()

datacursor(formatter='{label}'.format, bbox=dict(fc='yellow', alpha =10.0, snap =True))
zradius, zangle, zamp = [], [], []

def onclick(event):
    global iz, iradius, iangle, iamplitudez
    iz, iradius, iangle, iamplitudez = event.zdata, event.rdata, event.adata, event.ampdata

    global zradius, zangle, zamp
    zradius.append((iz, iradius))
    zangle.append((iz, iangle))
    zamp.append((iz, iamplitudez))

    if len(zradius) == len(actual_x):
        fig.canvas.mpl_disconnect(cid)

    return zradius, zangle, zamp
cid = fig.canvas.mpl_connect('button_press_event', onclick)

原输出

In[7]: zradius
Out[7]: []
问题分析与修改方案

核心问题

  1. 图形对象混乱:原代码先创建fig1, ax = plt.subplots(),随后又重新赋值fig1 = plt.figure(figsize=(10,10)),导致后续绑定事件的画布对象与实际显示的画布不匹配,点击事件根本无法触发。
  2. 事件属性错误:Matplotlib的button_press_event事件对象没有zdata、rdata这类自定义属性,仅能通过xdata、ydata获取点击位置的坐标轴坐标。
  3. 未区分点击点组别:原代码没有判断点击的点属于哪一组数据,无法将数据对应存入目标列表。

修改后的完整代码

import matplotlib.pyplot as plt
import numpy as np
from mpldatacursor import datacursor

# 实际数据定义
actual_x = np.random.rand(10)*100
actual_y = np.random.rand(10)*100
actual_z = np.linspace(100,700,10)
amp0 = np.random.rand(10)*100

# 统一创建画布与坐标轴
fig1, ax = plt.subplots(figsize=(10,10))
radius = np.sqrt(actual_x**2 + actual_y**2)
angle = np.arctan2(actual_y, actual_x) / 0.01745  # 弧度转角度
amplitudez = amp0 / 10.

# 保存每组散点对象,用于判断点击归属
scatter_radius = []
scatter_angle = []
scatter_amp = []

for i in range(len(actual_x)):
    # 绘制radius组并保存对象
    s_r = ax.scatter(actual_z[i], radius[i], color='black',
                     label=f'ID: Z: {actual_z[i]:.2f},Radius: {radius[i]:.2f}',
                     marker='*', s=20)
    scatter_radius.append(s_r)
    # 绘制angle组并保存对象
    s_a = ax.scatter(actual_z[i], angle[i], color='green',
                     label=f'ID: Z: {actual_z[i]:.2f},Angle: {angle[i]:.2f}',
                     marker='*', s=20)
    scatter_angle.append(s_a)
    # 绘制amplitudez组并保存对象
    s_amp = ax.scatter(actual_z[i], amplitudez[i], color='brown',
                       label=f'ID: Z: {actual_z[i]:.2f},AmpZ: {amplitudez[i]:.2f}',
                       marker='x', s=20)
    scatter_amp.append(s_amp)

ax.set_title('Experimental Data event:')
ax.set_xlabel('Z values')
ax.set_ylabel('Y/X values')
ax.set_xlim(100,700)
ax.grid()

datacursor(formatter='{label}'.format, bbox=dict(fc='yellow', alpha=0.8, snap=True))

zradius, zangle, zamp = [], [], []

def onclick(event):
    # 仅处理坐标轴内的有效点击
    if event.inaxes != ax:
        return
    
    click_x, click_y = event.xdata, event.ydata
    # 匹配radius组点
    for i, s in enumerate(scatter_radius):
        x_data, y_data = s.get_offsets()[0]
        if np.sqrt((click_x - x_data)**2 + (click_y - y_data)**2) < 5:
            zradius.append((actual_z[i], radius[i]))
            print(f"已添加radius点: Z={actual_z[i]:.2f}, Radius={radius[i]:.2f}")
            return
    # 匹配angle组点
    for i, s in enumerate(scatter_angle):
        x_data, y_data = s.get_offsets()[0]
        if np.sqrt((click_x - x_data)**2 + (click_y - y_data)**2) < 5:
            zangle.append((actual_z[i], angle[i]))
            print(f"已添加angle点: Z={actual_z[i]:.2f}, Angle={angle[i]:.2f}")
            return
    # 匹配amplitudez组点
    for i, s in enumerate(scatter_amp):
        x_data, y_data = s.get_offsets()[0]
        if np.sqrt((click_x - x_data)**2 + (click_y - y_data)**2) < 5:
            zamp.append((actual_z[i], amplitudez[i]))
            print(f"已添加amp点: Z={actual_z[i]:.2f}, AmpZ={amplitudez[i]:.2f}")
            return
    # 所有点收集完成后断开事件
    if len(zradius) == len(actual_x) and len(zangle) == len(actual_x) and len(zamp) == len(actual_x):
        fig1.canvas.mpl_disconnect(cid)

# 绑定事件到正确的画布对象
cid = fig1.canvas.mpl_connect('button_press_event', onclick)
plt.show()

修改说明

  1. 统一画布对象:用plt.subplots(figsize=(10,10))一次性创建画布和坐标轴,避免重复创建导致的对象不匹配。
  2. 保存散点对象:绘制每组数据时,将散点实例存入列表,后续通过计算点击位置与散点的距离判断点击归属。
  3. 有效点击判断:先过滤坐标轴外的无效点击,再通过距离阈值(此处设为5)判断是否点击到目标点。
  4. 分组存储逻辑:根据点击点的组别,将对应坐标存入zradius、zangle或zamp列表。

内容的提问来源于stack exchange,提问作者theheretic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:37:22