Can't enter values without updating a plotly figure

hey, would appreciate advice from anybody more knowledgeable:)

i am plotting a figure, and below i have multiple fields that the user is to enter after inspecting a figure. i expect the user to zoom in, and then enter the values. problem is, that any time you enter anything, the plot resets. i don’t want that.

my desired behavior is as follows:

  1. zoom in on the plotly figure (client state of plotly figure is changed)
  2. enter a number / select an option
  3. press a button to update the plot based on number/select inputs

my problem is that the plotly figure resets itself after step 2 (after even an unsubmitted select, the client state of the plotly figure is reset).

i tried putting everything into an st.form(), tried caching / session-stating the figure; neither work. client changes to plotly figure always get reset

i attached a simple code snippet as an example.

would appreciate advice!

import streamlit as st
import plotly.figure_factory as ff
import numpy as np

if 'bin_size' not in st.session_state:
  st.session_state['bin_size'] = 0.1
if 'plot_select' not in st.session_state:
  st.session_state['plot_select'] = 'plot1'

# put figure inside a form?
with st.form("form2"):
  submitted2 = st.form_submit_button(label='update plot')
if submitted2:
  x = {'plot1': np.random.randn(100), 'plot2': np.random.randn(50)}
  fig = ff.create_distplot([x[st.session_state['plot_select']]], [st.session_state['plot_select']], bin_size=[st.session_state['bin_size']])
  st.plotly_chart(fig, use_container_width=True)

# put select inside a form?
with st.form("form"):
  new_plot_select = st.selectbox("Select plot", ['plot1', 'plot2'])
  new_bin_size = st.number_input('Enter bin size', value = 0)
  submitted = st.form_submit_button(label='i want  plot to reset on button press only')
if submitted:
  st.session_state['bin_size'] = new_bin_size
  st.session_state['plot_select'] = new_plot_select

st.header("zoom in on the plot before selecting/entering values")
st.header("any action would cause the plot to reset; i don't want that")

Hi @savva, welcome to the Streamlit Community! :wave: :partying_face:

Firstly, thank you for providing a reproducible example! It makes debugging so much easier.

go.Layout() has a uirevision parameter that when set to any value preserves the state of the figure.

In your example, add the following line after you create the figure:

fig.update_layout({"uirevision": "foo"}, overwrite=True)

Example

import numpy as np
import plotly.figure_factory as ff

import streamlit as st

if "bin_size" not in st.session_state:
    st.session_state["bin_size"] = 0.1
if "plot_select" not in st.session_state:
    st.session_state["plot_select"] = "plot1"

# put figure inside a form?
with st.form("form2"):
    submitted2 = st.form_submit_button(label="update plot")
if submitted2:
    x = {"plot1": np.random.randn(100), "plot2": np.random.randn(50)}
    fig = ff.create_distplot(
        [x[st.session_state["plot_select"]]],
        [st.session_state["plot_select"]],
        bin_size=[st.session_state["bin_size"]],
    )
    fig.update_layout({"uirevision": "foo"}, overwrite=True)
    st.plotly_chart(fig, use_container_width=True)

# put select inside a form?
with st.form("form"):
    new_plot_select = st.selectbox("Select plot", ["plot1", "plot2"])
    new_bin_size = st.number_input("Enter bin size", value=0)
    submitted = st.form_submit_button(
        label="i want  plot to reset on button press only"
    )
if submitted:
    st.session_state["bin_size"] = new_bin_size
    st.session_state["plot_select"] = new_plot_select

st.header("zoom in on the plot before selecting/entering values")
st.header("any action would cause the plot to reset; i don't want that")

Output

plotly-preserve-state

Happy Streamlit-ing! :balloon:
Snehan

3 Likes

hey, thanks so much for your help, that did it!

1 Like

Hi @snehankekre ,

Until Streamlit version 1.9.0 my plotly charts with uirevision parameter worked as expected, but after updating to the latest Streamlit 1.11.1 they seem to not work anymore.

Here a simple example adding the uirevision parameter - that doesn’t work. Do you know what is wrong? Thanks a lot in advance!

import streamlit as st
import plotly.figure_factory as ff
import numpy as np

# Add a "useless" button just to be able to rerun app with a click and see if uirevision works
st.button('Rerun app')

# Add histogram data
if 'x1' not in st.session_state:
    st.session_state.x1 = np.random.randn(200) - 2
    st.session_state.x2 = np.random.randn(200)
    st.session_state.x3 = np.random.randn(200) + 2

# Group data together
hist_data = [st.session_state.x1, st.session_state.x2, st.session_state.x3]
group_labels = ['Group 1', 'Group 2', 'Group 3']

# Create distplot with custom bin_size
fig = ff.create_distplot(
         hist_data, group_labels, bin_size=[.1, .25, .5])

# Add uirevision parameter to fig
fig.update_layout({"uirevision": "foo"}, overwrite=True)

# Plot!
st.plotly_chart(fig, use_container_width=True)

Hi @marduk / @snehankekre

I’ve just come across the same issue: setting the uirevision parameter like this doesn’t seem to work in Streamlit 1.13.0 but does in 1.9.0.

Did you find a workaround? Or, do you know if this is logged anywhere as a bug?

Cheers, Ian.

Hi there @ianreah ,

  • I opened an issue on github and the team confirmed the bug.
  • Recently a pull request that might solve the issue was merged. Not sure yet though, we have to wait until a next Streamlit release to test it.
1 Like

Hi,
I did some testing with the app above and I think this should be fixed in streamlit==1.14.0 !

If you want to check for yourself and test it or need it right away, you can install the streamlit nightly (streamlit-nightly · PyPI)

3 Likes

Hi, is there a way to get the ‘updated’ state out of the ‘updated’ figure? for example if the user zooms in, how can we get the updated x axes range out of the layout? if you st.write(fig.layout) this doesn’t seem to change…

Any ideas/help please ? thanks in advance,
M