Button Disabled During While Loop

Summary

Dear Streamlit Community,

I’ve encountered an issue with my Streamlit application that I would greatly appreciate your input on.

In this application, I am running a while loop to update the stream of a camera. While running this loop, I want to allow the user to interact with a button to perform a certain action. However, I noticed that when the loop is running, the button becomes unresponsive. I suspect this is due to the loop blocking the rest of the application’s execution.

Steps to reproduce

To run the code, please set fastReruns to false, otherwise you may face st.session_state issues:
streamlit run minimal_example.py --runner.fastReruns False

Code snippet:

import time
import streamlit as st


def minimal_example():

    if 'is_streaming' not in st.session_state:
        st.session_state.is_streaming = False

    if 'counter' not in st.session_state:
        st.session_state.counter = 0

    def start_stream():
        if st.button("Start Streaming"):
            st.session_state.is_streaming = True

        text_placeholder = st.empty()

        while st.session_state.is_streaming:
            text_placeholder.text(f"Streaming {st.session_state.counter }")
            st.session_state.counter += 1
            time.sleep(1)

    def button_function():
        if st.button("Test Button"):
            st.text("Clicked")

    start_stream()
    button_function()


minimal_example()

Expected behavior:

Test button should stay enabled.

Actual behavior:

Test button disabled.

Debug info

  • Streamlit version: 1.25.0
  • Python version: 3.9.9
  • Using PyCharm
  • OS version: Windows 11
  • Browser version: Chrome Version 115.0.5790.17

Additional information

  1. I attempted to use a separate thread for the stream, but it appears that the created thread does not communicate with the main thread. However, the prints show that the new thread is running:
import time
import streamlit as st
import threading
from streamlit.runtime.scriptrunner import add_script_run_ctx


def minimal_example_2():
    if 'thread_started' not in st.session_state:
        st.session_state.thread_started = False

    if 'counter' not in st.session_state:
        st.session_state.counter = 0

    def start_stream():
        if st.button("Start Streaming") and not st.session_state.thread_started:
            st.session_state.thread_started = True
            start_updating(text_placeholder)

    def start_updating(placeholder):
        thread = threading.Thread(target=update_counter, args=(placeholder,),
                                  daemon=True)
        add_script_run_ctx(thread)
        thread.start()

    def update_counter(placeholder):
        while st.session_state.thread_started:
            placeholder.write(f"Streaming = {st.session_state.counter}")
            st.session_state.counter += 1
            print(
                f"New Thread is increasing the streaming to: {st.session_state.counter}")
            time.sleep(1)

    def stop_stream():
        if st.button("Stop Streaming"):
            st.session_state.thread_started = False

    def button_function():
        if st.button("Test Button"):
            st.text("Clicked")

    text_placeholder = st.empty()
    start_stream()
    stop_stream()
    button_function()


minimal_example_2()

If any of you have come across a similar situation or have any ideas on how I can address this problem, I would be very grateful for your suggestions.

Thank you in advance for your time and insights!

Hi there!

Yes, you are right. The while loop appears to be infinite, due to which the button is “disabled”. However, calling the button_function in your start_stream function, before the while loop should work. I do not know if this is the best method, but here’s what I came up with:

import time
import streamlit as st


def minimal_example():

    if 'is_streaming' not in st.session_state:
        st.session_state.is_streaming = False

    if 'counter' not in st.session_state:
        st.session_state.counter = 0

    if "clicks" not in st.session_state:
        st.session_state.clicks= []
        
    def start_stream():
        if st.button("Start Streaming"):
            st.session_state.is_streaming = True

        text_placeholder = st.empty()
        button_function()
        while st.session_state.is_streaming:
            text_placeholder.text(f"Streaming {st.session_state.counter }")
            st.session_state.counter += 1
            time.sleep(1)
        
    
    def button_function():
        if st.button("Test Button"):
            st.session_state.clicks.append("Clicked")
            st.write(st.session_state.clicks)
    
    start_stream()
    


minimal_example()

I just added a session state list just to show that the button works multiple times too, as the while loop is executing.

P.S. Threading in Python notorious for causing problems. It is usually useful only when used with I/O bound operations. So you need to do a bit of research to see if your code will benefit from threading in Streamlit, and Python in general.
P.P.S I think you may be able to find a better way for executing your camera stream update, since the while loop will continuously run if not broken externally, and this might cause your program to get stuck in infinite loops.

Cheers,
Moiz

1 Like

Hi Moiz,

Thank you for your time and help! Your suggestion to call button_function() inside the start_stream() function indeed helped resolve the issue, even though it’s not the ideal separation of concerns for my original case. However, I will implement it considering that I have no other alternatives.

The while loop in my code has a mechanism to break out of the loop when a specific button is pressed.

Thanks again for your assistance!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.