基于Plotly高亮功能控制图例交互:实现点击图例高亮数据点
实现Plotly Dash图例点击高亮对应数据点
需求说明
原代码实现的是点击图例时对应数据点透明度降低(近似隐藏),现需修改为点击图例时高亮对应数据点(即目标数据点保持高可见度,其余数据点降低透明度,或切换目标数据点的高亮状态)。
修改后的代码
import plotly.graph_objects as go import dash from dash import dcc, html from dash.dependencies import Input, Output import plotly.express as px # 加载iris数据集 iris = px.data.iris() app = dash.Dash(__name__) # 初始化散点图:所有trace默认低透明度,方便高亮对比 fig = go.Figure() for s in iris['species'].unique(): subset = iris[iris['species'] == s] fig.add_trace(go.Scatter( x=subset['petal_length'], y=subset['petal_width'], mode='markers', name=s, marker=dict(opacity=0.3) # 初始低透明度 )) # 禁用Plotly默认的图例点击隐藏/显示行为,自定义交互 fig.update_layout( legend=dict( itemclick=False, # 禁用单点击默认行为 itemdoubleclick=False # 禁用双击默认行为 ) ) app.layout = html.Div([ dcc.Graph(id='iris-plot', figure=fig), ]) @app.callback( Output('iris-plot', 'figure'), Input('iris-plot', 'relayoutData'), prevent_initial_call=True # 防止初始加载触发回调 ) def highlight_trace(relayoutData): # 捕捉图例点击事件 if relayoutData and 'legend.clickeditems' in relayoutData: clicked_item = relayoutData['legend.clickeditems'][0] curve_number = clicked_item['curveNumber'] # 复制当前figure,避免修改原始全局figure updated_fig = go.Figure(fig) # 切换目标trace的透明度:点击时高亮(opacity=1),再次点击恢复低透明度(0.3) current_opacity = updated_fig.data[curve_number].marker.opacity new_opacity = 1 if current_opacity == 0.3 else 0.3 updated_fig.data[curve_number].marker.opacity = new_opacity return updated_fig # 未触发图例点击时返回原figure return fig if __name__ == '__main__': app.run_server(debug=True)
关键改动说明
- 初始透明度设置:将所有数据点的初始
opacity设为0.3,让未高亮的点处于低可见度状态,高亮时切换为1,形成明显视觉对比。 - 禁用默认图例行为:通过
fig.update_layout(legend=dict(itemclick=False, itemdoubleclick=False))关闭Plotly默认的图例点击隐藏/显示功能,确保自定义交互逻辑生效。 - 事件捕捉修正:将回调输入从
clickData改为relayoutData,因为图例点击属于布局重排事件,clickData仅捕捉数据点的点击操作。 - 无副作用修改:在回调中创建原figure的副本
updated_fig,而非直接修改全局的fig,符合Dash回调的无状态要求。 - 状态切换逻辑:根据点击状态切换目标trace的透明度,实现“点击高亮、再次点击取消高亮”的交互效果。
如果需要实现每次仅高亮单个类别(点击图例时,只有目标类别保持高透明度,其余类别均为低透明度),可修改回调逻辑如下:
@app.callback( Output('iris-plot', 'figure'), Input('iris-plot', 'relayoutData'), prevent_initial_call=True ) def single_highlight_trace(relayoutData): if relayoutData and 'legend.clickeditems' in relayoutData: clicked_item = relayoutData['legend.clickeditems'][0] curve_number = clicked_item['curveNumber'] updated_fig = go.Figure(fig) # 遍历所有trace:目标trace设为1,其余设为0.3 for i in range(len(updated_fig.data)): updated_fig.data[i].marker.opacity = 1 if i == curve_number else 0.3 return updated_fig return fig
内容的提问来源于stack exchange,提问作者user2940774
相关产品推荐
相关产品推荐

