St.Button is not working inside a dialogue box

Hello Everyone, I am trying to implement a application in which I have created a Dialogue box and pausing the execution of script script1.py and resuming the the script1.py when ADD TESTS button is pressed . Here I am facing the issue that the the code inside the if condition when ADD_TESTS button is not able to execute.

Any help is much appreciated. Thanks in advance!

script1

function_1(client)  #Doing Some communication operations

print(“PAUSE_EXECUTION_30-CONNECT_SENSOR”)

client.close()

trigger_file = “/script2_ready.flag”

print(“Script 1: Waiting for trigger…”)

Loop until the file exists

while not os.path.exists(trigger_file):
time.sleep(1) # Check every 1 second (saves CPU)

print(“Script 1: Trigger received! Continuing…”)

— Perform actions after trigger —

os.remove(trigger_file)

function_2(client) # #Doing Some communication operations

streamlit script in which script 1 is running

import streamlit as st
import subprocess
import time
from datetime import datetime, timedelta
import keyboard
import os
import psutil
from streamlit_extras.bottom_container import bottom

st.set_page_config(page_title=“TERMINAL”, layout=‘wide’)

Initialize session state variables

if ‘start_time’ not in st.session_state:
st.session_state.start_time = None
if ‘elapsed_time’ not in st.session_state:
st.session_state.elapsed_time = timedelta(0)
if ‘running’ not in st.session_state:
st.session_state.running = False
if ‘completed’ not in st.session_state:
st.session_state.completed = False

Create two columns

col1, col2, col3 = st.columns([1, 6, 1])

with st.sidebar:
# Timer placeholder in the sidebar
st.markdown(‘⏲️  TEST EXECUTION TIME’, unsafe_allow_html=True,help=“Will be displayed once the Execution is started”)
timer_placeholder = st.empty()
# Progress bar placeholder in the sidebar
st.markdown(‘⏳  TEST RUN PROGRESS’, unsafe_allow_html=True,help=“Will be displayed once the Execution is started”)
progress_bar = st.progress(0)
create_pdf_reports = st.button(“Get Reports”,key=‘pp’)
st.info("To Get Reports  in XLSX and/or PDF Format ", icon=“ℹ️”)
if create_pdf_reports:
keyboard.press_and_release(‘ctrl+w’)
pid = os.getpid()
p = psutil.Process(pid)
p.terminate()

Place the buttons in the respective columns

start_button = st.button(“START EXECUTION”,key=“hhh”)
st.markdown(“”"

<div class='info-box'>
    Click on <strong>START EXECUTION</strong> to Start the Test Execution.
</div>
""", unsafe_allow_html=True)

if start_button:
if not st.session_state.running:
st.session_state.start_time = datetime.now() - st.session_state.elapsed_time
st.session_state.running = True
st.session_state.completed = False

if start_button:
command = [“python”, ‘-u’, ‘script1.py’]
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
universal_newlines=True, shell=True)
ps_process = psutil.Process(pid=process.pid)
print(f"Process running with PID: {process.pid}")

with bottom():
    @st.dialog("Execution Paused for Manual Operations !!")
    def working_dialog():
        trigger_file = "/script2_ready.flag"
        print("Script 2: Doing work...")
        # --- Do work here ---

        if st.button('ADD TESTS', key=9):
            with open(trigger_file, 'w') as f:
                f.write("ready")
            print("Script 2: Trigger sent.")

    while process.poll() is None:
        line = process.stdout.readline()
        if not line:
            continue
        if line.startswith("PROGRESS:"):
            f = line.rstrip()
            progress_percentage = int(f.split(':')[1])
            print(progress_percentage)
            progress_bar.progress(progress_percentage)
            continue

        if line.startswith("PAUSE_EXECUTION"):
            working_dialog()
            #print(string_after)

        if "NOTE: Please click on  \"Get Reports\" to get results in XLSX and/or PDF Format" in line.strip():

            if st.session_state.running:
                st.session_state.elapsed_time = datetime.now() - st.session_state.start_time
                st.session_state.running = False
                st.session_state.completed = True
            # Set progress bar to 100% when the specific line is detected
            progress_bar.progress(100)
        st.markdown(line.strip())
        # Update the timer display
        if st.session_state.running:
            elapsed_time = datetime.now() - st.session_state.start_time
        else:
            elapsed_time = st.session_state.elapsed_time

        total_seconds = int(elapsed_time.total_seconds())
        hours, remainder = divmod(total_seconds, 3600)
        minutes, seconds = divmod(remainder, 60)

        timer_display = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
        timer_placeholder.metric(" ", "   "+timer_display)

        # Sleep for a short time to allow the UI to update
        #time.sleep(0.1)

Update the display every second if the timer is running

if st.session_state.running:
time.sleep(1)
st.rerun()

Ensure the final state is displayed

if st.session_state.completed:
timer_placeholder.metric(“ELAPSED TIME”, timer_display)
progress_bar.progress(1.0)  # Ensure progress bar is at 100% when completed


Welcome to the community and thanks for sharing your detailed question! :balloon: The main issue here is that Streamlit’s execution model reruns the entire script from top to bottom on every user interaction, including button clicks. This means that code inside an if st.button() block only executes during the rerun triggered by that button click, and any nested logic or state changes must be managed carefully. Also, blocking calls like while not os.path.exists(trigger_file): time.sleep(1) in your subprocess can cause the UI to freeze or not update as expected.

Key points and recommendations:

  • Button logic: Code inside if st.button('ADD TESTS'): only runs on the rerun caused by that button click. If you want to persist state or trigger actions, use st.session_state or callbacks.
  • Dialog and subprocess: The dialog (@st.dialog) and the subprocess communication need to be coordinated using files or session state, but blocking loops in the main thread will prevent UI updates.
  • Non-blocking UI: Avoid blocking the main Streamlit script with infinite or long-running loops. Instead, use polling with st.rerun() or subprocesses, and update the UI based on state.

Example pattern for your use case:

import streamlit as st
import subprocess
import os
import time

trigger_file = "/script2_ready.flag"

if "waiting_for_trigger" not in st.session_state:
    st.session_state.waiting_for_trigger = False

if st.button("START EXECUTION"):
    # Start the subprocess
    st.session_state.process = subprocess.Popen(
        ["python", "-u", "script1.py"],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        universal_newlines=True,
        shell=True
    )
    st.session_state.waiting_for_trigger = False

if st.session_state.get("process"):
    # Read output line by line
    line = st.session_state.process.stdout.readline()
    if "PAUSE_EXECUTION" in line:
        st.session_state.waiting_for_trigger = True

    if st.session_state.waiting_for_trigger:
        st.info("Execution paused. Click 'ADD TESTS' to continue.")
        if st.button("ADD TESTS"):
            with open(trigger_file, "w") as f:
                f.write("ready")
            st.session_state.waiting_for_trigger = False
            st.success("Trigger sent. Script will continue.")
    else:
        st.write(line)

This approach uses st.session_state to track when the script is waiting for the trigger, and only shows the “ADD TESTS” button at the right time. Avoid blocking the main thread with infinite loops—let Streamlit rerun and check for the trigger file or process state on each run.

For more on button behavior and session state, see Button behavior and examples - Streamlit Docs and Session State - Streamlit Docs.

Sources: