This is an update to my previous post in case people would like similar functionality, summarized in the title of this post and detailed in the comments of the full code example below:
# app.py
import streamlit as st
import one
import two
import three
def main():
# Use the new st.naviation()/st.Page() API to create a multi-page app
pg = st.navigation({
'first section':
[st.Page(one.main, title="Home", url_path='home'),
st.Page(two.main, title="Second page", url_path='two')],
'second section':
[st.Page(three.main, title="Third page", url_path='three')]
})
# For widget persistence, we need always copy the session state to itself, being careful with widgets that cannot be persisted, like st.data_editor() (where we use the "__do_not_persist" suffix to avoid persisting it)
for key in st.session_state.keys():
if not key.endswith('__do_not_persist'):
st.session_state[key] = st.session_state[key]
# This is needed for the st.dataframe_editor() class (https://github.com/andrew-weisman/streamlit-dataframe-editor) but is useful for seeing where we are and where we've been
st.session_state['url_path'] = pg.url_path if pg.url_path != '' else 'Home'
if 'url_path_prev' not in st.session_state:
st.session_state['url_path_prev'] = st.session_state['url_path']
# On every page, display its title
st.title(pg.title)
# Output where we are and where we just were
st.write(f'Your page location: {st.session_state["url_path"]}')
st.write(f'Previous page location: {st.session_state["url_path_prev"]}')
# Render the select page
pg.run()
# Update the previous page location
st.session_state['url_path_prev'] = st.session_state['url_path']
# Needed for rendering pages which use multiprocessing (https://docs.python.org/3/library/multiprocessing.html#the-spawn-and-forkserver-start-methods)
if __name__ == '__main__':
main()
# one.py
import streamlit as st
def main():
# Slider widget
if 'slider' not in st.session_state:
st.session_state['slider'] = 40
st.slider("Slider", 0, 100, key="slider")
# Display dataframe_editor2's contents (from three.py)
if 'dataframe_editor2' in st.session_state:
st.write(st.session_state['dataframe_editor2'].reconstruct_edited_dataframe())
else:
st.write('No dataframe editors have been initialized; do so on the third page')
# two.py
import streamlit as st
import multiprocessing as mp
def f(x):
return x*x
def main():
# Simulate cleanly returning from a page
if st.button('Return'):
st.warning('Returning')
return
# Toggle widget
if 'toggle' not in st.session_state:
st.session_state['toggle'] = False
st.toggle('Toggle me', key='toggle')
# Run a parallel process
if st.button('Run parallel process'):
with mp.get_context('forkserver').Pool(4) as p:
st.write(p.map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
# three.py
import streamlit as st
import multiprocessing as mp
import streamlit_dataframe_editor as sde
import pandas as pd
def f(x):
return x**3
def main():
# Selectbox widget
if 'selectbox' not in st.session_state:
st.session_state['selectbox'] = 'one'
st.selectbox('Selectbox', ['one', 'two', 'three'], key='selectbox')
# Run a parallel process
if st.button('Run parallel process'):
with mp.get_context('forkserver').Pool(4) as p:
st.write(p.map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
# Create a default dataframe
df_default = pd.DataFrame({'a': [1, 2, 3], 'b': [False, False, True], 'c': [4, 5, 6]})
# Dataframe editor widget
if 'dataframe_editor' not in st.session_state:
st.session_state['dataframe_editor'] = sde.DataframeEditor(df_name='my_dataframe', default_df_contents=df_default)
st.session_state['dataframe_editor'].dataframe_editor(current_page_key='url_path', previous_page_key='url_path_prev')
# Dataframe editor widget with identical contents (will be displayed on one.py)
if 'dataframe_editor2' not in st.session_state:
st.session_state['dataframe_editor2'] = sde.DataframeEditor(df_name='my_dataframe2', default_df_contents=df_default)
st.session_state['dataframe_editor2'].dataframe_editor(current_page_key='url_path', previous_page_key='url_path_prev')