How to make a timer?

Hi all,

I would like to integrate a countdown in my app - say from 5 minutes to zero, updating every second.

I can reload the app every second and maybe save the initial timestamp in memory to print the difference but this is not ideal for obvious reasons.
Do you see a better approach?

thanks

Hi!

You could use the metrics and display a countdown:

import streamlit as st
import time

st.set_page_config()

ph = st.empty()
N = 5*60
for secs in range(N,0,-1):
    mm, ss = secs//60, secs%60
    ph.metric("Countdown", f"{mm:02d}:{ss:02d}")
    time.sleep(1)

countdown

But there is a huge catch: the app wont be able to do anything else until the countdown ends (It stays on the countdown loop). I’d love to know if there’s a way to avoid this!

2 Likes

Hi @sebastiandres , I think you should be able to get out of a for loop by using an if condition with a break statement (just above your time.sleep command). You could probably even check for an Escape keypress using some of the available python libraries (pyautogui, keyboard, etc.).

I haven’t tried it out, but here seems an example of a break from a while loop. https://www.delftstack.com/howto/python/python-detect-keypress/

Cheers

1 Like

Thanks @sebastiandres , it is an interesting solution.

As you pointed out, the app cannot do anything else and that’s a problem. I’ll try to combine it with a break option and I’ll save the countdown starting time to resume the countdown consistently when any other interaction with the app is done.

Another option would be to have the countdown in pure javascript+html (no streamlit), but you would need some knowledge to make it look good. A bare bones version could be:

import streamlit as st
from streamlit.components.v1 import html
import time

st.set_page_config()

my_html = """
<script>
function startTimer(duration, display) {
    var timer = duration, minutes, seconds;
    setInterval(function () {
        minutes = parseInt(timer / 60, 10)
        seconds = parseInt(timer % 60, 10);

        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;

        display.textContent = minutes + ":" + seconds;

        if (--timer < 0) {
            timer = duration;
        }
    }, 1000);
}

window.onload = function () {
    var fiveMinutes = 60 * 5,
        display = document.querySelector('#time');
    startTimer(fiveMinutes, display);
};
</script>

<body>
  <div>Registration closes in <span id="time">05:00</span> minutes!</div>
</body>
"""

html(my_html)


if st.button("Is blocked?"):
  st.write("No, you can still interact")
  st.balloons()

(Not my javascript, I googled and copied the first result of javascript countdown)

1 Like

You can also create an external asyncio thread managing the timer for you :slight_smile:

1 Like

This async solution is very solid. Thanks

1 Like