Summary
I have multiple st.expanders and inside each one I want to have a button that will then rerun code that’s related to that expander (without rerunning everything else), and then update the text written in the expander.
I’ve tried using st.session_state in multiple ways to achieve this behavior, but I couldn’t figure out how to do it. I would appreciate your help!
Steps to reproduce
Code snippet:
import streamlit as st
from dataclasses import dataclass
@dataclass
class ExampleClass:
name: str
text: str
def __int__(self, name, text):
self.name = name
self.text = text
def edit_text(self):
'''
Runs external logic that modifies the self.text field
'''
self.text = self.text + ' modified text!'
def write_example_class(example_class: ExampleClass):
st.subheader("Name:")
st.write(f"{example_class.name}")
st.subheader("Text:")
st.write(f"{example_class.text}")
def create_streamlit_app():
st.title("My title")
if st.button("Run program"):
# Here I have other code that creates two lists of ExampleClass
# objects - relevant ones and not relevant ones.
# For this example I just created the list of objects manually
relevant_1 = ExampleClass('name1', 'relevant1')
relevant_2 = ExampleClass('name2', 'relevant2')
relevant_3 = ExampleClass('name3', 'relevant3')
not_relevant_1 = ExampleClass('name1', 'not_relevant1')
not_relevant_2 = ExampleClass('name2', 'not_relevant2')
not_relevant_3 = ExampleClass('name3', 'not_relevant3')
relevant_example_class = [relevant_1, relevant_2, relevant_3]
not_relevant_example_class = [not_relevant_1, not_relevant_2, not_relevant_3]
# continuation of real code
st.header(f"Relevant examples ")
for i, example_class in enumerate(relevant_example_class,
start=1):
expander_text = rf""" **{example_class.name}**"""
example_class.edit_text() #run some code on the example_class
# object which modifies the "text" field
with st.expander(expander_text):
write_example_class(example_class) # writes the modified text
st.header(f"Not relevant examples:")
for i, example_class in enumerate(not_relevant_example_class,
start=1):
expander_text = rf""" **{example_class.name}**"""
with st.expander(expander_text):
write_example_class(example_class)
# here I want to give the user an option to run the
# edit_text function on the not relevant examples
if st.button('modify text', key=i):
example_class.edit_text()
write_example_class(example_class)
# but this here doesn't work, because when button is
# clicked, the script restarts from the beginning
if __name__ == '__main__':
create_streamlit_app()
If applicable, please provide the steps we should take to reproduce the error or specified behavior.
Expected behavior:
When the user clicks the button “modify text” inside the not_relevant_ expander, the text in the not_relevant_ expander will update to say “not_relevant modified text!”
For example, if the user clicks the “modify text” inside the not_relevant_1 expander, the text there will change to be “not_relevant1 modified text!”
Actual behavior:
When I click the “modify text” button, the entire page refreshes and brings me back to the original state where all I see is the title and the “Run program” button.
I’ve tried adding a session state variable to each button, but it doesn’t seem to work. What is the correct code design to use for getting this behavior?
Debug info
- Streamlit version: 1.26.0
- Python version: 3.11.5
- Using pip
- OS version: Windows
- Browser version: Chrome
Requirements file
My requirements.txt file:
aiohttp~=3.8.5
tiktoken~=0.4.0
openai~=0.28.0
streamlit~=1.26.0
extract_msg==0.45.0
Thank you very much!