Skip to content

Commit 96d8de2

Browse files
pngwngradio-pr-bot
andauthored
add delete event to File component (#8417)
* fix param name * format * add delete event to File component * fix * add changeset * fix * fix * fix * add changeset * format * add changeset * file file layout * add changeset --------- Co-authored-by: gradio-pr-bot <[email protected]>
1 parent 63d36fb commit 96d8de2

File tree

13 files changed

+189
-40
lines changed

13 files changed

+189
-40
lines changed

.changeset/full-flies-join.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@gradio/file": minor
3+
"@gradio/tootils": minor
4+
"@gradio/upload": minor
5+
"gradio": minor
6+
---
7+
8+
feat:add delete event to `File` component
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: file_component_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 gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " \n", " with gr.Row():\n", " with gr.Column():\n", " file_component = gr.File(label=\"Upload Single File\", file_count=\"single\")\n", " with gr.Column():\n", " output_file_1 = gr.File(label=\"Upload Single File Output\", file_count=\"single\")\n", " num_load_btn_1 = gr.Number(label=\"# Load Upload Single File\", value=0)\n", " file_component.upload(lambda s,n: (s, n + 1), [file_component, num_load_btn_1], [output_file_1, num_load_btn_1])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_multiple = gr.File(label=\"Upload Multiple Files\", file_count=\"multiple\")\n", " with gr.Column():\n", " output_file_2 = gr.File(label=\"Upload Multiple Files Output\", file_count=\"multiple\")\n", " num_load_btn_2 = gr.Number(label=\"# Load Upload Multiple Files\", value=0)\n", " file_component_multiple.upload(lambda s,n: (s, n + 1), [file_component_multiple, num_load_btn_2], [output_file_2, num_load_btn_2])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_specific = gr.File(label=\"Upload Multiple Files Image/Video\", file_count=\"multiple\", file_types=[\"image\", \"video\"])\n", " with gr.Column():\n", " output_file_3 = gr.File(label=\"Upload Multiple Files Output Image/Video\", file_count=\"multiple\")\n", " num_load_btn_3 = gr.Number(label=\"# Load Upload Multiple Files Image/Video\", value=0)\n", " file_component_specific.upload(lambda s,n: (s, n + 1), [file_component_specific, num_load_btn_3], [output_file_3, num_load_btn_3])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_pdf = gr.File(label=\"Upload PDF File\", file_types=[\"pdf\"])\n", " with gr.Column():\n", " output_file_4 = gr.File(label=\"Upload PDF File Output\")\n", " num_load_btn_4 = gr.Number(label=\"# Load Upload PDF File\", value=0)\n", " file_component_pdf.upload(lambda s,n: (s, n + 1), [file_component_pdf, num_load_btn_4], [output_file_4, num_load_btn_4])\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_invalid = gr.File(label=\"Upload File with Invalid file_types\", file_types=[\"invalid file_type\"])\n", " with gr.Column():\n", " output_file_5 = gr.File(label=\"Upload File with Invalid file_types Output\")\n", " num_load_btn_5 = gr.Number(label=\"# Load Upload File with Invalid file_types\", value=0)\n", " file_component_invalid.upload(lambda s,n: (s, n + 1), [file_component_invalid, num_load_btn_5], [output_file_5, num_load_btn_5])\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
1+
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: file_component_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 gradio as gr\n", "\n", "\n", "def delete_file(n: int, file: gr.DeletedFileData):\n", " return [file.file.path, n + 1]\n", "\n", "\n", "with gr.Blocks() as demo:\n", "\n", " with gr.Row():\n", " with gr.Column():\n", " file_component = gr.File(label=\"Upload Single File\", file_count=\"single\")\n", " with gr.Column():\n", " output_file_1 = gr.File(\n", " label=\"Upload Single File Output\", file_count=\"single\"\n", " )\n", " num_load_btn_1 = gr.Number(label=\"# Load Upload Single File\", value=0)\n", " file_component.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component, num_load_btn_1],\n", " [output_file_1, num_load_btn_1],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_multiple = gr.File(\n", " label=\"Upload Multiple Files\", file_count=\"multiple\"\n", " )\n", " with gr.Column():\n", " output_file_2 = gr.File(\n", " label=\"Upload Multiple Files Output\", file_count=\"multiple\"\n", " )\n", " num_load_btn_2 = gr.Number(label=\"# Load Upload Multiple Files\", value=0)\n", " file_component_multiple.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_multiple, num_load_btn_2],\n", " [output_file_2, num_load_btn_2],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_specific = gr.File(\n", " label=\"Upload Multiple Files Image/Video\",\n", " file_count=\"multiple\",\n", " file_types=[\"image\", \"video\"],\n", " )\n", " with gr.Column():\n", " output_file_3 = gr.File(\n", " label=\"Upload Multiple Files Output Image/Video\", file_count=\"multiple\"\n", " )\n", " num_load_btn_3 = gr.Number(\n", " label=\"# Load Upload Multiple Files Image/Video\", value=0\n", " )\n", " file_component_specific.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_specific, num_load_btn_3],\n", " [output_file_3, num_load_btn_3],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_pdf = gr.File(label=\"Upload PDF File\", file_types=[\"pdf\"])\n", " with gr.Column():\n", " output_file_4 = gr.File(label=\"Upload PDF File Output\")\n", " num_load_btn_4 = gr.Number(label=\"# Load Upload PDF File\", value=0)\n", " file_component_pdf.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_pdf, num_load_btn_4],\n", " [output_file_4, num_load_btn_4],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " file_component_invalid = gr.File(\n", " label=\"Upload File with Invalid file_types\",\n", " file_types=[\"invalid file_type\"],\n", " )\n", " with gr.Column():\n", " output_file_5 = gr.File(label=\"Upload File with Invalid file_types Output\")\n", " num_load_btn_5 = gr.Number(\n", " label=\"# Load Upload File with Invalid file_types\", value=0\n", " )\n", " file_component_invalid.upload(\n", " lambda s, n: (s, n + 1),\n", " [file_component_invalid, num_load_btn_5],\n", " [output_file_5, num_load_btn_5],\n", " )\n", " with gr.Row():\n", " with gr.Column():\n", " del_file_input = gr.File(label=\"Delete File\", file_count=\"multiple\")\n", " with gr.Column():\n", " del_file_data = gr.Textbox(label=\"Delete file data\")\n", " num_load_btn_6 = gr.Number(label=\"# Deleted File\", value=0)\n", " del_file_input.delete(\n", " delete_file,\n", " [num_load_btn_6],\n", " [del_file_data, num_load_btn_6],\n", " )\n", " # f = gr.File(label=\"Upload many File\", file_count=\"multiple\")\n", " # # f.delete(delete_file)\n", " # f.delete(delete_file, inputs=None, outputs=None)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

demo/file_component_events/run.py

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,100 @@
11
import gradio as gr
22

3+
4+
def delete_file(n: int, file: gr.DeletedFileData):
5+
return [file.file.path, n + 1]
6+
7+
38
with gr.Blocks() as demo:
4-
9+
510
with gr.Row():
611
with gr.Column():
712
file_component = gr.File(label="Upload Single File", file_count="single")
813
with gr.Column():
9-
output_file_1 = gr.File(label="Upload Single File Output", file_count="single")
14+
output_file_1 = gr.File(
15+
label="Upload Single File Output", file_count="single"
16+
)
1017
num_load_btn_1 = gr.Number(label="# Load Upload Single File", value=0)
11-
file_component.upload(lambda s,n: (s, n + 1), [file_component, num_load_btn_1], [output_file_1, num_load_btn_1])
18+
file_component.upload(
19+
lambda s, n: (s, n + 1),
20+
[file_component, num_load_btn_1],
21+
[output_file_1, num_load_btn_1],
22+
)
1223
with gr.Row():
1324
with gr.Column():
14-
file_component_multiple = gr.File(label="Upload Multiple Files", file_count="multiple")
25+
file_component_multiple = gr.File(
26+
label="Upload Multiple Files", file_count="multiple"
27+
)
1528
with gr.Column():
16-
output_file_2 = gr.File(label="Upload Multiple Files Output", file_count="multiple")
29+
output_file_2 = gr.File(
30+
label="Upload Multiple Files Output", file_count="multiple"
31+
)
1732
num_load_btn_2 = gr.Number(label="# Load Upload Multiple Files", value=0)
18-
file_component_multiple.upload(lambda s,n: (s, n + 1), [file_component_multiple, num_load_btn_2], [output_file_2, num_load_btn_2])
33+
file_component_multiple.upload(
34+
lambda s, n: (s, n + 1),
35+
[file_component_multiple, num_load_btn_2],
36+
[output_file_2, num_load_btn_2],
37+
)
1938
with gr.Row():
2039
with gr.Column():
21-
file_component_specific = gr.File(label="Upload Multiple Files Image/Video", file_count="multiple", file_types=["image", "video"])
40+
file_component_specific = gr.File(
41+
label="Upload Multiple Files Image/Video",
42+
file_count="multiple",
43+
file_types=["image", "video"],
44+
)
2245
with gr.Column():
23-
output_file_3 = gr.File(label="Upload Multiple Files Output Image/Video", file_count="multiple")
24-
num_load_btn_3 = gr.Number(label="# Load Upload Multiple Files Image/Video", value=0)
25-
file_component_specific.upload(lambda s,n: (s, n + 1), [file_component_specific, num_load_btn_3], [output_file_3, num_load_btn_3])
46+
output_file_3 = gr.File(
47+
label="Upload Multiple Files Output Image/Video", file_count="multiple"
48+
)
49+
num_load_btn_3 = gr.Number(
50+
label="# Load Upload Multiple Files Image/Video", value=0
51+
)
52+
file_component_specific.upload(
53+
lambda s, n: (s, n + 1),
54+
[file_component_specific, num_load_btn_3],
55+
[output_file_3, num_load_btn_3],
56+
)
2657
with gr.Row():
2758
with gr.Column():
2859
file_component_pdf = gr.File(label="Upload PDF File", file_types=["pdf"])
2960
with gr.Column():
3061
output_file_4 = gr.File(label="Upload PDF File Output")
3162
num_load_btn_4 = gr.Number(label="# Load Upload PDF File", value=0)
32-
file_component_pdf.upload(lambda s,n: (s, n + 1), [file_component_pdf, num_load_btn_4], [output_file_4, num_load_btn_4])
63+
file_component_pdf.upload(
64+
lambda s, n: (s, n + 1),
65+
[file_component_pdf, num_load_btn_4],
66+
[output_file_4, num_load_btn_4],
67+
)
3368
with gr.Row():
3469
with gr.Column():
35-
file_component_invalid = gr.File(label="Upload File with Invalid file_types", file_types=["invalid file_type"])
70+
file_component_invalid = gr.File(
71+
label="Upload File with Invalid file_types",
72+
file_types=["invalid file_type"],
73+
)
3674
with gr.Column():
3775
output_file_5 = gr.File(label="Upload File with Invalid file_types Output")
38-
num_load_btn_5 = gr.Number(label="# Load Upload File with Invalid file_types", value=0)
39-
file_component_invalid.upload(lambda s,n: (s, n + 1), [file_component_invalid, num_load_btn_5], [output_file_5, num_load_btn_5])
76+
num_load_btn_5 = gr.Number(
77+
label="# Load Upload File with Invalid file_types", value=0
78+
)
79+
file_component_invalid.upload(
80+
lambda s, n: (s, n + 1),
81+
[file_component_invalid, num_load_btn_5],
82+
[output_file_5, num_load_btn_5],
83+
)
84+
with gr.Row():
85+
with gr.Column():
86+
del_file_input = gr.File(label="Delete File", file_count="multiple")
87+
with gr.Column():
88+
del_file_data = gr.Textbox(label="Delete file data")
89+
num_load_btn_6 = gr.Number(label="# Deleted File", value=0)
90+
del_file_input.delete(
91+
delete_file,
92+
[num_load_btn_6],
93+
[del_file_data, num_load_btn_6],
94+
)
95+
# f = gr.File(label="Upload many File", file_count="multiple")
96+
# # f.delete(delete_file)
97+
# f.delete(delete_file, inputs=None, outputs=None)
4098

4199
if __name__ == "__main__":
42100
demo.launch()

gradio/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,14 @@
6060
from gradio.components.audio import WaveformOptions
6161
from gradio.components.image_editor import Brush, Eraser
6262
from gradio.data_classes import FileData
63-
from gradio.events import EventData, KeyUpData, LikeData, SelectData, on
63+
from gradio.events import (
64+
DeletedFileData,
65+
EventData,
66+
KeyUpData,
67+
LikeData,
68+
SelectData,
69+
on,
70+
)
6471
from gradio.exceptions import Error
6572
from gradio.external import load
6673
from gradio.flagging import (

gradio/components/file.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class File(Component):
2626
Demo: zip_files, zip_to_json
2727
"""
2828

29-
EVENTS = [Events.change, Events.select, Events.clear, Events.upload]
29+
EVENTS = [Events.change, Events.select, Events.clear, Events.upload, Events.delete]
3030

3131
def __init__(
3232
self,

gradio/data_classes.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import shutil
99
from abc import ABC, abstractmethod
1010
from enum import Enum, auto
11-
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
11+
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, TypedDict, Union
1212

1313
from fastapi import Request
1414
from gradio_client.utils import traverse
@@ -189,6 +189,16 @@ def from_json(cls, x) -> GradioRootModel:
189189
GradioDataModel = Union[GradioModel, GradioRootModel]
190190

191191

192+
class FileDataDict(TypedDict):
193+
path: str # server filepath
194+
url: Optional[str] # normalised server url
195+
size: Optional[int] # size in bytes
196+
orig_name: Optional[str] # original filename
197+
mime_type: Optional[str]
198+
is_stream: bool
199+
meta: dict
200+
201+
192202
class FileData(GradioModel):
193203
path: str # server filepath
194204
url: Optional[str] = None # normalised server url

gradio/events.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from gradio_client.documentation import document
2121
from jinja2 import Template
2222

23+
from gradio.data_classes import FileData, FileDataDict
24+
2325
if TYPE_CHECKING:
2426
from gradio.blocks import Block, Component
2527

@@ -149,6 +151,15 @@ def __init__(self, target: Block | None, data: Any):
149151
"""
150152

151153

154+
class DeletedFileData(EventData):
155+
def __init__(self, target: Block | None, data: FileDataDict):
156+
super().__init__(target, data)
157+
self.file: FileData = FileData(**data)
158+
"""
159+
The file that was deleted.
160+
"""
161+
162+
152163
@dataclasses.dataclass
153164
class EventListenerMethod:
154165
block: Block | None
@@ -585,6 +596,10 @@ class Events:
585596
"apply",
586597
doc="This listener is triggered when the user applies changes to the {{ component }} through an integrated UI action.",
587598
)
599+
delete = EventListener(
600+
"delete",
601+
doc="This listener is triggered when the user deletes and item from the {{ component }}. Uses event data gradio.DeletedFileData to carry `value` referring to the file that was deleted as an instance of FileData. See EventData documentation on how to use this event data",
602+
)
588603

589604

590605
class LikeData(EventData):

js/app/test/file_component_events.spec.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { test, expect, drag_and_drop_file } from "@gradio/tootils";
22

3+
async function error_modal_showed(page) {
4+
const toast = page.getByTestId("toast-body");
5+
expect(toast).toContainText("error");
6+
const close = page.getByTestId("toast-close");
7+
await close.click();
8+
await expect(page.getByTestId("toast-body")).toHaveCount(0);
9+
}
10+
311
test("File component properly dispatches load event for the single file case.", async ({
412
page
513
}) => {
@@ -63,11 +71,33 @@ test("File component properly handles drag and drop of pdf file.", async ({
6371
test("File component properly handles invalid file_types.", async ({
6472
page
6573
}) => {
66-
const uploader = await page.locator("input[type=file]").last();
67-
await uploader.setInputFiles(["./test/files/cheetah1.jpg"]);
74+
const locator = page.locator("input[type=file]").nth(4);
75+
await drag_and_drop_file(
76+
page,
77+
locator,
78+
"./test/files/cheetah1.jpg",
79+
"cheetah1.jpg",
80+
"image/jpeg"
81+
);
6882

69-
// Check that the pdf file was uploaded
70-
await expect(
71-
page.getByLabel("# Load Upload File with Invalid file_types")
72-
).toHaveValue("1");
83+
await error_modal_showed(page);
84+
});
85+
86+
test("Delete event is fired correctly", async ({ page }) => {
87+
const locator = page.locator("input[type=file]").nth(5);
88+
await drag_and_drop_file(
89+
page,
90+
locator,
91+
"./test/files/cheetah1.jpg",
92+
"cheetah1.jpg",
93+
"image/jpeg",
94+
2
95+
);
96+
97+
await page.getByLabel("Remove this file").first().click();
98+
99+
await expect(page.getByLabel("# Deleted File")).toHaveValue("1");
100+
expect(
101+
(await page.getByLabel("Delete file data").inputValue()).length
102+
).toBeGreaterThan(5);
73103
});

js/file/Index.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
clear: never;
4141
select: SelectData;
4242
clear_status: LoadingStatus;
43+
delete: FileData;
4344
}>;
4445
export let file_count: string;
4546
export let file_types: string[] = ["file"];
@@ -110,6 +111,9 @@
110111
loading_status.status = "error";
111112
gradio.dispatch("error", detail);
112113
}}
114+
on:delete={({ detail }) => {
115+
gradio.dispatch("delete", detail);
116+
}}
113117
i18n={gradio.i18n}
114118
>
115119
<UploadText i18n={gradio.i18n} type="file" />

js/file/shared/FilePreview.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
const dispatch = createEventDispatcher<{
99
select: SelectData;
1010
change: FileData[] | FileData;
11+
delete: FileData;
1112
}>();
1213
export let value: FileData | FileData[];
1314
export let selectable = false;
@@ -46,9 +47,10 @@
4647
}
4748
4849
function remove_file(index: number): void {
49-
normalized_files.splice(index, 1);
50+
const removed = normalized_files.splice(index, 1);
5051
normalized_files = [...normalized_files];
5152
value = normalized_files;
53+
dispatch("delete", removed[0]);
5254
dispatch("change", normalized_files);
5355
}
5456
</script>

0 commit comments

Comments
 (0)