mirror of
https://github.com/gradio-app/gradio.git
synced 2025-04-12 12:40:29 +08:00
Improve rendering (#8398)
* changes * add changeset * changes * changes * changes * changes * changes * changes * changeas * changes * add changeset * changes * add changeset * changes * changes * changes * changes * changes * changes * changes * changes * add changeset * changes * cganges * changes * changes * changes * changes * add changeset * changes * chagnes * changes * changes * changes * changes * changes * js * remove console log * changes * changes * changes * changes * changes * changes * changes * changes * changes * add changeset * changes * chnages * changes * cnages * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * add changeset * changes * changes * changes * changes * changes * changes * changes * add changeset * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * add changeset * Add `state.change` listener (#8297) * state changes * changes --------- Co-authored-by: Ali Abid <aliabid94@gmail.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * changes * changes * add changeset * changes * changes * changes * changes * changes * changes * updates * changes * add changeset * changes * changes * add changeset * fix * changes * changes * changes * changes * changes * changes * add changeset * changes --------- 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>
This commit is contained in:
parent
d078621928
commit
945ac837e7
10
.changeset/fine-pillows-open.md
Normal file
10
.changeset/fine-pillows-open.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
"@gradio/app": patch
|
||||
"@gradio/client": patch
|
||||
"@gradio/column": patch
|
||||
"@gradio/row": patch
|
||||
"@gradio/statustracker": patch
|
||||
"gradio": patch
|
||||
---
|
||||
|
||||
feat:Improve rendering
|
@ -162,6 +162,25 @@ export function submit(
|
||||
}
|
||||
}
|
||||
|
||||
function handle_render_config(render_config: any): void {
|
||||
if (!config) return;
|
||||
let render_id: number = render_config.render_id;
|
||||
config.components = [
|
||||
...config.components.filter((c) => c.rendered_in !== render_id),
|
||||
...render_config.components
|
||||
];
|
||||
config.dependencies = [
|
||||
...config.dependencies.filter((d) => d.rendered_in !== render_id),
|
||||
...render_config.dependencies
|
||||
];
|
||||
fire_event({
|
||||
type: "render",
|
||||
data: render_config,
|
||||
endpoint: _endpoint,
|
||||
fn_index
|
||||
});
|
||||
}
|
||||
|
||||
this.handle_blob(config.root, resolved_data, endpoint_info).then(
|
||||
async (_payload) => {
|
||||
payload = {
|
||||
@ -201,6 +220,9 @@ export function submit(
|
||||
event_data,
|
||||
trigger_id
|
||||
});
|
||||
if (output.render_config) {
|
||||
handle_render_config(output.render_config);
|
||||
}
|
||||
|
||||
fire_event({
|
||||
type: "status",
|
||||
@ -606,20 +628,7 @@ export function submit(
|
||||
fn_index
|
||||
});
|
||||
if (data.render_config) {
|
||||
config.components = [
|
||||
...config.components,
|
||||
...data.render_config.components
|
||||
];
|
||||
config.dependencies = [
|
||||
...config.dependencies,
|
||||
...data.render_config.dependencies
|
||||
];
|
||||
fire_event({
|
||||
type: "render",
|
||||
data: data.render_config,
|
||||
endpoint: _endpoint,
|
||||
fn_index
|
||||
});
|
||||
handle_render_config(data.render_config);
|
||||
}
|
||||
|
||||
if (complete) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import dataclasses
|
||||
import hashlib
|
||||
import inspect
|
||||
import json
|
||||
@ -123,6 +124,7 @@ class Block:
|
||||
self.proxy_url = proxy_url
|
||||
self.share_token = secrets.token_urlsafe(32)
|
||||
self.parent: BlockContext | None = None
|
||||
self.rendered_in: Renderable | None = None
|
||||
self.is_rendered: bool = False
|
||||
self._constructor_args: list[dict]
|
||||
self.state_session_capacity = 10000
|
||||
@ -166,6 +168,7 @@ class Block:
|
||||
"""
|
||||
root_context = get_blocks_context()
|
||||
render_context = get_render_context()
|
||||
self.rendered_in = LocalContext.renderable.get()
|
||||
if root_context is not None and self._id in root_context.blocks:
|
||||
raise DuplicateBlockError(
|
||||
f"A block with id: {self._id} has already been rendered in the current Blocks."
|
||||
@ -233,13 +236,17 @@ class Block:
|
||||
for parameter in signature.parameters.values():
|
||||
if hasattr(self, parameter.name):
|
||||
value = getattr(self, parameter.name)
|
||||
config[parameter.name] = utils.convert_to_dict_if_dataclass(value)
|
||||
if dataclasses.is_dataclass(value):
|
||||
value = dataclasses.asdict(value)
|
||||
config[parameter.name] = value
|
||||
for e in self.events:
|
||||
to_add = e.config_data()
|
||||
if to_add:
|
||||
config = {**to_add, **config}
|
||||
config.pop("render", None)
|
||||
config = {**config, "proxy_url": self.proxy_url, "name": self.get_block_class()}
|
||||
if self.rendered_in is not None:
|
||||
config["rendered_in"] = self.rendered_in._id
|
||||
if (_selectable := getattr(self, "_selectable", None)) is not None:
|
||||
config["_selectable"] = _selectable
|
||||
return config
|
||||
@ -468,7 +475,7 @@ class BlockFunction:
|
||||
self,
|
||||
fn: Callable | None,
|
||||
inputs: list[Component],
|
||||
outputs: list[Component],
|
||||
outputs: list[Block] | list[Component],
|
||||
preprocess: bool,
|
||||
postprocess: bool,
|
||||
inputs_as_dict: bool,
|
||||
@ -658,7 +665,7 @@ class BlocksConfig:
|
||||
targets: Sequence[EventListenerMethod],
|
||||
fn: Callable | None,
|
||||
inputs: Component | list[Component] | set[Component] | None,
|
||||
outputs: Component | list[Component] | None,
|
||||
outputs: Block | list[Block] | list[Component] | None,
|
||||
preprocess: bool = True,
|
||||
postprocess: bool = True,
|
||||
scroll_to_output: bool = False,
|
||||
@ -860,7 +867,7 @@ class BlocksConfig:
|
||||
return {"id": block._id, "children": children_layout}
|
||||
|
||||
if renderable:
|
||||
root_block = self.blocks[renderable.column_id]
|
||||
root_block = self.blocks[renderable.container_id]
|
||||
else:
|
||||
root_block = self.root_block
|
||||
config["layout"] = get_layout(root_block)
|
||||
|
@ -210,7 +210,7 @@ class EventListener(str):
|
||||
block: Block | None,
|
||||
fn: Callable | None | Literal["decorator"] = "decorator",
|
||||
inputs: Component | list[Component] | set[Component] | None = None,
|
||||
outputs: Component | list[Component] | None = None,
|
||||
outputs: Block | list[Block] | list[Component] | None = None,
|
||||
api_name: str | None | Literal[False] = None,
|
||||
scroll_to_output: bool = False,
|
||||
show_progress: Literal["full", "minimal", "hidden"] = _show_progress,
|
||||
@ -334,7 +334,7 @@ def on(
|
||||
triggers: Sequence[Any] | Any | None = None,
|
||||
fn: Callable | None | Literal["decorator"] = "decorator",
|
||||
inputs: Component | list[Component] | set[Component] | None = None,
|
||||
outputs: Component | list[Component] | None = None,
|
||||
outputs: Block | list[Block] | list[Component] | None = None,
|
||||
*,
|
||||
api_name: str | None | Literal[False] = None,
|
||||
scroll_to_output: bool = False,
|
||||
|
@ -38,6 +38,7 @@ class Column(BlockContext, metaclass=ComponentMeta):
|
||||
elem_id: str | None = None,
|
||||
elem_classes: list[str] | str | None = None,
|
||||
render: bool = True,
|
||||
show_progress: bool = False,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
@ -48,6 +49,7 @@ class Column(BlockContext, metaclass=ComponentMeta):
|
||||
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
elem_classes: An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
render: If False, component will not render be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
|
||||
show_progress: If True, shows progress animation when being updated.
|
||||
"""
|
||||
if scale != round(scale):
|
||||
warnings.warn(
|
||||
@ -59,6 +61,7 @@ class Column(BlockContext, metaclass=ComponentMeta):
|
||||
self.variant = variant
|
||||
if variant == "compact":
|
||||
self.allow_expected_parents = False
|
||||
self.show_progress = show_progress
|
||||
BlockContext.__init__(
|
||||
self,
|
||||
visible=visible,
|
||||
|
@ -32,6 +32,7 @@ class Row(BlockContext, metaclass=ComponentMeta):
|
||||
elem_classes: list[str] | str | None = None,
|
||||
render: bool = True,
|
||||
equal_height: bool = True,
|
||||
show_progress: bool = False,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
@ -41,11 +42,13 @@ class Row(BlockContext, metaclass=ComponentMeta):
|
||||
elem_classes: An optional string or list of strings that are assigned as the class of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
render: If False, this layout will not be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
|
||||
equal_height: If True, makes every child element have equal height
|
||||
show_progress: If True, shows progress animation when being updated.
|
||||
"""
|
||||
self.variant = variant
|
||||
self.equal_height = equal_height
|
||||
if variant == "compact":
|
||||
self.allow_expected_parents = False
|
||||
self.show_progress = show_progress
|
||||
BlockContext.__init__(
|
||||
self,
|
||||
visible=visible,
|
||||
|
@ -6,7 +6,7 @@ from gradio.blocks import Block
|
||||
from gradio.components import Component
|
||||
from gradio.context import Context, LocalContext
|
||||
from gradio.events import EventListener, EventListenerMethod
|
||||
from gradio.layouts import Column
|
||||
from gradio.layouts import Column, Row
|
||||
|
||||
|
||||
class Renderable:
|
||||
@ -17,14 +17,17 @@ class Renderable:
|
||||
triggers: list[tuple[Block | None, str]],
|
||||
concurrency_limit: int | None | Literal["default"],
|
||||
concurrency_id: str | None,
|
||||
trigger_mode: Literal["once", "multiple", "always_last"] | None,
|
||||
queue: bool,
|
||||
):
|
||||
if Context.root_block is None:
|
||||
raise ValueError("Reactive render must be inside a Blocks context.")
|
||||
|
||||
self._id = len(Context.root_block.renderables)
|
||||
Context.root_block.renderables.append(self)
|
||||
self.column = Column(render=False)
|
||||
self.column_id = Column()._id
|
||||
self.ContainerClass = Row if isinstance(Context.block, Row) else Column
|
||||
self.container = self.ContainerClass(show_progress=True)
|
||||
self.container_id = self.container._id
|
||||
|
||||
self.fn = fn
|
||||
self.inputs = inputs
|
||||
@ -35,11 +38,14 @@ class Renderable:
|
||||
self.triggers,
|
||||
self.apply,
|
||||
self.inputs,
|
||||
None,
|
||||
self.container,
|
||||
show_api=False,
|
||||
concurrency_limit=concurrency_limit,
|
||||
concurrency_id=concurrency_id,
|
||||
renderable=self,
|
||||
trigger_mode=trigger_mode,
|
||||
postprocess=False,
|
||||
queue=queue,
|
||||
)
|
||||
|
||||
def apply(self, *args, **kwargs):
|
||||
@ -54,14 +60,14 @@ class Renderable:
|
||||
for _id in fn_ids_to_remove_from_last_render:
|
||||
del blocks_config.fns[_id]
|
||||
|
||||
column_copy = Column(render=False)
|
||||
column_copy._id = self.column_id
|
||||
container_copy = self.ContainerClass(render=False, show_progress=True)
|
||||
container_copy._id = self.container_id
|
||||
LocalContext.renderable.set(self)
|
||||
|
||||
try:
|
||||
with column_copy:
|
||||
with container_copy:
|
||||
self.fn(*args, **kwargs)
|
||||
blocks_config.blocks[self.column_id] = column_copy
|
||||
blocks_config.blocks[self.container_id] = container_copy
|
||||
finally:
|
||||
LocalContext.renderable.set(None)
|
||||
|
||||
@ -71,6 +77,8 @@ def render(
|
||||
triggers: list[EventListener] | EventListener | None = None,
|
||||
concurrency_limit: int | None | Literal["default"] = None,
|
||||
concurrency_id: str | None = None,
|
||||
trigger_mode: Literal["once", "multiple", "always_last"] | None = "always_last",
|
||||
queue: bool = True,
|
||||
):
|
||||
if Context.root_block is None:
|
||||
raise ValueError("Reactive render must be inside a Blocks context.")
|
||||
@ -92,7 +100,15 @@ def render(
|
||||
]
|
||||
|
||||
def wrapper_function(fn):
|
||||
Renderable(fn, inputs, _triggers, concurrency_limit, concurrency_id)
|
||||
Renderable(
|
||||
fn,
|
||||
inputs,
|
||||
_triggers,
|
||||
concurrency_limit,
|
||||
concurrency_id,
|
||||
trigger_mode,
|
||||
queue,
|
||||
)
|
||||
return fn
|
||||
|
||||
return wrapper_function
|
||||
|
@ -860,7 +860,7 @@ class App(FastAPI):
|
||||
session_hash: str,
|
||||
):
|
||||
def process_msg(message: EventMessage) -> str:
|
||||
return f"data: {orjson.dumps(message.model_dump()).decode('utf-8')}\n\n"
|
||||
return f"data: {orjson.dumps(message.model_dump(), default=str).decode('utf-8')}\n\n"
|
||||
|
||||
return await queue_data_helper(request, session_hash, process_msg)
|
||||
|
||||
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
import ast
|
||||
import asyncio
|
||||
import copy
|
||||
import dataclasses
|
||||
import functools
|
||||
import importlib
|
||||
import importlib.util
|
||||
@ -1228,12 +1227,6 @@ def get_extension_from_file_path_or_url(file_path_or_url: str) -> str:
|
||||
return file_extension[1:] if file_extension else ""
|
||||
|
||||
|
||||
def convert_to_dict_if_dataclass(value):
|
||||
if dataclasses.is_dataclass(value):
|
||||
return dataclasses.asdict(value)
|
||||
return value
|
||||
|
||||
|
||||
K = TypeVar("K")
|
||||
V = TypeVar("V")
|
||||
|
||||
|
@ -62,7 +62,6 @@ export function create_components(): {
|
||||
let _components: ComponentMeta[] = [];
|
||||
let app: client_return;
|
||||
let keyed_component_values: Record<string | number, any> = {};
|
||||
let rendered_fns_per_render_id: Record<number, number[]> = {};
|
||||
let _rootNode: ComponentMeta;
|
||||
|
||||
function create_layout({
|
||||
@ -157,16 +156,6 @@ export function create_components(): {
|
||||
constructor_map.set(k, v);
|
||||
});
|
||||
|
||||
let previous_rendered_fn_ids = rendered_fns_per_render_id[render_id] || [];
|
||||
Object.values(_target_map).forEach((event_fn_ids_map) => {
|
||||
Object.values(event_fn_ids_map).forEach((fn_ids) => {
|
||||
previous_rendered_fn_ids.forEach((fn_id) => {
|
||||
if (fn_ids.includes(fn_id)) {
|
||||
fn_ids.splice(fn_ids.indexOf(fn_id), 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
_target_map = {};
|
||||
|
||||
dependencies.forEach((dep) => {
|
||||
@ -196,6 +185,16 @@ export function create_components(): {
|
||||
add_to_current_children(current_element);
|
||||
store_keyed_values(all_current_children);
|
||||
|
||||
Object.entries(instance_map).forEach(([id, component]) => {
|
||||
let _id = Number(id);
|
||||
if (component.rendered_in === render_id) {
|
||||
delete instance_map[_id];
|
||||
if (_component_map.has(_id)) {
|
||||
_component_map.delete(_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
components.forEach((c) => {
|
||||
instance_map[c.id] = c;
|
||||
_component_map.set(c.id, c);
|
||||
|
@ -28,6 +28,7 @@ export interface ComponentMeta {
|
||||
value?: any;
|
||||
component_class_id: string;
|
||||
key: string | number | null;
|
||||
rendered_in?: number;
|
||||
}
|
||||
|
||||
/** Dictates whether a dependency is continous and/or a generator */
|
||||
|
@ -1,4 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { StatusTracker } from "@gradio/statustracker";
|
||||
import type { LoadingStatus } from "@gradio/statustracker";
|
||||
import type { Gradio } from "@gradio/utils";
|
||||
|
||||
export let scale: number | null = null;
|
||||
export let gap = true;
|
||||
export let min_width = 0;
|
||||
@ -6,6 +10,9 @@
|
||||
export let elem_classes: string[] = [];
|
||||
export let visible = true;
|
||||
export let variant: "default" | "panel" | "compact" = "default";
|
||||
export let loading_status: LoadingStatus | undefined = undefined;
|
||||
export let gradio: Gradio | undefined = undefined;
|
||||
export let show_progress = false;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -18,6 +25,18 @@
|
||||
style:flex-grow={scale}
|
||||
style:min-width="calc(min({min_width}px, 100%))"
|
||||
>
|
||||
{#if loading_status && show_progress && gradio}
|
||||
<StatusTracker
|
||||
autoscroll={gradio.autoscroll}
|
||||
i18n={gradio.i18n}
|
||||
{...loading_status}
|
||||
status={loading_status
|
||||
? loading_status.status == "pending"
|
||||
? "generating"
|
||||
: loading_status.status
|
||||
: null}
|
||||
/>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gradio/preview": "workspace:^"
|
||||
"@gradio/preview": "workspace:^",
|
||||
"@gradio/statustracker": "workspace:^",
|
||||
"@gradio/utils": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { StatusTracker } from "@gradio/statustracker";
|
||||
import type { LoadingStatus } from "@gradio/statustracker";
|
||||
import type { Gradio } from "@gradio/utils";
|
||||
|
||||
export let equal_height = true;
|
||||
export let elem_id: string;
|
||||
export let elem_classes: string[] = [];
|
||||
export let visible = true;
|
||||
export let variant: "default" | "panel" | "compact" = "default";
|
||||
export let loading_status: LoadingStatus | undefined = undefined;
|
||||
export let gradio: Gradio | undefined = undefined;
|
||||
export let show_progress = false;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -15,6 +22,18 @@
|
||||
id={elem_id}
|
||||
class={elem_classes.join(" ")}
|
||||
>
|
||||
{#if loading_status && show_progress && gradio}
|
||||
<StatusTracker
|
||||
autoscroll={gradio.autoscroll}
|
||||
i18n={gradio.i18n}
|
||||
{...loading_status}
|
||||
status={loading_status
|
||||
? loading_status.status == "pending"
|
||||
? "generating"
|
||||
: loading_status.status
|
||||
: null}
|
||||
/>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@ -24,6 +43,7 @@
|
||||
flex-wrap: wrap;
|
||||
gap: var(--layout-gap);
|
||||
width: var(--size-full);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hide {
|
||||
|
@ -8,7 +8,9 @@
|
||||
"private": false,
|
||||
"main_changeset": true,
|
||||
"devDependencies": {
|
||||
"@gradio/preview": "workspace:^"
|
||||
"@gradio/preview": "workspace:^",
|
||||
"@gradio/statustracker": "workspace:^",
|
||||
"@gradio/utils": "workspace:^"
|
||||
},
|
||||
"exports": {
|
||||
".": "./Index.svelte",
|
||||
|
@ -327,7 +327,9 @@
|
||||
}
|
||||
|
||||
.generating {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
animation:
|
||||
pulseStart 1s cubic-bezier(0.4, 0, 0.6, 1),
|
||||
pulse 2s cubic-bezier(0.4, 0, 0.6, 1) 1s infinite;
|
||||
border: 2px solid var(--color-accent);
|
||||
background: transparent;
|
||||
z-index: var(--layer-1);
|
||||
@ -338,6 +340,15 @@
|
||||
background: none;
|
||||
}
|
||||
|
||||
@keyframes pulseStart {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%,
|
||||
100% {
|
||||
|
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@ -818,6 +818,12 @@ importers:
|
||||
'@gradio/preview':
|
||||
specifier: workspace:^
|
||||
version: link:../preview
|
||||
'@gradio/statustracker':
|
||||
specifier: workspace:^
|
||||
version: link:../statustracker
|
||||
'@gradio/utils':
|
||||
specifier: workspace:^
|
||||
version: link:../utils
|
||||
|
||||
js/dataframe:
|
||||
dependencies:
|
||||
@ -1515,6 +1521,12 @@ importers:
|
||||
'@gradio/preview':
|
||||
specifier: workspace:^
|
||||
version: link:../preview
|
||||
'@gradio/statustracker':
|
||||
specifier: workspace:^
|
||||
version: link:../statustracker
|
||||
'@gradio/utils':
|
||||
specifier: workspace:^
|
||||
version: link:../utils
|
||||
|
||||
js/simpledropdown:
|
||||
dependencies:
|
||||
|
Loading…
x
Reference in New Issue
Block a user