Skip to content

Commit

Permalink
Gui/colorpicker (#2354)
Browse files Browse the repository at this point in the history
* Add color picker example
  • Loading branch information
eruvanos authored Aug 24, 2024
1 parent 10143b9 commit 19b53a1
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 15 deletions.
5 changes: 2 additions & 3 deletions arcade/examples/gui/0_basic_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
UITextureButton,
UIAnchorLayout,
)
from arcade.types import Color

# Preload textures, because they are mostly used multiple times, so they are not
# loaded multiple times
Expand Down Expand Up @@ -60,7 +59,7 @@ def on_hide_view(self) -> None:

def on_draw(self):
# Clear the screen
self.clear(color=Color(46, 204, 113))
self.clear(color=arcade.uicolor.GREEN_EMERALD)

# Add draw commands that should be below the UI
# ...
Expand All @@ -76,7 +75,7 @@ class BlueView(arcade.gui.UIView):

def __init__(self):
super().__init__()
self.background_color = Color(52, 152, 219)
self.background_color = arcade.uicolor.BLUE_PETER_RIVER

# Create a anchor layout, which can be used to position widgets on screen
anchor = self.add_widget(UIAnchorLayout())
Expand Down
13 changes: 6 additions & 7 deletions arcade/examples/gui/1_layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import arcade
from arcade.gui import UIAnchorLayout
from arcade.types import Color

arcade.resources.load_system_fonts()

Expand Down Expand Up @@ -66,22 +65,22 @@ class LayoutView(arcade.gui.UIView):

def __init__(self):
super().__init__()
self.background_color = Color(52, 152, 219)
self.background_color = arcade.uicolor.BLUE_PETER_RIVER

# Create a anchor layout, which can be used to position widgets on screen
self.anchor = self.add_widget(UIAnchorLayout())

# Add describing text in center
text_area = arcade.gui.UITextArea(
text=DESCRIPTION,
text_color=Color(236, 240, 241),
text_color=arcade.uicolor.WHITE_CLOUDS,
font_name=("Lato", "proxima-nova", "Helvetica Neue", "Arial", "sans-serif"),
font_size=12,
size_hint=(0.5, 0.8),
)
self.anchor.add(text_area, anchor_x="center_x", anchor_y="center_y")
text_area.with_border(color=Color(149, 165, 166))
text_area.with_background(color=Color(149, 165, 166, 125))
text_area.with_border(color=arcade.uicolor.GRAY_CONCRETE)
text_area.with_background(color=arcade.uicolor.GRAY_CONCRETE.replace(a=125))
text_area.with_padding(left=5)

