我正在编辑【python】代码,遇到了 【raise RuntimeError("Another Axes already grabs mouse input")
RuntimeError: Another Axes already grabs mouse input
Ignoring fixed y limits to fulfill fixed data aspect with adjustable data limits.】
,请帮我检查并改正错误点。我的原始代码如下:
【# 主函数
def main():
# 创建光滑轨迹
md_smooth, tvd_smooth, north_smooth, east_smooth = create_smooth_trajectory(md, tvd, north, east)
# 创建深度转换插值器
md_to_tvd, tvd_to_md = create_depth_interpolators(md_smooth, tvd_smooth)
# 计算对应的垂深值
valid_md, multi_arm_tvd = calculate_corresponding_tvd(arm_md, md_to_tvd)
arm_md0 = get_3d_coordinates_by_md(valid_md, md_smooth, east_smooth, north_smooth, tvd_smooth)
arm1_3d = get_3d_coordinates_by_md(arm_md1, md_smooth, east_smooth, north_smooth, tvd_smooth)
arm2_3d = get_3d_coordinates_by_md(arm_md2, md_smooth, east_smooth, north_smooth, tvd_smooth)
print('arm_md0', arm_md0)
diff_arm = arm1_3d - arm2_3d
sq_diff = diff_arm ** 2
sum_sq = np.sum(sq_diff, axis=1)
distances = np.sqrt(sum_sq)
angle_x = np.divide(diff_arm[:,0], distances)
angle_y = np.divide(diff_arm[:,1], distances)
angle_z = np.divide(diff_arm[:,2], distances)
print('angle', angle_x, '\n', angle_y, '\n',angle_z)
# 建立坐标变换矩阵
trans_matrix = np.zeros((len(angle_x), 3, 3))
for i in range(len(angle_x)):
trans_matrix[i, 0, 0] = - angle_y[i]/np.sqrt(angle_x[i] ** 2 + angle_y[i] ** 2)
trans_matrix[i, 0, 1] = angle_x[i] * angle_z[i]/np.sqrt(angle_x[i] ** 2 + angle_y[i] ** 2)
trans_matrix[i, 0, 2] = angle_x[i]
trans_matrix[i, 1, 0] = angle_x[i]/np.sqrt(angle_x[i] ** 2 + angle_y[i] ** 2)
trans_matrix[i, 1, 1] = (angle_z[i] * angle_y[i])/np.sqrt(angle_x[i] ** 2 + angle_y[i] ** 2)
trans_matrix[i, 1, 2] = angle_y[i]
trans_matrix[i, 2, 0] = 0
trans_matrix[i, 2, 1] = np.sqrt(angle_x[i] ** 2 + angle_y[i] ** 2)
trans_matrix[i, 2, 2] = - angle_z[i]
print(trans_matrix)
# 各接触点的局部坐标计算
loc_positions_x = np.zeros((len(afilter), 40))
loc_positions_y = np.zeros((len(afilter), 40))
loc_positions_z = np.zeros((len(afilter), 40))
for i in range(len(afilter)):
for j in range(40):
loc_positions_x[i, j] = afilter[i, j] * np.cos(np.pi/20 * j)
loc_positions_y[i, j] = afilter[i, j] * np.sin(np.pi/20 * j)
# 组合为三维数组
sum_loc = np.stack((loc_positions_x, loc_positions_y, loc_positions_z), axis=0)
print('111', sum_loc.shape)
# 获取40臂各臂腿曲线采集点的实际坐标值
arms_positions = np.zeros((len(angle_x), 40, 3, 1))
for i in range(len(afilter)):
for j in range(40):
ddn = sum_loc[:, i , j].reshape(-1, 1)
ddw = trans_matrix[i, :, :]
ddm = arm_md0[i, :].reshape(-1, 1)
arms_positions[i, j, :, :] = ddw @ ddn + ddm
print('arms_positions', arms_positions)
# 创建插值函数(根据垂深查询坐标)
north_interp = PchipInterpolator(tvd_smooth, north_smooth)
east_interp = PchipInterpolator(tvd_smooth, east_smooth)
# 创建图形和3D坐标轴
fig = plt.figure(figsize=(16, 14))
# 使用GridSpec创建更灵活的布局
gs = GridSpec(4, 3, height_ratios=[1, 0.1, 0.05, 0.08], width_ratios=[0.7, 0.15, 0.15])
# 3D主视图
ax_main = fig.add_subplot(gs[0, 0], projection='3d')
# 水平剖面图
ax_profile = fig.add_subplot(gs[0, 1:])
ax_profile.set_aspect('equal', adjustable='datalim')
# 滑块区域
ax_slider = fig.add_subplot(gs[1, 0])
# 坐标比例控制区域
ax_scale = fig.add_subplot(gs[1, 1:])
ax_scale.axis('off')
# 按钮区域
ax_buttons = fig.add_subplot(gs[2, 0])
ax_buttons.axis('off')
# 文本信息区域
ax_text = fig.add_subplot(gs[2, 1:])
ax_text.axis('off')
# 底部信息区域
ax_bottom = fig.add_subplot(gs[3, :])
ax_bottom.axis('off')
# 调整布局
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95,
wspace=0.15, hspace=0.25)
# 绘制多臂井径图
all_points, ax_main, n_depths = plot_40_arms_smooth(arms_positions, ax_main)
# 绘制光滑轨迹
ax_main.plot(north_smooth, east_smooth, tvd_smooth, 'b-', linewidth=3, alpha=0.9, label='Smooth borehole trajectory')
# 绘制原始数据点
ax_main.scatter(north, east, tvd, c='r', s=1, label='origin_point')
# 收集所有点用于范围计算
all_points = np.vstack(all_points) if all_points else np.array([[0, 0, 0]])
all_north = np.concatenate([all_points[:, 0], north])
all_east = np.concatenate([all_points[:, 1], east])
all_tvd = np.concatenate([all_points[:, 2], tvd])
# 计算范围
max_range = np.array([
all_north.max() - all_north.min(),
all_east.max() - all_east.min(),
all_tvd.max() - all_tvd.min()
]).max() / 2.0
mid_x = (all_north.max() + all_north.min()) * 0.5
mid_y = (all_east.max() + all_east.min()) * 0.5
mid_z = (all_tvd.max() + all_tvd.min()) * 0.5
# 设置主视图范围
ax_main.set_xlim(mid_x - max_range, mid_x + max_range)
ax_main.set_ylim(mid_y - max_range, mid_y + max_range)
ax_main.set_zlim(mid_z - max_range, mid_z + max_range)
# 保存全局范围用于重置
global_limits = {
'xlim': ax_main.get_xlim(),
'ylim': ax_main.get_ylim(),
'zlim': ax_main.get_zlim()
}
# 设置坐标轴标签
ax_main.set_xlabel('North (m)', fontsize=10)
ax_main.set_ylabel('East (m)', fontsize=10)
ax_main.set_zlabel('Depth (m)', fontsize=10)
ax_main.set_title('Combined Caliper & Trajectory Visualization', fontsize=12)
ax_main.legend(loc='upper right', fontsize=9)
ax_main.grid(True, alpha=0.3)
# 设置视角
ax_main.view_init(elev=25, azim=45)
# 计算每个深度的椭圆参数
ellipse_params = []
# 获取深度级别
depth_levels = arms_positions[:, 0, 2, 0] # 假设所有臂在相同深度
for depth_idx in range(n_depths):
points_at_depth = arms_positions[depth_idx, :, :2, 0] # (n_arms, 2)
# 计算中心点
center = np.mean(points_at_depth, axis=0)
# 计算点集协方差矩阵
cov_matrix = np.cov(points_at_depth.T)
# 计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
# 计算椭圆参数
angle = np.degrees(np.arctan2(*eigenvectors[:, 0][::-1]))
width = 2 * np.sqrt(eigenvalues[0])
height = 2 * np.sqrt(eigenvalues[1])
# 计算椭圆度 (长轴/短轴)
ellipse_ratio = max(width, height) / min(width, height) if min(width, height) > 0 else 1.0
ellipse_params.append({
'center': center,
'width': width,
'height': height,
'angle': angle,
'ratio': ellipse_ratio,
'depth': depth_levels[depth_idx],
'points': points_at_depth
})
# 按深度排序椭圆参数
ellipse_params.sort(key=lambda x: x['depth'])
# 初始垂深值(取中间点)
init_depth = np.median(tvd_smooth)
# 计算初始坐标
init_east = east_interp(init_depth)
init_north = north_interp(init_depth)
# 在主视图中绘制初始点
point = ax_main.scatter([init_north], [init_east], [init_depth],
c='g', s=100, label='Current Depth')
# 添加深度滑块
slider = Slider(
ax=ax_slider,
label='Depth (m)',
valmin=tvd.min(),
valmax=tvd.max(),
valinit=init_depth,
valstep=0.5,
facecolor='lightblue'
)
# 添加坐标比例输入框
scale_text_props = dict(fontsize=9, ha='right', va='center')
input_props = dict(fontsize=9, bbox=dict(facecolor='white', alpha=0.7))
# X轴范围
ax_scale.text(0.05, 0.75, "X Range (min,max):", **scale_text_props)
x_range_box = TextBox(fig.add_axes([0.25, 0.75, 0.2, 0.03]), "",
initial=f"{mid_x - max_range:.2f}, {mid_x + max_range:.2f}")
# Y轴范围
ax_scale.text(0.05, 0.5, "Y Range (min,max):", **scale_text_props)
y_range_box = TextBox(fig.add_axes([0.25, 0.5, 0.2, 0.03]), "",
initial=f"{mid_y - max_range:.2f}, {mid_y + max_range:.2f}")
# Z轴范围
ax_scale.text(0.05, 0.25, "Z Range (min,max):", **scale_text_props)
z_range_box = TextBox(fig.add_axes([0.25, 0.25, 0.2, 0.03]), "",
initial=f"{mid_z - max_range:.2f}, {mid_z + max_range:.2f}")
# 比例应用按钮
apply_ax = plt.axes([0.5, 0.25, 0.1, 0.03])
apply_button = Button(apply_ax, 'Apply Scale', color='lightgreen')
apply_button.label.set_fontsize(9)
# 比例重置按钮
reset_scale_ax = plt.axes([0.62, 0.25, 0.1, 0.03])
reset_scale_button = Button(reset_scale_ax, 'Reset Scale', color='lightcoral')
reset_scale_button.label.set_fontsize(9)
# 在按钮区域创建两个小按钮
reset_ax = plt.axes([0.15, 0.04, 0.15, 0.03]) # [左, 下, 宽, 高]
rotate_ax = plt.axes([0.35, 0.04, 0.15, 0.03])
# 创建更小的按钮
reset_button = Button(reset_ax, 'Reset View', color='lightgoldenrodyellow')
rotate_button = Button(rotate_ax, 'Rotate View', color='lightblue')
# 设置按钮字体大小
for button in [reset_button, rotate_button]:
button.label.set_fontsize(8)
# 添加坐标显示文本
coord_text = ax_text.text(
0.5, 0.5,
f'Depth: {init_depth:.2f}m | East: {init_east:.2f}m | North: {init_north:.2f}m',
ha='center', va='center', fontsize=10
)
# 添加底部信息
ax_bottom.text(0.5, 0.8, "3D Borehole Visualization with Caliper Data",
ha='center', va='center', fontsize=12, weight='bold')
ax_bottom.text(0.5, 0.4, "Use sliders and buttons to explore the data | Adjust scales for detailed views",
ha='center', va='center', fontsize=9, alpha=0.7)
# 绘制初始水平剖面
def plot_depth_profile(depth):
# 找到最接近的深度索引
closest_idx = np.argmin(np.abs(depth_levels - depth))
params = ellipse_params[closest_idx]
# 清除之前的绘图
ax_profile.clear()
# 设置标题和标签
ax_profile.set_title(f'Horizontal Profile at {params["depth"]:.1f}m Depth', fontsize=10)
ax_profile.set_xlabel('North (m)', fontsize=9)
ax_profile.set_ylabel('East (m)', fontsize=9)
ax_profile.grid(True, alpha=0.3)
# 绘制所有臂点
ax_profile.scatter(params['points'][:, 0], params['points'][:, 1],
c='b', s=30, alpha=0.7, label='Caliper Points')
# 绘制中心点
ax_profile.scatter(params['center'][0], params['center'][1],
c='r', s=80, marker='x', label='Center')
# 绘制拟合椭圆
ellipse = Ellipse(
xy=params['center'],
width=params['width'],
height=params['height'],
angle=params['angle'],
edgecolor='g',
facecolor='none',
linewidth=2,
alpha=0.8,
label=f'Ellipticity: {params["ratio"]:.2f}'
)
ax_profile.add_patch(ellipse)
# 添加椭圆度信息
ax_profile.text(0.05, 0.95,
f'Ellipticity: {params["ratio"]:.2f}\n'
f'Major Axis: {max(params["width"], params["height"]):.2f}m\n'
f'Minor Axis: {min(params["width"], params["height"]):.2f}m',
transform=ax_profile.transAxes,
fontsize=9,
verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
# 添加图例
ax_profile.legend(loc='lower right', fontsize=8)
# 设置相同的比例
ax_profile.set_aspect('equal', adjustable='datalim')
# 设置坐标轴范围(包含所有点)
margin = 0.1 # 10%的边距
x_min, x_max = np.min(params['points'][:, 0]), np.max(params['points'][:, 0])
y_min, y_max = np.min(params['points'][:, 1]), np.max(params['points'][:, 1])
x_range = x_max - x_min
y_range = y_max - y_min
max_range = max(x_range, y_range) * (1 + margin) / 2
mid_x = (x_min + x_max) / 2
mid_y = (y_min + y_max) / 2
ax_profile.set_xlim(mid_x - max_range, mid_x + max_range)
ax_profile.set_ylim(mid_y - max_range, mid_y + max_range)
# 初始绘制
plot_depth_profile(init_depth)
# 更新函数
def update(val):
depth = slider.val
# 计算新坐标
new_east = east_interp(depth)
new_north = north_interp(depth)
# 更新主视图点位置
point._offsets3d = ([new_north], [new_east], [depth])
# 更新水平剖面图
plot_depth_profile(depth)
# 更新文本
coord_text.set_text(f'Depth: {depth:.2f}m | East: {new_east:.2f}m | North: {new_north:.2f}m')
# 重绘图形
fig.canvas.draw_idle()
slider.on_changed(update)
# 重置视图函数
def reset_view(event):
ax_main.set_xlim(global_limits['xlim'][0], global_limits['xlim'][1])
ax_main.set_ylim(global_limits['ylim'][0], global_limits['ylim'][1])
ax_main.set_zlim(global_limits['zlim'][0], global_limits['zlim'][1])
ax_main.view_init(elev=25, azim=45)
# 更新输入框值
x_range_box.set_val(f"{global_limits['xlim'][0]:.2f}, {global_limits['xlim'][1]:.2f}")
y_range_box.set_val(f"{global_limits['ylim'][0]:.2f}, {global_limits['ylim'][1]:.2f}")
z_range_box.set_val(f"{global_limits['zlim'][0]:.2f}, {global_limits['zlim'][1]:.2f}")
fig.canvas.draw_idle()
reset_button.on_clicked(reset_view)
# 旋转视图函数
def rotate_view(event):
current_azim = ax_main.azim
ax_main.view_init(elev=5, azim=current_azim + 20)
fig.canvas.draw_idle()
rotate_button.on_clicked(rotate_view)
# 应用比例函数
def apply_scale(event):
try:
# 解析X范围
x_min, x_max = map(float, x_range_box.text.split(','))
ax_main.set_xlim(x_min, x_max)
# 解析Y范围
y_min, y_max = map(float, y_range_box.text.split(','))
ax_main.set_ylim(y_min, y_max)
# 解析Z范围
z_min, z_max = map(float, z_range_box.text.split(','))
ax_main.set_zlim(z_min, z_max)
fig.canvas.draw_idle()
except Exception as e:
print(f"Invalid scale input: {e}")
apply_button.on_clicked(apply_scale)
# 重置比例函数
def reset_scale(event):
ax_main.set_xlim(global_limits['xlim'][0], global_limits['xlim'][1])
ax_main.set_ylim(global_limits['ylim'][0], global_limits['ylim'][1])
ax_main.set_zlim(global_limits['zlim'][0], global_limits['zlim'][1])
# 更新输入框值
x_range_box.set_val(f"{global_limits['xlim'][0]:.2f}, {global_limits['xlim'][1]:.2f}")
y_range_box.set_val(f"{global_limits['ylim'][0]:.2f}, {global_limits['ylim'][1]:.2f}")
z_range_box.set_val(f"{global_limits['zlim'][0]:.2f}, {global_limits['zlim'][1]:.2f}")
fig.canvas.draw_idle()
reset_scale_button.on_clicked(reset_scale)
plt.show()】