Case of Disappearing Interactive Widget

First–I love Streamlit!

I’m trying to write a survey tool. I’m using the sidebar to control workflow. The first selectbox is used to determine whether the user is entering a survey or reviewing past surveys.

def main():
    st.sidebar.title("Actions")
    action_selected = st.sidebar.selectbox('Choose below', ['Administer Survey', 'See Past Surveys']) 
    if action_selected == 'See Past Surveys':
        data = Data() ##New instance of class Data
        if st.sidebar.button('Show All Saved Surveys'):
            data.show_all_data()
        if st.sidebar.button('Choose Questions to Include in Analysis'):
            data.select_questions()      
    elif action_selected == 'Administer Survey':
        st.sidebar.success('You are now adminstering a survey. Please complete the survey to the right.')
        survey_instance = run_survey()
        if st.sidebar.button('New Survey'):
            survey_instance.session.run_id += 1
            run_survey() #Possibly related question: this has to be clicked twice to 'reset' the main survey... why isn't this reseting on just one click
        if st.sidebar.button('Save Survey'):
            survey_instance.save_survey()
        if st.sidebar.button('Show Survey'):
            survey_instance.show_survey()

When I press the ‘Choose Questions…’ button, it calls this class method and displays it correctly… until I select a choice. At that point, it all reruns and the screen goes back to the main page. It never lets me make more than one choice.

class Data():
    def __init__(self):
        self.session = SessionState.get(run_id=0)
        self.data = open_survey_db()
    
    def show_all_data(self):
        st.write(self.data)
    
    def select_questions(self):
        self.selected_questions = st.multiselect('Pick the questions to include in analysis', self.data['Question'].unique(), key=self.session.run_id)
        return self.selected_questions

Is it not working because of the nested if statements?Preformatted text

1 Like

The problem is that buttons only return a value of True on the first run after they are clicked (that is, on the run triggered by their being clicked). This means that once you select a question to be included in the survey the value return by st.sidebar.button('Choose Questions to Include in Analysis') is going to go back to False.

I find it makes buttons annoying to use. If you want to show/hide some extra features like that with them defaulting to being hidden, you probably want to try using a radio widget instead, since that way the selection will persist between reruns of the app.

The only useful use-case I’ve found for buttons in my work is to give the user the option to save the current state of something to a file.

3 Likes

Thanks Yasha! That makes a lot of sense and I’ll rewrite it using radio buttons. Really appreciate your feedback and answer!

1 Like

Any pointer on below question.?

I’m not entirely sure if this is what you want, but there is another good use case that I’ve discovered for buttons that might be applicable for your situation.

If you need the user to enter a number of different settings on different widgets, and then have an expensive function call that needs to be run based on the combination of those inputs. If you don’t want to start running your expensive function call every time the user changes one of the inputs, you can guard the expensive function call with something like:

st.write("Ready to move on to the slow function?")
if st.button("Next"):
    expensive_function(widget_output1, widet_output2, ...)

Unfortunately, this is only really useful if there is no state below the expensive function that can be updated, otherwise the button will revert back to False.

The scenario where I have found this useful is one in which the user needs to iterate between modifying a batch of parameters at once and then run something expensive. It’s more useful than using a radio button because in that case the user needs to remember to change the state of the radio button back to False before beginning to modify the parameters otherwise the expensive function starts running before all of the parameters have been updated correctly.

I’m not sure if you can use this in your problem, though, since I don’t know what you’re doing after you’ve loaded the image.

1 Like