Hey everyone,
I’m working on visualizing live sensor data in Streamlit, and I’m stuck trying to find a better way to do it. Here’s what I’ve tried so far:
Current Approach: While Loop for Constant Updates
I’m using an infinite loop to keep updating the data in real-time, as shown in this blog post:
import streamlit as st
import random
import time
def read_data():
"""Simulate sensor data reading."""
return random.random()
with st.empty():
while True:
data = read_data()
st.write(data)
time.sleep(1 / 30)
The Problem: The loop messes up other Streamlit features. For example, in this code, st.selectbox
doesn’t update st.write
properly because of the while True
loop:
import streamlit as st
import random
import time
@st.dialog('Choose an option')
def option():
choice = st.selectbox(label='Options', options=['A', 'B'])
st.write(choice)
def read_data():
return random.random()
if st.button('Choose an option'):
option()
with st.empty():
while True:
data = read_data()
st.write(data)
time.sleep(1 / 30)
Other Things I Tried
1. Using st.write_stream
Here’s another approach using st.write_stream
:
import streamlit as st
import random
import time
def read_data():
while True:
yield random.random()
time.sleep(1 / 30)
with st.empty():
print('1st stream')
st.write_stream(read_data)
# This will never run
with st.empty():
print('2nd stream')
st.write_stream(read_data)
The Problem: Infinite streams keep running without stopping, making it impossible to handle and visualize multiple data streams at the same time.
2. Using st.fragment
I also tried st.fragment
for periodic updates:
import streamlit as st
import random
def read_data():
return random.random()
@st.fragment(run_every='33ms') # Runs at 30 Hz
def show_data():
data = read_data()
st.write(data)
show_data()
The Problem: Fragments freeze when the refresh rate (run_every
) is set too high. Even worse, the refresh rate at which a fragment freezes varies depending on the content being rendered. For instance, the simpler example from above runs smoothly at 20 Hz (run_every='50ms'
) on my machine. However, adding a basic plot causes it to freeze again.
import streamlit as st
import random
import altair as alt
import pandas as pd
def read_data():
return random.random()
@st.fragment(run_every='50ms') # Runs at 20 Hz
def show_data():
data = pd.DataFrame({
"x": [read_data() for _ in range(5)],
"y": [read_data() for _ in range(5)]
})
chart = alt.Chart(data).mark_circle().encode(x='x:Q', y='y:Q')
st.write(chart)
show_data()
Trying to use multiple st.fragment
instances makes things even worse.
An Idea: st.camera_input
What I really want is a way to render data smoothly without breaking anything else. The st.camera_input
feature looks promising. For example:
import streamlit as st
st.camera_input("1st camera stream")
st.camera_input("2nd camera stream")
st.write("Hello World!")
This works great - no endless loops, and both streams update without blocking each other.
The Question
Could something like st.camera_input
be adapted for other live sensor data? I watched both tutorials on creating custom Streamlit components, but I’m not sure how st.camera_input
works internally to replicate that behavior for other infinite data streams. (Also, I’ve never written custom Streamlit components before or had any experience with web development.)
I’d really appreciate any ideas on how to smoothly visualize multiple data streams at once.
Thanks in advance!