I’m trying to write a simple app where we cycle a list of animals, and we ask the user if they like the animal or not.
The current app is here. You can check the code below:
animals = ['cat', 'dog', 'fish', 'turtle', 'hare', 'hamster']
liked_animals = []
disliked_animals = []
for animal in animals:
st.subheader(f"Do you like {animal}?")
yes_key = f"yes_{animal}"
no_key = f"no_{animal}"
yes_button = st.button("YES", key=yes_key)
no_button = st.button("NO", key=no_key)
while not yes_button and not no_button:
pass
if yes_button:
liked_animals.append(animal)
elif no_button:
disliked_animals.append(animal)
Unfortunately, the app gets stuck in the second interaction (with “dog”), though no error is thrown. Moreover, I’d like to “overwrite” the text and the buttons of the previous interactions (so when asking about dogs, the user doesn’t see the previous question about cats).
Regarding your code, button plus loop is going to be difficult to debug. When users press that button, streamlit will rerun the code from top to bottom. We need to rethink the flow of your code.
In this page there is a search button located at the top right.
Use this button to search for similar issues that you have. I saw a component/library/app that supports Q&A type app. It might give a hint to help with your issue.
A key part of how streamlit works is that all the code is re-run each time a user interacts with a widget. For example, if your user presses ‘YES’ for ‘cat’, the script re-runs, appends ‘cat’ to liked_animals and continues to ‘dog’ in the loop. If your user now presses ‘YES’ for ‘dog’, the script re-runs, creates a new empty liked_animals list and appends ‘cat’ to it and continues to ‘dog’.
An easy way around this would be to replace the buttons with widgets that retain their state between re-runs (like st.radio or st.checkbox) and include every question on the page.
If you really like the buttons and the design of prompting the user with each question, you will need to store the user’s selection in st.session_state so that the value is retained between re-runs. It’s a bit complicated because this isn’t really the sort of user interaction streamlit is geared toward. Here’s one way to do it:
animals = ['cat', 'dog', 'fish', 'turtle', 'hare', 'hamster']
#initialize lists in session_state
if 'liked_animals' not in st.session_state:
st.session_state['liked_animals'] = []
if 'disliked_animals' not in st.session_state:
st.session_state['disliked_animals'] = []
for animal in animals:
#Check if animal already in liked_animals or disliked_animals
if animal in st.session_state['liked_animals']:
continue
if animal in st.session_state['disliked_animals']:
continue
#Append animal from previous run to liked or disliked list
try:
if st.session_state['yes_button']:
st.session_state['liked_animals'].append(animal)
del st.session_state['yes_button']
continue
if st.session_state['no_button']:
st.session_state['disliked_animals'].append(animal)
del st.session_state['no_button']
continue
#On first run neither yes_button or no_button are set
except KeyError:
pass
st.subheader(f"Do you like {animal}?")
st.button("YES",key='yes_button')
st.button("NO",key='no_button')
break
st.write(st.session_state['liked_animals'])
st.write(st.session_state['disliked_animals'])
import streamlit as st
from streamlit import session_state as ss
animals = ['cat', 'dog', 'fish', 'turtle', 'hare', 'hamster']
# Declare some session variables. It is like a global variable
# in python script. It is called session variables because once
# the browser is reset, their values will go back to its initial
# values. We also do this because streamlit reruns the
# code from top to bottom if there are changes to the states
# or an explicit "st.rerun()" command is called.
# By doing this, their values will not be overwritten as you update
# while streamlit reruns the code from top to bottom on your code.
if 'a_index' not in ss:
ss.a_index = 0
if 'liked_animals' not in ss:
ss.liked_animals = []
if 'disliked_animals' not in ss:
ss.disliked_animals = []
# Once all the amimals are shown.
if 'done' not in ss:
ss.done = False
def update_cb(animal):
"""A callback function to update the answer.
Whenever a button is used, try the callback method as much as possible.
"""
# That ans is the key we assign in radio button.
if ss.ans == 'YES':
ss.liked_animals.append(animal)
else:
ss.disliked_animals.append(animal)
ss.a_index += 1 # load next animal
def restart_cb():
"""Enable the answer form button again."""
ss.done = False
if ss.a_index > len(animals) - 1:
animal = animals[0] # avoid index overflow
ss.done = True
else:
animal = animals[ss.a_index]
# The best widget to use when dealing with user input is the form.
with st.form('form_k'):
st.subheader(f"Do you like {animal}?")
st.radio('Select', ['YES', 'NO'], horizontal=True, key='ans')
st.form_submit_button('Submit', on_click=update_cb, disabled=ss.done, args=(animal,))
# Report after exhausting the number of animals
if ss.a_index > len(animals) - 1:
st.write('### Report')
with st.container(border=True):
st.markdown(f'''number of **YES**: {len(ss.liked_animals)}
animals: {ss.liked_animals}
''')
st.markdown(f'''number of **NO**: {len(ss.disliked_animals)}
animals: {ss.disliked_animals}
''')
# Reset variables values ready for next round.
ss.a_index = 0
ss.liked_animals = []
ss.disliked_animals = []
# Build a button for next round.
st.button(':+1: Next round', on_click=restart_cb)
for animal in animals:
# Create an empty space to dynamically update content
content_placeholder = st.empty()
# Display the question and buttons
content_placeholder.subheader(f"Do you like {animal}?")
yes_button = content_placeholder.button("YES")
no_button = content_placeholder.button("NO")
# Wait for user input
if yes_button:
liked_animals.append(animal)
elif no_button:
disliked_animals.append(animal)
# Clear the content for the next iteration
content_placeholder.empty()
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.