Skip to content

Commit

Permalink
Allow showing progress updates on arbitrary components (#10492)
Browse files Browse the repository at this point in the history
* changes

* changes

* add changeset

* changes

* changes

* add changeset

* add changeset

* format

* typing

* changes

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Ali Abid <aliabid94@gmail.com>
  • Loading branch information
3 people authored Feb 6, 2025
1 parent 0c62e3e commit 29880d5
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 6 deletions.
7 changes: 7 additions & 0 deletions .changeset/neat-cobras-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@gradio/client": minor
"@gradio/core": minor
"gradio": minor
---

feat:Allow showing progress updates on arbitrary components
1 change: 1 addition & 0 deletions client/js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ export interface Dependency {
trigger: "click" | "load" | string;
max_batch_size: number;
show_progress: "full" | "minimal" | "hidden";
show_progress_on: number[] | null;
frontend_fn: ((...args: unknown[]) => Promise<unknown[]>) | null;
status?: string;
queue: boolean | null;
Expand Down
2 changes: 1 addition & 1 deletion demo/cancel_events/run.ipynb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: cancel_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import time\n", "import gradio as gr\n", "import atexit\n", "import pathlib\n", "\n", "log_file = pathlib.Path(__file__).parent / \"cancel_events_output_log.txt\"\n", "\n", "def fake_diffusion(steps):\n", " log_file.write_text(\"\")\n", " for i in range(steps):\n", " print(f\"Current step: {i}\")\n", " with log_file.open(\"a\") as f:\n", " f.write(f\"Current step: {i}\\n\")\n", " time.sleep(0.2)\n", " yield str(i)\n", "\n", "def long_prediction(*args, **kwargs):\n", " time.sleep(10)\n", " return 42\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " n = gr.Slider(1, 10, value=9, step=1, label=\"Number Steps\")\n", " run = gr.Button(value=\"Start Iterating\")\n", " output = gr.Textbox(label=\"Iterative Output\")\n", " stop = gr.Button(value=\"Stop Iterating\")\n", " with gr.Column():\n", " textbox = gr.Textbox(label=\"Prompt\")\n", " prediction = gr.Number(label=\"Expensive Calculation\")\n", " run_pred = gr.Button(value=\"Run Expensive Calculation\")\n", " with gr.Column():\n", " cancel_on_change = gr.Textbox(\n", " label=\"Cancel Iteration and Expensive Calculation on Change\"\n", " )\n", " cancel_on_submit = gr.Textbox(\n", " label=\"Cancel Iteration and Expensive Calculation on Submit\"\n", " )\n", " echo = gr.Textbox(label=\"Echo\")\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.Image(\n", " sources=[\"webcam\"], label=\"Cancel on clear\", interactive=True\n", " )\n", " with gr.Column():\n", " video = gr.Video(\n", " sources=[\"webcam\"], label=\"Cancel on start recording\", interactive=True\n", " )\n", "\n", " click_event = run.click(fake_diffusion, n, output)\n", " stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])\n", " pred_event = run_pred.click(\n", " fn=long_prediction, inputs=[textbox], outputs=prediction\n", " )\n", "\n", " cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])\n", " cancel_on_submit.submit(\n", " lambda s: s, cancel_on_submit, echo, cancels=[click_event, pred_event]\n", " )\n", " image.clear(None, None, None, cancels=[click_event, pred_event])\n", " video.start_recording(None, None, None, cancels=[click_event, pred_event])\n", "\n", " demo.queue(max_size=20)\n", " atexit.register(lambda: log_file.unlink())\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: cancel_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import time\n", "import gradio as gr\n", "import atexit\n", "import pathlib\n", "\n", "log_file = pathlib.Path(__file__).parent / \"cancel_events_output_log.txt\"\n", "\n", "def fake_diffusion(steps):\n", " log_file.write_text(\"\")\n", " for i in range(steps):\n", " print(f\"Current step: {i}\")\n", " with log_file.open(\"a\") as f:\n", " f.write(f\"Current step: {i}\\n\")\n", " time.sleep(0.2)\n", " yield str(i)\n", "\n", "def long_prediction(*args, **kwargs):\n", " time.sleep(4)\n", " return 42, 42\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " n = gr.Slider(1, 10, value=9, step=1, label=\"Number Steps\")\n", " run = gr.Button(value=\"Start Iterating\")\n", " output = gr.Textbox(label=\"Iterative Output\")\n", " stop = gr.Button(value=\"Stop Iterating\")\n", " with gr.Column():\n", " textbox = gr.Textbox(label=\"Prompt\")\n", " loading_box = gr.Textbox(label=\"Loading indicator for expensive calculation\")\n", " loading_box2 = gr.Textbox(label=\"Loading indicator for expensive calculation\")\n", " prediction = gr.Number(label=\"Expensive Calculation\")\n", " prediction2 = gr.Number(label=\"Expensive Calculation\")\n", " run_pred = gr.Button(value=\"Run Expensive Calculation\")\n", " with gr.Column():\n", " cancel_on_change = gr.Textbox(\n", " label=\"Cancel Iteration and Expensive Calculation on Change\"\n", " )\n", " cancel_on_submit = gr.Textbox(\n", " label=\"Cancel Iteration and Expensive Calculation on Submit\"\n", " )\n", " echo = gr.Textbox(label=\"Echo\")\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.Image(\n", " sources=[\"webcam\"], label=\"Cancel on clear\", interactive=True\n", " )\n", " with gr.Column():\n", " video = gr.Video(\n", " sources=[\"webcam\"], label=\"Cancel on start recording\", interactive=True\n", " )\n", "\n", " click_event = run.click(fake_diffusion, n, output)\n", " stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])\n", " pred_event = run_pred.click(\n", " fn=long_prediction, inputs=[textbox], outputs=[prediction, prediction2], show_progress_on=[loading_box, loading_box2]\n", " )\n", "\n", " cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])\n", " cancel_on_submit.submit(\n", " lambda s: s, cancel_on_submit, echo, cancels=[click_event, pred_event]\n", " )\n", " image.clear(None, None, None, cancels=[click_event, pred_event])\n", " video.start_recording(None, None, None, cancels=[click_event, pred_event])\n", "\n", " demo.queue(max_size=20)\n", " atexit.register(lambda: log_file.unlink())\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
9 changes: 6 additions & 3 deletions demo/cancel_events/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def fake_diffusion(steps):
yield str(i)

def long_prediction(*args, **kwargs):
time.sleep(10)
return 42
time.sleep(4)
return 42, 42

with gr.Blocks() as demo:
with gr.Row():
Expand All @@ -27,7 +27,10 @@ def long_prediction(*args, **kwargs):
stop = gr.Button(value="Stop Iterating")
with gr.Column():
textbox = gr.Textbox(label="Prompt")
loading_box = gr.Textbox(label="Loading indicator for expensive calculation")
loading_box2 = gr.Textbox(label="Loading indicator for expensive calculation")
prediction = gr.Number(label="Expensive Calculation")
prediction2 = gr.Number(label="Expensive Calculation")
run_pred = gr.Button(value="Run Expensive Calculation")
with gr.Column():
cancel_on_change = gr.Textbox(
Expand All @@ -50,7 +53,7 @@ def long_prediction(*args, **kwargs):
click_event = run.click(fake_diffusion, n, output)
stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])
pred_event = run_pred.click(
fn=long_prediction, inputs=[textbox], outputs=prediction
fn=long_prediction, inputs=[textbox], outputs=[prediction, prediction2], show_progress_on=[loading_box, loading_box2]
)

cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])
Expand Down
10 changes: 10 additions & 0 deletions gradio/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ def __init__(
api_name: str | Literal[False] = False,
js: str | None = None,
show_progress: Literal["full", "minimal", "hidden"] = "full",
show_progress_on: Sequence[Component] | None = None,
cancels: list[int] | None = None,
collects_event_data: bool = False,
trigger_after: int | None = None,
Expand Down Expand Up @@ -548,6 +549,7 @@ def __init__(
self.api_name = api_name
self.js = js
self.show_progress = show_progress
self.show_progress_on = show_progress_on
self.cancels = cancels or []
self.collects_event_data = collects_event_data
self.trigger_after = trigger_after
Expand Down Expand Up @@ -605,6 +607,9 @@ def get_config(self):
"api_name": self.api_name,
"scroll_to_output": self.scroll_to_output,
"show_progress": self.show_progress,
"show_progress_on": None
if self.show_progress_on is None
else [block._id for block in self.show_progress_on],
"batch": self.batch,
"max_batch_size": self.max_batch_size,
"cancels": self.cancels,
Expand Down Expand Up @@ -708,6 +713,7 @@ def set_event_trigger(
postprocess: bool = True,
scroll_to_output: bool = False,
show_progress: Literal["full", "minimal", "hidden"] = "full",
show_progress_on: Component | Sequence[Component] | None = None,
api_name: str | None | Literal[False] = None,
js: str | None = None,
no_target: bool = False,
Expand Down Expand Up @@ -741,6 +747,7 @@ def set_event_trigger(
postprocess: whether to run the postprocess methods of the output components after running the function
scroll_to_output: whether to scroll to output of dependency on trigger
show_progress: how to show the progress animation while event is running: "full" shows a spinner which covers the output component area as well as a runtime display in the upper right corner, "minimal" only shows the runtime display, "hidden" shows no progress animation at all
show_progress_on: Component or list of components to show the progress animation on. If None, will show the progress animation on all of the output components.
api_name: defines how the endpoint appears in the API docs. Can be a string, None, or False. If set to a string, the endpoint will be exposed in the API docs with the given name. If None (default), the name of the function will be used as the API endpoint. If False, the endpoint will not be exposed in the API docs and downstream apps (including those that `gr.load` this app) will not be able to use this event.
js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components
no_target: if True, sets "targets" to [], used for the Blocks.load() event and .then() events
Expand Down Expand Up @@ -785,6 +792,8 @@ def set_event_trigger(
outputs = []
elif not isinstance(outputs, Sequence):
outputs = [outputs]
if show_progress_on and not isinstance(show_progress_on, Sequence):
show_progress_on = [show_progress_on]

if fn is not None and not cancels:
check_function_inputs_match(fn, inputs, inputs_as_dict)
Expand Down Expand Up @@ -863,6 +872,7 @@ def set_event_trigger(
api_name=api_name,
js=js,
show_progress=show_progress,
show_progress_on=show_progress_on,
cancels=cancels,
collects_event_data=collects_event_data,
trigger_after=trigger_after,
Expand Down
2 changes: 2 additions & 0 deletions gradio/component_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def {{ event.event_name }}(self,
api_name: str | None | Literal[False] = None,
scroll_to_output: bool = False,
show_progress: Literal["full", "minimal", "hidden"] = "full",
show_progress_on: Component | Sequence[Component] | None = None,
queue: bool | None = None,
batch: bool = False,
max_batch_size: int = 4,
Expand All @@ -51,6 +52,7 @@ def {{ event.event_name }}(self,
api_name: defines how the endpoint appears in the API docs. Can be a string, None, or False. If False, the endpoint will not be exposed in the api docs. If set to None, will use the functions name as the endpoint route. If set to a string, the endpoint will be exposed in the api docs with the given name.
scroll_to_output: if True, will scroll to output component on completion
show_progress: how to show the progress animation while event is running: "full" shows a spinner which covers the output component area as well as a runtime display in the upper right corner, "minimal" only shows the runtime display, "hidden" shows no progress animation at all
show_progress_on: Component or list of components to show the progress animation on. If None, will show the progress animation on all of the output components.
queue: if True, will place the request on the queue, if the queue has been enabled. If False, will not put this event on the queue, even if the queue has been enabled. If None, will use the queue setting of the gradio app.
batch: if True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. The lists should be of equal length (and be up to length `max_batch_size`). The function is then *required* to return a tuple of lists (even if there is only 1 output component), with each list in the tuple corresponding to one output component.
max_batch_size: maximum number of inputs to batch together if this is called from the queue (only relevant if batch=True)
Expand Down
Loading

0 comments on commit 29880d5

Please sign in to comment.