Control flow confusing

I am trying to upload a csv file to create a pandas data frame. I also need some input from the user to use in the data frame methods. What I would like my app to do is

  1. Upload a selected file
  2. Create a data frame
  3. In a loop:
    a. Ask for 2 input variables
    b. Calculate and print the output

I don’t understand how to manage streamlit’s control flow to avoid executing the same commands multiple times since it runs the script from the beginning each time an input has been submitted.
The following code doesn’t work as expected. After filling the text inputs and hitting the second submit button, the data frame is read again but no output is generated:

@st.cache_data
def create_dataframe(uploaded_file):
    st.write("Creating dataframe from '" + uploaded_file.name + "'...") 
    dataframe = pd.read_csv(uploaded_file)
    st.write("Dataframe created!")
    return dataframe

with st.form("submitform", clear_on_submit=False):
    uploaded_file = st.file_uploader("Choose a file")
    if uploaded_file is not None:
        df=create_dataframe(uploaded_file)

    submitted_upload = st.form_submit_button("Submit")

if submitted_upload:
    index1=0
    while index1 in range(100):
        with st.form("getinput"+ str(index1), clear_on_submit=False):
            input1 = st.text_input(label="input1: ",key="ti1") 
            input2 = st.text_input(label="input2: ",key="ti2")

            submitted_input = st.form_submit_button("Submit")

        if submitted_input:
            st.write("Processing...")
			#do calculations... print output...

You don’t need the loop, streamlit already does that for you.

With the following code I get the error “Missing submit button” although I have a submit button in both forms. Any idea what is wrong?

@st.cache_data
def uploadfile():
    uploaded_file = st.file_uploader("Choose a file")
    return uploaded_file

@st.cache_data
def create_dataframe(uploaded_file):
    st.write("Creating dataframe from '" + uploaded_file.name + "'...") 
    dataframe = pd.read_csv(uploaded_file)
    st.write("Dataframe created!")
    return dataframe
	
with st.form("submitform", clear_on_submit=False):
    uploaded_file = uploadfile()
    if uploaded_file is not None:
        df=create_dataframe(uploaded_file)
		uploadfile.clear()
		
    submitted_upload = st.form_submit_button("Submit")

if submitted_upload:
	with st.form("getinput", clear_on_submit=False):
		input1 = st.text_input(label="input1: ",key="ti1") 
		input2 = st.text_input(label="input2: ",key="ti2")

		submitted_input = st.form_submit_button("Submit")

	if submitted_input:
		st.write("Processing...")
		#do calculations... print output...

It looks like you need to do two things to get your example script working:

  1. Remove st.cache_data on the file uploader – I’m not sure exactly what behavior you’re expecting from that, but you shouldn’t need to cache the output of a file uploader
  2. Remove uploadfile.clear(), which is not defined for the function uploadfile

After that it seems to work.

Your suggestions debugged the code but it still does not produce the results I am looking for.

  1. According to st.chache_data a cached function’s cache can be procedurally cleared. I don’t need the uploaded file any more once the data frame is created, so I want to delete it from the cache. This is why I used uploadfile.clear().
  2. Still the code executes create_dataframe after hitting the second submit button although I use a @st.cache_data before the definition of the function. Also no output is generated. Instead the first form is reactivated and the second form disappears.
  1. Trying to cache something only to immediately clear it defeats the purpose of caching.
  2. For any widget inside of a form, the output of that widget will not update until after the form is submitted.
  3. Buttons aren’t stateful. They will return true only on the page load resulting from their click then go back to false.

Here’s an example based on your code:

import streamlit as st
import pandas as pd

def reset():
    st.session_state.df = None

st.sidebar.button('Reset', on_click=reset)

if 'df' not in st.session_state:
    st.session_state.df = None

if st.session_state.df is None:
    uploaded_file = st.file_uploader("Choose a file")
    if uploaded_file != None:
        st.write("Creating dataframe from '" + uploaded_file.name + "'...") 
        dataframe = pd.read_csv(uploaded_file)
        st.write("Dataframe created!")
        st.session_state.df = dataframe
        st.button('Continue')
else:
    with st.form("getinput", clear_on_submit=False):
        input1 = st.text_input(label="input1: ",key="ti1") 
        input2 = st.text_input(label="input2: ",key="ti2")

        submitted_input = st.form_submit_button("Submit")

    if submitted_input:
        st.write("Processing...")
		#do calculations... print output...
        # If you have other buttons on the page, this processing and output will
        # disappear as soon as anything is clicked. Processing will be redone if
        # the submit button above is clicked again.
1 Like

@mathcatsand Thanks.

The fact that the script reruns whenever a user interacts with widgets takes a bit getting used to.

Yeah. I try to explain it by telling people to throw away the idea of waiting for user input and think of all widgets as just “plowing through” with their default values. When someone provides an input or change, everything starts over from the beginning with that one change in effect.