Button inside the Button is not triggering the action

In the below code execution the button operation Save to file is not working. I have added the print statement inside the Save as TXT Button (Execution is not going up to this print statement).
Steps:

  1. Click on TAKEOUT
  2. Click on Save as TXT
  3. elements_text data from st.text_area should be save in saves.txt file
import streamlit as st
extract_button = st.button(
        'TAKEOUT',
        key='extract_elements_button' )

if extract_button:
    elements = ["List"]
    if elements:
        elements_text='Test_String'
        tab1, tab2, tab3 = st.tabs(["Elements", "Screen", "Stats"])
        with tab1:
            st.text_area('Elements', elements_text, height=300)

            # Add export options
            col1, col2, col3 = st.columns([1, 1, 1])
            with col1:
                save_path = st.text_input(
                    'Save to file:',
                    'saves.txt',
                    key='save_path',
                    help="Enter filename to save results"
                )
            with col2:
                if st.button('Save as TXT', key='save_txt'):
                    print("Code is Not coming Here")
                    try:
                        os.makedirs(os.path.dirname(save_path), exist_ok=True)
                        with open(save_path, 'w', encoding='utf-8') as f:
                            f.write(elements_text)
                        st.success(f'Successfully saved to {save_path}')
                    except Exception as e:
                        st.error(f"Error saving file: {e}")
            with col3:
                if st.button('Save as JSON', key='save_json'):
                    try:
                        os.makedirs(os.path.dirname(save_path.replace('.txt', '.json')), exist_ok=True)
                        json_path = save_path.replace('.txt', '.json')
                        with open(json_path, 'w', encoding='utf-8') as f:
                            if 'elements_raw' in st.session_state:
                                json.dump(st.session_state['elements_raw'], f, indent=2)
                            else:
                                json.dump(elements, f, indent=2)
                        st.success(f'Successfully saved to {json_path}')
                    except Exception as e:
                        st.error(f"Error saving JSON: {e}")
        with tab2:
            # Display screenshots if available
            elements_text1='Test_String1'
            st.text_area('Elements1', elements_text1, height=300)
        with tab3:
            # Display element statistics
            elements_text2 = 'Test_String2'
            st.text_area('Elements2', elements_text2, height=300)

Hi @mayur1khandagale

With your current code, besides the buttons issue, you will not be able to provide the save file name too, without the screen refresh happening. Please read the help docs on how & why a button triggers a refresh and its workarounds.

I have shown you one way to code the solution; there are other ways too. I am using streamlit v1.45.1

I would ideally not have the TAKEOUT button at all; however, I tried to use as much of your code and program flow as possible. If this revised code works for you, you can add the (missing) part which actually writes the file to disk in the revised code.

I suggest that you:

  1. don’t duplicate code to write files to disk for both json and txt options - make this code common via a function and needed variables
  2. Using endswith() check for user provided save file extension and/or add it to the save file name appropriately
  3. Consider using an expander instead of a button for the TAKEOUT if you want to code this way.
  4. Deactivate both the save as buttons if the save file name has not been provided or if it is invalid.
  5. Use a callback for your save file name text input to check for file name validity
import streamlit as st
from streamlit import session_state as ss
import os
import json

def button(*args, key=None, **kwargs):
  if key is None: raise ValueError("Must pass key")
  if key not in ss: ss[key] = False

  if st.button(*args, **kwargs):
    ss[key] = not ss[key]
    st.rerun()

  return ss[key]

def SaveAsFile(button_key, file_name_key):
  save_file_name = ss[file_name_key] + ('.txt' if button_key == 'save_txt' else '.json')
  st.write(f"File ({save_file_name}) saved...")

extract_button = button('TAKEOUT', key='extract_elements_button' )
if extract_button:
    elements = ["List"]
    if elements:
        elements_text='Test_String'
        tab1, tab2, tab3 = st.tabs(["Elements", "Screen", "Stats"])
        with tab1:
            st.text_area('Elements', elements_text, height=300)

            # Add export options
            col1, col2, col3 = st.columns([1, 1, 1], gap="medium", vertical_alignment='bottom')
            col1.text_input('Save to file:', 'saves_file_name', key='save_path', help="Enter filename to save results")
            col2.button('Save as TXT', key='save_txt', on_click=SaveAsFile, args=('save_txt', 'save_path'))
            col3.button('Save as JSON', key='save_json', on_click=SaveAsFile, args=('save_json', 'save_path'))
                    
        with tab2: # Display screenshots if available
            elements_text1='Test_String1'
            st.text_area('Elements1', elements_text1, height=300)
        
        with tab3: # Display element statistics
            elements_text2 = 'Test_String2'
            st.text_area('Elements2', elements_text2, height=300)

Cheers

1 Like

@Shawn_Pereira
Thank you so much for for response and detailed solution. I have implemented the session state for buttons as per your suggestion it is working Good.

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