# add a grid layout with the window and grid size and grid position
Expand All @@ -90,8 +89,8 @@ def __init__(self):
row_count=2,
align_horizontal="left",
)
self.grid.with_background(color=Color(149, 165, 166))
self.grid.with_border(color=Color(127, 140, 141))
self.grid.with_background(color=arcade.uicolor.GRAY_CONCRETE)
self.grid.with_border(color=arcade.uicolor.GRAY_ASBESTOS)
self.grid.with_padding(all=10)
self.anchor.add(self.grid, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
self.grid.add(
Expand Down
2 changes: 1 addition & 1 deletion arcade/examples/gui/4_gui_and_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,6 @@ def on_key_release(self, symbol: int, modifiers: int) -> Optional[bool]:

if __name__ == "__main__":
window = arcade.Window(1280, 720, "GUI Example: Coin Game (Camera)", resizable=False)
window.background_color = arcade.color.DARK_BLUE_GRAY
window.background_color = arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE
window.show_view(MyCoinGame())
window.run()
198 changes: 198 additions & 0 deletions arcade/examples/gui/5_uicolor_picker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
"""Show all arcade.uicolors in a grid.
Click on a color to select
it and copy the arcade reference to the clipboard.
If arcade and Python are properly installed, you can run this example with:
python -m arcade.examples.gui.5_uicolor_picker
"""

from dataclasses import dataclass

import arcade
from arcade.gui import (
UIAnchorLayout,
UIEvent,
UIInteractiveWidget,
UITextWidget,
)


@dataclass
class ChooseColorEvent(UIEvent):
"""Custom event, which is dispatched when a color button is clicked."""

color_name: str
color: arcade.color.Color


class Toast(arcade.gui.UILabel):
"""Label which disappears after a certain time."""

def __init__(self, text: str, duration: float = 2.0, **kwargs):
super().__init__(**kwargs)
self.text = text
self.duration = duration
self.time = 0

def on_update(self, dt):
self.time += dt

if self.time > self.duration:
self.parent.remove(self)


class ColorButton(UITextWidget, UIInteractiveWidget):
"""Button which shows a color and color name and
emits a ChooseColorEvent event when clicked."""

def __init__(
self,
color_name: str,
color: arcade.color.Color,
**kwargs,
):
super().__init__(text=color_name, **kwargs)
# set color and place text on the bottom
self.with_background(color=color)
self.place_text(anchor_y="bottom")

# set font color based on background color
f = 2 if color_name.startswith("DARK") else 0.5
self.ui_label.update_font(
font_color=arcade.color.Color(int(color[0] * f), int(color[1] * f), int(color[2] * f))
)

# store color name and color for later reference
self._color_name = color_name
self._color = color

# register custom event
self.register_event_type("on_choose_color")

def on_update(self, dt):
"""Update the button state.
UIInteractiveWidget provides properties like hovered and pressed,
which can be used to highlight the button."""
if self.pressed:
self.with_border(color=arcade.uicolor.WHITE_CLOUDS, width=3)
elif self.hovered:
self.with_border(color=arcade.uicolor.WHITE_CLOUDS, width=2)
else:
self.with_border(color=arcade.color.BLACK, width=1)

def on_click(self, event) -> bool:
"""Emit a ChooseColorEvent event when clicked."""
self.dispatch_event(
"on_choose_color", ChooseColorEvent(self, self._color_name, self._color)
)
return True

def on_choose_color(self, event: ChooseColorEvent):
"""ChooseColorEvent event handler, which can be overridden."""
pass


class ColorView(arcade.gui.UIView):
"""Uses the arcade.gui.UIView which takes care about the UIManager setup."""

def __init__(self):
super().__init__()
# Create an anchor layout, which can be used to position widgets on screen
self.root = self.add_widget(UIAnchorLayout())

# Define colors in grid order
self.colors = {
# row 0
"GREEN_TURQUOISE": arcade.uicolor.GREEN_TURQUOISE,
"GREEN_EMERALD": arcade.uicolor.GREEN_EMERALD,
"BLUE_PETER_RIVER": arcade.uicolor.BLUE_PETER_RIVER,
"PURPLE_AMETHYST": arcade.uicolor.PURPLE_AMETHYST,
"DARK_BLUE_WET_ASPHALT": arcade.uicolor.DARK_BLUE_WET_ASPHALT,
# row 1
"GREEN_GREEN_SEA": arcade.uicolor.GREEN_GREEN_SEA,
"GREEN_NEPHRITIS": arcade.uicolor.GREEN_NEPHRITIS,
"BLUE_BELIZE_HOLE": arcade.uicolor.BLUE_BELIZE_HOLE,
"PURPLE_WISTERIA": arcade.uicolor.PURPLE_WISTERIA,
"DARK_BLUE_MIDNIGHT_BLUE": arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE,
# row 2
"YELLOW_SUN_FLOWER": arcade.uicolor.YELLOW_SUN_FLOWER,
"ORANGE_CARROT": arcade.uicolor.ORANGE_CARROT,
"RED_ALIZARIN": arcade.uicolor.RED_ALIZARIN,
"WHITE_CLOUDS": arcade.uicolor.WHITE_CLOUDS,
"GRAY_CONCRETE": arcade.uicolor.GRAY_CONCRETE,
# row 3
"YELLOW_ORANGE": arcade.uicolor.YELLOW_ORANGE,
"ORANGE_PUMPKIN": arcade.uicolor.ORANGE_PUMPKIN,
"RED_POMEGRANATE": arcade.uicolor.RED_POMEGRANATE,
"WHITE_SILVER": arcade.uicolor.WHITE_SILVER,
"GRAY_ASBESTOS": arcade.uicolor.GRAY_ASBESTOS,
}

# setup grid with colors
self.grid = self.root.add(
arcade.gui.UIGridLayout(
column_count=5,
row_count=4,
size_hint=(1, 1),
)
)
for i, (name, color) in enumerate(self.colors.items()):
button = self.root.add(
ColorButton(
color_name=name,
color=color,
# giving width and height is a workaround for a bug in the grid layout
# we would want to set size_hint=(1, 1) and let
# the grid layout handle the size
width=self.window.width // 5,
height=self.window.height // 4,
size_hint=None,
)
)
self.grid.add(button, row=i // 5, column=i % 5)

# connect event handler
button.on_choose_color = self.on_color_button_choose_color

# setup toasts (temporary messages)
self.toasts = self.root.add(
arcade.gui.UIBoxLayout(space_between=2), anchor_x="right", anchor_y="top"
)
self.toasts.with_padding(all=10)

def on_color_button_choose_color(self, event: ChooseColorEvent) -> bool:
"""Color button click event handler, which copies the color name to the clipboard.
And shows a temporary message."""
self.window.set_clipboard_text(f"arcade.uicolor.{event.color_name}")

# prepare and show toast
toast = Toast(f"Copied {event.color_name}", width=250, size_hint=(None, 0))
toast.update_font(
font_color=arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE,
font_size=9,
bold=True,
)
toast.with_background(color=arcade.uicolor.GREEN_EMERALD)
toast.with_padding(all=10)

self.toasts.add(toast)

return True

def on_draw_before_ui(self):
# Add draw commands that should be below the UI
pass

def on_draw_after_ui(self):
# Add draw commands that should be on top of the UI (uncommon)
pass


if __name__ == "__main__":
window = arcade.Window(title="GUI Example: Color Picker")
window.show_view(ColorView())
window.run()
4 changes: 2 additions & 2 deletions arcade/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,8 +540,8 @@ def __init__(
*,
x: float = 0,
y: float = 0,
width: float,
height: float,
width: float = 100,
height: float = 100,
size_hint=None,
size_hint_min=None,
size_hint_max=None,
Expand Down
18 changes: 17 additions & 1 deletion arcade/gui/widgets/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ def update_font(
font_name: Optional[FontNameOrNames] = None,
font_size: Optional[float] = None,
font_color: Optional[Color] = None,
bold: Optional[bool | str] = None,
italic: Optional[bool] = None,
):
"""Update font of the label.
Expand All @@ -231,20 +233,34 @@ def update_font(
success.
font_size: Font size of font.
font_color: Color of the text.
bold: If enabled, the label's text will be in a **bold** style.
italic: If enabled, the label's text will be in an *italic*
"""
font_name = font_name or self._label.font_name
font_size = font_size or self._label.font_size
font_color = font_color or self._label.color
font_bold = bold if bold is not None else self._label.bold
font_italic = italic if italic is not None else self._label.italic

# Check if values actually changed, if then update and trigger render
font_name_changed = self._label.font_name != font_name
font_size_changed = self._label.font_size != font_size
font_color_changed = self._label.color != font_color
if font_name_changed or font_size_changed or font_color_changed:
font_bold_changed = self._label.bold != font_bold
font_italic_changed = self._label.italic != font_italic
if (
font_name_changed
or font_size_changed
or font_color_changed
or font_bold_changed
or font_italic_changed
):
with self._label:
self._label.font_name = font_name
self._label.font_size = font_size
self._label.color = font_color
self._label.bold = font_bold
self._label.italic = font_italic
self._update_size_hint_min()

# Optimised render behaviour
Expand Down
2 changes: 1 addition & 1 deletion arcade/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ def italic(self) -> bool | str:
return self._label.italic

@italic.setter
def italic(self, italic: bool):
def italic(self, italic: bool | str):
self._label.italic = italic

@property
Expand Down
21 changes: 21 additions & 0 deletions doc/example_code/gui_5_uicolor_picker.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
:orphan:

.. _gui_5_uicolor_picker:

GUI UIColor Picker
==================

Arcade provides constants for colors, which work great for GUI widgets.
They are based on FlatUI colors.

This example provides an interactive color picker, widgets can be used as a util
to find the right color for your GUI widgets.

.. image:: images/gui_5_uicolor_picker.png
:width: 600px
:align: center
:alt: Screen shot of advanced button usage

.. literalinclude:: ../../arcade/examples/gui/5_uicolor_picker.py
:caption: 5_uicolor_picker.py
:linenos:
Binary file added doc/example_code/images/gui_5_uicolor_picker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions doc/example_code/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,12 @@ Graphical User Interface
:ref:`gui_3_buttons`


.. figure:: images/thumbs/gui_5_uicolor_picker.png
:figwidth: 170px
:target: gui_5_uicolor_picker.html

:ref:`gui_5_uicolor_picker`

.. note::

Not all existing examples made it into this section. You can find more under `Arcade GUI Examples <https://github.com/pythonarcade/arcade/tree/development/arcade/examples/gui>`_
Expand Down

0 comments on commit 19b53a1

Please sign in to comment.