Thank you for submitting multiple use case of multi-page apps!
I noticed you used the following trick to make widget states persistent:
#--- I don't understand the necessity of this line. But it is needed
# to preserve session_state in the cloud. Not locally.
st.session_state.update(st.session_state)
Unfortunately this will cause an error if you assign a key to a button, download_button, file_uploader or a form.
If you’re interested, here’s a helper function which should deal with this case.
Thanks for the tip, @okld
I will look into your code. I just started using Streamlit a few days ago, so there is still lots to learn.
The st.session_state.update(st.session_state) statement is a mystery to me. It does something under the hood that I can not grasp yet. In a ‘real’ Python program it would not make any sense. Even more weird is that this line is not needed when run locally. Only when run in the cloud …
[Added ] This last line is false. This statement is also needed when run locally. [/Added]
Unfortunately this will cause an error if you assign a key to a button, download_button, file_uploader or a form.
In which case would you need a key for a button? Can’t a callback function (on_click = …) work as an equivalent? I can’t think of a case where you would want to store the state of a button in session_state.
Even more weird is that this line is not needed when run locally. Only when run in the cloud …
That’s strange, I do need this line locally as well. Which version of Streamlit do you use?
In which case would you need a key for a button?
It can be useful if you need, but don’t have access to the button return value, in another function for instance, or if you use multiple buttons with the same label. In that case you must specify different keys to each of them to avoid a Duplicate Widget ID error.
Although using a key with buttons might not be that common, it is required when you use st.form().
That’s strange, I do need this line locally as well. Which version of Streamlit do you use?
1.5.1 locally, online it is 1.5.0.
But I checked again, and you are right. It is also needed locally. This must have been before I modified my code. Sorry for the false alarm.
Although using a key with buttons might not be that common, it is required when you use st.form()
I haven’t used forms yet. But I just read this:
st.button and st.download_button cannot be added to a form.
@okld , check the below code for more weirdness …
All widgets states are preserved. Including those in the form.
(PS. Never mind the messy code. I just put this together quickly)
#-------------------------------------------------------
# Streamlit demo program to handle multiple pages with widget state
# preservation.
# Page selection: Buttons on the page top.
#
# Ray J.
#-------------------------------------------------------
import streamlit as st
#--- I don't understand the necessity of this line. But it is needed
# to preserve session_state in the cloud. Not locally.
#st.session_state.update(st.session_state)
#--- Init session_state
if 'active_page' not in st.session_state:
st.session_state.active_page = 'Home'
st.session_state.slider1 = 0
st.session_state.slider2 = 0
#st.session_state.MyForm = 0
st.session_state.check1 = False
st.session_state.check2 = False
st.session_state.active_page = st.session_state.active_page
st.session_state.slider1 = st.session_state.slider1
st.session_state.check1 = st.session_state.check1
st.session_state.slider2 = st.session_state.slider2
st.session_state.check2 = st.session_state.check2
#--- Callback functions
def CB_HomeButton():
st.session_state.active_page = 'Home'
def CB_SliderButton():
st.session_state.active_page = 'Slider'
def CB_ContactButton():
st.session_state.active_page = 'Contact'
#--- Payload code of each page
def home():
st.write('Welcome to home page')
link = '[GitHub](http://github.com)'
st.markdown(link, unsafe_allow_html=True)
st.checkbox('Check me', key='check1')
if st.button('Click Home'):
st.write('Welcome to home page')
def slider():
st.write('Welcome to the slider page')
slide1 = st.slider('this is a slider',min_value=0,max_value=15,key='slider1' )
st.write('Slider position:',slide1)
def contact():
st.title('Welcome to contact page')
st.write(f'Multipage app. Streamlit {st.__version__}')
if st.button('Click Contact'):
st.write('Welcome to contact page')
with st.form(key='MyForm', clear_on_submit=False):
st.write("Inside the form")
slider_val = st.slider("Form slider",key='slider2')
checkbox_val = st.checkbox("Form checkbox", key='check2')
# Every form must have a submit button.
submitted = st.form_submit_button("Submit")
if submitted:
st.write("slider", slider_val, "checkbox", checkbox_val)
st.write("Outside the form")
#--- Page selection buttons
col1, col2, col3 = st.columns(3)
col1.button('Home', on_click=CB_HomeButton)
col2.button('Slider', on_click=CB_SliderButton)
col3.button('Contact', on_click=CB_ContactButton)
#--- Run the active page
if st.session_state.active_page == 'Home':
home()
elif st.session_state.active_page == 'Slider':
slider()
elif st.session_state.active_page == 'Contact':
contact()
Actually that’s what st.session_state.update(st.session_state) does for every item of your session state, I’m just lazy to specify each one of them like that
Internally, Streamlit manages two different states : user-defined states (used when you store values like so: st.session_state.my_state = "hey"), and widget states (when you use a key parameter). These two states work a little bit differently. User-defined states are completely persistent after multiple runs. However if a widget with a key assigned disappear (when your page changes for example), its associated widget state will be cleared.
So to make widget state persistent, the trick is to transform a widget state into a user-defined state. And it is done by self-assigning session state items that were created by a widget, either with st.session_state.update(…), or like you’ve done it in your last example.
In practice, you don’t have to manage those two different states. What Streamlit does instead is, behind the scene, it merges both states into one single object (st.session_state) you can use in your scripts.
would be equivalent to : st.session_state.update(st.session_state)
But apparantly not for Streamlit…
In my last code snippet above, try to exchange one by the other. The ‘written out’ session_state lines give no error. The .update statement results in an error.
Internally, Streamlit manages two different states : …
Thanks for explaining this. I am beginning to understand. But I can’t help wondering if this isn’t overly complicated. Maybe the user (programmer) should have more control over this. It would certainly help to get a better insight. Now I have the feeling that some ‘un-Pythonic’ stuff is happening under the hood. And it makes me wonder: are there more Streamlit python statements that act un-Pythonic…?
Assuming you only have active_page, slider1, check1, slider2 and check2 in your session state, both code snippets are equivalent. This update() function acts just as you think it will, it works just like a regular python dict update function.
But I can’t help wondering if this isn’t overly complicated. Maybe the user (programmer) should have more control over this. It would certainly help to get a better insight.
On the one hand, I do feel like session state functioning is overly complicated. On the other hand it was developed this way to fit most common usage of session states, and there surely are some technical aspects I have no idea of that led to this implementation.
Now I have the feeling that some ‘un-Pythonic’ stuff is happening under the hood. And it makes me wonder: are there more Streamlit python statements that act un-Pythonic…?
Actually, I think the issue isn’t that Streamlit has un-pythonic behavior under the hood (we shouldn’t care about that), it is that Streamlit wasn’t intended to be used this way. Most apps don’t involve multiple pages with widgets that needs persistence. And if you need Streamlit to support this specific usecase, the steps to follow is to submit a github issue with your feature request.
This st.session_state.update(...) trick remains a trick that wasn’t intended by Streamlit, which could be patched in the next release with no warning, and which does not work in all cases.
If you want to continue your investigation on this matter, I’ve opened an issue a few weeks ago:
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.