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:
pngwn 2022-07-07 15:37:51 +01:00 committed by GitHub
parent c9f522e02b
commit 8c4e134094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 140 additions and 30 deletions

View File

@ -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()

View File

@ -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
}
]
}

View File

@ -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]):
"""

View File

@ -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,

View File

@ -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

View File

@ -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}
>

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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>