Insert st.form inside st.expander

Hi there,
I trying make form with submit button inside expander, here my code:

form = st.form('Manual assign')
with st.expander('Make Manual assign'):
    form.taskids_to_assign = st.multiselect('Select TaskIDs',
                                       taskids_list,
                                       key='taskids_multi'
                                       )
    form.option_user = st.selectbox('Select User', user_list, key='annotator')
    st.write(f'Task ids selected: **{form.taskids_to_assign}**')
    st.write(f'Annotator selected: **{form.option_user}**')
    form.submitted = form.form_submit_button("Submit")
    if form.submitted:
        st.write('Submitted')
        sql_assign_manual(form.option_user,form.taskids_to_assign)

So my output is:

As you can see, my Submit button is out from expander form. I trying something like this:

with st.expander('Make manual assign'):
     with st.form('Manual assign'):
            <block of code>

When I did this in this way, form doesn’t work, form don’t get input from select box and multi select. Nothing happens.

Debug info

  • Streamlit version: 1.12.2
  • Python version: 3.9
  • Conda
  • OS version: macOS big sur 11.6.8
  • Browser version: chrome 106.0.5249.119

This has to be inside the with st.expander block.

1 Like

Thanks, but it still ugly GUI;

Ugliness is in the eye of the beholder. Just showing us a picture and saying “it is ugly” does not tell us what you want or what problem you are trying to solve.

Maybe the problem is that you are creating a form with only a submit button in it.

here code, same as in question except solution:

with st.expander('Make Manual assign'):
    form = st.form('Manual assign')
    projecttaskobject = pd.concat([projecttaskobject_1, projecttaskobject_2]).sort_values(
        by=['taskname']).reset_index(drop=True)
    manual_taskids = pd.Series(projecttaskobject['taskid'].unique())
    manual_taskids.sort_values(ignore_index=True, inplace=True)
    form.taskids_to_assign = st.multiselect('Select TaskIDs',
                                       manual_taskids,
                                       key='taskids_multi'
                                       )
    form.option_annotator = st.selectbox('Select Annotator', annotators_list, key='annotator')
    st.write(f'Task ids selected: **{form.taskids_to_assign}**')
    st.write(f'Annotator selected: **{form.option_annotator}**')
    form.submitted = form.form_submit_button("Submit")
    if form.submitted:
        st.write('Submited')
        sql_assign_manual(form.option_annotator,form.taskids_to_assign)

This shoud be:

form.selectbox('Select Annotator', annotators_list, key='annotator')

You also need to change the rest similarly. Check the examples in the docs: st.form - Streamlit Docs

Nope, it doesn’t work

Maybe you could add more detail to your question/replys. It works for on my machine:

import streamlit as st


with st.expander('Make Manual assign'):
    form = st.form('Manual assign')
    # projecttaskobject = pd.concat([projecttaskobject_1, projecttaskobject_2]).sort_values(
    #     by=['taskname']).reset_index(drop=True)
    # manual_taskids = pd.Series(projecttaskobject['taskid'].unique())
    # manual_taskids.sort_values(ignore_index=True, inplace=True)
    form.multiselect('Select TaskIDs',
                                       ['1', '2'],
                                       key='taskids_multi'
                                       )
    form.selectbox('Select Annotator', ['A' ,'B'], key='annotator')
    
    submitted = form.form_submit_button("Submit")
    if submitted:
        st.write(f'Task ids selected: **{st.session_state.taskids_multi}**')
        st.write(f'Annotator selected: **{st.session_state.annotator}**')
        st.write('Submited')
        #sql_assign_manual(form.option_annotator,form.taskids_to_assign)

Hi,
thanks for answer. here the catch, you don’t assign new variables (like form.option_annotator and etc) instead you use st.session_state.<with key world>. I didn’t know that, and, yes from docs its not really clear. Thanks again.

Glad it helped. That’s why I linked the docs: st.form - Streamlit Docs
In the examples, they never assign new veriables as members of the st.form object. Actually, this is never the case for any streamlit widget.

But if I want use variables immediately after that was selected and show what user selected before submit st.write(f'selected {my_var}') it doesn’t work. And if it will work in continue, in next function, whcih I define before?

You are unable to use a variable immediately after selection if it is inside the form. Per the docs for forms, widget values inside a form won’t be processed until the form itself is submitted.

A useful way to understand this interaction is to include st.write(st.session_state) at the start of your app and watch how the session_state changes as you move through your app.

One interesting thing to note is that any change to session_state prior to submitting your form will cause the default values inside the form to be added to session_state, but they will not change until the form is submitted. Conversely, if you submit your form without interacting with the checkbox, it’s default value will also be added to session_state without any interaction.

See @Wally 's code below with a checkbox and writing the session_state to the app added.

import streamlit as st

st.write(st.session_state)

st.checkbox("Check me", key="check")

with st.expander('Make Manual assign'):
    form = st.form('Manual assign')
    # projecttaskobject = pd.concat([projecttaskobject_1, projecttaskobject_2]).sort_values(
    #     by=['taskname']).reset_index(drop=True)
    # manual_taskids = pd.Series(projecttaskobject['taskid'].unique())
    # manual_taskids.sort_values(ignore_index=True, inplace=True)
    form.multiselect('Select TaskIDs',
                                       ['1', '2'],
                                       key='taskids_multi'
                                       )
    form.selectbox('Select Annotator', ['A' ,'B'], key='annotator')
    
    submitted = form.form_submit_button("Submit")
    if submitted:
        st.write(f'Task ids selected: **{st.session_state.taskids_multi}**')
        st.write(f'Annotator selected: **{st.session_state.annotator}**')
        st.write('Submited')
        #sql_assign_manual(form.option_annotator,form.taskids_to_assign)

Great idea to print st.session.state! thx