kvex.colors
Home of XColor
, SubTheme
, and Theme
.
The most common use of this module is get_color
. It is able to conveniently retrieve
any color from any palette in PALETTES
. The PALETTES
dictionary can be modified to
add or replace palettes.
1"""Home of `XColor`, `SubTheme`, and `Theme`. 2 3The most common use of this module is `get_color`. It is able to conveniently retrieve 4any color from any palette in `PALETTES`. The `PALETTES` dictionary can be modified to 5add or replace palettes. 6""" 7 8from typing import NamedTuple 9import colorsys 10import json 11import random 12from .assets import ASSETS_DIR 13 14 15_THEME_DATA_FILE = ASSETS_DIR / "defaultthemes.json" 16 17 18class XColor: 19 """A class to represent a color.""" 20 21 def __init__( 22 self, 23 r: float = 1, 24 g: float = 1, 25 b: float = 1, 26 a: float = 1, 27 ): 28 """Initialize the class. 29 30 Args: 31 r: Red component. 32 g: Green component. 33 b: Blue component. 34 a: Alpha component. 35 """ 36 self.__rgba = r, g, b, a 37 self.__hsv = colorsys.rgb_to_hsv(r, g, b) 38 39 @classmethod 40 def from_hex(cls, hex_str: str, /) -> "XColor": 41 """`XColor` from hex format.""" 42 rgb = tuple( 43 int(hex_str.removeprefix("#")[i:i+2], 16) / 256 44 for i in (0, 2, 4) 45 ) 46 return cls(*rgb) 47 48 @classmethod 49 def from_hsv(cls, h: float, s: float = 1, v: float = 1, a: float = 1) -> "XColor": 50 """`XColor` from hsv values.""" 51 return cls(*colorsys.hsv_to_rgb(h, s, v), a) 52 53 @classmethod 54 def from_name(cls, name: str, /, *, a: float = 1) -> "XColor": 55 """`XColor` from color name in `RAINBOW`.""" 56 return cls(*RAINBOW[name], a) 57 58 def offset_hue(self, offset: float = 0.5, /) -> "XColor": 59 """`XColor` that is offset in hue (between 0 and 1) from self.""" 60 h = (self.h + offset) % 1 61 return self.from_hsv(h, self.s, self.v, self.a) 62 63 def modified_hue(self, hue: float, /) -> "XColor": 64 """Identical `XColor` with different hue.""" 65 return self.from_hsv(hue, self.s, self.v, self.a) 66 67 def modified_value(self, value: float, /) -> "XColor": 68 """Identical `XColor` with different value.""" 69 return self.from_hsv(self.h, self.s, value, self.a) 70 71 def modified_saturation(self, saturation: float, /) -> "XColor": 72 """Identical `XColor` with different saturation.""" 73 return self.from_hsv(self.h, saturation, self.v, self.a) 74 75 def modified_alpha(self, alpha: float, /) -> "XColor": 76 """Identical `XColor` with different alpha.""" 77 return self.from_hsv(self.h, self.s, self.v, alpha) 78 79 @classmethod 80 def random(cls) -> "XColor": 81 """Random `XColor`.""" 82 return cls.from_hsv(random.random(), 1, 1) 83 84 @classmethod 85 def grey(cls, v: float = 0.5, /) -> "XColor": 86 """Grey `XColor`.""" 87 return cls.from_hsv(0, 0, v=v) 88 89 @classmethod 90 def white(cls) -> "XColor": 91 """White `XColor`.""" 92 return cls.from_hsv(0, 0, v=1) 93 94 @classmethod 95 def black(cls) -> "XColor": 96 """Black `XColor`.""" 97 return cls.from_hsv(0, 0, v=0) 98 99 @property 100 def r(self) -> float: 101 """The red component.""" 102 return self.__rgba[0] 103 104 @property 105 def g(self) -> float: 106 """The green component.""" 107 return self.__rgba[1] 108 109 @property 110 def b(self) -> float: 111 """The blue component.""" 112 return self.__rgba[2] 113 114 @property 115 def a(self) -> float: 116 """Alpha.""" 117 return self.__rgba[3] 118 119 @property 120 def h(self): 121 """Hue.""" 122 return self.__hsv[0] 123 124 @property 125 def s(self): 126 """Saturation.""" 127 return self.__hsv[1] 128 129 @property 130 def v(self): 131 """Value (from hsv).""" 132 return self.__hsv[2] 133 134 @property 135 def hsv(self): 136 """Hue, saturation, and value.""" 137 return self.__hsv 138 139 @property 140 def rgb(self) -> tuple[float, float, float]: 141 """The red, green, and blue components.""" 142 return self.__rgba[:3] 143 144 @property 145 def rgba(self) -> tuple[float, float, float, float]: 146 """The red, green, blue, and alpha components.""" 147 return self.__rgba 148 149 @property 150 def hex(self) -> str: 151 """Hex representation.""" 152 return "#" + "".join(hex(round(value * 256))[2:].zfill(2) for value in self.rgb) 153 154 def markup(self, s: str) -> str: 155 """Wrap a string in color markup.""" 156 return f"[color={self.hex}]{s}[/color]" 157 158 def __repr__(self): 159 """Object repr.""" 160 return f"<{self.__class__.__qualname__} {self.hex}>" 161 162 163RAINBOW = { 164 "black": (0.0, 0.0, 0.0), 165 "grey": (0.5, 0.5, 0.5), 166 "white": (1.0, 1.0, 1.0), 167 "red": (0.6, 0.0, 0.1), 168 "pink": (0.9, 0.3, 0.4), 169 "yellow": (0.8, 0.7, 0.1), 170 "orange": (0.7, 0.4, 0.0), 171 "lime": (0.1, 0.4, 0.0), 172 "green": (0.4, 0.7, 0.1), 173 "cyan": (0.1, 0.7, 0.7), 174 "blue": (0.1, 0.4, 0.9), 175 "navy": (0.0, 0.1, 0.5), 176 "violet": (0.7, 0.1, 0.9), 177 "purple": (0.4, 0.0, 0.7), 178 "magenta": (0.7, 0.0, 0.5), 179} 180"""Rainbow colors in RGB.""" 181 182 183class SubTheme(NamedTuple): 184 """Tuple of XColor for a theme.""" 185 186 bg: XColor 187 """Background color.""" 188 fg: XColor 189 """Primary foreground (text) color.""" 190 fg2: XColor 191 """Secondary foreground (text) color.""" 192 accent1: XColor 193 """Primary accent color.""" 194 accent2: XColor 195 """Secondary accent color.""" 196 197 @classmethod 198 def from_hexes(cls, *hexes) -> "SubTheme": 199 """`SubTheme` from list of colors in (string) hex format.""" 200 return cls(*(XColor.from_hex(h) for h in hexes)) 201 202 203SUBTHEME_COLORS = SubTheme._fields 204"""Color names in a `SubTheme`.""" 205 206 207class Theme(NamedTuple): 208 """Tuple of `SubTheme`s.""" 209 210 primary: SubTheme 211 """Primary subtheme.""" 212 secondary: SubTheme 213 """Secondary subtheme.""" 214 accent: SubTheme 215 """Accented subtheme.""" 216 palette: list[XColor] 217 """All colors in the palette of this theme.""" 218 219 220SUBTHEME_NAMES = ("primary", "secondary", "accent") 221"""`SubTheme` names in a `Theme`.""" 222 223 224def _convert_hexes(*hexes): 225 return tuple(XColor.from_hex(h) for h in hexes) 226 227 228def _import_theme_data() -> dict: 229 themes = dict() 230 with open(_THEME_DATA_FILE) as f: 231 raw_data = json.load(f) 232 for theme_name, theme in raw_data.items(): 233 theme_data = dict() 234 theme_data["palette"] = tuple(XColor.from_hex(h) for h in theme["palette"]) 235 for subtheme_name in SUBTHEME_NAMES: 236 theme_data[subtheme_name] = SubTheme(**{ 237 color_name: XColor.from_hex(color) 238 for color_name, color in theme[subtheme_name].items() 239 }) 240 themes[theme_name] = Theme(**theme_data) 241 return themes 242 243 244THEMES = _import_theme_data() 245"""Themes data.""" 246 247THEME_NAMES = tuple(THEMES.keys()) 248"""`Theme` names.""" 249 250 251__all__ = ( 252 "XColor", 253 "Theme", 254 "SubTheme", 255 "SUBTHEME_NAMES", 256 "SUBTHEME_COLORS", 257 "THEME_NAMES", 258 "RAINBOW", 259)
19class XColor: 20 """A class to represent a color.""" 21 22 def __init__( 23 self, 24 r: float = 1, 25 g: float = 1, 26 b: float = 1, 27 a: float = 1, 28 ): 29 """Initialize the class. 30 31 Args: 32 r: Red component. 33 g: Green component. 34 b: Blue component. 35 a: Alpha component. 36 """ 37 self.__rgba = r, g, b, a 38 self.__hsv = colorsys.rgb_to_hsv(r, g, b) 39 40 @classmethod 41 def from_hex(cls, hex_str: str, /) -> "XColor": 42 """`XColor` from hex format.""" 43 rgb = tuple( 44 int(hex_str.removeprefix("#")[i:i+2], 16) / 256 45 for i in (0, 2, 4) 46 ) 47 return cls(*rgb) 48 49 @classmethod 50 def from_hsv(cls, h: float, s: float = 1, v: float = 1, a: float = 1) -> "XColor": 51 """`XColor` from hsv values.""" 52 return cls(*colorsys.hsv_to_rgb(h, s, v), a) 53 54 @classmethod 55 def from_name(cls, name: str, /, *, a: float = 1) -> "XColor": 56 """`XColor` from color name in `RAINBOW`.""" 57 return cls(*RAINBOW[name], a) 58 59 def offset_hue(self, offset: float = 0.5, /) -> "XColor": 60 """`XColor` that is offset in hue (between 0 and 1) from self.""" 61 h = (self.h + offset) % 1 62 return self.from_hsv(h, self.s, self.v, self.a) 63 64 def modified_hue(self, hue: float, /) -> "XColor": 65 """Identical `XColor` with different hue.""" 66 return self.from_hsv(hue, self.s, self.v, self.a) 67 68 def modified_value(self, value: float, /) -> "XColor": 69 """Identical `XColor` with different value.""" 70 return self.from_hsv(self.h, self.s, value, self.a) 71 72 def modified_saturation(self, saturation: float, /) -> "XColor": 73 """Identical `XColor` with different saturation.""" 74 return self.from_hsv(self.h, saturation, self.v, self.a) 75 76 def modified_alpha(self, alpha: float, /) -> "XColor": 77 """Identical `XColor` with different alpha.""" 78 return self.from_hsv(self.h, self.s, self.v, alpha) 79 80 @classmethod 81 def random(cls) -> "XColor": 82 """Random `XColor`.""" 83 return cls.from_hsv(random.random(), 1, 1) 84 85 @classmethod 86 def grey(cls, v: float = 0.5, /) -> "XColor": 87 """Grey `XColor`.""" 88 return cls.from_hsv(0, 0, v=v) 89 90 @classmethod 91 def white(cls) -> "XColor": 92 """White `XColor`.""" 93 return cls.from_hsv(0, 0, v=1) 94 95 @classmethod 96 def black(cls) -> "XColor": 97 """Black `XColor`.""" 98 return cls.from_hsv(0, 0, v=0) 99 100 @property 101 def r(self) -> float: 102 """The red component.""" 103 return self.__rgba[0] 104 105 @property 106 def g(self) -> float: 107 """The green component.""" 108 return self.__rgba[1] 109 110 @property 111 def b(self) -> float: 112 """The blue component.""" 113 return self.__rgba[2] 114 115 @property 116 def a(self) -> float: 117 """Alpha.""" 118 return self.__rgba[3] 119 120 @property 121 def h(self): 122 """Hue.""" 123 return self.__hsv[0] 124 125 @property 126 def s(self): 127 """Saturation.""" 128 return self.__hsv[1] 129 130 @property 131 def v(self): 132 """Value (from hsv).""" 133 return self.__hsv[2] 134 135 @property 136 def hsv(self): 137 """Hue, saturation, and value.""" 138 return self.__hsv 139 140 @property 141 def rgb(self) -> tuple[float, float, float]: 142 """The red, green, and blue components.""" 143 return self.__rgba[:3] 144 145 @property 146 def rgba(self) -> tuple[float, float, float, float]: 147 """The red, green, blue, and alpha components.""" 148 return self.__rgba 149 150 @property 151 def hex(self) -> str: 152 """Hex representation.""" 153 return "#" + "".join(hex(round(value * 256))[2:].zfill(2) for value in self.rgb) 154 155 def markup(self, s: str) -> str: 156 """Wrap a string in color markup.""" 157 return f"[color={self.hex}]{s}[/color]" 158 159 def __repr__(self): 160 """Object repr.""" 161 return f"<{self.__class__.__qualname__} {self.hex}>"
A class to represent a color.
22 def __init__( 23 self, 24 r: float = 1, 25 g: float = 1, 26 b: float = 1, 27 a: float = 1, 28 ): 29 """Initialize the class. 30 31 Args: 32 r: Red component. 33 g: Green component. 34 b: Blue component. 35 a: Alpha component. 36 """ 37 self.__rgba = r, g, b, a 38 self.__hsv = colorsys.rgb_to_hsv(r, g, b)
Initialize the class.
Arguments:
- r: Red component.
- g: Green component.
- b: Blue component.
- a: Alpha component.
40 @classmethod 41 def from_hex(cls, hex_str: str, /) -> "XColor": 42 """`XColor` from hex format.""" 43 rgb = tuple( 44 int(hex_str.removeprefix("#")[i:i+2], 16) / 256 45 for i in (0, 2, 4) 46 ) 47 return cls(*rgb)
XColor
from hex format.
49 @classmethod 50 def from_hsv(cls, h: float, s: float = 1, v: float = 1, a: float = 1) -> "XColor": 51 """`XColor` from hsv values.""" 52 return cls(*colorsys.hsv_to_rgb(h, s, v), a)
XColor
from hsv values.
59 def offset_hue(self, offset: float = 0.5, /) -> "XColor": 60 """`XColor` that is offset in hue (between 0 and 1) from self.""" 61 h = (self.h + offset) % 1 62 return self.from_hsv(h, self.s, self.v, self.a)
XColor
that is offset in hue (between 0 and 1) from self.
64 def modified_hue(self, hue: float, /) -> "XColor": 65 """Identical `XColor` with different hue.""" 66 return self.from_hsv(hue, self.s, self.v, self.a)
Identical XColor
with different hue.
68 def modified_value(self, value: float, /) -> "XColor": 69 """Identical `XColor` with different value.""" 70 return self.from_hsv(self.h, self.s, value, self.a)
Identical XColor
with different value.
72 def modified_saturation(self, saturation: float, /) -> "XColor": 73 """Identical `XColor` with different saturation.""" 74 return self.from_hsv(self.h, saturation, self.v, self.a)
Identical XColor
with different saturation.
76 def modified_alpha(self, alpha: float, /) -> "XColor": 77 """Identical `XColor` with different alpha.""" 78 return self.from_hsv(self.h, self.s, self.v, alpha)
Identical XColor
with different alpha.
80 @classmethod 81 def random(cls) -> "XColor": 82 """Random `XColor`.""" 83 return cls.from_hsv(random.random(), 1, 1)
Random XColor
.
85 @classmethod 86 def grey(cls, v: float = 0.5, /) -> "XColor": 87 """Grey `XColor`.""" 88 return cls.from_hsv(0, 0, v=v)
Grey XColor
.
90 @classmethod 91 def white(cls) -> "XColor": 92 """White `XColor`.""" 93 return cls.from_hsv(0, 0, v=1)
White XColor
.
208class Theme(NamedTuple): 209 """Tuple of `SubTheme`s.""" 210 211 primary: SubTheme 212 """Primary subtheme.""" 213 secondary: SubTheme 214 """Secondary subtheme.""" 215 accent: SubTheme 216 """Accented subtheme.""" 217 palette: list[XColor] 218 """All colors in the palette of this theme."""
Tuple of SubTheme
s.
Create new instance of Theme(primary, secondary, accent, palette)
Inherited Members
- builtins.tuple
- index
- count
184class SubTheme(NamedTuple): 185 """Tuple of XColor for a theme.""" 186 187 bg: XColor 188 """Background color.""" 189 fg: XColor 190 """Primary foreground (text) color.""" 191 fg2: XColor 192 """Secondary foreground (text) color.""" 193 accent1: XColor 194 """Primary accent color.""" 195 accent2: XColor 196 """Secondary accent color.""" 197 198 @classmethod 199 def from_hexes(cls, *hexes) -> "SubTheme": 200 """`SubTheme` from list of colors in (string) hex format.""" 201 return cls(*(XColor.from_hex(h) for h in hexes))
Tuple of XColor for a theme.
Create new instance of SubTheme(bg, fg, fg2, accent1, accent2)
198 @classmethod 199 def from_hexes(cls, *hexes) -> "SubTheme": 200 """`SubTheme` from list of colors in (string) hex format.""" 201 return cls(*(XColor.from_hex(h) for h in hexes))
SubTheme
from list of colors in (string) hex format.
Inherited Members
- builtins.tuple
- index
- count
Color names in a SubTheme
.
Theme
names.
Rainbow colors in RGB.