Dynamically alter Streamlit.multiselectbox options

3.6k Views Asked by At

I have a multiselectbox in the sidebar of my streamlit app. Without loss of generality:

  • I have 10 options to put in this multiselectbox (these are the numbers 1...10)
  • The user may not select both 1 and 2 simultaneously

I would therefore like to remove 2 from the list of possible selections if 1 is selected, but streamlit seems to have no capability for this, so I tried to wrap it in a loop, which also fails (DuplicateWidgetID: There are multiple identical st.multiselect widgets with the same generated key.):

options = [1,2,3,4,5,6,7,8,9,10]

space = st.sidebar.empty()
answer, _answer = [], None
while True:
    if answer != _answer:
        answer.append(space.multiselectbox("Pick a number",
                                           options,
                                           default=answer
                                           )
                      )
        options = [o for o in options if o not in answer]
        if 1 in options:
            if 2 in options: options.remove(2)
        if 2 in options:
            if 1 in options: options.remove(1)
        _answer = answer[:]

Any thoughts on how I could achieve this?

1

There are 1 best solutions below

3
On BEST ANSWER

Method 1

Here is one approach, provide a help on the widget, and just allow the user to select both 1 and 2 but then we have to filter out 2 if both are selected. Then you can just use the validated selection.

Code
import streamlit as st

ms = st.sidebar.multiselect('Pick a number',  list(range(1, 11)),
    help='Choose either 1 or 2 but not both. If both are selected 1 will be used.')

if 1 in ms and 2 in ms:
    ms.remove(2)

st.write('##### Valid Selection')
st.write(str(ms))
Output

Hover mouse on the ? to show the help.

enter image description here

Method 2

Use radio button for options 1 or 2 and the rest for multiselect.

Code
import streamlit as st

rb = st.sidebar.radio('Pick a number', [1, 2])

ms = st.sidebar.multiselect('Pick a number',  list(range(3, 11)))

selected = ms
selected.append(rb)

st.write('##### Valid Selection')
st.write(str(selected))
Output

enter image description here

Method 3

When 1 is selected remove the 2 and then rerun to update the options. Likewise when 2 is selected remove the 1 and rerun to update the options.

Code
import streamlit as st


init_options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


if 'options' not in st.session_state:
    st.session_state.options = init_options
if 'default' not in st.session_state:
    st.session_state.default = []


ms = st.sidebar.multiselect(
    label='Pick a number',
    options=st.session_state.options,
    default=st.session_state.default
)

# If 1 is selected, remove the 2 and rerun.
if 1 in ms:
    if 2 in st.session_state.options:
        st.session_state.options.remove(2)
        st.session_state.default = ms
        st.experimental_rerun()

# Else if 2 is selected, remove the 1 and rerun.
elif 2 in ms:
    if 1 in st.session_state.options:
        st.session_state.options.remove(1)
        st.session_state.default = ms
        st.experimental_rerun()


st.write('##### Valid Selection')
st.write(str(ms))
Output

enter image description here