I noticed a weird behaviour of query_params with the version 0.89
The query params keep resetting to the previous value, the user has to click twice to update the params. Verified that this works properly with versions 0.88 or lower
With version 0.89:
With version 0.88:
My MultiPage framework:
import streamlit as st
class MultiPage:
def __init__(self):
self.apps = []
self.app_names = []
self.default = ''
def add_app(self, title, func, args=None):
self.app_names.append(title)
self.apps.append({
"title": title,
"function": func,
"args":args
})
def run(self):
# get query_params
query_params = st.experimental_get_query_params()
self.default = query_params["app"][0] if "app" in query_params else None
default_index = self.app_names.index(self.default) if self.default else 0
# simple radio button for navigation
app = st.sidebar.radio(
'Go To',
self.apps,
index = default_index,
format_func=lambda app: app['title'],
key = 'Navigation')
# reflect the current app in query_params
self.default = app['title']
st.experimental_set_query_params(app=self.default)
# runs the selected app with passes args
app['function'](app['args'])
st.radio doesn’t return an object, it returns the selection as a string. So you need to resolve the selection to the corresponding app object. The code below works fine, including changing the page directly via the URL app query param. I also quoted and unquoted the URL params just in case.
Having said that, the double click issue remains in v0.89 Hopefully, @Charly_Wargnier can get it looked at by one of the engineers.
import streamlit as st
import urllib
class MultiPage:
def __init__(self):
self.apps = []
self.app_names = []
self.current_app_name = None
def add_app(self, title, func, *args, **kwargs):
self.app_names.append(title)
self.apps.append({
"title": title,
"function": func,
"args":args,
"kwargs":kwargs
})
def run(self, label='Go To'):
# make correct app selection from query param
query_params = st.experimental_get_query_params()
app_name = urllib.parse.unquote(query_params["app"][0]) if "app" in query_params else None
self.current_app_name = app_name if app_name in self.app_names else None
current_app_name_index = self.app_names.index(self.current_app_name) if self.current_app_name else 0
# configure radio buttons for app navigation
app_choice = st.sidebar.radio(
label,
self.app_names,
index = current_app_name_index,
key = 'Navigation')
app = self.apps[self.app_names.index(app_choice)]
# update current app name and query param from app choice
self.current_app_name = app['title']
# runs the selected app passing args/kwargs
app['function'](self.current_app_name, *app['args'], **app['kwargs'])
st.experimental_set_query_params(app=urllib.parse.quote(self.current_app_name))
def app1(title, info=None):
st.title(title)
st.write(info)
def app2(title, info=None):
st.title(title)
st.write(info)
def app3(title, info=None):
st.title(title)
st.write(info)
mp = MultiPage()
mp.add_app('Application 1', app1, info='Hello from App 1')
mp.add_app('Application 2', app2, info='Hello from App 2')
mp.add_app('Application 3', app3, info='Hello from App 3')
mp.run('Launch application')
Maybe the dev team (via @snehankekre, @Charly_Wargnier) can update the docs if arbitrary collections are indeed returned, which makes sense when a format_func is supplied. (One could always look at the source code in GitHub TBH.)
@akshanshkmr - I liked your update as it leverages the auto-created session state for widgets. Here’s a full working version so the Streamlit community can cut-and-paste and run it. Can you please mark this as the solution?
import streamlit as st
class MultiPage:
def __init__(self):
self.apps = []
self.app_names = []
def add_app(self, title, func, *args, **kwargs):
self.app_names.append(title)
self.apps.append({
"title": title,
"function": func,
"args":args,
"kwargs": kwargs
})
def run(self, label='Go To'):
# common key
key='Navigation'
# get app choice from query_params
query_params = st.experimental_get_query_params()
query_app_choice = query_params['app'][0] if 'app' in query_params else None
# update session state (this also sets the default radio button selection as it shares the key!)
st.session_state[key] = query_app_choice if query_app_choice in self.app_names else self.app_names[0]
# callback to update query param from app choice
def on_change():
params = st.experimental_get_query_params()
params['app'] = st.session_state[key]
st.experimental_set_query_params(**params)
app_choice = st.sidebar.radio(label, self.app_names, on_change=on_change, key=key)
# run the selected app
app = self.apps[self.app_names.index(app_choice)]
app['function'](app['title'], *app['args'], **app['kwargs'])
def app1(title, info=None):
st.title(title)
st.write(info)
def app2(title, info=None):
st.title(title)
st.write(info)
def app3(title, info=None):
st.title(title)
st.write(info)
mp = MultiPage()
mp.add_app('Application 1', app1, info='Hello from App 1')
mp.add_app('Application 2', app2, info='Hello from App 2')
mp.add_app('Application 3', app3, info='Hello from App 3')
mp.run('Launch application')