I have multiselect and text area input inside st.form and st.form_submit_button. After an output has been generated (selected input option, entered text and clicked the “Extract data” button) I would like the output to be displayed until the “Extract data” button is clicked on again. However, it seems that changing input in multiselect causes the app to re-run and clear the output before clicking the button.
Below is an example of the code
# Import libraries
import streamlit as st
from time import sleep
# User input
with st.form('input'):
selected_options = st.sidebar.multiselect(
'Select option:', ['Option1', 'Option2', 'Option3'], default='Option1')
text = st.text_area("Paste text here", height=350)
submit_button = st.form_submit_button(label="Extract data")
# Extract data on submit
if submit_button:
# Check that text field is not empty
if not text.strip():
st.error('WARNING: Please enter text')
else:
with st.spinner(text = 'Extracting information...'):
sleep(3)
st.write('You selected: {}'.format(selected_options))
Python 3.8.0
Streamlit 1.11.1
I am using conda on Windows 10
Chrome Version 103.0.5060.134 (Official Build) (64-bit)
HI @Jurgita! Thanks for your question. It’s a big hard to be sure of what the indentation of your code should be – is this right?
import streamlit as st
from time import sleep
with st.form('input'):
selected_options = st.sidebar.multiselect('Select option:', ['Option1', 'Option2', 'Option3'], default='Option1')
text = st.text_area('Paste text here', height=350)
submit_button = st.form_submit_button(label='Extract data')
if submit_button:
# Check that text field is not empty
if not text.strip():
st.error('WARNING: Please enter text')
else:
with st.spinner(text = 'Extracting information…'):
sleep(3)
st.write('You selected: {}'.format(selected_options))
One way to save the state is using session_state. I tried this code and seems to be what you need:
if "option" not in st.session_state:
st.session_state["option"] = None
with st.form("input"):
selected_options = st.sidebar.multiselect(
"Select option:", ["Option1", "Option2", "Option3"], default="Option1")
text = st.text_area("Paste text here", height=350)
submit_button = st.form_submit_button(label="Extract data")
if submit_button:
# Check that text field is not empty
if not text.strip():
st.error("WARNING: Please enter text")
else:
with st.spinner(text = "Extracting information…"):
sleep(3)
st.session_state["option"] = selected_options
st.write("You selected: {} - inside submit_button".format(selected_options))
st.write(text)
if st.session_state["option"] is not None:
st.write("You selected: {} - outside submit_button".format(st.session_state["option"]))
However, it seems that changing input in multiselect causes the app to re-run and clear the output before clicking the button.
Yes, this is a fundamental property of the Streamlit architecture. You can use cache, session_state or IO operations to create an state machine or applications that preserve the state and other logics. I recommend to read this comment that is related about this characteristic
Hi @blackary - apologies for the messy formatting. What you have posted is correct, and I have now also updated the code in my original question.
It seems to me that there is a bug with st.sidebar.multiselect. I don’t get this issue if I:
move multiselect from sidebar to the top of the page - replace st.sidebar.multiselect with st.multiselect.
Re-write the code using ‘with’ notation:
# User input
with st.form('input'):
with st.sidebar:
selected_options = st.multiselect('Select option:', ['Option1', 'Option2', 'Option3'], default='Option1')
text = st.text_area("Paste text here", height=350)
submit_button = st.form_submit_button(label="Extract data")
@Jurgita Huh. That’s pretty interesting. I’m not sure what’s going on there – some interaction between the form context manager and the sidebar context manager, I suppose. It does seem like there’s some unintended behavior there. My suggestion, if you want the selected text to always be shown, then @fhtsibuya’s suggestion of using session state to cache whatever was previously extracted, and then display it outside the if submit_button: block is a good idea. When you have some set of data to be saved across app runs, then session_state is often a good choice for storing that data.