Input widgets inside a while loop

Dear all,
I want to have a input widgets for a special period of time, and then I want it to disappear.
I’ve written the following piece of the code,

import streamlit as st
import time


st.write('hello')

st.session_state['A']= time.perf_counter()
#c=0
while time.perf_counter()-st.session_state['A']<10:
    st.session_state['B']=st.text_input('insert your name')


st.write(st.session_state['B'])

But it faces following error:

To fix this error, please pass a unique `key` argument to
`st.text_input`.

Then I change it to:

import streamlit as st
import time


st.write('hello')

st.session_state['A']= time.perf_counter()
c=0
while time.perf_counter()-st.session_state['A']<10:
    st.session_state['B']=st.text_input('insert your name',key=c)


st.write(st.session_state['B'])

Again the same error, finally, if I add c+=1 in the end of while loop, it creates me multiple text inputs under each other. How can I fix it?

You need the key to be different in each iteration of the loop. Try using a timestamp for the key:

key=time.time()

May I ask what result you are going for with the loop?

1 Like

Thanks mate. But it did not solve the error. I need to ask a question from the user in a limited period of time. I give the user 1 minute to respond a question.

Here’s an example, though I tried a few variations and have some unexpected behavior. There are some lines in here that may seem round about or redundant, but there are two things I was working around:

  1. When a widget is removed from a page, it’s key in session state goes away, so there’s an extra copy action to move it to another key (this is expected)
  2. The way I layered empty-container-empty I get some unexpected behavior of things not emptying the way I expect, so I need to do some digging. For now, the following was working for me:
import streamlit as st
import time

slate = st.empty()
temp = slate.container()

if 'submitted' not in st.session_state:
    st.session_state.submitted = False

def submit():
    st.session_state.response = st.session_state.from_widget
    st.session_state.submitted = True

def unsubmit():
    st.session_state.submitted = False

if not st.session_state.submitted:
    with temp:
        st.write('Please reply within 10 seconds.')
        st.text_input('Response', key='from_widget', on_change=submit)
        countdown = st.empty()
        for i in range(10):
            countdown.markdown(f'### {10-i}')
            time.sleep(1)
        countdown.empty()
    slate.empty()
    temp = slate.container()
    with temp:
        st.write('Times Up!')
        st.button('Try again')
else:
    with temp:
        st.write(f'#### The response was submitted:')
        st.write(f'## {st.session_state.response}')
        st.button('Redo', on_click=unsubmit)

limited time

1 Like

dear friend,
The problem is solved. But if you look at the photo, the parts of the app that should disappear, don’t disappear completely, they just become bright gray.
Do you have any idea?
I am very grateful for your answers.

This was one of the bugs I was encountering that prompted me to be a little redundant. Can you share how you coded that so I can debug it? I think there is a Streamlit bug here, but haven’t looped through a bunch of variations yet to decide that conclusively.

This is the simplified version of the related part of the code:

    if st.session_state["st"]==True:
        if st.session_state['n']==0:
            st.session_state['Nome'] = st.text_input("Nome:")
            st.session_state['Cognome'] = st.text_input("Cognome:")
            st.info('Premi start da iniziare il tuo 1 ora')
            with temp:
                if st.button('start'):
                    st.session_state['start']=not st.session_state['start']
                    st.session_state['n']=1
                    temp=slate.container()
                    st.experimental_rerun()
        elif st.session_state['n']==1:
            temp=slate.container()
            with temp:
                Qa=st.radio("**:red[1)]:green[{}]**".format(questions[str(st.session_state["rn"][0])]),st.session_state["cho0"],horizontal=False)
                if st.button("Confirm"):
                     st.session_state['n']=2
                     st.session_state["t1"]=time.time()
                     st.experimental_rerun()
                countdown = st.empty()
                for i in range(10):
                    countdown.markdown(f'### {10-i}')
                    time.sleep(1)

First it asks about the name and last name, then with changing session_state[‘n’] the next part (page) should be rendered. Without adding timing part, it was working without problem. But now it does not render ideally.
Thanks for your time.

If you need, I’ll share with you more complete version of the code, or I simplify it as much as I can and then share with you. Let me know please.

I filled in the blanks, so let me know if I guessed wrong.

In general, I prefer to have session state changes done in a callback function instead of directly in line with another st.experimental_rerun() call.

I put the radio boxes in a form so that they can be changed freely before clicking Confirm so it’s more intuitive.

The extra juggle with the saving of ‘Qa’ to session state is because the key for that widget will disappear with whatever next click happens since the widget itself is gone.

import streamlit as st
import time

if 'st' not in st.session_state:
    st.session_state.st = False
if 'n' not in st.session_state:
    st.session_state.n = 0

def toggle_st():
    st.session_state.st = not st.session_state.st
    st.session_state.n = 0

st.button('Start', on_click=toggle_st)

def set_ss(key_list, value_list):
    for i in range(len(key_list)):
        if value_list[i] == '~*~':
            value_list[i]=st.session_state['widget_'+key_list[i]]
        st.session_state[key_list[i]]=value_list[i]

slate = st.empty()
slate.empty()
temp = slate.container()

if st.session_state["st"]==True:
    if st.session_state['n']==0:
        with temp:
            st.session_state['Nome'] = st.text_input("Nome:")
            st.session_state['Cognome'] = st.text_input("Cognome:")
            st.info('Premi start da iniziare il tuo 1 ora')
            st.button('start', on_click=set_ss,args=(['n'],[1]))
    elif st.session_state['n']==1:
        with temp:
            form = st.form('Question')
            form.radio('Choose',['A','list','of','choices'], horizontal=False, 
                        key='widget_Qa')
            form.form_submit_button('Confirm', on_click=set_ss,
                        args=(['n','t1','Qa'],[2,time.time(),'~*~']))
            countdown = st.empty()
            for i in range(10):
                countdown.markdown(f'### {10-i}')
                time.sleep(1)

st.session_state

timed_form

I am really thankful for your time. Don’t worry about saving “Qa”, here I attached just simplified code, the Qa value would directly be saved in Database. But still the bug is there also in your code. The part which must be invisible becomes grey.

Ah yes. Add another emtpy statement in the callback and it fixes it. (Except the session state info which I am only displaying as debug, so delete the st.session_state line at the end to be clean.)

def set_ss(key_list, value_list):
    for i in range(len(key_list)):
        if value_list[i] == '~*~':
            value_list[i]=st.session_state['widget_'+key_list[i]]
        st.session_state[key_list[i]]=value_list[i]
    slate.empty()