Hi, I’m unable to understand the behaviour of st.empty(). In the code below I have used the function to have 3 different page kinds of behaviour, but on the last page, the content of page 2 is getting added. I have tried adding ph.empty() (This caused an error) and ph = st.empty() but still the output is the same. The Code:
def c():
st.session_state.b1 = True
def d():
st.session_state.b2 = True
b1 = False
b2 = False
ph = st.empty()
if 'b1' not in st.session_state:
st.session_state.b1 = False
if 'b2' not in st.session_state:
st.session_state.b2 = False
with ph.container():
st.write("Page 1")
b1 = st.button("Page 2",on_click=c)
if (st.session_state.b1 or b1):
with ph.container():
st.write("Page 2")
st.write("Page 2 contents")
b2 = st.button("Page 3",on_click=d)
if (st.session_state.b2 or b2) is True:
with ph.container():
st.write("Page 3")
I have found a workaround (i.e add st.write("") at page 3 for each element that is extra). Would like to understand why this happens. Thanks
The reason this is happening is that when you use with ... with an empty element, you are creating a multi-element container inside of it. And, when you use it the second time, the container isn’t emptied, but instead elements in it are replaced one at a time. You can work around this by calling ph.empty() before populating it. For some reason, you also have to add a small sleep for the empty to actually work. Here’s a slightly modified version of your script that calls an empty function before replacing the container’s contents.
from time import sleep
import streamlit as st
def empty():
ph.empty()
sleep(0.01)
def c():
st.session_state.b1 = True
def d():
st.session_state.b2 = True
b1 = False
b2 = False
ph = st.empty()
if "b1" not in st.session_state:
st.session_state.b1 = False
if "b2" not in st.session_state:
st.session_state.b2 = False
with ph.container():
st.write("Page 1")
b1 = st.button("Page 2", on_click=c)
if st.session_state.b1 or b1:
empty()
with ph.container():
st.write("Page 2")
st.write("Page 2 contents")
b2 = st.button("Page 3", on_click=d)
if (st.session_state.b2 or b2) is True:
empty()
with ph.container():
st.write("Page 3")
Just to overcomplicate expand a bit on that, you could think of ph = st.empty() as initializing an empty list, and of each line within each with ph.container() block as an assignment to the i-th element of ph. Check the comments in the code below:
import streamlit as st
## Logic control
def c(): st.session_state.b1 = True
def d(): st.session_state.b2 = True
if 'b1' not in st.session_state: st.session_state.b1 = False
if 'b2' not in st.session_state: st.session_state.b2 = False
## Think of the placeholder here as a list initialized with no elements
ph = st.empty()
## Here you start adding elements to the placeholder
with ph.container():
st.write("1️⃣ First element in placeholder") # ph[0] = a st.write
st.button("1️⃣ Second element in ph (Go to page 2)",on_click=c) # ph[1] = a st.button
if st.session_state.b1:
with ph.container():
st.write("2️⃣ This overwrites the first element") # ph[0] = a new st.write
st.write("2️⃣ This overwrites the second element") # ph[1] = a new st.write
st.write("2️⃣ Third element in placeholder") # ph[2] = a st.write
st.button("2️⃣ Fourth element in ph (Go to page 3)",on_click=d) # ph[3] = a st.button
if st.session_state.b2:
with ph.container():
st.write("3️⃣ This replaces again the first element") # ph[0] = a new st.write
st.write("3️⃣ This replaces again the second element") # ph[1] = a new st.write
## Nothing is replacing the third element # ph[2] = a st.write
## Nothing is replacing the fourth element # ph[3] = a st.button
imo, that stream of if-statements is not the most readable structure to achieve the intended pages-like behavior . Here’s an idea using if-elif blocks:
import streamlit as st
# Pages logic
if 'page' not in st.session_state: st.session_state.page = 0
def nextPage(): st.session_state.page += 1
def firstPage(): st.session_state.page = 0
ph = st.empty()
## Page 0
if st.session_state.page == 0:
with ph.container():
st.header("This is page 1")
st.button("Go to page 2",on_click=nextPage)
## Page 1
elif st.session_state.page == 1:
with ph.container():
st.header("This is page 2")
st.write("Other stuff in page 2")
st.write("More stuff in page 2")
st.write("More more stuff in page 2")
st.write("More more more stuff in page 2")
st.button("Go to page 3",on_click=nextPage)
## Page 2
elif st.session_state.page == 2:
with ph.container():
st.header("This is page 3")
st.image("https://placekitten.com/g/1400/600",caption=f"Meowhy")
st.button("Go back",on_click=firstPage)
Can I ask what is the point of this? I came across this topic trying to figure out the use of the containers and it seems to me that your code would work just the same without the containers. What am I not seeing?
Well, OP’s question was about the difference between st.empty, st.container and calling the container method of an st.empty instance.
I guess you are asking about how useful are these placeholders, and you are right, you can achieve the same results without setting placeholders at all. But imo, placeholders improve readability and make code easier to maintain if you separate layout and contents, in case, for instance, you’d like to redesign the page.
An example that can totally be rewritten without placeholders:
import streamlit as st
# Pages logic
if 'page' not in st.session_state: st.session_state.page = 0
def nextPage(): st.session_state.page += 1
def firstPage(): st.session_state.page = 0
############################
# Layout
############################
"# My title"
ph_description = st.empty()
"## My first section"
ph_dynamic_page = st.empty()
ph_dynamic_cat = st.empty()
"## My second section"
ph_dynamic_img = st.empty()
############################
## Contents
############################
## Description
with ph_description.container():
st.write("This is some text "*20)
## Second section
with ph_dynamic_img.container():
if st.session_state.page == 2:
st.write(f"This only appears with **cat {st.session_state.page}**")
st.image("https://picsum.photos/400", caption="Not cat")
else:
st.write("Nothing to see here 👻")
## First section
with ph_dynamic_page.container():
st.subheader(f"This is cat {st.session_state.page}")
with ph_dynamic_cat.container():
cat_size = int((st.session_state.page+1)*50)
st.image(f"https://placekitten.com/g/{cat_size}",caption=f"Meowhy: {cat_size}")
st.button(f"Go to cat {st.session_state.page}",
on_click=(nextPage, firstPage)[st.session_state.page >= 3])
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.