-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Render decorator documentation #8409
Changes from all commits
eafb3d1
91d1092
9bf48e8
95a41ef
dc59442
dad77ff
0fd8250
e8e9983
23b21ac
d6db975
4997e3e
2954602
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@gradio/statustracker": minor | ||
"gradio": minor | ||
"gradio_client": minor | ||
--- | ||
|
||
feat:Render decorator documentation |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: audio_mixer"]}, {"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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " track_count = gr.State(1)\n", " add_track_btn = gr.Button(\"Add Track\")\n", "\n", " @gr.render(inputs=track_count)\n", " def render_tracks(count):\n", " tracks = []\n", " with gr.Row():\n", " for i in range(count):\n", " with gr.Column(variant=\"panel\", scale=0):\n", " track_name = gr.Textbox(placeholder=\"Track Name\", key=f\"name-{i}\", show_label=False)\n", " track_audio = gr.Audio(label=f\"Track {i}\", key=f\"track-{i}\")\n", " track_volume = gr.Slider(0, 1, step=0.01, label=\"Volume\", key=f\"volume-{i}\")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import gradio as gr | ||
|
||
with gr.Blocks() as demo: | ||
track_count = gr.State(1) | ||
add_track_btn = gr.Button("Add Track") | ||
|
||
@gr.render(inputs=track_count) | ||
def render_tracks(count): | ||
tracks = [] | ||
with gr.Row(): | ||
for i in range(count): | ||
with gr.Column(variant="panel", scale=0): | ||
track_name = gr.Textbox(placeholder="Track Name", key=f"name-{i}", show_label=False) | ||
track_audio = gr.Audio(label=f"Track {i}", key=f"track-{i}") | ||
track_volume = gr.Slider(0, 1, step=0.01, label="Volume", key=f"volume-{i}") | ||
|
||
if __name__ == "__main__": | ||
demo.launch() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: render_merge"]}, {"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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " text_count = gr.Slider(1, 5, step=1, label=\"Textbox Count\")\n", "\n", " @gr.render(inputs=text_count)\n", " def render_count(count):\n", " boxes = []\n", " for i in range(count):\n", " box = gr.Textbox(key=i, label=f\"Box {i}\")\n", " boxes.append(box)\n", "\n", " def merge(*args):\n", " return \" \".join(args)\n", " \n", " merge_btn.click(merge, boxes, output)\n", "\n", " def clear():\n", " return [\"\"] * count\n", " \n", " clear_btn.click(clear, None, boxes)\n", "\n", " def countup():\n", " return [i for i in range(count)]\n", " \n", " count_btn.click(countup, None, boxes, queue=False)\n", "\n", " with gr.Row():\n", " merge_btn = gr.Button(\"Merge\")\n", " clear_btn = gr.Button(\"Clear\")\n", " count_btn = gr.Button(\"Count\")\n", " \n", " output = gr.Textbox()\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: render_merge"]}, {"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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " text_count = gr.Slider(1, 5, step=1, label=\"Textbox Count\")\n", "\n", " @gr.render(inputs=text_count)\n", " def render_count(count):\n", " boxes = []\n", " for i in range(count):\n", " box = gr.Textbox(key=i, label=f\"Box {i}\") \n", " boxes.append(box)\n", "\n", " def merge(*args):\n", " return \" \".join(args)\n", " \n", " merge_btn.click(merge, boxes, output)\n", "\n", " def clear():\n", " return [\"\"] * count\n", " \n", " clear_btn.click(clear, None, boxes)\n", "\n", " def countup():\n", " return [i for i in range(count)]\n", " \n", " count_btn.click(countup, None, boxes, queue=False)\n", "\n", " with gr.Row():\n", " merge_btn = gr.Button(\"Merge\")\n", " clear_btn = gr.Button(\"Clear\")\n", " count_btn = gr.Button(\"Count\")\n", " \n", " output = gr.Textbox()\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: render_merge_simple"]}, {"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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " text_count = gr.State(1)\n", " add_btn = gr.Button(\"Add Box\")\n", " add_btn.click(lambda x: x + 1, text_count, text_count)\n", "\n", " @gr.render(inputs=text_count)\n", " def render_count(count):\n", " boxes = []\n", " for i in range(count):\n", " box = gr.Textbox(key=i, label=f\"Box {i}\")\n", " boxes.append(box)\n", "\n", " def merge(*args):\n", " return \" \".join(args)\n", " \n", " merge_btn.click(merge, boxes, output)\n", "\n", "\n", " merge_btn = gr.Button(\"Merge\")\n", " output = gr.Textbox(label=\"Merged Output\")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import gradio as gr | ||
|
||
with gr.Blocks() as demo: | ||
text_count = gr.State(1) | ||
add_btn = gr.Button("Add Box") | ||
add_btn.click(lambda x: x + 1, text_count, text_count) | ||
|
||
@gr.render(inputs=text_count) | ||
def render_count(count): | ||
boxes = [] | ||
for i in range(count): | ||
box = gr.Textbox(key=i, label=f"Box {i}") | ||
boxes.append(box) | ||
|
||
def merge(*args): | ||
return " ".join(args) | ||
|
||
merge_btn.click(merge, boxes, output) | ||
|
||
|
||
merge_btn = gr.Button("Merge") | ||
output = gr.Textbox(label="Merged Output") | ||
|
||
if __name__ == "__main__": | ||
demo.launch() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: render_split_simple"]}, {"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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " input_text = gr.Textbox(label=\"input\")\n", "\n", " @gr.render(inputs=input_text)\n", " def show_split(text):\n", " if len(text) == 0:\n", " gr.Markdown(\"## No Input Provided\")\n", " else:\n", " for letter in text:\n", " gr.Textbox(letter)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import gradio as gr | ||
|
||
with gr.Blocks() as demo: | ||
input_text = gr.Textbox(label="input") | ||
|
||
@gr.render(inputs=input_text) | ||
def show_split(text): | ||
if len(text) == 0: | ||
gr.Markdown("## No Input Provided") | ||
else: | ||
for letter in text: | ||
gr.Textbox(letter) | ||
|
||
if __name__ == "__main__": | ||
demo.launch() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Dynamic Apps with the Render Decorator | ||
|
||
The components and event listeners you define in a Blocks so far have been fixed - once the demo was launched, new components and listeners could not be added, and existing one could not be removed. | ||
|
||
The `@gr.render` decorator introduces the ability to dynamically change this. Let's take a look. | ||
|
||
## Dynamic Number of Components | ||
|
||
In the example below, we will create a variable number of Textboxes. When the user edits the input Textbox, we create a Textbox for each letter in the input. Try it out below: | ||
|
||
$code_render_split_simple | ||
$demo_render_split_simple | ||
|
||
See how we can now create a variable number of Textboxes using our custom logic - in this case, a simple `for` loop. The `@gr.render` decorator enables this with the following steps: | ||
|
||
1. Create a function and attach the @gr.render decorator to it. | ||
2. Add the input components to the `inputs=` argument of @gr.render, and create a corresponding argument in your function for each component. This function will automatically re-run on any change to a component. | ||
3. Add all components inside the function that you want to render based on the inputs. | ||
|
||
Now whenever the inputs change, the funciton re-runs, and replaces the components created from the previous funciton run with the latest run. Pretty straightforward! Let's add a little more complexity to this app: | ||
|
||
$code_render_split | ||
$demo_render_split | ||
|
||
By default, `@gr.render` re-runs are triggered by the `.load` listener to the app and the `.change` listener to any input component provided. We can override this by explicitly setting the triggers in the decorator, as we have in this app to only trigger on `input_text.submit` instead. | ||
If you are setting custom triggers, and you also want an automatic render at the start of the app, make sure to add `demo.load` to your list of triggers. | ||
|
||
## Dynamic Event Listeners | ||
|
||
If you're creating components, you probably want to attach event listeners to them as well. Let's take a look at an example that takes in a variable number of Textbox as input, and merges all the text into a single box. | ||
|
||
$code_render_merge_simple | ||
$demo_render_merge_simple | ||
|
||
Let's take a look at what's happening here: | ||
|
||
1) The state variable `text_count` is keeping track of the number of Textboxes to create. By increasing it via the Add button, we trigger re-renders as `text_count` is an input to the render decorator. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This second sentence is a hard to read. Maybe something like
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'll fix in website pr |
||
2) Note that in every single Textbox we create in the render function, we explicitly set a `key=` argument. This key allows us to preserve the value of this Component between re-renders. If you type in a value in a textbox, and then click the Add button, all the Textboxes re-render, but their values aren't cleared because the `key=` maintains the the value of a Component across a render. | ||
3) We've stored the Textboxes created in a list, and provide this list as input to the merge button event listener. Note that **all event listeners that use Components created inside a render function must also be defined inside that render function**. The event listener can still reference Components outside the render function, as we do here by referencing `merge_btn` and `output` which are both defined outside the render function. | ||
|
||
Just as with Components, whenever a function re-renders, the event listeners created from the previous render are cleared and the new event listeners from the latest run are attached. | ||
|
||
This allows us to create highly customizable and complex interactions! Take a look at the example below, which spices up the previous example with a lot more event listeners: | ||
|
||
$code_render_merge | ||
$demo_render_merge |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo:
funciton