Skip to content

Commit

Permalink
add delete event to File component (#8417)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
pngwn and gradio-pr-bot authored Jun 5, 2024
1 parent 63d36fb commit 96d8de2
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 40 deletions.
8 changes: 8 additions & 0 deletions .changeset/full-flies-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@gradio/file": minor
"@gradio/tootils": minor
"@gradio/upload": minor
"gradio": minor
---

feat:add delete event to `File` component
2 changes: 1 addition & 1 deletion demo/file_component_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: 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}
{"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}
86 changes: 72 additions & 14 deletions demo/file_component_events/run.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,100 @@
import gradio as gr


def delete_file(n: int, file: gr.DeletedFileData):
return [file.file.path, n + 1]


with gr.Blocks() as demo:

with gr.Row():
with gr.Column():
file_component = gr.File(label="Upload Single File", file_count="single")
with gr.Column():
output_file_1 = gr.File(label="Upload Single File Output", file_count="single")
output_file_1 = gr.File(
label="Upload Single File Output", file_count="single"
)
num_load_btn_1 = gr.Number(label="# Load Upload Single File", value=0)
file_component.upload(lambda s,n: (s, n + 1), [file_component, num_load_btn_1], [output_file_1, num_load_btn_1])
file_component.upload(
lambda s, n: (s, n + 1),
[file_component, num_load_btn_1],
[output_file_1, num_load_btn_1],
)
with gr.Row():
with gr.Column():
file_component_multiple = gr.File(label="Upload Multiple Files", file_count="multiple")
file_component_multiple = gr.File(
label="Upload Multiple Files", file_count="multiple"
)
with gr.Column():
output_file_2 = gr.File(label="Upload Multiple Files Output", file_count="multiple")
output_file_2 = gr.File(
label="Upload Multiple Files Output", file_count="multiple"
)
num_load_btn_2 = gr.Number(label="# Load Upload Multiple Files", value=0)
file_component_multiple.upload(lambda s,n: (s, n + 1), [file_component_multiple, num_load_btn_2], [output_file_2, num_load_btn_2])
file_component_multiple.upload(
lambda s, n: (s, n + 1),
[file_component_multiple, num_load_btn_2],
[output_file_2, num_load_btn_2],
)
with gr.Row():
with gr.Column():
file_component_specific = gr.File(label="Upload Multiple Files Image/Video", file_count="multiple", file_types=["image", "video"])
file_component_specific = gr.File(
label="Upload Multiple Files Image/Video",
file_count="multiple",
file_types=["image", "video"],
)
with gr.Column():
output_file_3 = gr.File(label="Upload Multiple Files Output Image/Video", file_count="multiple")
num_load_btn_3 = gr.Number(label="# Load Upload Multiple Files Image/Video", value=0)
file_component_specific.upload(lambda s,n: (s, n + 1), [file_component_specific, num_load_btn_3], [output_file_3, num_load_btn_3])
output_file_3 = gr.File(
label="Upload Multiple Files Output Image/Video", file_count="multiple"
)
num_load_btn_3 = gr.Number(
label="# Load Upload Multiple Files Image/Video", value=0
)
file_component_specific.upload(
lambda s, n: (s, n + 1),
[file_component_specific, num_load_btn_3],
[output_file_3, num_load_btn_3],
)
with gr.Row():
with gr.Column():
file_component_pdf = gr.File(label="Upload PDF File", file_types=["pdf"])
with gr.Column():
output_file_4 = gr.File(label="Upload PDF File Output")
num_load_btn_4 = gr.Number(label="# Load Upload PDF File", value=0)
file_component_pdf.upload(lambda s,n: (s, n + 1), [file_component_pdf, num_load_btn_4], [output_file_4, num_load_btn_4])
file_component_pdf.upload(
lambda s, n: (s, n + 1),
[file_component_pdf, num_load_btn_4],
[output_file_4, num_load_btn_4],
)
with gr.Row():
with gr.Column():
file_component_invalid = gr.File(label="Upload File with Invalid file_types", file_types=["invalid file_type"])
file_component_invalid = gr.File(
label="Upload File with Invalid file_types",
file_types=["invalid file_type"],
)
with gr.Column():
output_file_5 = gr.File(label="Upload File with Invalid file_types Output")
num_load_btn_5 = gr.Number(label="# Load Upload File with Invalid file_types", value=0)
file_component_invalid.upload(lambda s,n: (s, n + 1), [file_component_invalid, num_load_btn_5], [output_file_5, num_load_btn_5])
num_load_btn_5 = gr.Number(
label="# Load Upload File with Invalid file_types", value=0
)
file_component_invalid.upload(
lambda s, n: (s, n + 1),
[file_component_invalid, num_load_btn_5],
[output_file_5, num_load_btn_5],
)
with gr.Row():
with gr.Column():
del_file_input = gr.File(label="Delete File", file_count="multiple")
with gr.Column():
del_file_data = gr.Textbox(label="Delete file data")
num_load_btn_6 = gr.Number(label="# Deleted File", value=0)
del_file_input.delete(
delete_file,
[num_load_btn_6],
[del_file_data, num_load_btn_6],
)
# f = gr.File(label="Upload many File", file_count="multiple")
# # f.delete(delete_file)
# f.delete(delete_file, inputs=None, outputs=None)

if __name__ == "__main__":
demo.launch()
9 changes: 8 additions & 1 deletion gradio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@
from gradio.components.audio import WaveformOptions
from gradio.components.image_editor import Brush, Eraser
from gradio.data_classes import FileData
from gradio.events import EventData, KeyUpData, LikeData, SelectData, on
from gradio.events import (
DeletedFileData,
EventData,
KeyUpData,
LikeData,
SelectData,
on,
)
from gradio.exceptions import Error
from gradio.external import load
from gradio.flagging import (
Expand Down
2 changes: 1 addition & 1 deletion gradio/components/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class File(Component):
Demo: zip_files, zip_to_json
"""

EVENTS = [Events.change, Events.select, Events.clear, Events.upload]
EVENTS = [Events.change, Events.select, Events.clear, Events.upload, Events.delete]

def __init__(
self,
Expand Down
12 changes: 11 additions & 1 deletion gradio/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import shutil
from abc import ABC, abstractmethod
from enum import Enum, auto
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, TypedDict, Union

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


class FileDataDict(TypedDict):
path: str # server filepath
url: Optional[str] # normalised server url
size: Optional[int] # size in bytes
orig_name: Optional[str] # original filename
mime_type: Optional[str]
is_stream: bool
meta: dict


class FileData(GradioModel):
path: str # server filepath
url: Optional[str] = None # normalised server url
Expand Down
15 changes: 15 additions & 0 deletions gradio/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from gradio_client.documentation import document
from jinja2 import Template

from gradio.data_classes import FileData, FileDataDict

if TYPE_CHECKING:
from gradio.blocks import Block, Component

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


class DeletedFileData(EventData):
def __init__(self, target: Block | None, data: FileDataDict):
super().__init__(target, data)
self.file: FileData = FileData(**data)
"""
The file that was deleted.
"""


@dataclasses.dataclass
class EventListenerMethod:
block: Block | None
Expand Down Expand Up @@ -585,6 +596,10 @@ class Events:
"apply",
doc="This listener is triggered when the user applies changes to the {{ component }} through an integrated UI action.",
)
delete = EventListener(
"delete",
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",
)


class LikeData(EventData):
Expand Down
42 changes: 36 additions & 6 deletions js/app/test/file_component_events.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { test, expect, drag_and_drop_file } from "@gradio/tootils";

async function error_modal_showed(page) {
const toast = page.getByTestId("toast-body");
expect(toast).toContainText("error");
const close = page.getByTestId("toast-close");
await close.click();
await expect(page.getByTestId("toast-body")).toHaveCount(0);
}

test("File component properly dispatches load event for the single file case.", async ({
page
}) => {
Expand Down Expand Up @@ -63,11 +71,33 @@ test("File component properly handles drag and drop of pdf file.", async ({
test("File component properly handles invalid file_types.", async ({
page
}) => {
const uploader = await page.locator("input[type=file]").last();
await uploader.setInputFiles(["./test/files/cheetah1.jpg"]);
const locator = page.locator("input[type=file]").nth(4);
await drag_and_drop_file(
page,
locator,
"./test/files/cheetah1.jpg",
"cheetah1.jpg",
"image/jpeg"
);

// Check that the pdf file was uploaded
await expect(
page.getByLabel("# Load Upload File with Invalid file_types")
).toHaveValue("1");
await error_modal_showed(page);
});

test("Delete event is fired correctly", async ({ page }) => {
const locator = page.locator("input[type=file]").nth(5);
await drag_and_drop_file(
page,
locator,
"./test/files/cheetah1.jpg",
"cheetah1.jpg",
"image/jpeg",
2
);

await page.getByLabel("Remove this file").first().click();

await expect(page.getByLabel("# Deleted File")).toHaveValue("1");
expect(
(await page.getByLabel("Delete file data").inputValue()).length
).toBeGreaterThan(5);
});
4 changes: 4 additions & 0 deletions js/file/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
clear: never;
select: SelectData;
clear_status: LoadingStatus;
delete: FileData;
}>;
export let file_count: string;
export let file_types: string[] = ["file"];
Expand Down Expand Up @@ -110,6 +111,9 @@
loading_status.status = "error";
gradio.dispatch("error", detail);
}}
on:delete={({ detail }) => {
gradio.dispatch("delete", detail);
}}
i18n={gradio.i18n}
>
<UploadText i18n={gradio.i18n} type="file" />
Expand Down
4 changes: 3 additions & 1 deletion js/file/shared/FilePreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
const dispatch = createEventDispatcher<{
select: SelectData;
change: FileData[] | FileData;
delete: FileData;
}>();
export let value: FileData | FileData[];
export let selectable = false;
Expand Down Expand Up @@ -46,9 +47,10 @@
}
function remove_file(index: number): void {
normalized_files.splice(index, 1);
const removed = normalized_files.splice(index, 1);
normalized_files = [...normalized_files];
value = normalized_files;
dispatch("delete", removed[0]);
dispatch("change", normalized_files);
}
</script>
Expand Down
17 changes: 10 additions & 7 deletions js/file/shared/FileUpload.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,19 @@
$: dispatch("drag", dragging);
</script>

<BlockLabel
{show_label}
Icon={File}
float={value === null}
label={label || "File"}
/>
<BlockLabel {show_label} Icon={File} float={!value} label={label || "File"} />

{#if value && (Array.isArray(value) ? value.length > 0 : true)}
<ModifyUpload {i18n} on:clear={handle_clear} absolute />
<FilePreview {i18n} on:select {selectable} {value} {height} on:change />
<FilePreview
{i18n}
on:select
{selectable}
{value}
{height}
on:change
on:delete
/>
{:else}
<Upload
on:load={handle_upload}
Expand Down
Loading

0 comments on commit 96d8de2

Please sign in to comment.