Cannot preserve the selections in multiselect

Hey there,

I am working on local and trying to create a human in to loop application. Below is my main function and at the end I create a form to let user decide on which of the outputs they wish to use to update the machine learning algorithm.

However, I cannot reach the selected values at all. All I see an empty array as shown in below figure.

Here is my code;

def main():
    title = st.title("Chemical Compound Optimizer")
    st.session_state.sess_title = title
    counter = 0
    # Step 1: User provides SMILES string
    smiles_input = st.text_input("Enter the SMILES string:")
    st.session_state["input_smiles"] = smiles_input
    # Step 2: Show the visualization of the chemical compound
    if smiles_input:
        mol = Chem.MolFromSmiles(smiles_input)
        if mol:
            st.session_state["input_chem"] = st.image(Draw.MolToImage(mol))
        else:
            st.warning("Invalid SMILES string. Please enter a valid SMILES.")

    # Step 3: User selects the property to optimize
    property_to_optimize = st.selectbox("Select property to optimize:", ["LogP", "QED","SAS", "PLogP"])
    st.session_state.button_pp = property_to_optimize
    input_smiles_property_value = calculate_property(smiles_input, property_to_optimize)
    if np.abs(input_smiles_property_value) > 0:
        sub_info = st.subheader("{} Property Value of Input Molecule is: {:.2f}".format(property_to_optimize, input_smiles_property_value))
        st.session_state["sub_info"] = sub_info
    # Step 4: User clicks Run
    st.session_state['button'] = st.checkbox("Run Optimization")
    if st.session_state['button']:
        if not smiles_input:
            st.warning("Please enter a valid SMILES string.")
        else:
            # Step 5: Optimization
            st.session_state["optimized_data"] = optimize_property(smiles_input, property_to_optimize, counter)
        st.session_state['results'] = st.subheader("Optimized De-Novo Compounds:")
        checkbox_key = 0
        good_molecules = []
        for i, (compound, property_value) in enumerate(st.session_state.optimized_data, 1):
            if property_value > input_smiles_property_value:
                if 'res'+str(i) not in st.session_state and 'message'+str(i) not in st.session_state:
                    st.session_state['message'+str(i)] = st.write(f"{i}. {compound}")                                       
                    st.session_state['res'+str(i)] = visualize_molecule(compound, property_to_optimize, property_value)
                    good_molecules.append(compound)                                                         
        st.session_state["good_molecules"] = good_molecules                                                         
        with st.form(key="some selection"):                                                                     
            multi = st.multiselect("Select molecules for further analysis", [f"{compound}" for compound in good_molecules]) 
            submit_button = st.form_submit_button(label = "Submit", on_click = temp_func(multi))
            if submit_button:
                st.write(multi) 

What temp_func does is that it tries to write the selected values to the data frame but it is writing an empty string with due to this issue. You can ignore other functions, they are for running a bayesian optimization for chemical compound optimization. Program first takes the input chemical compound, visualizes it, and then runs the optimization. At the end I am demonstrating the results and let user decide on which one to use. This is where the problem occurs, I cannot save selected values.

Thanks a lot!

Hi @Onur_Boyar

Can you ensure that all session state variables when defined for the first time it is initialized first:


if 'key' not in st.session_state:
    st.session_state['key'] = 'value'

Then when saving values from a widget to a session state variable, you can overwrite it as in:

st.session_state['key'] = st.selectbox(…) # where … are the defined parameters

Further info in the Docs Session State<!-- --> - Streamlit Docs

The problem comes down to 2 things:

  1. You’re using a form, which means that changing an input widget does not update the app, or any of the variables until after you click “submit”
  2. on_click should take a function, not the result of a function call

What I mean by number 2 is:

on_click = temp_func(multi)

That doesn’t say “whenever this is clicked, call temp_func”, but instead it says “call temp_func as soon as you create this widget, with whatever the current value of multi is”

The way to fix this is to pass the function temp_func to on_click, and change how temp_func works so that it looks up the value of the multiselect using session state

import streamlit as st

good_molecules = ["molecule1", "molecule2", "molecule3"]


def temp_func():
    st.info("In temp func")
    st.write(st.session_state["molecules"])


with st.form(key="some selection"):
    multi = st.multiselect(
        "Select molecules for further analysis",
        good_molecules,
        key="molecules"
    )
    submit_button = st.form_submit_button(label="Submit", on_click=temp_func)
    if submit_button:
        st.info("In if")
        st.write(multi)

You can see that in action here:

1 Like