如何访问并编辑Plotly回调创建的图形(Python)
问题描述
点击图片上的瑕疵位置时会添加圆形标记(效果如下):
我通过Plotly回调实现了这个功能,但所有添加的图形trace_name和trace_index都是0,只有坐标不同。现在我可以用鼠标选择并编辑任意图形,但无法删除它们,也没法获取这些图形的信息。Plotly肯定存储了这些数据,请问怎么访问这些图形及其属性,实现编辑或删除操作?
实现代码
# 使用的库: # 用于绘图和捕获用户交互的包 import plotly.graph_objects as go # 创建交互控件的包 import ipywidgets as widgets # 处理数组的库 import numpy as np # OpenCV库 import cv2 # 通过ipywidgets和opencv加载图片 # 存储上传文件的对象 uploader = widgets.FileUpload(accept = '', # 允许的文件扩展名,例如 '.txt', '.pdf', 'image/*' multiple = False # 是否允许多文件上传 ) # 显示上传按钮 # 本代码原本在笔记本环境运行,所以后续代码会暂停 # 如果直接"全部运行"或在Jupyter环境外执行会报错 display(uploader) # 获取文件内容并存储 uploaded_file = list(uploader.value.values())[0] # 用OpenCV加载图片 image = cv2.imdecode(np.asarray(bytearray(uploaded_file['content'])), cv2.IMREAD_UNCHANGED) # 如果图片加载失败,输出错误 if image is None: print('加载图片失败: {}'.format(uploaded_file['metadata']['name'])) else: print('成功加载图片: {}'.format(uploaded_file['metadata']['name'])) # 将BGR格式转为RGB image = image[:, :, :: -1] # 创建用于处理图片的FigureWidget对象 fig = go.FigureWidget() # 向图中添加图片 fig.add_image(z = image); # 更新布局 fig.update_layout(height = 800, width = 1200, title = f"图片文件名: {uploaded_file['metadata']['name']}", ); # 获取图片对应的trace对象 image_fig = fig.data[0] # 创建输出对象 out = widgets.Output() # 回调函数 @out.capture(clear_output = True) def get_mouse_clicks(trace, points, selector): size = 10 ind = points.point_inds[0] x = ind[1] y = ind[0] trace_name = points.trace_name trace_index = points.trace_index message = f"鼠标点击坐标: x = {x}, y = {y}.\nTrace名称: {trace_name}. \nTrace索引: {trace_index}." print(points) print() print(selector) print() print(message) # 左键点击时 if selector.button == 0: # 绘制圆形 fig.add_shape(editable = True, type = "circle", x0 = x - size / 2, y0 = y - size / 2, x1 = x + size / 2, y1 = y + size / 2, xref = 'x', yref = 'y', fillcolor = "grey", line_color = "black", ) # 注册点击回调 image_fig.on_click(get_mouse_clicks) # 显示输出 widgets.VBox([fig, out])
解决方案
Plotly的FigureWidget中添加的形状(shape)都存储在fig.layout.shapes列表里,你可以通过这个列表直接访问、修改或删除所有标记的圆形:
1. 访问所有形状
直接遍历fig.layout.shapes就能获取每个形状的属性,比如坐标、颜色等:
# 遍历所有形状并打印属性 for idx, shape in enumerate(fig.layout.shapes): print(f"形状索引 {idx}:") print(f" 类型: {shape.type}") print(f" 坐标范围: x0={shape.x0}, y0={shape.y0}, x1={shape.x1}, y1={shape.y1}") print(f" 填充色: {shape.fillcolor}")
2. 删除指定形状
你可以通过索引删除单个形状,或者清空所有形状:
# 删除索引为0的形状 if fig.layout.shapes: fig.layout.shapes.pop(0) # 清空所有形状 fig.layout.shapes = []
3. 添加右键删除功能
可以扩展原有的回调函数,添加右键点击删除选中形状的逻辑。Plotly的selector对象能获取当前选中的形状,结合右键点击事件实现删除:
# 修改后的回调函数 @out.capture(clear_output = True) def get_mouse_clicks(trace, points, selector): size = 10 ind = points.point_inds[0] x = ind[1] y = ind[0] # 左键添加圆形 if selector.button == 0: fig.add_shape(editable = True, type = "circle", x0 = x - size / 2, y0 = y - size / 2, x1 = x + size / 2, y1 = y + size / 2, xref = 'x', yref = 'y', fillcolor = "grey", line_color = "black", ) # 右键删除选中的形状 elif selector.button == 2: # 获取当前选中的形状索引 selected_shape_idx = selector.selected_shape_idx if selected_shape_idx is not None and 0 <= selected_shape_idx < len(fig.layout.shapes): fig.layout.shapes.pop(selected_shape_idx) print(f"已删除索引为 {selected_shape_idx} 的形状")
4. 编辑形状属性
直接修改fig.layout.shapes中对应形状的属性即可,比如修改颜色或大小:
# 修改第一个形状的填充色为红色 if fig.layout.shapes: fig.layout.shapes[0].fillcolor = "red" # 修改圆形大小 center_x = (fig.layout.shapes[0].x0 + fig.layout.shapes[0].x1)/2 center_y = (fig.layout.shapes[0].y0 + fig.layout.shapes[0].y1)/2 new_size = 15 fig.layout.shapes[0].x0 = center_x - new_size/2 fig.layout.shapes[0].x1 = center_x + new_size/2 fig.layout.shapes[0].y0 = center_y - new_size/2 fig.layout.shapes[0].y1 = center_y + new_size/2
内容的提问来源于stack exchange,提问作者iacisme
相关产品推荐
相关产品推荐

