Summary
I need to present some values that update every few seconds in a multitabs app using streamlit_option_menu
.
Metrics are currently updated in a infinite loop and visualized into a st.empty()
placeholder.
Problem: When changing the active tabs, “ghosts” of previous data are “forgotten” in the page because the frontend is never told to remove them.
The problem comes from the usage of st.empty()
together with time.sleep
that I need to use in order to update the metrics.
Steps to reproduce
import streamlit as st
from streamlit_option_menu import option_menu
import random
import time
st.header("Multipage with data refresh")
page = option_menu("", ["page1", "page2"], orientation="horizontal")
placeholder = st.empty()
if page == "page1":
while True:
with placeholder.container():
st.metric(label="Page 1 Metric 1", value=random.randrange(1000, 1109))
time.sleep(1)
elif page == "page2":
while True:
with placeholder.container():
st.metric(label="Page 2 Metric 1", value=random.randrange(2000, 2109))
st.metric(label="Page 2 Metric 2", value=random.randrange(2000, 2109))
st.metric(label="Page 2 Metric 3", value=random.randrange(2000, 2109))
time.sleep(1)
Alternative code without the while True
loop:
import streamlit as st
from streamlit_option_menu import option_menu
import random
import time
st.header("Multipage with data refresh")
page = option_menu("", ["page1", "page2"], orientation="horizontal")
placeholder = st.empty()
if page == "page1":
with placeholder.container():
st.metric(label="Page 1 Metric 1", value=random.randrange(1000, 1109))
elif page == "page2":
with placeholder.container():
st.metric(label="Page 2 Metric 1", value=random.randrange(2000, 2109))
st.metric(label="Page 2 Metric 2", value=random.randrange(2000, 2109))
st.metric(label="Page 2 Metric 3", value=random.randrange(2000, 2109))
time.sleep(2)
To see the problem: Click first on page2
and then page1
. You will see the “ghosts” of two metrics that shouldn’t be there.
In the second example the new tab is painted while the frontend has not been told that the old one has to be removed. For a couple of seconds the old metrics are grayed out (backend in time.sleep
loop, frontend tells us it is not receiving any update), and then finally removed at the end of the pause.
Question: Is there any way to force the frontend to refresh while in the infinite while True
?
As an alternative: how to implement a non-blocking time.sleep(1)
to allow backend and frontend to chat and fix all the pending stuff while they wait the pause to end?
Already tried
Multiple placeholders, one for tab
The most obvious solution would be to use TWO placeholders, and empty the unused one each time a tab is activated:
import streamlit as st
from streamlit_option_menu import option_menu
import random
import time
st.header("Multipage with data refresh")
page = option_menu("", ["page1", "page2"], orientation="horizontal")
placeholder1 = st.empty()
placeholder2 = st.empty()
if page == "page1":
placeholder2.empty()
while True:
with placeholder1.container():
st.metric(label="Page 1 Metric 1", value=random.randrange(1000, 1109))
time.sleep(1)
elif page == "page2":
placeholder1.empty()
while True:
with placeholder2.container():
st.metric(label="Page 2 Metric 1", value=random.randrange(2000, 2109))
st.metric(label="Page 2 Metric 2", value=random.randrange(2000, 2109))
st.metric(label="Page 2 Metric 3", value=random.randrange(2000, 2109))
time.sleep(1)
As my app should present around 100-200+ metrics split in a number of tabs, this would mean a HUGE quantity of placeholders to be emptied when changing pages and tabs. As a PLAN B this is the solution I am implementing at the moment, even if it is so cumbersome that some of the elegance of streamlit is lost along the way.
Adding streamlit_autorefresh component
It restarts the app after an interval. These metrics tabs need a frequent update and they share the app with a number of other static pages that don’t need to be updated at all. Continuously restarting the app doesn’t look an optimal solution as it involves loading tons of data from DB that cannot be effectively cached.
- Streamlit version: 1.15.2
- Python version: 3.8.10
- OS version: Ubuntu 20.04
- Browser version: Firefox 108.0, Brave 1.16.76, Chrome 103.0.5060.114
After 4 days NO answers, I opened an issue
I opened an issue Add st.sleep? #5883 to propose an enhancement: a non-blocking st.sleep
managed by streamlit instead of using the standard time.sleep
.