How to access Gradio components created dynamically in a global function

My goal:

  1. drop a list of files.
  2. Read files and extract columns.
  3. Dynamically generate dropdown componenets so the user can select the source and target columns.
  4. I need to return (globally) all the selections, so I can use them with process_files() function.
    I think (3) it should be solved using gr.State([ ]) but I am not sure how to update it.
    here is my code:

Function to process files

import gradio as gr
import pandas as pd


def process_files(files, selections):
    results = []

    for file, selection in zip(files, selections):
        file_name = file.name
        source_col, target_col, source_lang, target_lang = selection
        try:
            if file_name.endswith(".csv"):
                df = pd.read_csv(file.name)
            elif file_name.endswith(".xlsx"):
                df = pd.read_excel(file.name, engine="openpyxl")
            else:
                continue  # Skip unsupported files

            # Extract selected columns
            extracted_data = df[[source_col, target_col]]

            results.append(
                f"βœ… Processed {file_name} ({source_lang} β†’ {target_lang})")
        except Exception as e:
            results.append(f"Error processing {file_name}: {str(e)}")

    return "\n".join(results)

Gradio Interface

with gr.Blocks() as demo:
    gr.Markdown("## Upload Multiple Files and Select Columns")

    file_input = gr.File(file_types=[
                         ".csv", ".xlsx"], label="Drop your files here", interactive=True, file_count="multiple")

    all_selections = gr.State([])

    @gr.render(inputs=[file_input], triggers=[file_input.change])
    def generate_column_selectors(files):
        if not files:
            return "## No Files Uploaded", []  # Reset selections

        with gr.Column():
            for file in files:
                try:
                    if file.name.endswith(".csv"):
                        df = pd.read_csv(file.name)
                    elif file.name.endswith(".xlsx"):
                        df = pd.read_excel(file.name, engine="openpyxl")
                    else:
                        continue 

                    with gr.Row():
                        gr.Markdown(f"**{file.name}**")  # Display filename
                        source_col = gr.Dropdown(
                            df.columns.tolist(), label="Source Column", interactive=True)
                        target_col = gr.Dropdown(
                            df.columns.tolist(), label="Target Column", interactive=True)
                        source_lang = gr.Textbox(
                            label="Source Language", placeholder="e.g., English", interactive=True)
                        target_lang = gr.Textbox(
                            label="Target Language", placeholder="e.g., French", interactive=True)

                    all_selections.append(
                        (source_col, target_col, source_lang, target_lang)) # this does not work.

                except Exception as e:
                    gr.Markdown(f"Error processing {file.name}: {str(e)}")
    submit_button = gr.Button("Submit & Process", interactive=True)
    output_text = gr.Markdown()

    submit_button.click(process_files, inputs=[
                        file_input, all_selections], outputs=[output_text])

demo.launch()
1 Like
import gradio as gr
import pandas as pd

def process_files(files, selections):
    results = []

    for file, selection in zip(files, selections):
        file_name = file.name
        source_col, target_col, source_lang, target_lang = selection
        try:
            if file_name.endswith(".csv"):
                df = pd.read_csv(file.name)
            elif file_name.endswith(".xlsx"):
                df = pd.read_excel(file.name, engine="openpyxl")
            else:
                continue  # Skip unsupported files

            # Extract selected columns
            extracted_data = df[[source_col, target_col]]

            results.append(
                f"βœ… Processed {file_name} ({source_lang} β†’ {target_lang})")
        except Exception as e:
            results.append(f"Error processing {file_name}: {str(e)}")

    return "\n".join(results)

with gr.Blocks() as demo:
    gr.Markdown("## Upload Multiple Files and Select Columns")

    file_input = gr.File(file_types=[".csv", ".xlsx"], label="Drop your files here", interactive=True, file_count="multiple")

    all_selections = gr.State([])

    @gr.render(inputs=[file_input], triggers=[file_input.change])
    def generate_column_selectors(files):
        if not files:
            return "## No Files Uploaded", []  # Reset selections

        selections = []
        with gr.Column():
            for file in files:
                try:
                    if file.name.endswith(".csv"):
                        df = pd.read_csv(file.name)
                    elif file.name.endswith(".xlsx"):
                        df = pd.read_excel(file.name, engine="openpyxl")
                    else:
                        continue 

                    with gr.Row():
                        gr.Markdown(f"**{file.name}**")  # Display filename
                        source_col = gr.Dropdown(df.columns.tolist(), label="Source Column", interactive=True)
                        target_col = gr.Dropdown(df.columns.tolist(), label="Target Column", interactive=True)
                        source_lang = gr.Textbox(label="Source Language", placeholder="e.g., English", interactive=True)
                        target_lang = gr.Textbox(label="Target Language", placeholder="e.g., French", interactive=True)

                    selections.append((source_col, target_col, source_lang, target_lang))

                except Exception as e:
                    gr.Markdown(f"Error processing {file.name}: {str(e)}")

        # Update the global state with the new selections
        all_selections.value = selections

    submit_button = gr.Button("Submit & Process", interactive=True)
    output_text = gr.Markdown()

    submit_button.click(process_files, inputs=[file_input, all_selections], outputs=[output_text])

