Reputation: 51
I’m developing a streamlit
app which contains a multiselect widget with 10 options. Is there an alternative to add a button that selects all the options to avoid selecting them one by one?
I tried adding a previous button to the multiselect, but using another widget resets the selection. Here is an example code:
import streamlit as st
if st.sidebar.button('Add all years'):
year_filter = st.sidebar.multiselect('Select a year',[2000,2001,2002,2003,2004], default=[2000,2001,2002,2003,2004])
else:
year_filter = st.sidebar.multiselect('Select a year',[2000,2001,2002,2003,2004], default=[2000])
type_filter = st.sidebar.multiselect('Choose a type',['Control','Experimental'], default=['Experimental'])
st.write(year_filter, type_filter)
Upvotes: 5
Views: 4843
Reputation: 31640
There is a way using the state
:
import streamlit as st
from streamlit.ReportThread import get_report_ctx
from streamlit.hashing import _CodeHasher
from streamlit.server.Server import Server
class _SessionState:
def __init__(self, session, hash_funcs):
"""Initialize SessionState instance."""
self.__dict__["_state"] = {
"data": {},
"hash": None,
"hasher": _CodeHasher(hash_funcs),
"is_rerun": False,
"session": session,
}
def __call__(self, **kwargs):
"""Initialize state data once."""
for item, value in kwargs.items():
if item not in self._state["data"]:
self._state["data"][item] = value
def __getitem__(self, item):
"""Return a saved state value, None if item is undefined."""
return self._state["data"].get(item, None)
def __getattr__(self, item):
"""Return a saved state value, None if item is undefined."""
return self._state["data"].get(item, None)
def __setitem__(self, item, value):
"""Set state value."""
self._state["data"][item] = value
def __setattr__(self, item, value):
"""Set state value."""
self._state["data"][item] = value
def clear(self):
"""Clear session state and request a rerun."""
self._state["data"].clear()
self._state["session"].request_rerun()
def sync(self):
"""Rerun the app with all state values up to date from the beginning to fix rollbacks."""
# Ensure to rerun only once to avoid infinite loops
# caused by a constantly changing state value at each run.
#
# Example: state.value += 1
if self._state["is_rerun"]:
self._state["is_rerun"] = False
elif self._state["hash"] is not None:
if self._state["hash"] != self._state["hasher"].to_bytes(self._state["data"], None):
self._state["is_rerun"] = True
self._state["session"].request_rerun()
self._state["hash"] = self._state["hasher"].to_bytes(self._state["data"], None)
def _get_session():
session_id = get_report_ctx().session_id
session_info = Server.get_current()._get_session_info(session_id)
if session_info is None:
raise RuntimeError("Couldn't get your Streamlit Session object.")
return session_info.session
def _get_state(hash_funcs=None):
session = _get_session()
if not hasattr(session, "_custom_session_state"):
session._custom_session_state = _SessionState(session, hash_funcs)
return session._custom_session_state
def main():
state = _get_state()
if state.previous_years_selected is None:
state.previous_years_selected = [2000]
if st.sidebar.button('Add all years'):
year_filter = st.sidebar.multiselect('Select a year', [2000,2001,2002,2003,2004], default=[2000,2001,2002,2003,2004])
else:
year_filter = st.sidebar.multiselect('Select a year', [2000,2001,2002,2003,2004], default=state.previous_years_selected)
# The next default (in case no one clicks on "Add all years")
# will be the current years selected
state.previous_years_selected = year_filter
state.sync()
if __name__ == "__main__":
main()
The _StateSession
object keeps data across Streamlit's reloads, meaning that if you interact a widget, the page reloads but the state
keeps the information.
Here, we keep the information about the years that were selected during the last interaction with the "year selector" and provide it as a default for next reload (default=state.previous_years_selected
).
You can add whatever data you want to the state
object. Just make sure it does exist before modifying it:
if state.previous_years_selected is None:
state.previous_years_selected = [2000]
Upvotes: 3