Hi areo,
The problem is, that once you select an option, the script is rerun from the top (that is how streamlit works), which means a new array of choices is made, prompting creation of a new selectbox widget.
I think there are various different ways to make an app like yours work, I am not sure my solution is the most elegant, but it uses some concepts I think you may find useful.
It relies on caching and use of the session_state gist, see below
In addition, I had to make use of a rerun exception, which just prompts a rerun of the script from the top…
import streamlit as st
import numpy as np
import session_state
from streamlit.ScriptRunner import RerunException
from streamlit.ScriptRequestQueue import RerunData
state = session_state.get(question_number=0)
@st.cache
def get_question(question_number):
# np.random.seed(question_number) - could consider adding this
arr = np.random.randint(1, 100, 2)
q = f"{arr[0]} * {arr[1]}"
ans = arr[0]*arr[1]
choices = [0, ans, ans-1, ans+1, ans+2]
return arr, q, ans, choices
arr, q, ans, choices = get_question(state.question_number)
st.text(f"Solve: {q}")
a = int(st.selectbox('Answer:', choices))
if a != 0:
st.write(f"You chose {a}")
if (ans == a):
st.write("Correct!")
else:
st.write(f"Wrong!, the correct answer is {ans}")
if st.button('Rerun'):
state.question_number += 1
raise RerunException(RerunData(widget_state=None))
And in a seperate file in the same folder named session_state.py:
"""Hack to add per-session state to Streamlit.
Usage
-----
>>> import SessionState
>>>
>>> session_state = SessionState.get(user_name='', favorite_color='black')
>>> session_state.user_name
''
>>> session_state.user_name = 'Mary'
>>> session_state.favorite_color
'black'
Since you set user_name above, next time your script runs this will be the
result:
>>> session_state = get(user_name='', favorite_color='black')
>>> session_state.user_name
'Mary'
"""
import streamlit.ReportThread as ReportThread
from streamlit.server.Server import Server
class SessionState(object):
def __init__(self, **kwargs):
"""A new SessionState object.
Parameters
----------
**kwargs : any
Default values for the session state.
Example
-------
>>> session_state = SessionState(user_name='', favorite_color='black')
>>> session_state.user_name = 'Mary'
''
>>> session_state.favorite_color
'black'
"""
for key, val in kwargs.items():
setattr(self, key, val)
def get(**kwargs):
"""Gets a SessionState object for the current session.
Creates a new object if necessary.
Parameters
----------
**kwargs : any
Default values you want to add to the session state, if we're creating a
new one.
Example
-------
>>> session_state = get(user_name='', favorite_color='black')
>>> session_state.user_name
''
>>> session_state.user_name = 'Mary'
>>> session_state.favorite_color
'black'
Since you set user_name above, next time your script runs this will be the
result:
>>> session_state = get(user_name='', favorite_color='black')
>>> session_state.user_name
'Mary'
"""
# Hack to get the session object from Streamlit.
ctx = ReportThread.get_report_ctx()
this_session = None
current_server = Server.get_current()
if hasattr(current_server, "_session_infos"):
# Streamlit < 0.56
session_infos = Server.get_current()._session_infos.values()
else:
session_infos = Server.get_current()._session_info_by_id.values()
for session_info in session_infos:
s = session_info.session
if (
# Streamlit < 0.54.0
(hasattr(s, "_main_dg") and s._main_dg == ctx.main_dg)
or
# Streamlit >= 0.54.0
(not hasattr(s, "_main_dg") and s.enqueue == ctx.enqueue)
):
this_session = s
if this_session is None:
raise RuntimeError(
"Oh noes. Couldn't get your Streamlit Session object"
"Are you doing something fancy with threads?"
)
# Got the session object! Now let's attach some state into it.
if not hasattr(this_session, "_custom_session_state"):
this_session._custom_session_state = SessionState(**kwargs)
return this_session._custom_session_state
Hope this helps!