I have a simple streamlit app that displays text from a cell in a pandas data frame. There are buttons to navigate between entries. The user will then pick an element from a select box. Their choice should be stored when they move to the next example, and be displayed accordingly if they move back.
My problem is resetting the selectbox to it’s default state / changing it to the previously selected value. However if I attempt to access the select box from session state I will crash with:
raise StreamlitAPIException(streamlit.errors.StreamlitAPIException: st.session_state.selected_value cannot be modified after the widget with key selected_value is instantiated.
I’ve created a minimal app to reproduce the error:
streamlit 1.41.1
Python: 3.11.11
import streamlit as st
import pandas as pd
@st.cache_data
def load_data():
return pd.DataFrame({'a': list('abc')})
df = load_data()
if "row_index" not in st.session_state:
st.session_state.row_index = 0
if "selections" not in st.session_state:
st.session_state.selections = {}
if "selected_value" not in st.session_state:
st.session_state.selected_value = "-"
# Function to reset selection before rerunning the script
def reset_selection():
st.session_state.selected_value = "-"
# Function to go to the next row
def next_row():
st.session_state.selections[st.session_state.row_index] = st.session_state.selected_value
if st.session_state.row_index < len(df) - 1:
st.session_state.row_index += 1
reset_selection()
st.rerun() # Force re-run of the app to apply changes
# Function to go to the previous row
def prev_row():
st.session_state.selections[st.session_state.row_index] = st.session_state.selected_value
if st.session_state.row_index > 0:
st.session_state.row_index -= 1
reset_selection()
st.rerun() # Force re-run of the app to apply changes
# Display row content
row_text = df.iloc[st.session_state.row_index, 0] # Assuming text is in first column
st.write(f"**Text:** {row_text}")
# Dropdown selection
selected_value = st.selectbox(
"Select a value:",
["-", "A", "B", "C"],
key="selected_value",
)
# Navigation buttons
col1, col2 = st.columns(2)
with col1:
if st.button("Previous"):
prev_row()
with col2:
if st.button("Next"):
next_row()
# Display stored selections
st.write("### Stored Selections:")
st.write(st.session_state.selections)
Thanks for your reply. Basically what I want to build is part of an annotation system. The user should be able to navigate through texts and select them into categories. For selecting the categories I use the select box. Now what I want to accomplish is that when you go to the next record, it’s rest to it’s default value. If the user goes to a record they already worked on the selectbox should indicate what they chose in the past. Thus i wanted to change the state of the widget. But I think I can make your solution work, by additionally checking if a value was stored in the past.
But generally, you are not supposed to edit the state of a widget from another point of the code?
Yes I added the check and it works for me (code below) ! You are not supposed to update the session_state value associated with a widget : here is what the docs says :
import streamlit as st
import pandas as pd
@st.cache_data
def load_data():
return pd.DataFrame({"a": list("abcdef")})
df = load_data()
if "row_index" not in st.session_state:
st.session_state.row_index = 0
if "selections" not in st.session_state:
st.session_state.selections = {}
if "selected_value" not in st.session_state:
# Setting the value to the saved value or resetting to "-"
st.session_state.selected_value = st.session_state.selections.get(
st.session_state.row_index, "-"
)
# Function to reset selection before rerunning the script
def reset_selection():
del st.session_state.selected_value
# Function to go to the next row
def next_row():
st.session_state.selections[st.session_state.row_index] = (
st.session_state.selected_value
)
if st.session_state.row_index < len(df) - 1:
st.session_state.row_index += 1
reset_selection()
st.rerun() # Force re-run of the app to apply changes
# Function to go to the previous row
def prev_row():
st.session_state.selections[st.session_state.row_index] = (
st.session_state.selected_value
)
if st.session_state.row_index > 0:
st.session_state.row_index -= 1
reset_selection()
st.rerun() # Force re-run of the app to apply changes
# Display row content
row_text = df.iloc[st.session_state.row_index, 0] # Assuming text is in first column
st.write(f"**Text:** {row_text}")
# Dropdown selection
selected_value = st.selectbox(
"Select a value:",
["-", "A", "B", "C"],
key="selected_value",
)
# Navigation buttons
col1, col2 = st.columns(2)
with col1:
if st.button("Previous", disabled=st.session_state.row_index == 0):
prev_row()
with col2:
if st.button("Next", disabled=st.session_state.row_index == len(df)):
next_row()
# Display stored selections
st.write("### Stored Selections:")
st.write(st.session_state.selections)