Difficulties using button with session_state

Hi, I am creating a support web app to help the support team at my company, at the moment is running locally.
Is a simple application, will receive a number and execute a ‘select’ on database and show to user. After this, I want a second button be enabled when the query is shown on the screen.
So is a button to consult and other (to delete a line in db). But I can’t make it work, any help is welcome I’m open to advice.

Python 3.12.3
Streamlit, version 1.34.0

Hi @bentohiago, welcome to the forum!

Can you give a simplified example showing what you’ve tried, and what has gone wrong?

Here’s a very simple example that might be helpful using st.session_state as a fake database.

import streamlit as st

if "fake_db" not in st.session_state:
    st.session_state["fake_db"] = [
        {"id": 1, "name": "John Doe", "email": "john@doe.com"},
        {"id": 2, "name": "Jane Doe", "email": "jane@doe.com"},
    ]


def get_all_usernames():
    return [user["name"] for user in st.session_state["fake_db"]]


def get_user_by_name(name):
    return next(
        (user for user in st.session_state["fake_db"] if user["name"] == name), None
    )


def delete_user(user_id):
    st.session_state["fake_db"] = [
        user for user in st.session_state["fake_db"] if user["id"] != user_id
    ]


@st.experimental_dialog("Add user")
def add_user():
    name = st.text_input("Name")
    email = st.text_input("Email")
    if st.button("Add"):
        st.session_state["fake_db"].append(
            {"id": len(st.session_state["fake_db"]) + 1, "name": name, "email": email}
        )
        st.rerun()


selected_user = st.selectbox("Select a user", [None] + get_all_usernames())

selected_user_data = get_user_by_name(selected_user)

if selected_user_data:
    st.write(f"Name: {selected_user_data['name']}")
    st.write(f"Email: {selected_user_data['email']}")

    st.button("Delete user", on_click=delete_user, args=(selected_user_data["id"],))
else:
    if st.button("Add user"):
        add_user()

Hi, thanks man!

In below a simple example, a part of the full code.

I realize that the delete button ( btns[1] ) only activates when I click Two times on the first button.

[up there is the conection with database]

# start state
if 'botao_ativo' not in st.session_state:
    st.session_state.botao_ativo = False    

# if 'resultados_exibidos' not in st.session_state:
#     st.session_state.resultados_exibidos = False


# Title
st.title('Consulta Imagem GED')

# Caixa de texto para o campo EMPRESA DO CTE
emp = st.text_input('Digite a EMPRESA DO CTE:', placeholder= "Qualquer empresa do Sistema Senior")

# Caixa de texto para o campo NUM
num = st.text_input('Digite o NUMERO DO CTE:', placeholder="EX: '4665656' ou '4665656, 4665657'")


# Botoes para Consultar e deletar
btns = st.columns((3,3,7))

with btns[0]:
    consultar_bt = st.button('Consultar')

with btns[1]:
    deletar_bt = st.button("Deletar", key="del", type="primary", disabled=not st.session_state.botao_ativo)

# Botão para iniciar a consulta
if consultar_bt:
   
    # verifica se o campo empresa está preenchido
    if emp == "" and num != "":

        # Separa itens por vírgula
        lista_num = num.split(",")
        resultados = []

        # Verifica se possui apenas 1 número e executa a consulta se sim
        if len(lista_num) == 1:
            resultado = consult_ged_emp(lista_num[0])
            resultados.extend(resultado)
        else:
            # Se tiver mais de 1 número
            for num_cte in lista_num:
                resultado = consult_ged_emp(num_cte)
                resultados.extend(resultado)

        # Show Results to user
        if resultados:
            st.markdown('<h2>Resultado da consulta:</h2>', unsafe_allow_html=True)
            for resultado in resultados:
                #st.write(resultado)
                st.markdown(f"""
                <div style="background-color: #708090; padding: 10px; margin: 10px 0; border-radius: 5px; color: #000;">
                    <strong>Empresa:</strong> {resultado["empresa"]}<br>
                    <strong>Usuário:</strong> {resultado["usuario"]}<br>
                    <strong>Data e Arquivo:</strong> {resultado["data_arq"]}
                </div>
                """, unsafe_allow_html=True)     
            st.session_state.botao_ativo = True  # Atualiza o estado para indicar que os resultados foram exibidos   
        elif resultado:
            st.markdown('<h2>Resultado da consulta:</h2>', unsafe_allow_html=True)
            st.write(resultado)
            st.session_state.botao_ativo = True  # Atualiza o estado para indicar que os resultados foram exibidos
        else:
            st.error('Nenhum resultado encontrado para os valores fornecidos.')
            st.session_state.botao_ativo = False # atualiza o estado para indicar que os resultados nao foram exibidos

That’s because the streamlit script runs from top to bottom on any input, so in this case what happens is, if I’m reading your code correctly:

  1. You push btns[0]
  2. You check to see if butao_ativo is true, it’s not, so btns[1] stays disabled
  3. You check btns[0] and do a bunch of stuff if it’s clicked, including changing butao_ativo.

Note that 3 happens after 2, so it won’t be until the next time the script runs that btns[1] is active.

There are two ways around this:

  1. Move the code that should run if btns[0] is clicked into a function, and do on_click=that_function. on_click functions like that always run as if they were at the top of the script, so if you change butao_ativo inside of that function, it will be changed by the time the script gets to btns[1].
  2. After you change butao_ativo in your current script, call st.rerun() to rerun the script from top to bottom.

Ok, I will try.
I hope to do this in the best way.

I think its solved.

In addition to create a function to if btns, I turn the list resultados in a st.session_state.resultados = [...] , help me a use better the list.

Now I have problems to use @st.experimental_dialog conditionaly, but I that is problem to annother topic.

Thanks a lot for the help.

1 Like