Skip to content

Commit

Permalink
Add Timer component (#8505)
Browse files Browse the repository at this point in the history
* chagnes

* add changeset

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* change

* add docs

* add changeset

* remove demo

* add changeset

* add changeset

* changes

* changes

* changes

* add changeset

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* Update gradio/components/timer.py

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* changes

* 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: aliabd <ali.si3luwa@gmail.com>
Co-authored-by: pngwn <hello@pngwn.io>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
  • Loading branch information
6 people authored Jun 28, 2024
1 parent 12f81fc commit 2943d6d
Show file tree
Hide file tree
Showing 92 changed files with 1,041 additions and 451 deletions.
10 changes: 10 additions & 0 deletions .changeset/hungry-rice-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@gradio/app": minor
"@gradio/client": minor
"@gradio/timer": minor
"gradio": minor
"gradio_client": minor
"website": minor
---

fix:Add Timer component
2 changes: 1 addition & 1 deletion client/js/src/helpers/api_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function transform_api_info(
dependencyIndex !== -1
? config.dependencies.find((dep) => dep.id == dependencyIndex)
?.types
: { continuous: false, generator: false, cancel: false };
: { generator: false, cancel: false };

if (
dependencyIndex !== -1 &&
Expand Down
8 changes: 2 additions & 6 deletions client/js/src/test/test_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const transformed_api_info: ApiInfo<ApiData> = {
component: "Textbox"
}
],
type: { continuous: false, generator: false, cancel: false }
type: { generator: false, cancel: false }
}
},
unnamed_endpoints: {
Expand All @@ -68,7 +68,7 @@ export const transformed_api_info: ApiInfo<ApiData> = {
component: "Textbox"
}
],
type: { continuous: false, generator: false, cancel: false }
type: { generator: false, cancel: false }
}
}
};
Expand Down Expand Up @@ -394,7 +394,6 @@ export const config_response: Config = {
max_batch_size: 4,
cancels: [],
types: {
continuous: false,
generator: false,
cancel: false
},
Expand All @@ -421,7 +420,6 @@ export const config_response: Config = {
max_batch_size: 4,
cancels: [],
types: {
continuous: false,
generator: false,
cancel: false
},
Expand All @@ -448,7 +446,6 @@ export const config_response: Config = {
max_batch_size: 4,
cancels: [],
types: {
continuous: false,
generator: false,
cancel: false
},
Expand Down Expand Up @@ -550,7 +547,6 @@ export const endpoint_info: EndpointInfo<ApiData> = {
}
],
type: {
continuous: false,
generator: false
}
};
Expand Down
1 change: 0 additions & 1 deletion client/js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ export interface Dependency {
}

export interface DependencyTypes {
continuous: boolean;
generator: boolean;
cancel: boolean;
}
Expand Down
6 changes: 0 additions & 6 deletions client/js/src/utils/predict.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ export async function predict(
)!;
}

if (dependency?.types.continuous) {
throw new Error(
"Cannot call predict on this function as it may run forever. Use submit instead"
);
}

