mirror of
https://github.com/gradio-app/gradio.git
synced 2025-02-11 11:19:58 +08:00
allow users to update the current tab from python SDK (#1709)
* allow users to update the current tab from python SDK * rename var * ensure tabs work when no selected or id kwargs are passed * update xray config * update test * update test again * Apply suggestions from code review Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * change false to None * update test configs * fix test * fix test * update global tab selection Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
parent
c9f522e02b
commit
8c4e134094
@ -1,9 +1,14 @@
|
||||
import gradio as gr
|
||||
|
||||
|
||||
def change_tab():
|
||||
return gr.Tabs.update(selected=2)
|
||||
|
||||
|
||||
identity_demo, input_demo, output_demo = gr.Blocks(), gr.Blocks(), gr.Blocks()
|
||||
|
||||
with identity_demo:
|
||||
gr.Interface(lambda x:x, "text", "text")
|
||||
gr.Interface(lambda x: x, "text", "text")
|
||||
|
||||
with input_demo:
|
||||
t = gr.Textbox(label="Enter your text here")
|
||||
@ -17,14 +22,15 @@ with output_demo:
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
gr.Markdown("Three demos in one!")
|
||||
with gr.Tabs():
|
||||
with gr.TabItem("Text Identity"):
|
||||
with gr.Tabs(selected=1) as tabs:
|
||||
with gr.TabItem("Text Identity", id=0):
|
||||
identity_demo.render()
|
||||
with gr.TabItem("Text Input"):
|
||||
with gr.TabItem("Text Input", id=1):
|
||||
input_demo.render()
|
||||
with gr.TabItem("Text Static"):
|
||||
with gr.TabItem("Text Static", id=2):
|
||||
output_demo.render()
|
||||
|
||||
btn = gr.Button("Change tab")
|
||||
btn.click(inputs=None, outputs=tabs, fn=change_tab)
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "3.0.6",
|
||||
"version": "3.0.24\n",
|
||||
"mode": "blocks",
|
||||
"dev_mode": true,
|
||||
"components": [
|
||||
@ -271,7 +271,19 @@
|
||||
"backend_fn": true,
|
||||
"js": null,
|
||||
"status_tracker": 9,
|
||||
"queue": null
|
||||
"queue": null,
|
||||
"api_name": "xray_model",
|
||||
"scroll_to_output": false,
|
||||
"show_progress": true,
|
||||
"documentation": [
|
||||
[
|
||||
"(List[str]): list of selected choices",
|
||||
"(str | dict): base64 url data, or (if tool == \"sketch) a dict of image and mask base64 url data"
|
||||
],
|
||||
[
|
||||
"(Dict | List): JSON output"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
@ -288,7 +300,19 @@
|
||||
"backend_fn": true,
|
||||
"js": null,
|
||||
"status_tracker": 15,
|
||||
"queue": null
|
||||
"queue": null,
|
||||
"api_name": "ct_model",
|
||||
"scroll_to_output": false,
|
||||
"show_progress": true,
|
||||
"documentation": [
|
||||
[
|
||||
"(List[str]): list of selected choices",
|
||||
"(str | dict): base64 url data, or (if tool == \"sketch) a dict of image and mask base64 url data"
|
||||
],
|
||||
[
|
||||
"(Dict | List): JSON output"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"targets": [
|
||||
@ -303,7 +327,10 @@
|
||||
"backend_fn": true,
|
||||
"js": null,
|
||||
"status_tracker": 17,
|
||||
"queue": null
|
||||
"queue": null,
|
||||
"api_name": null,
|
||||
"scroll_to_output": false,
|
||||
"show_progress": true
|
||||
}
|
||||
]
|
||||
}
|
@ -126,6 +126,26 @@ class Tabs(BlockContext):
|
||||
gr.Button("New Tiger")
|
||||
"""
|
||||
|
||||
def __init__(self, selected: Optional[int | str] = None, **kwargs):
|
||||
"""
|
||||
Parameters:
|
||||
label (str): The visual label for the tab
|
||||
selected: (Optional[int | str]): The currently selected tab. Must correspdong to an id passed to the one of the child TabItems. Defaults to the first TabItem.
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self.selected = selected
|
||||
|
||||
def get_config(self):
|
||||
return {"selected": self.selected, **super().get_config()}
|
||||
|
||||
def update(
|
||||
selected: Optional[int | str] = None,
|
||||
):
|
||||
return {
|
||||
"selected": selected,
|
||||
"__type__": "update",
|
||||
}
|
||||
|
||||
def change(self, fn: Callable, inputs: List[Component], outputs: List[Component]):
|
||||
"""
|
||||
Parameters:
|
||||
@ -143,12 +163,18 @@ class TabItem(BlockContext):
|
||||
components defined within the TabItem will be rendered within a tab.
|
||||
"""
|
||||
|
||||
def __init__(self, label, **kwargs):
|
||||
def __init__(self, label: str, id: Optional[int | str] = None, **kwargs):
|
||||
"""
|
||||
Parameters:
|
||||
label (str): The visual label for the tab
|
||||
id: (Optional[int | str]): An optional identifier for the tab, required if you wish to control the selected tab from a predict function.
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self.label = label
|
||||
self.id = id
|
||||
|
||||
def get_config(self):
|
||||
return {"label": self.label, **super().get_config()}
|
||||
return {"label": self.label, "id": self.id, **super().get_config()}
|
||||
|
||||
def select(self, fn: Callable, inputs: List[Component], outputs: List[Component]):
|
||||
"""
|
||||
|
@ -27,11 +27,22 @@ XRAY_CONFIG = {
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{"id": 3, "type": "tabs", "props": {"visible": True, "style": {}}},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "tabs",
|
||||
"props": {
|
||||
"visible": True,
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "tabitem",
|
||||
"props": {"label": "X-ray", "visible": True, "style": {}},
|
||||
"props": {
|
||||
"label": "X-ray",
|
||||
"visible": True,
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
@ -72,7 +83,11 @@ XRAY_CONFIG = {
|
||||
{
|
||||
"id": 9,
|
||||
"type": "tabitem",
|
||||
"props": {"label": "CT Scan", "visible": True, "style": {}},
|
||||
"props": {
|
||||
"label": "CT Scan",
|
||||
"visible": True,
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
@ -224,11 +239,22 @@ XRAY_CONFIG_DIFF_IDS = {
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{"id": 3, "type": "tabs", "props": {"visible": True, "style": {}}},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "tabs",
|
||||
"props": {
|
||||
"visible": True,
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 444,
|
||||
"type": "tabitem",
|
||||
"props": {"label": "X-ray", "visible": True, "style": {}},
|
||||
"props": {
|
||||
"label": "X-ray",
|
||||
"visible": True,
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
@ -274,7 +300,11 @@ XRAY_CONFIG_DIFF_IDS = {
|
||||
{
|
||||
"id": 9,
|
||||
"type": "tabitem",
|
||||
"props": {"label": "CT Scan", "visible": True, "style": {}},
|
||||
"props": {
|
||||
"label": "CT Scan",
|
||||
"visible": True,
|
||||
"style": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
@ -432,7 +462,10 @@ XRAY_CONFIG_WITH_MISTAKE = {
|
||||
{
|
||||
"id": 3,
|
||||
"type": "tabs",
|
||||
"props": {"style": {}, "value": True},
|
||||
"props": {
|
||||
"style": {},
|
||||
"value": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
|
@ -293,7 +293,7 @@
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
value.__type__ == "update"
|
||||
value.__type__ === "update"
|
||||
) {
|
||||
for (const [update_key, update_value] of Object.entries(
|
||||
value
|
||||
|
@ -91,6 +91,12 @@
|
||||
children.filter((v) => instance_map[v.id].type !== "statustracker");
|
||||
|
||||
setContext("BLOCK_KEY", parent);
|
||||
|
||||
function handle_prop_change(e: { detail: Record<string, any> }) {
|
||||
for (const k in e.detail) {
|
||||
instance_map[id].props[k] = e.detail[k];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:component
|
||||
@ -98,6 +104,7 @@
|
||||
bind:this={instance_map[id].instance}
|
||||
bind:value={instance_map[id].props.value}
|
||||
elem_id={props.elem_id || id}
|
||||
on:prop_change={handle_prop_change}
|
||||
{...props}
|
||||
{root}
|
||||
>
|
||||
|
@ -4,8 +4,9 @@
|
||||
export let elem_id: string = "";
|
||||
export let visible: boolean = true;
|
||||
export let label: string;
|
||||
export let id: string | number;
|
||||
</script>
|
||||
|
||||
<TabItem {elem_id} {visible} name={label} on:select>
|
||||
<TabItem {elem_id} {visible} name={label} {id} on:select>
|
||||
<slot />
|
||||
</TabItem>
|
||||
|
@ -1,10 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { Tabs } from "@gradio/tabs";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let elem_id: string = "";
|
||||
export let visible: boolean = true;
|
||||
export let selected: number | string;
|
||||
|
||||
$: dispatch("prop_change", { selected });
|
||||
</script>
|
||||
|
||||
<Tabs {elem_id} {visible} on:change>
|
||||
<Tabs {elem_id} {visible} bind:selected on:change>
|
||||
<slot />
|
||||
</Tabs>
|
||||
|
@ -5,9 +5,9 @@
|
||||
export let elem_id: string = "";
|
||||
export let visible: boolean = true;
|
||||
export let name: string;
|
||||
export let id: string | number | object = {};
|
||||
|
||||
const dispatch = createEventDispatcher<{ select: undefined }>();
|
||||
const id = {};
|
||||
|
||||
const { register_tab, unregister_tab, selected_tab } = getContext(TABS);
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { setContext, createEventDispatcher } from "svelte";
|
||||
import { setContext, createEventDispatcher, onMount, tick } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
interface Tab {
|
||||
@ -13,16 +13,18 @@
|
||||
|
||||
export let elem_id: string;
|
||||
export let visible: boolean = true;
|
||||
export let selected: number | string | object;
|
||||
|
||||
const tabs: Array<Tab> = [];
|
||||
let tabs: Array<Tab> = [];
|
||||
|
||||
const selected_tab = writable<false | object>(false);
|
||||
const selected_tab = writable<false | object | number | string>(false);
|
||||
const dispatch = createEventDispatcher<{ change: undefined }>();
|
||||
|
||||
setContext(TABS, {
|
||||
register_tab: (tab: Tab) => {
|
||||
tabs.push({ name: tab.name, id: tab.id });
|
||||
selected_tab.update((current) => current || tab.id);
|
||||
selected_tab.update((current) => current ?? tab.id);
|
||||
tabs = tabs;
|
||||
},
|
||||
unregister_tab: (tab: Tab) => {
|
||||
const i = tabs.findIndex((t) => t.id === tab.id);
|
||||
@ -35,15 +37,17 @@
|
||||
selected_tab
|
||||
});
|
||||
|
||||
function handle_click(id: object) {
|
||||
function change_tab(id: object | string | number) {
|
||||
$selected_tab = id;
|
||||
dispatch("change");
|
||||
}
|
||||
|
||||
$: selected !== null && change_tab(selected);
|
||||
</script>
|
||||
|
||||
<div class="tabs flex flex-col my-4" id={elem_id}>
|
||||
<div class="flex border-b-2 dark:border-gray-700">
|
||||
{#each tabs as t, i}
|
||||
{#each tabs as t (t.id)}
|
||||
{#if t.id === $selected_tab}
|
||||
<button
|
||||
class="bg-white px-4 pb-2 pt-1.5 rounded-t-lg border-gray-200 -mb-[2px] border-2 border-b-0"
|
||||
@ -53,7 +57,7 @@
|
||||
{:else}
|
||||
<button
|
||||
class="px-4 pb-2 pt-1.5 border-transparent text-gray-400 hover:text-gray-700 -mb-[2px] border-2 border-b-0"
|
||||
on:click={() => handle_click(t.id)}
|
||||
on:click={() => (selected = t.id)}
|
||||
>
|
||||
{t.name}
|
||||
</button>
|
||||
|
Loading…
Reference in New Issue
Block a user