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

回调后维持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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 21:11:39