demo.launch()
1 Like

@Alanturner2 Did you test it. It does not work. ChatGPT proposes this solution but it is not workable. It looks like that the elements in the gr.state are not pickable!!

File β€œ/usr/lib/python3.10/copy.py”, line 231, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
File β€œ/usr/lib/python3.10/copy.py”, line 161, in deepcopy
rv = reductor(4)
TypeError: cannot pickle β€˜_thread.lock’ object

1 Like

like this? It’s not perfect yet.

        with gr.Column():
            for file in files:
                try:
                    if file.name.endswith(".csv"):
                        df = pd.read_csv(file.name)
                    elif file.name.endswith(".xlsx"):
                        df = pd.read_excel(file.name, engine="openpyxl")
                    else:
                        continue 

                    with gr.Row():
                        gr.Markdown(f"**{file.name}**")  # Display filename
                        source_col = gr.Dropdown(
                            df.columns.tolist(), label="Source Column", interactive=True)
                        target_col = gr.Dropdown(
                            df.columns.tolist(), label="Target Column", interactive=True)
                        source_lang = gr.Textbox(
                            label="Source Language", placeholder="e.g., English", interactive=True)
                        target_lang = gr.Textbox(
                            label="Target Language", placeholder="e.g., French", interactive=True)
                        
                    def append_sel(source_col, target_col, source_lang, target_lang, selections):
                        selections.append((source_col, target_col, source_lang, target_lang))
                        return selections

                    gr.on(triggers=[source_col.change, target_col.change, source_lang.change, target_lang.change],
                          fn=append_sel, inputs=[source_col, target_col, source_lang, target_lang, all_selections], outputs=[all_selections])

thanks @John6666. it still not perfect as gr.on() is trigeered on the first change of any element. if I insert just one Character in source_lang, it will trigger once and done.

1 Like

Finally! It is working! I still not fully understand the utility of gr.State() if it does not work in such scenario.

import gradio as gr
import pandas as pd


with gr.Blocks() as demo:
    gr.Markdown("## Upload Multiple Files and Select Columns")

    file_input = gr.File(file_types=[
                         ".csv", ".xlsx"], label="Drop your files here", interactive=True, file_count="multiple")

    @gr.render(inputs=[file_input], triggers=[file_input.change])
    def generate_column_selectors(files):
        selections = []
        with gr.Column():
            for file in files:
                try:
                    if file.name.endswith(".csv"):
                        df = pd.read_csv(file.name)
                    elif file.name.endswith(".xlsx"):
                        df = pd.read_excel(file.name, engine="openpyxl")
                    else:
                        continue

                    with gr.Row():
                        gr.Markdown(f"**{file.name}**")
                        source_col = gr.Dropdown(
                            df.columns.tolist(), label="Source Column", interactive=True)
                        target_col = gr.Dropdown(
                            df.columns.tolist(), label="Target Column", interactive=True)
                        source_lang = gr.Textbox(
                            label="Source Language", placeholder="e.g., English", interactive=True)
                        target_lang = gr.Textbox(
                            label="Target Language", placeholder="e.g., French", interactive=True)

                    selections.extend(
                        [source_col, target_col, source_lang, target_lang])

                except Exception as e:
                    gr.Markdown(f"Error processing {file.name}: {str(e)}")

            def process_files(*args):
                results = []
                args = [tuple(args[i:i + 4]) for i in range(0, len(args), 4)]
                out = ""
                for file, (source_col, target_col, source_lang, target_lang) in zip(files, args):
                    out = file + "::" + source_col + "::" + target_col + \
                        "::" + source_lang + "::" + target_lang
                    results.append(out)

                return "\n".join(results)

            submit_button.click(
                fn=process_files, inputs=selections, outputs=output_text)

    submit_button = gr.Button("Submit & Process", interactive=True)
    output_text = gr.Markdown()


demo.launch()
1 Like