If I change values of some widgets in a dialog, next close it by pressing “X” or clicking anywhere on screen and open it again I see widgets with edited values.
How to prevent streamlint dialog from keeping changed values in such case?
Looks like it could be possible by ss.rerun() in “X” / clicking anywhere event handler but there is not such event.
You can add keys to the widgets and set them to a default value before creating them:
import streamlit as st
@st.dialog("A dialog")
def show_dialog():
st.session_state.dialog_text = ""
st.text_input("Say something", key="dialog_text")
if st.button("Show dialog"):
show_dialog()
As you add more widgets this can become inconvenient, I guess you could use a form with clear_on_submit=True
in that case (not tested).
By doing that, the text in your input is not persistent. With only 2 inputs we run into problems
Here is an example, reusing your solution that does not work with 2 inputs
import streamlit as st
@st.dialog("A dialog")
def show_dialog():
st.session_state.first_name_input = ""
st.session_state.last_name_input = ""
first_name = st.text_input("First name", key="first_name_input")
last_name = st.text_input("Last name", key="last_name_input")
if st.button("save"):
st.write(f"Hello {first_name} {last_name}")
if st.button("Show dialog"):
show_dialog()
This is because each dialog is its own fragment, and inputting a value reruns the fragment and erases the session state.
One way to go around it is then to encapsulate your code inside its own fragment inside the dialog fragment, and reset the session state only in the dialog fragment.
Here is the solution I’ve found based on your idea. This allows the session states to be cleared, not when the dialog is closed, but when it’s opened
import streamlit as st
@st.fragment
def _show_dialog():
first_name = st.text_input("First name", key="first_name_input")
last_name = st.text_input("Last name", key="last_name_input")
if st.button("save"):
st.write(f"Hello {first_name} {last_name}")
@st.dialog("A dialog")
def show_dialog():
st.session_state.first_name_input = ""
st.session_state.last_name_input = ""
_show_dialog()
if st.button("Show dialog"):
show_dialog()
alternatively, for a factorized version, for more session states:
import streamlit as st
@st.fragment
def _show_dialog(key_prefix: str):
# Input keys starts with the prefix
first_name = st.text_input("First name", key=key_prefix + "_first_name_input")
last_name = st.text_input("Last name", key=key_prefix + "_last_name_input")
if st.button("save"):
st.write(f"Hello {first_name} {last_name}")
@st.dialog("A dialog")
def show_dialog():
# Set a unique prefix for the dialog session states
key_prefix = "__dialog_name_input"
# Erase session states that share the dialog prefix
for key in st.session_state.keys():
if key.startswith(key_prefix):
st.session_state[key] = None
_show_dialog(key_prefix)
if st.button("Show dialog"):
show_dialog()
It looks like this is related to Rerun when dismissing st.dialog by clicking on X or pressing Escape. I agree this would be valuable. My use case involves st.data_editors within dialogs and I have not been able to find a workaround.
What do you mean exactly ?
I think to empty a widget you need the corresponding “none” value, so maybe you would need to have
st.session_state[key] = pd.DataFrame()
instead of
st.session_state[key] = None # or ""
but idk if that’s your problem
Sort of. The full issue is a bit more complex.
Within a dialog I have a form. Within the form I have a number of data_editors. The form has two buttons: Cancel and Submit.
- Submit button has a callback that updates the dataframes associated with the data_editors based on the session_state information associated with the data_editors’ keys
- Cancel has a callback clears that information from session_state
st.rerun is invoked following the callback.
Each button works as expected: the dataframe is updated or not updated per the user action and if the dialog is relaunched the data_editors correctly display the dataframe values.
But if the user exits the dialog without clicking one of those buttons (e.g., by clicking the ‘X’) the behavior is not as expected. The dataframe is correctly unchanged but if the dialog is relaunched it continues to display whatever edits had been made before the dialog was closed. The desired behavior should have the data_editor display the current value of the dataframe.
I tried several approaches to address this. For example, I used
st.session_state.pop(key)
early in the dialog hoping that would clear out the edits before the dataframe widgets were invoked. But that did not work. I am not sure why.
st.session_state[key] = None # or ""
leads to
StreamlitValueAssignmentNotAllowedError: Values for the widget with key [key]cannot be set using st.session_state.
per https://discuss.streamlit.io/t/clearing-a-widget-from-session-state/84531