Attempting to preserve statefullness for a `st.dataframe` widget in a multipage app

I have a multipage app with the following project directory structure:

├── Home.py
├── utils.py
├── Pages
│   ├── Data_Entry.py

This is a multi-page app where I’m trying to preserve the statefullness of the widgets in the Data_Entry.py script.

For the sake of this discussion, I have two widgets, a text input widget and a dataframe selector widget. I was able to figure out how to preserve the statefullness of the text input widget. However, the approach I’m using for preserving the statefullness of the text input widget isn’t working for the st.dataframe widget.

Here’s the relevant code for the text input widget, for clarity:

Data_Entry.py

import streamlit as st
import utils as ut
ut.init_session_state()
def main():
        st.markdown("### ProcessName")
        ut.load_value("process_name_widget")
        st.text_input(
            "Enter the process' name here.",
            help="Enter a concise name for the process here.",
            max_chars=50,
            key="_process_name_widget",
            on_change=ut.store_value,
            args=["process_name_widget"],
        )
        process_name= st.session_state._process_name_widget
if __name__ == "__main__":
    main()

Here’s the code from utils.py:

import streamlit as st  ## Streamlit
def init_session_state():
    """
    This function initiates the session state for the Streamlit app.
    """
    if "process_name_widget" not in st.session_state:
        st.session_state.process_name_widget= ""
    load_value("process_name_widget")

def store_value(key):
    st.session_state[key] = st.session_state[f"_{key}"]
def load_value(key):
    st.session_state[f"_{key}"] = st.session_state[key]

This approach for the text input widget’s statefullness works perfectly. My issue comes when I’m trying to do the same thing for preserving the statefullness of the st.dataframe widget. Here’s the code snippet for the dataframe widgets:

Data_Entry.py:

### Same as for the previous snippet for Data_entry.py

import streamlit as st
import utils as ut
ut.init_session_state()
def main():
        ### The text_input widget code precedes this section
        ut.load_value("team_selection_widget")
        team_selection = st.dataframe(
            teams_result_df,
            use_container_width=    True,
            hide_index=True,
            on_select=ut.store_value,
            column_order=("team", "context"),
            column_config={"team": "Team Abbreviation", "context": "Team Context"},
            selection_mode="single-row",
            key = "_team_selection_widget"
        )
if __name__ == "__main__":
    main()

Here’s the modified code for utils.py:

import streamlit as st  ## Streamlit
def init_session_state():
    """
    This function initiates the session state for the Streamlit app.
    """
    if "process_name_widget" not in st.session_state:
        st.session_state.process_name_widget= ""
    if "team_selection_widget" not in st.session_state:
        st.session_state.team_selection_widget = ""
    load_value("process_name_widget")
    load_value("team_selection_widget")

def store_value(key):
    st.session_state[key] = st.session_state[f"_{key}"]
def load_value(key):
    st.session_state[f"_{key}"] = st.session_state[key]

The only change is that I updated init_session_state(). I’m getting this error:

StreamlitAPIException: Values for the widget with key '_team_selection_widget' cannot be set using st.session_state.
Traceback:

File "/home/Data_Entry.py", line 342, in <module>
    main()
File "/home/Data_Entry.py", line 67, in main
    team_selection = st.dataframe(
                                         ^^^^^^^^^^^^^

I’m not sure why the approach that works for the text_input widget doesn’t work for the st.dataframe widget. Whatever approach I use to store the session state, this error never stops. What am I doing wrong? It’s particularly confusing because I have a working approach for another widget.

Attempted Fixes

Passing an arg to the store_value callable

I attempt to pass an argument to store_value as follows:


def main():
        ### The text_input widget code precedes this section
        ut.load_value("team_selection_widget")
        team_selection = st.dataframe(
            teams_result_df,
            use_container_width=    True,
            hide_index=True,
            on_select=ut.store_value(key="team_selection_widget"),
            column_order=("team", "context"),
            column_config={"team": "Team Abbreviation", "context": "Team Context"},
            selection_mode="single-row",
            key = "_team_selection_widget"
        )
if __name__ == "__main__":
    main()

utils.py remains the same. This produces a different error:

StreamlitAPIException: You have passed None to on_select. But only 'ignore', 'rerun', or a callable is supported.
Traceback:

File "/home/Data_Entry.py", line 342, in <module>
    main()
File "/home/Data_Entry.py", line 67, in main
    team_selection = st.dataframe(
                     ^^^^^^^^^^^^^

I would greatly appreciate any help on this! Thank you :slight_smile:

1 Like