Create a clock/timer with streamlit tools

  1. Are you running your app locally or is it deployed?

locally

  1. Share the link to your app’s public GitHub repository (including a requirements file).

there is none (yet)

  1. Share the full text of the error message (not a screenshot).

no error message

  1. Share the Streamlit and Python versions.

streamlit 1.29.0, python 3.9.2

I want to create a time tracking app where it would be convenient to have a clock running. Two solutions were discussed so far: using pure JS or an async background-task. See How to make a timer? - #5 by sebastiandres and How to make a timer? - #6 by andfanilo

However, there are problems with both:

  • The JS solution has no access to high-level widgets, such as the metric
  • The async task is continously running, which is apparent from the “running” symbol in the upper right corner. Furthermore, it gets a bit messy when more than one such task is required.

My current solution uses such tasks though:

import asyncio        
import streamlit as st     
import time
from functools import partial
from datetime import datetime

def clock(field, name, starttime):
    """Print a time in a field"""
    tdelta =  datetime.now().replace(microsecond=0) - starttime.replace(microsecond=0)
    minutes, seconds = divmod(int(tdelta.total_seconds()), 60)      
    hours, minutes = divmod(minutes, 60)                                                                       
    field.metric(name, f"{hours}:{minutes:02d}:{seconds:02d}")

async def run_jobs(job_list):
    while True:
        for job in job_list:
            job()
        # Not sure why asyncio.sleep was used here...
        time.sleep(0.1)

col1, col2 = st.columns(2)
# Placeholder Fields for Timers
with col1:
    all_tasks = st.empty()
with col2:
    ph = st.empty()

jobs = []

# Jobs are queued for the fields
jobs.append(partial(clock, all_tasks, "foo", datetime(2023, 12, 28, 14)))
jobs.append(partial(clock, ph, "baz", datetime(2023, 12, 28, 16)))

if jobs:
    # not sure why asyncio is actually needed - a normal function works as well.
    asyncio.run(run_jobs(jobs))

I wonder now, if there are any new tools that can handle this a bit more elegantly and in the browser’s context, such that no running script is shown?