Skip to content

Commit

Permalink
state.change listener with deep hash check (#8446)
Browse files Browse the repository at this point in the history
* changes

* changes

* add changeset

* changes

* changes

* changes

* changes

* Update guides/04_building-with-blocks/04_dynamic-apps-with-render-decorator.md

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

---------

Co-authored-by: Ali Abid <aliabid94@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
  • Loading branch information
4 people committed Jun 4, 2024
1 parent 33c8081 commit 4a55157
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-lamps-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gradio": minor
---

feat:state.change listener with deep hash check
2 changes: 1 addition & 1 deletion demo/audio_mixer/run.ipynb
Original file line number Diff line number Diff line change
@@ -1 +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", "import numpy as np\n", "\n", "with gr.Blocks() as demo:\n", " track_count = gr.State(1)\n", " add_track_btn = gr.Button(\"Add Track\")\n", "\n", " add_track_btn.click(lambda count: count + 1, track_count, track_count)\n", "\n", " @gr.render(inputs=track_count)\n", " def render_tracks(count):\n", " audios = []\n", " volumes = []\n", " with gr.Row():\n", " for i in range(count):\n", " with gr.Column(variant=\"panel\", min_width=200):\n", " 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, 100, value=100, label=\"Volume\", key=f\"volume-{i}\")\n", " audios.append(track_audio)\n", " volumes.append(track_volume)\n", "\n", " def merge(data):\n", " sr, output = None, None\n", " for audio, volume in zip(audios, volumes):\n", " sr, audio_val = data[audio]\n", " volume_val = data[volume]\n", " final_track = audio_val * (volume_val / 100)\n", " if output is None:\n", " output = final_track\n", " else:\n", " min_shape = tuple(min(s1, s2) for s1, s2 in zip(output.shape, final_track.shape))\n", " trimmed_output = output[:min_shape[0], ...][:, :min_shape[1], ...] if output.ndim > 1 else output[:min_shape[0]]\n", " trimmed_final = final_track[:min_shape[0], ...][:, :final_track[1], ...] if final_track.ndim > 1 else final_track[:min_shape[0]]\n", " output += trimmed_output + trimmed_final\n", " return (sr, output)\n", " \n", " merge_btn.click(merge, set(audios + volumes), output_audio)\n", "\n", " merge_btn = gr.Button(\"Merge Tracks\")\n", " output_audio = gr.Audio(label=\"Output\")\n", " \n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"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", "import numpy as np\n", "\n", "with gr.Blocks() as demo:\n", " track_count = gr.State(1)\n", " add_track_btn = gr.Button(\"Add Track\")\n", "\n", " add_track_btn.click(lambda count: count + 1, track_count, track_count)\n", "\n", " @gr.render(inputs=track_count)\n", " def render_tracks(count):\n", " audios = []\n", " volumes = []\n", " with gr.Row():\n", " for i in range(count):\n", " with gr.Column(variant=\"panel\", min_width=200):\n", " 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, 100, value=100, label=\"Volume\", key=f\"volume-{i}\")\n", " audios.append(track_audio)\n", " volumes.append(track_volume)\n", "\n", " def merge(data):\n", " sr, output = None, None\n", " for audio, volume in zip(audios, volumes):\n", " sr, audio_val = data[audio]\n", " volume_val = data[volume]\n", " final_track = audio_val * (volume_val / 100)\n", " if output is None:\n", " output = final_track\n", " else:\n", " min_shape = tuple(min(s1, s2) for s1, s2 in zip(output.shape, final_track.shape))\n", " trimmed_output = output[:min_shape[0], ...][:, :min_shape[1], ...] if output.ndim > 1 else output[:min_shape[0]]\n", " trimmed_final = final_track[:min_shape[0], ...][:, :final_track[1], ...] if final_track.ndim > 1 else final_track[:min_shape[0]]\n", " output += trimmed_output + trimmed_final\n", " return (sr, output)\n", " \n", " merge_btn.click(merge, set(audios + volumes), output_audio)\n", "\n", " merge_btn = gr.Button(\"Merge Tracks\")\n", " output_audio = gr.Audio(label=\"Output\", interactive=False)\n", " \n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
2 changes: 1 addition & 1 deletion demo/audio_mixer/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def merge(data):
merge_btn.click(merge, set(audios + volumes), output_audio)

merge_btn = gr.Button("Merge Tracks")
output_audio = gr.Audio(label="Output")
output_audio = gr.Audio(label="Output", interactive=False)

if __name__ == "__main__":
demo.launch()
2 changes: 1 addition & 1 deletion demo/state_change/run.ipynb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: state_change"]}, {"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", "\n", " with gr.Row():\n", " state_a = gr.State(0)\n", " btn_a = gr.Button(\"Increment A\")\n", " value_a = gr.Number(label=\"A\")\n", " btn_a.click(lambda x: x+1, state_a, state_a)\n", " state_a.change(lambda x: x, state_a, value_a)\n", " with gr.Row():\n", " state_b = gr.State(0)\n", " btn_b = gr.Button(\"Increment B\")\n", " value_b = gr.Number(label=\"num\")\n", " btn_b.click(lambda x: x+1, state_b, state_b)\n", "\n", " @gr.on(inputs=state_b, outputs=value_b)\n", " def identity(x):\n", " return x\n", "\n", " @gr.render(inputs=[state_a, state_b])\n", " def render(a, b):\n", " for x in range(a):\n", " with gr.Row():\n", " for y in range(b):\n", " gr.Button(f\"Button {x}, {y}\")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: state_change"]}, {"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", "\n", " with gr.Row():\n", " state_a = gr.State(0)\n", " btn_a = gr.Button(\"Increment A\")\n", " value_a = gr.Number(label=\"A\")\n", " btn_a.click(lambda x: x+1, state_a, state_a)\n", " state_a.change(lambda x: x, state_a, value_a)\n", " with gr.Row():\n", " state_b = gr.State(0)\n", " btn_b = gr.Button(\"Increment B\")\n", " value_b = gr.Number(label=\"num\")\n", " btn_b.click(lambda x: x+1, state_b, state_b)\n", "\n", " @gr.on(inputs=state_b, outputs=value_b)\n", " def identity(x):\n", " return x\n", "\n", " @gr.render(inputs=[state_a, state_b])\n", " def render(a, b):\n", " for x in range(a):\n", " with gr.Row():\n", " for y in range(b):\n", " gr.Button(f\"Button {x}, {y}\")\n", "\n", " list_state = gr.State([])\n", " dict_state = gr.State(dict())\n", " nested_list_state = gr.State([])\n", " set_state = gr.State(set())\n", "\n", " def transform_list(x):\n", " return {n: n for n in x}, [x[:] for _ in range(len(x))], set(x)\n", " \n", " list_state.change(\n", " transform_list,\n", " inputs=list_state,\n", " outputs=[dict_state, nested_list_state, set_state],\n", " )\n", "\n", " all_textbox = gr.Textbox(label=\"Output\")\n", " change_count = gr.Number(label=\"Changes\")\n", " gr.on(\n", " inputs=[change_count, dict_state, nested_list_state, set_state],\n", " triggers=[dict_state.change, nested_list_state.change, set_state.change],\n", " fn=lambda x, *args: (x+1, \"\\n\".join(str(arg) for arg in args)),\n", " outputs=[change_count, all_textbox],\n", " )\n", "\n", " count_to_3_btn = gr.Button(\"Count to 3\")\n", " count_to_3_btn.click(lambda: [1, 2, 3], outputs=list_state)\n", " zero_all_btn = gr.Button(\"Zero All\")\n", " zero_all_btn.click(\n", " lambda x: [0] * len(x), inputs=list_state, outputs=list_state\n", " )\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
30 changes: 30 additions & 0 deletions demo/state_change/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,35 @@ def render(a, b):
for y in range(b):
gr.Button(f"Button {x}, {y}")

list_state = gr.State([])
dict_state = gr.State(dict())
nested_list_state = gr.State([])
set_state = gr.State(set())

def transform_list(x):
return {n: n for n in x}, [x[:] for _ in range(len(x))], set(x)

list_state.change(
transform_list,
inputs=list_state,
outputs=[dict_state, nested_list_state, set_state],
)

all_textbox = gr.Textbox(label="Output")
change_count = gr.Number(label="Changes")
gr.on(
inputs=[change_count, dict_state, nested_list_state, set_state],
triggers=[dict_state.change, nested_list_state.change, set_state.change],
fn=lambda x, *args: (x+1, "\n".join(str(arg) for arg in args)),
outputs=[change_count, all_textbox],
)

count_to_3_btn = gr.Button("Count to 3")
count_to_3_btn.click(lambda: [1, 2, 3], outputs=list_state)
zero_all_btn = gr.Button("Zero All")
zero_all_btn.click(
lambda x: [0] * len(x), inputs=list_state, outputs=list_state
)

if __name__ == "__main__":
demo.launch()
2 changes: 1 addition & 1 deletion demo/todo_list/run.ipynb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: todo_list"]}, {"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", " \n", " tasks = gr.State([])\n", " new_task = gr.Textbox(label=\"Task Name\", autofocus=True)\n", "\n", " def add_task(tasks, new_task_name):\n", " return tasks + [{\"name\": new_task_name, \"complete\": False}], \"\"\n", "\n", " new_task.submit(add_task, [tasks, new_task], [tasks, new_task])\n", "\n", " @gr.render(inputs=tasks)\n", " def render_todos(task_list):\n", " complete = [task for task in task_list if task[\"complete\"]]\n", " incomplete = [task for task in task_list if not task[\"complete\"]]\n", " gr.Markdown(f\"### Incomplete Tasks ({len(incomplete)})\")\n", " for task in incomplete:\n", " with gr.Row():\n", " gr.Textbox(task['name'], show_label=False, container=False)\n", " done_btn = gr.Button(\"Done\", scale=0)\n", " def mark_done(task=task):\n", " _task_list = task_list[:]\n", " _task_list[task_list.index(task)] = {\"name\": task[\"name\"], \"complete\": True}\n", " return _task_list\n", " done_btn.click(mark_done, None, [tasks])\n", "\n", " delete_btn = gr.Button(\"Delete\", scale=0, variant=\"stop\")\n", " def delete(task=task):\n", " task_index = task_list.index(task)\n", " return task_list[:task_index] + task_list[task_index+1:]\n", " delete_btn.click(delete, None, [tasks])\n", "\n", " gr.Markdown(f\"### Complete Tasks ({len(complete)})\")\n", " for task in complete:\n", " gr.Textbox(task['name'], show_label=False, container=False)\n", "\n", "\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: todo_list"]}, {"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", " \n", " tasks = gr.State([])\n", " new_task = gr.Textbox(label=\"Task Name\", autofocus=True)\n", "\n", " def add_task(tasks, new_task_name):\n", " return tasks + [{\"name\": new_task_name, \"complete\": False}], \"\"\n", "\n", " new_task.submit(add_task, [tasks, new_task], [tasks, new_task])\n", "\n", " @gr.render(inputs=tasks)\n", " def render_todos(task_list):\n", " complete = [task for task in task_list if task[\"complete\"]]\n", " incomplete = [task for task in task_list if not task[\"complete\"]]\n", " gr.Markdown(f\"### Incomplete Tasks ({len(incomplete)})\")\n", " for task in incomplete:\n", " with gr.Row():\n", " gr.Textbox(task['name'], show_label=False, container=False)\n", " done_btn = gr.Button(\"Done\", scale=0)\n", " def mark_done(task=task):\n", " task[\"complete\"] = True\n", " return task_list\n", " done_btn.click(mark_done, None, [tasks])\n", "\n", " delete_btn = gr.Button(\"Delete\", scale=0, variant=\"stop\")\n", " def delete(task=task):\n", " task_list.remove(task)\n", " return task_list\n", " delete_btn.click(delete, None, [tasks])\n", "\n", " gr.Markdown(f\"### Complete Tasks ({len(complete)})\")\n", " for task in complete:\n", " gr.Textbox(task['name'], show_label=False, container=False)\n", "\n", "\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
9 changes: 4 additions & 5 deletions demo/todo_list/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ def render_todos(task_list):
gr.Textbox(task['name'], show_label=False, container=False)
done_btn = gr.Button("Done", scale=0)
def mark_done(task=task):
_task_list = task_list[:]
_task_list[task_list.index(task)] = {"name": task["name"], "complete": True}
return _task_list
task["complete"] = True
return task_list
done_btn.click(mark_done, None, [tasks])

delete_btn = gr.Button("Delete", scale=0, variant="stop")
def delete(task=task):
task_index = task_list.index(task)
return task_list[:task_index] + task_list[task_index+1:]
task_list.remove(task)
return task_list
delete_btn.click(delete, None, [tasks])

gr.Markdown(f"### Complete Tasks ({len(complete)})")
Expand Down
Loading

0 comments on commit 4a55157

Please sign in to comment.