回调后维持Plotly图表状态:Streamlit组件数据捕获问题
问题描述
使用Streamlit+Plotly组件实现交互式散点图时,用户完成选择操作后,Streamlit会重新运行整个脚本,导致图表的缩放、标注、绘制线条等状态全部丢失,重置为初始状态。尝试基于react-plotly.js的状态管理文档修复,但频繁出现data、layout等变量未正确声明的错误。
解决方案思路
核心是在React组件内部维护Plotly图表的实时状态,避免Streamlit重渲染时覆盖用户的交互操作:
- 用React状态保存图表的
data、layout、config,初始值来自传入的spec - 监听Plotly的
onUpdate事件,实时更新组件内部状态,记录用户的缩放、标注等操作 - 添加可选的重置机制,允许用户手动恢复初始状态
- 修正
revision属性,确保状态更新时图表正确重渲染而非重置
修改后完整代码
init.py
import json import random import plotly.express as px import streamlit as st import plotly.utils import streamlit.components.v1 as components _component_func = components.declare_component( "streamlit_scatterplot_selection", url="http://localhost:3001", ) def st_scatterplot(fig, key="st_plotly", reset_state=False): spec = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder) selected_points = _component_func(spec=spec, default=[], key=key, reset_state=reset_state) return selected_points @st.cache_data # 使用Streamlit新版缓存装饰器 def random_data(): return random.sample(range(100), 50), random.sample(range(100), 50) st.subheader("Plotly interactive scatterplot") x, y = random_data() fig = px.scatter(x=x, y=y, title="My fancy plot") # 可选:添加重置按钮恢复初始状态 reset_btn = st.button("重置图表状态") v = st_scatterplot(fig, reset_state=reset_btn) st.write("选中的数据点:", v)
PlotlyScatterplot.tsx
import React, { useState, useEffect } from "react" import { withStreamlitConnection, Streamlit, ComponentProps, } from "streamlit-component-lib" import Plot from "react-plotly.js" const PlotlyScatterplot = (props: ComponentProps) => { // 解析初始传入的图表配置 const initialSpec = JSON.parse(props.args.spec) // 用React状态维护图表实时状态 const [plotState, setPlotState] = useState({ data: initialSpec.data, layout: initialSpec.layout, config: initialSpec.config || {}, frames: initialSpec.frames || [] }) // 监听重置指令,恢复初始状态 useEffect(() => { if (props.args.reset_state) { setPlotState({ data: initialSpec.data, layout: initialSpec.layout, config: initialSpec.config || {}, frames: initialSpec.frames || [] }) } }, [props.args.reset_state, initialSpec]) // 捕获图表交互更新,保存用户操作状态 const handleUpdate = (update: any) => { setPlotState(prevState => ({ ...prevState, data: update.data || prevState.data, layout: update.layout || prevState.layout, config: update.config || prevState.config })) } // 处理点选中事件,返回数据给Streamlit const handleSelected = (eventData: any) => { Streamlit.setComponentValue( eventData.points.map((p: any) => ({ index: p.pointIndex, x: p.x, y: p.y })) ) } return ( <Plot data={plotState.data} layout={plotState.layout} frames={plotState.frames} config={plotState.config} // 用状态的字符串化值作为revision,确保状态变化时正确重渲染 revision={JSON.stringify(plotState)} onSelected={handleSelected} onUpdate={handleUpdate} onInitialized={() => Streamlit.setFrameHeight()} /> ) } export default withStreamlitConnection(PlotlyScatterplot)
index.tsx(无修改)
import React from "react" import ReactDOM from "react-dom" import PlotlyScatterplot from "./PlotlyScatterplot" ReactDOM.render( <React.StrictMode> <PlotlyScatterplot /> </React.StrictMode>, document.getElementById("root") )
react-app-env.d.ts(无修改)
/// <reference types="react-scripts" />
关键说明
- 状态持久化:用户的缩放、标注等交互操作会通过
onUpdate事件实时保存到React状态中,Streamlit重渲染时不会丢失 - 可控重置:通过
reset_state参数提供手动重置入口,避免误操作导致状态丢失 - 正确重渲染:
revision属性绑定状态的字符串化值,确保只有状态变化时才重渲染图表,而非完全重置
内容的提问来源于stack exchange,提问作者RightmireM
相关产品推荐
相关产品推荐

