mirror of
https://github.com/gradio-app/gradio.git
synced 2025-03-31 12:20:26 +08:00
Color picker (#1695)
* Add new colorpicker component skeleton * Updated colorpicker skeleton with some functionalities * - Fix default input color - Change input style * Add colorpicker demo * Add tests and code format Co-authored-by: mfumanelli <fumanellimartina@gmail.com>
This commit is contained in:
parent
68afe67497
commit
8f89270f50
42
demo/color_picker/run.py
Normal file
42
demo/color_picker/run.py
Normal file
@ -0,0 +1,42 @@
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
from PIL import Image, ImageColor
|
||||
|
||||
|
||||
|
||||
def change_color(icon, color):
|
||||
|
||||
|
||||
"""
|
||||
Function that given an icon in .png format changes its color
|
||||
Args:
|
||||
icon: Icon whose color needs to be changed.
|
||||
color: Chosen color with which to edit the input icon.
|
||||
Returns:
|
||||
edited_image: Edited icon.
|
||||
"""
|
||||
img = icon.convert("LA")
|
||||
img = img.convert("RGBA")
|
||||
image_np = np.array(icon)
|
||||
_, _, _, alpha = image_np.T
|
||||
mask = (alpha > 0)
|
||||
image_np[..., :-1][mask.T] = ImageColor.getcolor(color, "RGB")
|
||||
edited_image = Image.fromarray(image_np)
|
||||
return edited_image
|
||||
|
||||
|
||||
inputs = [
|
||||
gr.Image(label="icon", type="pil", image_mode="RGBA"),
|
||||
gr.ColorPicker(label="color")
|
||||
]
|
||||
outputs = gr.Image(label="colored icon")
|
||||
|
||||
demo = gr.Interface(
|
||||
fn=change_color,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
||||
|
BIN
demo/color_picker/screenshot.gif
Normal file
BIN
demo/color_picker/screenshot.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 402 KiB |
BIN
demo/color_picker/screenshot.png
Normal file
BIN
demo/color_picker/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -16,6 +16,7 @@ from gradio.components import (
|
||||
Checkbox,
|
||||
Checkboxgroup,
|
||||
CheckboxGroup,
|
||||
ColorPicker,
|
||||
DataFrame,
|
||||
Dataframe,
|
||||
Dataset,
|
||||
|
@ -2826,6 +2826,117 @@ class Button(Clickable, IOComponent):
|
||||
)
|
||||
|
||||
|
||||
class ColorPicker(Changeable, Submittable, IOComponent):
|
||||
"""
|
||||
Creates a color picker for user to select a color as string input.
|
||||
Preprocessing: passes selected color value as a {str} into the function.
|
||||
Postprocessing: expects a {str} returned from function and sets color picker value to it.
|
||||
Demos: color_picker
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: str = None,
|
||||
*,
|
||||
label: Optional[str] = None,
|
||||
show_label: bool = True,
|
||||
interactive: Optional[bool] = None,
|
||||
visible: bool = True,
|
||||
elem_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
value (str): default text to provide in color picker.
|
||||
label (Optional[str]): component name in interface.
|
||||
show_label (bool): if True, will display label.
|
||||
interactive (Optional[bool]): if True, will be rendered as an editable color picker; if False, editing will be disabled. If not provided, this is inferred based on whether the component is used as an input or output.
|
||||
visible (bool): If False, component will be hidden.
|
||||
elem_id (Optional[str]): An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
"""
|
||||
self.value = self.postprocess(value)
|
||||
self.cleared_value = "#000000"
|
||||
self.test_input = value
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
show_label=show_label,
|
||||
interactive=interactive,
|
||||
visible=visible,
|
||||
elem_id=elem_id,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
"value": self.value,
|
||||
**IOComponent.get_config(self),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def update(
|
||||
value: Optional[Any] = None,
|
||||
label: Optional[str] = None,
|
||||
show_label: Optional[bool] = None,
|
||||
visible: Optional[bool] = None,
|
||||
interactive: Optional[bool] = None,
|
||||
):
|
||||
updated_config = {
|
||||
"value": value,
|
||||
"label": label,
|
||||
"show_label": show_label,
|
||||
"visible": visible,
|
||||
"__type__": "update",
|
||||
}
|
||||
return IOComponent.add_interactive_to_config(updated_config, interactive)
|
||||
|
||||
# Input Functionalities
|
||||
def preprocess(self, x: str | None) -> Any:
|
||||
"""
|
||||
Any preprocessing needed to be performed on function input.
|
||||
Parameters:
|
||||
x (str): text
|
||||
Returns:
|
||||
(str): text
|
||||
"""
|
||||
if x is None:
|
||||
return None
|
||||
else:
|
||||
return str(x)
|
||||
|
||||
def preprocess_example(self, x: str | None) -> Any:
|
||||
"""
|
||||
Any preprocessing needed to be performed on an example before being passed to the main function.
|
||||
"""
|
||||
if x is None:
|
||||
return None
|
||||
else:
|
||||
return str(x)
|
||||
|
||||
def generate_sample(self) -> str:
|
||||
return "#000000"
|
||||
|
||||
# Output Functionalities
|
||||
def postprocess(self, y: str | None):
|
||||
"""
|
||||
Any postprocessing needed to be performed on function output.
|
||||
Parameters:
|
||||
y (str | None): text
|
||||
Returns:
|
||||
(str | None): text
|
||||
"""
|
||||
if y is None:
|
||||
return None
|
||||
else:
|
||||
return str(y)
|
||||
|
||||
def deserialize(self, x):
|
||||
"""
|
||||
Convert from serialized output (e.g. base64 representation) from a call() to the interface to a human-readable version of the output (path of an image, etc.)
|
||||
"""
|
||||
return x
|
||||
|
||||
|
||||
############################
|
||||
# Only Output Components
|
||||
############################
|
||||
|
@ -1669,5 +1669,57 @@ class TestModel3D(unittest.TestCase):
|
||||
self.assertEqual(input_data.split(";")[1], output_data.split(";")[1])
|
||||
|
||||
|
||||
class TestColorPicker(unittest.TestCase):
|
||||
def test_component_functions(self):
|
||||
"""
|
||||
Preprocess, postprocess, serialize, save_flagged, restore_flagged, tokenize, generate_sample, get_config
|
||||
"""
|
||||
color_picker_input = gr.ColorPicker()
|
||||
self.assertEqual(color_picker_input.preprocess("#000000"), "#000000")
|
||||
self.assertEqual(color_picker_input.preprocess_example("#000000"), "#000000")
|
||||
self.assertEqual(color_picker_input.postprocess(None), None)
|
||||
self.assertEqual(color_picker_input.postprocess("#FFFFFF"), "#FFFFFF")
|
||||
self.assertEqual(color_picker_input.serialize("#000000", True), "#000000")
|
||||
|
||||
color_picker_input.interpretation_replacement = "unknown"
|
||||
|
||||
self.assertEqual(
|
||||
color_picker_input.get_config(),
|
||||
{
|
||||
"value": None,
|
||||
"show_label": True,
|
||||
"label": None,
|
||||
"style": {},
|
||||
"elem_id": None,
|
||||
"visible": True,
|
||||
"interactive": None,
|
||||
"name": "colorpicker",
|
||||
},
|
||||
)
|
||||
self.assertIsInstance(color_picker_input.generate_sample(), str)
|
||||
|
||||
def test_in_interface_as_input(self):
|
||||
"""
|
||||
Interface, process, interpret,
|
||||
"""
|
||||
iface = gr.Interface(lambda x: x, "colorpicker", "colorpicker")
|
||||
self.assertEqual(iface.process(["#000000"]), ["#000000"])
|
||||
|
||||
def test_in_interface_as_output(self):
|
||||
"""
|
||||
Interface, process
|
||||
|
||||
"""
|
||||
iface = gr.Interface(lambda x: x, "colorpicker", gr.ColorPicker())
|
||||
self.assertEqual(iface.process(["#000000"]), ["#000000"])
|
||||
|
||||
def test_static(self):
|
||||
"""
|
||||
postprocess
|
||||
"""
|
||||
component = gr.ColorPicker("#000000")
|
||||
self.assertEqual(component.get_config().get("value"), "#000000")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
@ -0,0 +1,40 @@
|
||||
<svelte:options accessors={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import { ColorPicker } from "@gradio/form";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import StatusTracker from "../StatusTracker/StatusTracker.svelte";
|
||||
import type { LoadingStatus } from "../StatusTracker/types";
|
||||
|
||||
export let label: string = "ColorPicker";
|
||||
export let elem_id: string = "";
|
||||
export let visible: boolean = true;
|
||||
export let value: string;
|
||||
export let form_position: "first" | "last" | "mid" | "single" = "single";
|
||||
export let show_label: boolean;
|
||||
|
||||
export let style: Record<string, unknown> = {};
|
||||
|
||||
export let loading_status: LoadingStatus;
|
||||
|
||||
export let mode: "static" | "dynamic";
|
||||
</script>
|
||||
|
||||
<Block
|
||||
{visible}
|
||||
{form_position}
|
||||
{elem_id}
|
||||
disable={typeof style.container === "boolean" && !style.container}
|
||||
>
|
||||
<StatusTracker {...loading_status} />
|
||||
|
||||
<ColorPicker
|
||||
{style}
|
||||
bind:value
|
||||
{label}
|
||||
{show_label}
|
||||
on:change
|
||||
on:submit
|
||||
disabled={mode === "static"}
|
||||
/>
|
||||
</Block>
|
@ -0,0 +1,35 @@
|
||||
import { test, describe, assert, afterEach } from "vitest";
|
||||
import { cleanup, render } from "@gradio/tootils";
|
||||
|
||||
import ColorPicker from "./ColorPicker.svelte";
|
||||
|
||||
describe("ColorPicker", () => {
|
||||
afterEach(() => cleanup());
|
||||
|
||||
test("renders provided value", () => {
|
||||
const { getByDisplayValue } = render(ColorPicker, {
|
||||
value: "#000000",
|
||||
label: "ColorPicker"
|
||||
});
|
||||
|
||||
const item: HTMLInputElement = getByDisplayValue("#000000");
|
||||
assert.equal(item.value, "#000000");
|
||||
});
|
||||
|
||||
test("changing the color should update the value", async () => {
|
||||
const { component, getByDisplayValue } = render(ColorPicker, {
|
||||
value: "#000000",
|
||||
label: "ColorPicker"
|
||||
});
|
||||
|
||||
const item: HTMLInputElement = getByDisplayValue("#000000");
|
||||
|
||||
assert.equal(item.value, "#000000");
|
||||
|
||||
await component.$set({
|
||||
value: "#FFFFFF"
|
||||
});
|
||||
|
||||
assert.equal(component.value, "#FFFFFF");
|
||||
});
|
||||
});
|
2
ui/packages/app/src/components/ColorPicker/index.ts
Normal file
2
ui/packages/app/src/components/ColorPicker/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as Component } from "./ColorPicker.svelte";
|
||||
export const modes = ["static", "dynamic"];
|
@ -6,6 +6,7 @@ export const component_map: Record<string, any> = {
|
||||
chatbot: () => import("./Chatbot"),
|
||||
checkbox: () => import("./Checkbox"),
|
||||
checkboxgroup: () => import("./CheckboxGroup"),
|
||||
colorpicker: () => import("./ColorPicker"),
|
||||
column: () => import("./Column"),
|
||||
dataframe: () => import("./DataFrame"),
|
||||
dataset: () => import("./Dataset"),
|
||||
|
36
ui/packages/form/src/ColorPicker.svelte
Normal file
36
ui/packages/form/src/ColorPicker.svelte
Normal file
@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { get_styles } from "@gradio/utils";
|
||||
import { BlockTitle } from "@gradio/atoms";
|
||||
|
||||
export let value: string = "#000000";
|
||||
export let style: Record<string, unknown> = {};
|
||||
export let label: string;
|
||||
export let disabled = false;
|
||||
export let show_label: boolean = true;
|
||||
|
||||
$: value;
|
||||
$: handle_change(value);
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: string;
|
||||
submit: undefined;
|
||||
}>();
|
||||
|
||||
function handle_change(val: string) {
|
||||
dispatch("change", val);
|
||||
}
|
||||
|
||||
$: ({ classes } = get_styles(style, ["rounded", "border"]));
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label class="block">
|
||||
<BlockTitle {show_label}>{label}</BlockTitle>
|
||||
<input
|
||||
type="color"
|
||||
class="gr-box-unrounded {classes}"
|
||||
bind:value
|
||||
{disabled}
|
||||
/>
|
||||
</label>
|
@ -5,3 +5,4 @@ export { default as Number } from "./Number.svelte";
|
||||
export { default as Radio } from "./Radio.svelte";
|
||||
export { default as TextBox } from "./Textbox.svelte";
|
||||
export { default as Range } from "./Range.svelte";
|
||||
export { default as ColorPicker } from "./ColorPicker.svelte";
|
||||
|
Loading…
x
Reference in New Issue
Block a user