Search code examples
python-asynciostreamlit

running asyncio with streamlit dashboard


How do I create a streamlit dashboard that doesn't continuously append the new values from the asyncio.gather(firstWorker(),secondWorker()? The code below runs but will very quickly turn into a very long dashboard where I was hoping to figure out if I could just 2 fixed streamlit title or metric to represent workerOne workerTwo where only the val_one and val_two are updated on the dashboard. streamlit metric or text elements seem to all have the same behavior of continuously appending...any tips appreciated.

Am using python 3.10 on Windows 10 running the dashboard with: $ streamlit run app.py

import streamlit as st
import asyncio
import random as r

val_one = 0
val_two = 0


st.title("Hello World")

async def firstWorker():
    global val_one
    while True:
        await asyncio.sleep(r.randint(1, 3))
        val_one = r.randint(1, 10)
        st.metric("First Worker Executed: ",val_one)


async def secondWorker():
    global val_two
    while True:
        await asyncio.sleep(r.randint(1, 3))
        val_two = r.randint(1, 10)
        st.metric("Second Worker Executed: ",val_two)


async def main():
    await(asyncio.gather(
        firstWorker(), 
        secondWorker()
            )
        )

if __name__ == '__main__':
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    finally:
        print("Closing Loop")
        loop.close()

Trying to avoid if possible an infinite long dashboard as well as have the anyscio processes run in the background even if the streamlit dashboard isnt being utilized by a web browser.

enter image description here


Solution

  • Put the code after if __name__ == '__main__': into st.empty() container to overwrite the previous content whenever an update is made.

    import streamlit as st
    import asyncio
    import random as r
    
    val_one = 0
    val_two = 0
    
    
    st.title("Hello World")
    async def firstWorker():
        global val_one
        while True:
            await asyncio.sleep(r.randint(1, 3))
            val_one = r.randint(1, 10)
            st.metric("First Worker Executed: ",val_one)
    
    
    async def secondWorker():
        global val_two
        while True:
            await asyncio.sleep(r.randint(1, 3))
            val_two = r.randint(1, 10)
            st.metric("Second Worker Executed: ",val_two)
    
    
    async def main():
        await(asyncio.gather(
            firstWorker(),
            secondWorker()
                )
            )
    
    if __name__ == '__main__':
        with st.empty(): # Modified to use empty container
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                asyncio.run(main())
            except KeyboardInterrupt:
                pass
            finally:
                print("Closing Loop")
                loop.close()
    

    Output:

    enter image description here

    Version 2:

    st.title("Hello World")
    
    async def firstWorker():
        await asyncio.sleep(r.randint(1, 3))
        val_one = r.randint(1, 10)
        st.metric("First Worker Executed: ", val_one)
    
    
    async def secondWorker():
        await asyncio.sleep(r.randint(1, 3))
        val_two = r.randint(1, 10)
        st.metric("Second Worker Executed: ", val_two)
    
    
    async def main():
        with st.empty():
            while True:
                left_col, right_col = st.columns(2)
                with left_col:
                    await (asyncio.gather(firstWorker()))
                with right_col:
                    await (asyncio.gather(secondWorker()))
    
    
    if __name__ == '__main__':
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            asyncio.run(main())
        except KeyboardInterrupt:
            pass
        finally:
            print("Closing Loop")
            loop.close()
    

    Output:

    enter image description here

    I don't know why you use global variables. I got rid of them this time unless otherwise you need them.