return new Promise(async (resolve, reject) => {
const app = this.submit(endpoint, data, null, null, true);
let result: unknown;
Expand Down
7 changes: 1 addition & 6 deletions client/python/gradio_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,7 @@ def predict(
client.predict(5, "add", 4, api_name="/predict")
>> 9.0
"""
inferred_fn_index = self._infer_fn_index(api_name, fn_index)
if self.endpoints[inferred_fn_index].is_continuous:
raise ValueError(
"Cannot call predict on this function as it may run forever. Use submit instead."
)
self._infer_fn_index(api_name, fn_index)
return self.submit(
*args, api_name=api_name, fn_index=fn_index, **kwargs
).result()
Expand Down Expand Up @@ -1061,7 +1057,6 @@ def __init__(
self.parameters_info = self._get_parameters_info()

self.root_url = client.src + "/" if not client.src.endswith("/") else client.src
self.is_continuous = dependency.get("types", {}).get("continuous", False)

# Disallow hitting endpoints that the Gradio app has disabled
self.is_valid = self.api_name is not False
Expand Down
1 change: 0 additions & 1 deletion client/python/gradio_client/compatibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def __init__(self, client: Client, fn_index: int, dependency: dict, *_args):
self.input_component_types = []
self.output_component_types = []
self.root_url = client.src + "/" if not client.src.endswith("/") else client.src
self.is_continuous = dependency.get("types", {}).get("continuous", False)
try:
# Only a real API endpoint if backend_fn is True (so not just a frontend function), serializers are valid,
# and api_name is not False (meaning that the developer has explicitly disabled the API endpoint)
Expand Down
3 changes: 0 additions & 3 deletions client/python/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,3 @@ def test_upstream_exceptions(count_generator_demo_exception):
match="The upstream Gradio app has raised an exception but has not enabled verbose error reporting.",
):
client.predict(7, api_name="/count")

with pytest.raises(ValueError, match="Cannot call predict on this function"):
client.predict(5, api_name="/count_forever")
1 change: 1 addition & 0 deletions demo/function_values/run.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: function_values"]}, {"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", "import random\n", "\n", "countries = [\n", " \"Algeria\", \"Argentina\", \"Australia\", \"Brazil\", \"Canada\", \"China\", \"Democratic Republic of the Congo\", \"Greenland (Denmark)\", \"India\", \"Kazakhstan\", \"Mexico\", \"Mongolia\", \"Peru\", \"Russia\", \"Saudi Arabia\", \"Sudan\", \"United States\"\n", "]\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " count = gr.Slider(1, 10, step=1, label=\"Country Count\")\n", " alpha_order = gr.Checkbox(True, label=\"Alphabetical Order\")\n", "\n", " gr.JSON(lambda count, alpha_order: countries[:count] if alpha_order else countries[-count:], inputs=[count, alpha_order])\n", " timer = gr.Timer(1)\n", " with gr.Row():\n", " gr.Textbox(lambda: random.choice(countries), label=\"Random Country\", every=timer)\n", " gr.Textbox(lambda count: \", \".join(random.sample(countries, count)), inputs=count, label=\"Random Countries\", every=timer)\n", " with gr.Row():\n", " gr.Button(\"Start\").click(lambda: gr.Timer(active=True), None, timer)\n", " gr.Button(\"Stop\").click(lambda: gr.Timer(active=False), None, timer)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
24 changes: 24 additions & 0 deletions demo/function_values/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import gradio as gr
import random

countries = [
"Algeria", "Argentina", "Australia", "Brazil", "Canada", "China", "Democratic Republic of the Congo", "Greenland (Denmark)", "India", "Kazakhstan", "Mexico", "Mongolia", "Peru", "Russia", "Saudi Arabia", "Sudan", "United States"
]

with gr.Blocks() as demo:
with gr.Row():
count = gr.Slider(1, 10, step=1, label="Country Count")
alpha_order = gr.Checkbox(True, label="Alphabetical Order")

gr.JSON(lambda count, alpha_order: countries[:count] if alpha_order else countries[-count:], inputs=[count, alpha_order])
timer = gr.Timer(1)
with gr.Row():
gr.Textbox(lambda: random.choice(countries), label="Random Country", every=timer)
gr.Textbox(lambda count: ", ".join(random.sample(countries, count)), inputs=count, label="Random Countries", every=timer)
with gr.Row():
gr.Button("Start").click(lambda: gr.Timer(active=True), None, timer)
gr.Button("Stop").click(lambda: gr.Timer(active=False), None, timer)


if __name__ == "__main__":
demo.launch()
1 change: 1 addition & 0 deletions demo/timer/run.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: timer"]}, {"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", "import random\n", "import time\n", "\n", "with gr.Blocks() as demo:\n", " timer = gr.Timer(1)\n", " timestamp = gr.Number(label=\"Time\")\n", " timer.tick(lambda: round(time.time()), outputs=timestamp)\n", " gr.Number(lambda: round(time.time()), label=\"Time 2\", every=1)\n", "\n", " with gr.Row():\n", " timestamp_3 = gr.Number()\n", " start_btn = gr.Button(\"Start\")\n", " stop_btn = gr.Button(\"Stop\")\n", "\n", " time_3 = start_btn.click(lambda: round(time.time()), None, timestamp_3, every=1)\n", " stop_btn.click(fn=None, cancels=time_3)\n", "\n", " with gr.Row():\n", " min = gr.Number(1, label=\"Min\")\n", " max = gr.Number(10, label=\"Max\")\n", " timer2 = gr.Timer(1)\n", " number = gr.Number(lambda a, b: random.randint(a, b), inputs=[min, max], every=timer2, label=\"Random Number\")\n", " with gr.Row():\n", " gr.Button(\"Start\").click(lambda: gr.Timer(active=True), None, timer2)\n", " gr.Button(\"Stop\").click(lambda: gr.Timer(active=False), None, timer2)\n", " gr.Button(\"Go Fast\").click(lambda: 0.2, None, timer2)\n", " gr.Button(\"Go Slow\").click(lambda: 2, None, timer2)\n", " \n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
31 changes: 31 additions & 0 deletions demo/timer/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import gradio as gr
import random
import time

with gr.Blocks() as demo:
timer = gr.Timer(1)
timestamp = gr.Number(label="Time")
timer.tick(lambda: round(time.time()), outputs=timestamp)
gr.Number(lambda: round(time.time()), label="Time 2", every=1)

with gr.Row():
timestamp_3 = gr.Number()
start_btn = gr.Button("Start")
stop_btn = gr.Button("Stop")

time_3 = start_btn.click(lambda: round(time.time()), None, timestamp_3, every=1)
stop_btn.click(fn=None, cancels=time_3)

with gr.Row():
min = gr.Number(1, label="Min")
max = gr.Number(10, label="Max")
timer2 = gr.Timer(1)
number = gr.Number(lambda a, b: random.randint(a, b), inputs=[min, max], every=timer2, label="Random Number")
with gr.Row():
gr.Button("Start").click(lambda: gr.Timer(active=True), None, timer2)
gr.Button("Stop").click(lambda: gr.Timer(active=False), None, timer2)
gr.Button("Go Fast").click(lambda: 0.2, None, timer2)
gr.Button("Go Slow").click(lambda: 2, None, timer2)

if __name__ == "__main__":
demo.launch()
1 change: 1 addition & 0 deletions gradio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
State,
Text,
Textbox,
Timer,
UploadButton,
Video,
component,
Expand Down
15 changes: 11 additions & 4 deletions gradio/_simple_templates/simpledropdown.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from __future__ import annotations

import warnings
from typing import Any, Callable
from typing import TYPE_CHECKING, Any, Callable

from gradio.components.base import FormComponent
from gradio.components.base import Component, FormComponent
from gradio.events import Events

if TYPE_CHECKING:
from gradio.components import Timer


class SimpleDropdown(FormComponent):
"""
Expand All @@ -21,7 +24,8 @@ def __init__(
value: str | int | float | Callable | None = None,
label: str | None = None,
info: str | None = None,
every: float | None = None,
every: Timer | float | None = None,
inputs: Component | list[Component] | set[Component] | None = None,
show_label: bool | None = None,
scale: int | None = None,
min_width: int = 160,
Expand All @@ -38,7 +42,9 @@ def __init__(
value: default value selected in dropdown. If None, no value is selected by default. If callable, the function will be called whenever the app loads to set the initial value of the component.
label: component name in interface.
info: additional component description.
every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute.
every: Continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
inputs: Components that are used as inputs to calculate `value` if `value` is a function (has no effect otherwise). `value` is recalculated any time the inputs change.
inputs: Components that are used as inputs to calculate `value` if `value` is a function (has no effect otherwise). `value` is recalculated any time the inputs change.
show_label: if True, will display label.
scale: relative size compared to adjacent Components. For example if Components A and B are in a Row, and A has scale=2, and B has scale=1, A will be twice as wide as B. Should be an integer. scale applies in Rows, and to top-level Components in Blocks where fill_height=True.
min_width: minimum pixel width, will wrap if not sufficient screen space to satisfy this value. If a certain scale value results in this Component being narrower than min_width, the min_width parameter will be respected first.
Expand All @@ -58,6 +64,7 @@ def __init__(
label=label,
info=info,
every=every,
inputs=inputs,
show_label=show_label,
scale=scale,
min_width=min_width,
Expand Down
12 changes: 9 additions & 3 deletions gradio/_simple_templates/simpleimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from pathlib import Path
from typing import Any
from typing import TYPE_CHECKING, Any

from gradio_client import handle_file
from gradio_client.documentation import document
Expand All @@ -12,6 +12,9 @@
from gradio.data_classes import FileData
from gradio.events import Events

if TYPE_CHECKING:
from gradio.components import Timer


@document()
class SimpleImage(Component):
Expand All @@ -32,7 +35,8 @@ def __init__(
value: str | None = None,
*,
label: str | None = None,
every: float | None = None,
every: Timer | float | None = None,
inputs: Component | list[Component] | set[Component] | None = None,
show_label: bool | None = None,
show_download_button: bool = True,
container: bool = True,
Expand All @@ -49,7 +53,8 @@ def __init__(
Parameters:
value: A path or URL for the default value that SimpleImage component is going to take. If callable, the function will be called whenever the app loads to set the initial value of the component.
label: The label for this component. Appears above the component and is also used as the header if there are a table of examples for this component. If None and used in a `gr.Interface`, the label will be the name of the parameter this component is assigned to.
every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. Queue must be enabled. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute.
every: Continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
inputs: Components that are used as inputs to calculate `value` if `value` is a function (has no effect otherwise). `value` is recalculated any time the inputs change.
show_label: if True, will display label.
show_download_button: If True, will display button to download image.
container: If True, will place the component in a container - providing some extra padding around the border.
Expand All @@ -66,6 +71,7 @@ def __init__(
super().__init__(
label=label,
every=every,
inputs=inputs,
show_label=show_label,
container=container,
scale=scale,
Expand Down
14 changes: 10 additions & 4 deletions gradio/_simple_templates/simpletextbox.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import annotations

from typing import Any, Callable
from typing import TYPE_CHECKING, Any, Callable

from gradio.components.base import FormComponent
from gradio.components.base import Component, FormComponent
from gradio.events import Events

if TYPE_CHECKING:
from gradio.components import Timer


class SimpleTextbox(FormComponent):
"""
Expand All @@ -23,7 +26,8 @@ def __init__(
*,
placeholder: str | None = None,
label: str | None = None,
every: float | None = None,
every: Timer | float | None = None,
inputs: Component | list[Component] | set[Component] | None = None,
show_label: bool | None = None,
scale: int | None = None,
min_width: int = 160,
Expand All @@ -40,7 +44,8 @@ def __init__(
value: default text to provide in textbox. If callable, the function will be called whenever the app loads to set the initial value of the component.
placeholder: placeholder hint to provide behind textbox.
label: component name in interface.
every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute.
every: Continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
inputs: Components that are used as inputs to calculate `value` if `value` is a function (has no effect otherwise). `value` is recalculated any time the inputs change.
show_label: if True, will display label.
scale: relative size compared to adjacent Components. For example if Components A and B are in a Row, and A has scale=2, and B has scale=1, A will be twice as wide as B. Should be an integer. scale applies in Rows, and to top-level Components in Blocks where fill_height=True.
min_width: minimum pixel width, will wrap if not sufficient screen space to satisfy this value. If a certain scale value results in this Component being narrower than min_width, the min_width parameter will be respected first.
Expand All @@ -57,6 +62,7 @@ def __init__(
super().__init__(
label=label,
every=every,
inputs=inputs,
show_label=show_label,
scale=scale,
min_width=min_width,
Expand Down
Loading

0 comments on commit 2943d6d

Please sign in to comment.