diff --git a/.changeset/sixty-impalas-bathe.md b/.changeset/sixty-impalas-bathe.md new file mode 100644 index 0000000000000..d04a93a42e1b5 --- /dev/null +++ b/.changeset/sixty-impalas-bathe.md @@ -0,0 +1,6 @@ +--- +"@gradio/dataframe": patch +"gradio": patch +--- + +fix:Fix event triggers and recent regressions related to `gr.DataFrame` diff --git a/demo/dataframe_streaming/run.ipynb b/demo/dataframe_streaming/run.ipynb index 1079464f9d88c..0be32c2ab0ddf 100644 --- a/demo/dataframe_streaming/run.ipynb +++ b/demo/dataframe_streaming/run.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: dataframe_streaming"]}, {"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 pandas as pd\n", "import time\n", "\n", "def update_dataframe(df):\n", " df.iloc[:, :] = 1\n", " yield df, 1\n", " time.sleep(0.1)\n", " df.iloc[:, :] = 2\n", " yield df, 2\n", "\n", "initial_df = pd.DataFrame(0, index=range(5), columns=range(5))\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " button = gr.Button(\"Update DataFrame\")\n", " number = gr.Number(value=0, label=\"Number\")\n", " dataframe = gr.Dataframe(value=initial_df, label=\"Dataframe\")\n", " button.click(fn=update_dataframe, inputs=dataframe, outputs=[dataframe, number])\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: dataframe_streaming"]}, {"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 pandas as pd\n", "import time\n", "\n", "def update_dataframe(df):\n", " df.iloc[:, :] = 1\n", " yield df, 1\n", " time.sleep(0.1)\n", " df.iloc[:, :] = 2\n", " yield df, 2\n", "\n", "initial_df = pd.DataFrame(0, index=range(5), columns=range(5))\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " button = gr.Button(\"Update DataFrame\")\n", " number = gr.Number(value=0, label=\"Number\")\n", " dataframe = gr.Dataframe(value=initial_df, label=\"Dataframe\")\n", " button.click(fn=update_dataframe, inputs=dataframe, outputs=[dataframe, number])\n", " with gr.Row():\n", " change_events = gr.Number(label=\"Change events\")\n", " input_events = gr.Number(label=\"Input events\")\n", "\n", " dataframe.change(lambda x:x+1, inputs=change_events, outputs=change_events)\n", " dataframe.input(lambda x:x+1, inputs=input_events, outputs=input_events)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/dataframe_streaming/run.py b/demo/dataframe_streaming/run.py index d92e597d34554..24ea58503ee64 100644 --- a/demo/dataframe_streaming/run.py +++ b/demo/dataframe_streaming/run.py @@ -17,6 +17,12 @@ def update_dataframe(df): number = gr.Number(value=0, label="Number") dataframe = gr.Dataframe(value=initial_df, label="Dataframe") button.click(fn=update_dataframe, inputs=dataframe, outputs=[dataframe, number]) + with gr.Row(): + change_events = gr.Number(label="Change events") + input_events = gr.Number(label="Input events") + + dataframe.change(lambda x:x+1, inputs=change_events, outputs=change_events) + dataframe.input(lambda x:x+1, inputs=input_events, outputs=input_events) if __name__ == "__main__": demo.launch() diff --git a/js/dataframe/Index.svelte b/js/dataframe/Index.svelte index 531d2f24b9d6e..6ef6c75d7468a 100644 --- a/js/dataframe/Index.svelte +++ b/js/dataframe/Index.svelte @@ -88,6 +88,7 @@ {styling} headers={_headers} on:change={(e) => gradio.dispatch("change")} + on:input={(e) => gradio.dispatch("input")} on:select={(e) => gradio.dispatch("select", e.detail)} {wrap} {datatype} diff --git a/js/dataframe/shared/Table.svelte b/js/dataframe/shared/Table.svelte index d055b2ef34ffe..4c9bd4017aa8c 100644 --- a/js/dataframe/shared/Table.svelte +++ b/js/dataframe/shared/Table.svelte @@ -2,7 +2,6 @@ import { afterUpdate, createEventDispatcher, tick, onMount } from "svelte"; import { dsvFormat } from "d3-dsv"; import { dequal } from "dequal/lite"; - import { copy } from "@gradio/utils"; import { Upload } from "@gradio/upload"; import EditableCell from "./EditableCell.svelte"; @@ -144,34 +143,44 @@ } let _headers = make_headers(headers); - let old_headers: string[] | undefined; + let old_headers: string[] = headers; $: { if (!dequal(headers, old_headers)) { - trigger_headers(); + _headers = make_headers(headers); + old_headers = JSON.parse(JSON.stringify(headers)); } } - function trigger_headers(): void { - _headers = make_headers(headers); - - old_headers = headers.slice(); - trigger_change(); - } + let data: { id: string; value: string | number }[][] = [[]]; + let old_val: undefined | (string | number)[][] = undefined; $: if (!dequal(values, old_val)) { data = process_data(values as (string | number)[][]); old_val = JSON.parse(JSON.stringify(values)) as (string | number)[][]; } - let data: { id: string; value: string | number }[][] = [[]]; - - let old_val: undefined | (string | number)[][] = undefined; + let previous_headers_string = JSON.stringify(_headers.map((h) => h.value)); + let previous_data_string = JSON.stringify( + data.map((row) => row.map((cell) => String(cell.value))) + ); async function trigger_change(): Promise { - dispatch("change"); - if (!value_is_output) { - dispatch("input"); + const current_headers_string = JSON.stringify(_headers.map((h) => h.value)); + const current_data_string = JSON.stringify( + data.map((row) => row.map((cell) => String(cell.value))) + ); + + if ( + current_data_string !== previous_data_string || + current_headers_string !== previous_headers_string + ) { + dispatch("change"); + if (!value_is_output) { + dispatch("input"); + } + previous_data_string = current_data_string; + previous_headers_string = current_headers_string; } } @@ -414,7 +423,7 @@ selected = [index !== undefined ? index : data.length - 1, 0]; } - $: (data || selected_header) && trigger_change(); + $: (data || _headers) && trigger_change(); async function add_col(index?: number): Promise { parent.focus(); @@ -639,8 +648,18 @@ observer.observe(parent); + document.addEventListener("click", handle_click_outside); + window.addEventListener("resize", handle_resize); + document.addEventListener("fullscreenchange", handle_fullscreen_change); + return () => { observer.disconnect(); + document.removeEventListener("click", handle_click_outside); + window.removeEventListener("resize", handle_resize); + document.removeEventListener( + "fullscreenchange", + handle_fullscreen_change + ); }; }); @@ -695,20 +714,6 @@ set_cell_widths(); } - onMount(() => { - document.addEventListener("click", handle_click_outside); - window.addEventListener("resize", handle_resize); - document.addEventListener("fullscreenchange", handle_fullscreen_change); - return () => { - document.removeEventListener("click", handle_click_outside); - window.removeEventListener("resize", handle_resize); - document.removeEventListener( - "fullscreenchange", - handle_fullscreen_change - ); - }; - }); - let active_button: { type: "header" | "cell"; row?: number; @@ -969,6 +974,8 @@ clear_on_focus = false; clicked_cell = { row: index, col: j }; selected = [index, j]; + selected_header = false; + header_edit = false; if (editable) { editing = [index, j]; } @@ -986,6 +993,8 @@ active_header_menu = null; clicked_cell = { row: index, col: j }; selected = [index, j]; + selected_header = false; + header_edit = false; if (editable) { editing = [index, j]; } diff --git a/js/spa/test/dataframe_streaming.spec.ts b/js/spa/test/dataframe_streaming.spec.ts index 54fa8fd3a7115..8eddac7402cb2 100644 --- a/js/spa/test/dataframe_streaming.spec.ts +++ b/js/spa/test/dataframe_streaming.spec.ts @@ -1,8 +1,14 @@ import { test, expect } from "@self/tootils"; -test("DataFrame updates correctly when button is clicked", async ({ page }) => { +test("DataFrame updates and events are tracked correctly", async ({ page }) => { + await expect(page.getByLabel("Change events")).toHaveValue("0"); + await expect(page.getByLabel("Input events")).toHaveValue("0"); + await page.getByRole("button", { name: "Update DataFrame" }).click(); await expect( page.getByRole("table", { name: "Dataframe" }).locator("td").first() ).toHaveText("2"); + + await expect(page.getByLabel("Change events")).toHaveValue("2"); + await expect(page.getByLabel("Input events")).toHaveValue("0"); });