Changing / Resetting Selectbox

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)

Hello ! I am not 100% confident on what you aim to do with your code ! However to make your code work you can replace this :

def reset_selection():
    st.session_state.selected_value = "-"

with :

def reset_selection():
    del st.session_state.selected_value

This will work because when the app reruns, you set selected_value to “-” when it is not in the session state.

Do not hesitate if you have more details on what your app aims to do or further questions !

1 Like

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?

And again thank you for your help.

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)
1 Like