xulbux 1.5.5__py3-none-any.whl → 1.5.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xulbux might be problematic. Click here for more details.

xulbux/xx_color.py CHANGED
@@ -31,14 +31,11 @@ The `Color` class, which contains all sorts of different color-related methods:
31
31
  - saturation
32
32
  """
33
33
 
34
-
35
34
  from .xx_regex import *
36
35
 
37
36
  import re as _re
38
37
 
39
38
 
40
-
41
-
42
39
  class rgba:
43
40
  """An RGB/RGBA color: is a tuple of 3 integers, representing the red (`0`-`255`), green (`0`-`255`), and blue (`0`-`255`).<br>
44
41
  Also includes an optional 4th param, which is a float, that represents the alpha channel (`0.0`-`1.0`).\n
@@ -62,14 +59,18 @@ class rgba:
62
59
  - `with_alpha(alpha)` to create a new color with different alpha
63
60
  - `complementary()` to get the complementary color"""
64
61
 
65
- def __init__(self, r:int, g:int, b:int, a:float = None):
62
+ def __init__(self, r: int, g: int, b: int, a: float = None):
66
63
  if any(isinstance(x, rgba) for x in (r, g, b)):
67
- raise ValueError('Color is already a rgba() color')
64
+ raise ValueError("Color is already a rgba() color")
68
65
  elif not all(isinstance(x, int) and 0 <= x <= 255 for x in (r, g, b)):
69
- raise ValueError('RGBA color must have R G B as integers in [0, 255]: got', (r, g, b))
70
- elif not a is None and not (isinstance(a, (int, float)) and 0 <= a <= 1):
71
- raise ValueError('Alpha channel must be a float/int in [0.0, 1.0]: got', a)
72
- self.r, self.g, self.b, self.a = r, g, b, (1.0 if a > 1.0 else float(a)) if a else None
66
+ raise ValueError(
67
+ "RGBA color must have R G B as integers in [0, 255]: got",
68
+ (r, g, b),
69
+ )
70
+ elif a is not None and not (isinstance(a, (int, float)) and 0 <= a <= 1):
71
+ raise ValueError("Alpha channel must be a float/int in [0.0, 1.0]: got", a)
72
+ self.r, self.g, self.b = r, g, b
73
+ self.a = (1.0 if a > 1.0 else float(a)) if a else None
73
74
 
74
75
  def __len__(self):
75
76
  return 4 if self.a else 3
@@ -77,6 +78,9 @@ class rgba:
77
78
  def __iter__(self):
78
79
  return iter((self.r, self.g, self.b) + ((self.a,) if self.a else ()))
79
80
 
81
+ def __dict__(self):
82
+ return self.dict()
83
+
80
84
  def __getitem__(self, index):
81
85
  return ((self.r, self.g, self.b) + ((self.a,) if self.a else ()))[index]
82
86
 
@@ -87,16 +91,14 @@ class rgba:
87
91
  return f'({self.r}, {self.g}, {self.b}{f", {self.a}" if self.a else ""})'
88
92
 
89
93
  def __eq__(self, other):
90
- if not isinstance(other, rgba): return False
91
- return (self.r, self.g, self.b, self.a) == (other[0], other[1], other[2], other[3])
92
-
93
- def list(self) -> list:
94
- """Returns the color components as a list `[r, g, b]` or `[r, g, b, a]` if alpha is present"""
95
- return [self.r, self.g, self.b] + ([self.a] if self.a else [])
96
-
97
- def tuple(self) -> tuple:
98
- """Returns the color components as a tuple `(r, g, b)` or `(r, g, b, a)` if alpha is present"""
99
- return tuple(self.list())
94
+ if not isinstance(other, rgba):
95
+ return False
96
+ return (self.r, self.g, self.b, self.a) == (
97
+ other[0],
98
+ other[1],
99
+ other[2],
100
+ other[3],
101
+ )
100
102
 
101
103
  def dict(self) -> dict:
102
104
  """Returns the color components as a dictionary with keys `'r'`, `'g'`, `'b'` and optionally `'a'`"""
@@ -106,59 +108,61 @@ class rgba:
106
108
  """Returns the color components as separate values `r, g, b, a`"""
107
109
  return self.r, self.g, self.b, self.a
108
110
 
109
- def to_hsla(self) -> 'hsla':
111
+ def to_hsla(self) -> "hsla":
110
112
  """Returns the color as a `hsla()` color"""
111
113
  return hsla(*self._rgb_to_hsl(self.r, self.g, self.b), self.a)
112
114
 
113
- def to_hexa(self) -> 'hexa':
115
+ def to_hexa(self) -> "hexa":
114
116
  """Returns the color as a `hexa()` color"""
115
117
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
116
118
 
117
119
  def has_alpha(self) -> bool:
118
120
  """Returns `True` if the color has an alpha channel and `False` otherwise"""
119
- return self.a != None
121
+ return self.a is not None
120
122
 
121
- def lighten(self, amount:float) -> 'rgba':
123
+ def lighten(self, amount: float) -> "rgba":
122
124
  """Increases the colors lightness by the specified amount (`0.0`-`1.0`)"""
123
125
  self.r, self.g, self.b, self.a = self.to_hsla().lighten(amount).to_rgba().values()
124
126
  return rgba(self.r, self.g, self.b, self.a)
125
127
 
126
- def darken(self, amount:float) -> 'rgba':
128
+ def darken(self, amount: float) -> "rgba":
127
129
  """Decreases the colors lightness by the specified amount (`0.0`-`1.0`)"""
128
130
  self.r, self.g, self.b, self.a = self.to_hsla().darken(amount).to_rgba().values()
129
131
  return rgba(self.r, self.g, self.b, self.a)
130
132
 
131
- def saturate(self, amount:float) -> 'rgba':
133
+ def saturate(self, amount: float) -> "rgba":
132
134
  """Increases the colors saturation by the specified amount (`0.0`-`1.0`)"""
133
135
  self.r, self.g, self.b, self.a = self.to_hsla().saturate(amount).to_rgba().values()
134
136
  return rgba(self.r, self.g, self.b, self.a)
135
137
 
136
- def desaturate(self, amount:float) -> 'rgba':
138
+ def desaturate(self, amount: float) -> "rgba":
137
139
  """Decreases the colors saturation by the specified amount (`0.0`-`1.0`)"""
138
140
  self.r, self.g, self.b, self.a = self.to_hsla().desaturate(amount).to_rgba().values()
139
141
  return rgba(self.r, self.g, self.b, self.a)
140
142
 
141
- def rotate(self, degrees:int) -> 'rgba':
143
+ def rotate(self, degrees: int) -> "rgba":
142
144
  """Rotates the colors hue by the specified number of degrees"""
143
145
  self.r, self.g, self.b, self.a = self.to_hsla().rotate(degrees).to_rgba().values()
144
146
  return rgba(self.r, self.g, self.b, self.a)
145
147
 
146
- def invert(self, invert_alpha:bool = False) -> 'rgba':
148
+ def invert(self, invert_alpha: bool = False) -> "rgba":
147
149
  """Inverts the color by rotating hue by 180 degrees and inverting lightness"""
148
150
  self.r, self.g, self.b = 255 - self.r, 255 - self.g, 255 - self.b
149
- if invert_alpha: self.a = 1 - self.a
151
+ if invert_alpha:
152
+ self.a = 1 - self.a
150
153
  return rgba(self.r, self.g, self.b, self.a)
151
154
 
152
- def grayscale(self) -> 'rgba':
155
+ def grayscale(self) -> "rgba":
153
156
  """Converts the color to grayscale using the luminance formula"""
154
157
  self.r = self.g = self.b = Color.luminance(self.r, self.g, self.b)
155
158
  return rgba(self.r, self.g, self.b, self.a)
156
159
 
157
- def blend(self, other:'rgba', ratio:float = 0.5, additive_alpha:bool = False) -> 'rgba':
160
+ def blend(self, other: "rgba", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
158
161
  """Blends the current color with another color using the specified ratio (`0.0`-`1.0`):<br>
159
162
  If `ratio` is `0.0` it means 100% of the current color and 0% of the `other` color (1:0 mixture)<br>
160
163
  If `ratio` is `0.5` it means 50% of both colors (1:1 mixture)<br>
161
- If `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:1 mixture)"""
164
+ If `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:1 mixture)
165
+ """
162
166
  if not (isinstance(ratio, (int, float)) and 0 <= ratio <= 1):
163
167
  raise ValueError("'ratio' must be a float/int in [0.0, 1.0]")
164
168
  elif not isinstance(other, rgba):
@@ -177,7 +181,13 @@ class rgba:
177
181
  if additive_alpha:
178
182
  self.a = max(0, min(1, (self_a * (2 - ratio)) + (other_a * ratio)))
179
183
  else:
180
- self.a = max(0, min(1, (self_a * (1 - (ratio / 2))) + (other_a * (ratio / 2))))
184
+ self.a = max(
185
+ 0,
186
+ min(
187
+ 1,
188
+ (self_a * (1 - (ratio / 2))) + (other_a * (ratio / 2)),
189
+ ),
190
+ )
181
191
  else:
182
192
  self.a = None
183
193
  return rgba(self.r, self.g, self.b, None if none_alpha else self.a)
@@ -198,17 +208,17 @@ class rgba:
198
208
  """Returns `True` if the color has no transparency"""
199
209
  return self.a == 1 or self.a is None
200
210
 
201
- def with_alpha(self, alpha:float) -> 'rgba':
211
+ def with_alpha(self, alpha: float) -> "rgba":
202
212
  """Returns a new color with the specified alpha value"""
203
213
  if not (isinstance(alpha, (int, float)) and 0 <= alpha <= 1):
204
214
  raise ValueError("'alpha' must be a float/int in [0.0, 1.0]")
205
215
  return rgba(self.r, self.g, self.b, alpha)
206
216
 
207
- def complementary(self) -> 'rgba':
217
+ def complementary(self) -> "rgba":
208
218
  """Returns the complementary color (180 degrees on the color wheel)"""
209
219
  return self.to_hsla().complementary().to_rgba()
210
220
 
211
- def _rgb_to_hsl(self, r:int, g:int, b:int) -> tuple:
221
+ def _rgb_to_hsl(self, r: int, g: int, b: int) -> tuple:
212
222
  r, g, b = r / 255.0, g / 255.0, b / 255.0
213
223
  max_c, min_c = max(r, g, b), min(r, g, b)
214
224
  l = (max_c + min_c) / 2
@@ -217,15 +227,16 @@ class rgba:
217
227
  else:
218
228
  delta = max_c - min_c
219
229
  s = delta / (1 - abs(2 * l - 1))
220
- if max_c == r: h = ((g - b) / delta) % 6
221
- elif max_c == g: h = ((b - r) / delta) + 2
222
- else: h = ((r - g) / delta) + 4
230
+ if max_c == r:
231
+ h = ((g - b) / delta) % 6
232
+ elif max_c == g:
233
+ h = ((b - r) / delta) + 2
234
+ else:
235
+ h = ((r - g) / delta) + 4
223
236
  h /= 6
224
237
  return int(round(h * 360)), int(round(s * 100)), int(round(l * 100))
225
238
 
226
239
 
227
-
228
-
229
240
  class hsla:
230
241
  """A HSL/HSLA color: is a tuple of 3 integers, representing hue (`0`-`360`), saturation (`0`-`100`), and lightness (`0`-`100`).<br>
231
242
  Also includes an optional 4th param, which is a float, that represents the alpha channel (`0.0`-`1.0`).\n
@@ -249,14 +260,18 @@ class hsla:
249
260
  - `with_alpha(alpha)` to create a new color with different alpha
250
261
  - `complementary()` to get the complementary color"""
251
262
 
252
- def __init__(self, h:int, s:int, l:int, a:float = None):
263
+ def __init__(self, h: int, s: int, l: int, a: float = None):
253
264
  if any(isinstance(x, hsla) for x in (h, s, l)):
254
- raise ValueError('Color is already a hsla() color')
265
+ raise ValueError("Color is already a hsla() color")
255
266
  elif not (isinstance(h, int) and (0 <= h <= 360) and all(isinstance(x, int) and (0 <= x <= 100) for x in (s, l))):
256
- raise ValueError('HSL color must have H as integer in [0, 360] and S L as integers in [0, 100]: got', (h, s, l))
257
- elif not a is None and (not isinstance(a, (int, float)) or not 0 <= a <= 1):
258
- raise ValueError('Alpha channel must be a float/int in [0.0, 1.0]: got', a)
259
- self.h, self.s, self.l, self.a = h, s, l, (1.0 if a > 1.0 else float(a)) if a else None
267
+ raise ValueError(
268
+ "HSL color must have H as integer in [0, 360] and S L as integers in [0, 100]: got",
269
+ (h, s, l),
270
+ )
271
+ elif a is not None and (not isinstance(a, (int, float)) or not 0 <= a <= 1):
272
+ raise ValueError("Alpha channel must be a float/int in [0.0, 1.0]: got", a)
273
+ self.h, self.s, self.l = h, s, l
274
+ self.a = (1.0 if a > 1.0 else float(a)) if a else None
260
275
 
261
276
  def __len__(self):
262
277
  return 4 if self.a else 3
@@ -264,6 +279,9 @@ class hsla:
264
279
  def __iter__(self):
265
280
  return iter((self.h, self.s, self.l) + ((self.a,) if self.a else ()))
266
281
 
282
+ def __dict__(self):
283
+ return self.dict()
284
+
267
285
  def __getitem__(self, index):
268
286
  return ((self.h, self.s, self.l) + ((self.a,) if self.a else ()))[index]
269
287
 
@@ -276,15 +294,12 @@ class hsla:
276
294
  def __eq__(self, other):
277
295
  if not isinstance(other, hsla):
278
296
  return False
279
- return (self.h, self.s, self.l, self.a) == (other[0], other[1], other[2], other[3])
280
-
281
- def list(self) -> list:
282
- """Returns the color components as a list `[h, s, l]` or `[h, s, l, a]` if alpha is present"""
283
- return [self.h, self.s, self.l] + ([self.a] if self.a else [])
284
-
285
- def tuple(self) -> tuple:
286
- """Returns the color components as a tuple `(h, s, l)` or `(h, s, l, a)` if alpha is present"""
287
- return tuple(self.list())
297
+ return (self.h, self.s, self.l, self.a) == (
298
+ other[0],
299
+ other[1],
300
+ other[2],
301
+ other[3],
302
+ )
288
303
 
289
304
  def dict(self) -> dict:
290
305
  """Returns the color components as a dictionary with keys `'h'`, `'s'`, `'l'` and optionally `'a'`"""
@@ -294,71 +309,73 @@ class hsla:
294
309
  """Returns the color components as separate values `h, s, l, a`"""
295
310
  return self.h, self.s, self.l, self.a
296
311
 
297
- def to_rgba(self) -> 'rgba':
312
+ def to_rgba(self) -> "rgba":
298
313
  """Returns the color as a `rgba()` color"""
299
314
  return rgba(*self._hsl_to_rgb(self.h, self.s, self.l), self.a)
300
315
 
301
- def to_hexa(self) -> 'hexa':
316
+ def to_hexa(self) -> "hexa":
302
317
  """Returns the color as a `hexa()` color"""
303
318
  r, g, b = self._hsl_to_rgb(self.h, self.s, self.l)
304
319
  return hexa(f'#{r:02X}{g:02X}{b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
305
320
 
306
321
  def has_alpha(self) -> bool:
307
322
  """Returns `True` if the color has an alpha channel and `False` otherwise"""
308
- return self.a != None
323
+ return self.a is not None
309
324
 
310
- def lighten(self, amount:float) -> 'hsla':
325
+ def lighten(self, amount: float) -> "hsla":
311
326
  """Increases the colors lightness by the specified amount (`0.0`-`1.0`)"""
312
327
  if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
313
328
  raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
314
329
  self.l = int(min(100, self.l + (100 - self.l) * amount))
315
330
  return hsla(self.h, self.s, self.l, self.a)
316
331
 
317
- def darken(self, amount:float) -> 'hsla':
332
+ def darken(self, amount: float) -> "hsla":
318
333
  """Decreases the colors lightness by the specified amount (`0.0`-`1.0`)"""
319
334
  if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
320
335
  raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
321
336
  self.l = int(max(0, self.l * (1 - amount)))
322
337
  return hsla(self.h, self.s, self.l, self.a)
323
338
 
324
- def saturate(self, amount:float) -> 'hsla':
339
+ def saturate(self, amount: float) -> "hsla":
325
340
  """Increases the colors saturation by the specified amount (`0.0`-`1.0`)"""
326
341
  if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
327
342
  raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
328
343
  self.s = int(min(100, self.s + (100 - self.s) * amount))
329
344
  return hsla(self.h, self.s, self.l, self.a)
330
345
 
331
- def desaturate(self, amount:float) -> 'hsla':
346
+ def desaturate(self, amount: float) -> "hsla":
332
347
  """Decreases the colors saturation by the specified amount (`0.0`-`1.0`)"""
333
348
  if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
334
349
  raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
335
350
  self.s = int(max(0, self.s * (1 - amount)))
336
351
  return hsla(self.h, self.s, self.l, self.a)
337
352
 
338
- def rotate(self, degrees:int) -> 'hsla':
353
+ def rotate(self, degrees: int) -> "hsla":
339
354
  """Rotates the colors hue by the specified number of degrees"""
340
355
  self.h = (self.h + degrees) % 360
341
356
  return hsla(self.h, self.s, self.l, self.a)
342
357
 
343
- def invert(self, invert_alpha:bool = False) -> 'hsla':
358
+ def invert(self, invert_alpha: bool = False) -> "hsla":
344
359
  """Inverts the color by rotating hue by 180 degrees and inverting lightness"""
345
360
  self.h = (self.h + 180) % 360
346
361
  self.l = 100 - self.l
347
- if invert_alpha: self.a = 1 - self.a
362
+ if invert_alpha:
363
+ self.a = 1 - self.a
348
364
  return hsla(self.h, self.s, self.l, self.a)
349
365
 
350
- def grayscale(self) -> 'hsla':
366
+ def grayscale(self) -> "hsla":
351
367
  """Converts the color to grayscale using the luminance formula"""
352
368
  l = Color.luminance(*self._hsl_to_rgb(self.h, self.s, self.l))
353
369
  self.h, self.s, self.l, _ = rgba(l, l, l).to_hsla().values()
354
370
  return hsla(self.h, self.s, self.l, self.a)
355
371
 
356
- def blend(self, other:'hsla', ratio:float = 0.5, additive_alpha:bool = False) -> 'rgba':
372
+ def blend(self, other: "hsla", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
357
373
  """Blends the current color with another color using the specified ratio (`0.0`-`1.0`):<br>
358
374
  If `ratio` is `0.0` it means 100% of the current color and 0% of the `other` color (1:0 mixture)<br>
359
375
  If `ratio` is `0.5` it means 50% of both colors (1:1 mixture)<br>
360
- If `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:1 mixture)"""
361
- self.h, self.s, self.l, self.a = self.to_hsla().blend(Color.to_hsla(other), ratio, additive_alpha).values()
376
+ If `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:1 mixture)
377
+ """
378
+ self.h, self.s, self.l, self.a = self.to_rgba().blend(Color.to_rgba(other), ratio, additive_alpha).to_hsla().values()
362
379
  return hsla(self.h, self.s, self.l, self.a)
363
380
 
364
381
  def is_dark(self) -> bool:
@@ -377,38 +394,43 @@ class hsla:
377
394
  """Returns `True` if the color has no transparency"""
378
395
  return self.a == 1 or self.a is None
379
396
 
380
- def with_alpha(self, alpha:float) -> 'hsla':
397
+ def with_alpha(self, alpha: float) -> "hsla":
381
398
  """Returns a new color with the specified alpha value"""
382
399
  if not (isinstance(alpha, (int, float)) and 0 <= alpha <= 1):
383
400
  raise ValueError("'alpha' must be a float/int in [0.0, 1.0]")
384
401
  return hsla(self.h, self.s, self.l, alpha)
385
402
 
386
- def complementary(self) -> 'hsla':
403
+ def complementary(self) -> "hsla":
387
404
  """Returns the complementary color (180 degrees on the color wheel)"""
388
405
  return hsla((self.h + 180) % 360, self.s, self.l, self.a)
389
406
 
390
- def _hsl_to_rgb(self, h:int, s:int, l:int) -> tuple:
407
+ def _hsl_to_rgb(self, h: int, s: int, l: int) -> tuple:
391
408
  h, s, l = h / 360, s / 100, l / 100
392
409
  if s == 0:
393
410
  r = g = b = int(l * 255)
394
411
  else:
412
+
395
413
  def hue_to_rgb(p, q, t):
396
- if t < 0: t += 1
397
- if t > 1: t -= 1
398
- if t < 1/6: return p + (q - p) * 6 * t
399
- if t < 1/2: return q
400
- if t < 2/3: return p + (q - p) * (2/3 - t) * 6
414
+ if t < 0:
415
+ t += 1
416
+ if t > 1:
417
+ t -= 1
418
+ if t < 1 / 6:
419
+ return p + (q - p) * 6 * t
420
+ if t < 1 / 2:
421
+ return q
422
+ if t < 2 / 3:
423
+ return p + (q - p) * (2 / 3 - t) * 6
401
424
  return p
425
+
402
426
  q = l * (1 + s) if l < 0.5 else l + s - l * s
403
427
  p = 2 * l - q
404
- r = int(round(hue_to_rgb(p, q, h + 1/3) * 255))
428
+ r = int(round(hue_to_rgb(p, q, h + 1 / 3) * 255))
405
429
  g = int(round(hue_to_rgb(p, q, h) * 255))
406
- b = int(round(hue_to_rgb(p, q, h - 1/3) * 255))
430
+ b = int(round(hue_to_rgb(p, q, h - 1 / 3) * 255))
407
431
  return r, g, b
408
432
 
409
433
 
410
-
411
-
412
434
  class hexa:
413
435
  """A HEX color: is a string representing a hexadecimal color code with optional alpha channel.\n
414
436
  -------------------------------------------------------------------------------------------------
@@ -432,26 +454,46 @@ class hexa:
432
454
  - `with_alpha(alpha)` to create a new color with different alpha
433
455
  - `complementary()` to get the complementary color"""
434
456
 
435
- def __init__(self, color:str|int):
457
+ def __init__(self, color: str | int):
436
458
  if isinstance(color, hexa):
437
- raise ValueError('Color is already a hexa() color')
459
+ raise ValueError("Color is already a hexa() color")
438
460
  if isinstance(color, str):
439
- if color.startswith('#'):
461
+ if color.startswith("#"):
440
462
  color = color[1:].upper()
441
- elif color.startswith('0x'):
463
+ elif color.startswith("0x"):
442
464
  color = color[2:].upper()
443
465
  if len(color) == 3: # RGB
444
- self.r, self.g, self.b, self.a = int(color[0] * 2, 16), int(color[1] * 2, 16), int(color[2] * 2, 16), None
466
+ self.r, self.g, self.b, self.a = (
467
+ int(color[0] * 2, 16),
468
+ int(color[1] * 2, 16),
469
+ int(color[2] * 2, 16),
470
+ None,
471
+ )
445
472
  elif len(color) == 4: # RGBA
446
- self.r, self.g, self.b, self.a = int(color[0] * 2, 16), int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) / 255.0
473
+ self.r, self.g, self.b, self.a = (
474
+ int(color[0] * 2, 16),
475
+ int(color[1] * 2, 16),
476
+ int(color[2] * 2, 16),
477
+ int(color[3] * 2, 16) / 255.0,
478
+ )
447
479
  elif len(color) == 6: # RRGGBB
448
- self.r, self.g, self.b, self.a = int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16), None
480
+ self.r, self.g, self.b, self.a = (
481
+ int(color[0:2], 16),
482
+ int(color[2:4], 16),
483
+ int(color[4:6], 16),
484
+ None,
485
+ )
449
486
  elif len(color) == 8: # RRGGBBAA
450
- self.r, self.g, self.b, self.a = int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16), int(color[6:8], 16) / 255.0
487
+ self.r, self.g, self.b, self.a = (
488
+ int(color[0:2], 16),
489
+ int(color[2:4], 16),
490
+ int(color[4:6], 16),
491
+ int(color[6:8], 16) / 255.0,
492
+ )
451
493
  else:
452
494
  raise ValueError(f"Invalid HEX format '{color}'")
453
495
  elif isinstance(color, int):
454
- self.r, self.g, self.b, self.a = Color.hex_to_rgba(color)
496
+ self.r, self.g, self.b, self.a = Color.hex_int_to_rgba(color)
455
497
  else:
456
498
  raise TypeError("HEX color must be of type 'str' or 'int': got", type(color))
457
499
 
@@ -459,10 +501,13 @@ class hexa:
459
501
  return 4 if self.a else 3
460
502
 
461
503
  def __iter__(self):
462
- return iter((f'{self.r:02X}', f'{self.g:02X}', f'{self.b:02X}') + ((f'{int(self.a * 255):02X}',) if self.a else ()))
504
+ return iter((f"{self.r:02X}", f"{self.g:02X}", f"{self.b:02X}") + ((f"{int(self.a * 255):02X}",) if self.a else ()))
505
+
506
+ def __dict__(self):
507
+ return self.dict()
463
508
 
464
509
  def __getitem__(self, index):
465
- return ((f'{self.r:02X}', f'{self.g:02X}', f'{self.b:02X}') + ((f'{int(self.a * 255):02X}',) if self.a else ()))[index]
510
+ return ((f"{self.r:02X}", f"{self.g:02X}", f"{self.b:02X}") + ((f"{int(self.a * 255):02X}",) if self.a else ()))[index]
466
511
 
467
512
  def __repr__(self):
468
513
  return f'hexa(#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""})'
@@ -471,30 +516,42 @@ class hexa:
471
516
  return f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}'
472
517
 
473
518
  def __eq__(self, other):
474
- if not isinstance(other, hexa): return False
475
- return (self.r, self.g, self.b, self.a) == (other[0], other[1], other[2], other[3])
476
-
477
- def list(self) -> list:
478
- """Returns the color components as a list of hex strings `[RR, GG, BB]` or `[RR, GG, BB, AA]` if alpha is present"""
479
- return [f'{self.r:02X}', f'{self.g:02X}', f'{self.b:02X}'] + ([f'{int(self.a * 255):02X}'] if self.a else [])
480
-
481
- def tuple(self) -> tuple:
482
- """Returns the color components as a tuple of hex strings `(RR, GG, BB)` or `(RR, GG, BB, AA)` if alpha is present"""
483
- return tuple(self.list())
519
+ if not isinstance(other, hexa):
520
+ return False
521
+ return (self.r, self.g, self.b, self.a) == (
522
+ other[0],
523
+ other[1],
524
+ other[2],
525
+ other[3],
526
+ )
484
527
 
485
528
  def dict(self) -> dict:
486
529
  """Returns the color components as a dictionary with hex string values for keys `'r'`, `'g'`, `'b'` and optionally `'a'`"""
487
- return dict(r=f'{self.r:02X}', g=f'{self.g:02X}', b=f'{self.b:02X}', a=f'{int(self.a * 255):02X}') if self.a else dict(r=f'{self.r:02X}', g=f'{self.g:02X}', b=f'{self.b:02X}')
530
+ return (
531
+ dict(
532
+ r=f"{self.r:02X}",
533
+ g=f"{self.g:02X}",
534
+ b=f"{self.b:02X}",
535
+ a=f"{int(self.a * 255):02X}",
536
+ )
537
+ if self.a
538
+ else dict(r=f"{self.r:02X}", g=f"{self.g:02X}", b=f"{self.b:02X}")
539
+ )
488
540
 
489
541
  def values(self) -> tuple:
490
542
  """Returns the color components as separate values `r, g, b, a`"""
491
543
  return self.r, self.g, self.b, self.a
492
544
 
493
- def to_rgba(self, round_alpha:bool = True) -> 'rgba':
545
+ def to_rgba(self, round_alpha: bool = True) -> "rgba":
494
546
  """Returns the color as a `rgba()` color"""
495
- return rgba(self.r, self.g, self.b, (round(self.a, 2) if round_alpha else self.a) if self.a else None)
496
-
497
- def to_hsla(self, round_alpha:bool = True) -> 'hsla':
547
+ return rgba(
548
+ self.r,
549
+ self.g,
550
+ self.b,
551
+ (round(self.a, 2) if round_alpha else self.a) if self.a else None,
552
+ )
553
+
554
+ def to_hsla(self, round_alpha: bool = True) -> "hsla":
498
555
  """Returns the color as a `hsla()` color"""
499
556
  return self.to_rgba(round_alpha).to_hsla()
500
557
 
@@ -502,47 +559,49 @@ class hexa:
502
559
  """Returns `True` if the color has an alpha channel and `False` otherwise"""
503
560
  return self.a is not None
504
561
 
505
- def lighten(self, amount:float) -> 'hexa':
562
+ def lighten(self, amount: float) -> "hexa":
506
563
  """Increases the colors lightness by the specified amount (`0.0`-`1.0`)"""
507
564
  self.r, self.g, self.b, self.a = self.to_rgba(False).lighten(amount).values()
508
565
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
509
566
 
510
- def darken(self, amount:float) -> 'hexa':
567
+ def darken(self, amount: float) -> "hexa":
511
568
  """Decreases the colors lightness by the specified amount (`0.0`-`1.0`)"""
512
569
  self.r, self.g, self.b, self.a = self.to_rgba(False).darken(amount).values()
513
570
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
514
571
 
515
- def saturate(self, amount:float) -> 'hexa':
572
+ def saturate(self, amount: float) -> "hexa":
516
573
  """Increases the colors saturation by the specified amount (`0.0`-`1.0`)"""
517
574
  self.r, self.g, self.b, self.a = self.to_rgba(False).saturate(amount).values()
518
575
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
519
576
 
520
- def desaturate(self, amount:float) -> 'hexa':
577
+ def desaturate(self, amount: float) -> "hexa":
521
578
  """Decreases the colors saturation by the specified amount (`0.0`-`1.0`)"""
522
579
  self.r, self.g, self.b, self.a = self.to_rgba(False).desaturate(amount).values()
523
580
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
524
581
 
525
- def rotate(self, degrees:int) -> 'hexa':
582
+ def rotate(self, degrees: int) -> "hexa":
526
583
  """Rotates the colors hue by the specified number of degrees"""
527
584
  self.r, self.g, self.b, self.a = self.to_rgba(False).rotate(degrees).values()
528
585
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
529
586
 
530
- def invert(self, invert_alpha:bool = False) -> 'hexa':
587
+ def invert(self, invert_alpha: bool = False) -> "hexa":
531
588
  """Inverts the color by rotating hue by 180 degrees and inverting lightness"""
532
589
  self.r, self.g, self.b, self.a = self.to_rgba(False).invert().values()
533
- if invert_alpha: self.a = 1 - self.a
590
+ if invert_alpha:
591
+ self.a = 1 - self.a
534
592
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
535
593
 
536
- def grayscale(self) -> 'hexa':
594
+ def grayscale(self) -> "hexa":
537
595
  """Converts the color to grayscale using the luminance formula"""
538
596
  self.r = self.g = self.b = Color.luminance(self.r, self.g, self.b)
539
597
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
540
598
 
541
- def blend(self, other:'hexa', ratio:float = 0.5, additive_alpha:bool = False) -> 'rgba':
599
+ def blend(self, other: "hexa", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
542
600
  """Blends the current color with another color using the specified ratio (`0.0`-`1.0`):<br>
543
601
  If `ratio` is `0.0` it means 100% of the current color and 0% of the `other` color (1:0 mixture)<br>
544
602
  If `ratio` is `0.5` it means 50% of both colors (1:1 mixture)<br>
545
- If `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:1 mixture)"""
603
+ If `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:1 mixture)
604
+ """
546
605
  self.r, self.g, self.b, self.a = self.to_rgba(False).blend(Color.to_rgba(other), ratio, additive_alpha).values()
547
606
  return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
548
607
 
@@ -562,105 +621,133 @@ class hexa:
562
621
  """Returns `True` if the color has no transparency (`alpha == 1.0`)"""
563
622
  return self.to_hsla(False).is_opaque()
564
623
 
565
- def with_alpha(self, alpha:float) -> 'hexa':
624
+ def with_alpha(self, alpha: float) -> "hexa":
566
625
  """Returns a new color with the specified alpha value"""
567
626
  if not (isinstance(alpha, (int, float)) and 0 <= alpha <= 1):
568
627
  raise ValueError("'alpha' must be in [0.0, 1.0]")
569
- return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}" if self.a else ""}')
628
+ return hexa(f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(alpha * 255):02X}" if alpha else ""}')
570
629
 
571
- def complementary(self) -> 'hexa':
630
+ def complementary(self) -> "hexa":
572
631
  """Returns the complementary color (180 degrees on the color wheel)"""
573
632
  return self.to_hsla(False).complementary().to_hexa()
574
633
 
575
634
 
576
-
577
-
578
635
  class Color:
579
636
 
580
637
  @staticmethod
581
- def is_valid_rgba(color:str|list|tuple|dict, allow_alpha:bool = True) -> bool:
638
+ def is_valid_rgba(color: str | list | tuple | dict, allow_alpha: bool = True) -> bool:
582
639
  try:
583
640
  if isinstance(color, rgba):
584
641
  return True
585
642
  elif isinstance(color, (list, tuple)):
586
643
  if allow_alpha and Color.has_alpha(color):
587
- return 0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255 and (0 <= color[3] <= 1 or color[3] is None)
644
+ return (
645
+ 0 <= color[0] <= 255
646
+ and 0 <= color[1] <= 255
647
+ and 0 <= color[2] <= 255
648
+ and (0 <= color[3] <= 1 or color[3] is None)
649
+ )
588
650
  return 0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
589
651
  elif isinstance(color, dict):
590
652
  if allow_alpha and Color.has_alpha(color):
591
- return 0 <= color['r'] <= 255 and 0 <= color['g'] <= 255 and 0 <= color['b'] <= 255 and (0 <= color['a'] <= 1 or color['a'] is None)
592
- return 0 <= color['r'] <= 255 and 0 <= color['g'] <= 255 and 0 <= color['b'] <= 255
653
+ return (
654
+ 0 <= color["r"] <= 255
655
+ and 0 <= color["g"] <= 255
656
+ and 0 <= color["b"] <= 255
657
+ and (0 <= color["a"] <= 1 or color["a"] is None)
658
+ )
659
+ return 0 <= color["r"] <= 255 and 0 <= color["g"] <= 255 and 0 <= color["b"] <= 255
593
660
  elif isinstance(color, str):
594
661
  return bool(_re.fullmatch(Regex.rgba_str(), color))
595
662
  return False
596
- except: return False
663
+ except Exception:
664
+ return False
597
665
 
598
666
  @staticmethod
599
- def is_valid_hsla(color:str|list|tuple|dict, allow_alpha:bool = True) -> bool:
667
+ def is_valid_hsla(color: str | list | tuple | dict, allow_alpha: bool = True) -> bool:
600
668
  try:
601
669
  if isinstance(color, hsla):
602
670
  return True
603
671
  elif isinstance(color, (list, tuple)):
604
672
  if allow_alpha and Color.has_alpha(color):
605
- return 0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100 and (0 <= color[3] <= 1 or color[3] is None)
673
+ return (
674
+ 0 <= color[0] <= 360
675
+ and 0 <= color[1] <= 100
676
+ and 0 <= color[2] <= 100
677
+ and (0 <= color[3] <= 1 or color[3] is None)
678
+ )
606
679
  else:
607
680
  return 0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
608
681
  elif isinstance(color, dict):
609
682
  if allow_alpha and Color.has_alpha(color):
610
- return 0 <= color['h'] <= 360 and 0 <= color['s'] <= 100 and 0 <= color['l'] <= 100 and (0 <= color['a'] <= 1 or color['a'] is None)
683
+ return (
684
+ 0 <= color["h"] <= 360
685
+ and 0 <= color["s"] <= 100
686
+ and 0 <= color["l"] <= 100
687
+ and (0 <= color["a"] <= 1 or color["a"] is None)
688
+ )
611
689
  else:
612
- return 0 <= color['h'] <= 360 and 0 <= color['s'] <= 100 and 0 <= color['l'] <= 100
690
+ return 0 <= color["h"] <= 360 and 0 <= color["s"] <= 100 and 0 <= color["l"] <= 100
613
691
  elif isinstance(color, str):
614
692
  return bool(_re.fullmatch(Regex.hsla_str(), color))
615
- except:
693
+ except Exception:
616
694
  return False
617
695
 
618
696
  @staticmethod
619
- def is_valid_hexa(color:str, allow_alpha:bool = True, get_prefix:bool = False) -> bool|tuple[bool,str]:
697
+ def is_valid_hexa(color: str, allow_alpha: bool = True, get_prefix: bool = False) -> bool | tuple[bool, str]:
620
698
  try:
621
699
  if isinstance(color, hexa):
622
- return (True, '#')
700
+ return (True, "#")
623
701
  elif isinstance(color, int):
624
702
  is_valid = 0 <= color <= (0xFFFFFFFF if allow_alpha else 0xFFFFFF)
625
- return (is_valid, '0x') if get_prefix else is_valid
703
+ return (is_valid, "0x") if get_prefix else is_valid
626
704
  else:
627
- if color.startswith('#'):
628
- color, prefix = color[1:], '#'
629
- elif color.startswith('0x'):
630
- color, prefix = color[2:], '0x'
631
- pattern = r'(?i)^[0-9A-F]{8}|[0-9A-F]{6}|[0-9A-F]{4}|[0-9A-F]{3}$' if allow_alpha else r'(?i)^[0-9A-F]{6}|[0-9A-F]{3}$'
705
+ if color.startswith("#"):
706
+ color, prefix = color[1:], "#"
707
+ elif color.startswith("0x"):
708
+ color, prefix = color[2:], "0x"
709
+ pattern = (
710
+ r"(?i)^[0-9A-F]{8}|[0-9A-F]{6}|[0-9A-F]{4}|[0-9A-F]{3}$"
711
+ if allow_alpha
712
+ else r"(?i)^[0-9A-F]{6}|[0-9A-F]{3}$"
713
+ )
632
714
  return (bool(_re.fullmatch(pattern, color)), prefix) if get_prefix else bool(_re.fullmatch(pattern, color))
633
- except:
715
+ except Exception:
634
716
  return (False, None) if get_prefix else False
635
717
 
636
718
  @staticmethod
637
- def is_valid(color:str|list|tuple|dict, allow_alpha:bool = True) -> bool:
638
- return Color.is_valid_hexa(color, allow_alpha) or Color.is_valid_rgba(color, allow_alpha) or Color.is_valid_hsla(color, allow_alpha)
719
+ def is_valid(color: str | list | tuple | dict, allow_alpha: bool = True) -> bool:
720
+ return (
721
+ Color.is_valid_rgba(color, allow_alpha)
722
+ or Color.is_valid_hsla(color, allow_alpha)
723
+ or Color.is_valid_hexa(color, allow_alpha)
724
+ )
639
725
 
640
726
  @staticmethod
641
- def has_alpha(color:rgba|hsla|hexa) -> bool:
727
+ def has_alpha(color: rgba | hsla | hexa) -> bool:
642
728
  """Check if the given color has an alpha channel.\n
643
729
  --------------------------------------------------------------------------------
644
730
  Input a RGBA, HSLA or HEXA color as `color`.<br>
645
- Returns `True` if the color has an alpha channel and `False` otherwise."""
731
+ Returns `True` if the color has an alpha channel and `False` otherwise.
732
+ """
646
733
  if isinstance(color, (rgba, hsla, hexa)):
647
734
  return color.has_alpha()
648
735
  if Color.is_valid_hexa(color):
649
736
  if isinstance(color, str):
650
- if color.startswith('#'):
737
+ if color.startswith("#"):
651
738
  color = color[1:]
652
739
  return len(color) == 4 or len(color) == 8
653
740
  if isinstance(color, int):
654
- hex_length = len(f'{color:X}')
741
+ hex_length = len(f"{color:X}")
655
742
  return hex_length == 4 or hex_length == 8
656
743
  elif isinstance(color, (list, tuple)) and len(color) == 4 and color[3] is not None:
657
744
  return True
658
- elif isinstance(color, dict) and len(color) == 4 and color['a'] is not None:
745
+ elif isinstance(color, dict) and len(color) == 4 and color["a"] is not None:
659
746
  return True
660
747
  return False
661
748
 
662
749
  @staticmethod
663
- def to_rgba(color:hsla|hexa) -> rgba:
750
+ def to_rgba(color: hsla | hexa) -> rgba:
664
751
  """Will try to convert any color type to a color of type RGBA."""
665
752
  if isinstance(color, (hsla, hexa)):
666
753
  return color.to_rgba()
@@ -669,11 +756,15 @@ class Color:
669
756
  elif Color.is_valid_hexa(color):
670
757
  return hexa(color).to_rgba()
671
758
  elif Color.is_valid_rgba(color):
672
- return color if isinstance(color, rgba) else rgba(*color) if Color.has_alpha(color) else rgba(color[0], color[1], color[2])
759
+ return (
760
+ color
761
+ if isinstance(color, rgba)
762
+ else (rgba(*color) if Color.has_alpha(color) else rgba(color[0], color[1], color[2]))
763
+ )
673
764
  raise ValueError(f"Invalid color format '{color}'")
674
765
 
675
766
  @staticmethod
676
- def to_hsla(color:rgba|hexa) -> hsla:
767
+ def to_hsla(color: rgba | hexa) -> hsla:
677
768
  """Will try to convert any color type to a color of type HSLA."""
678
769
  if isinstance(color, (rgba, hexa)):
679
770
  return color.to_hsla()
@@ -682,11 +773,15 @@ class Color:
682
773
  elif Color.is_valid_hexa(color):
683
774
  return hexa(color).to_hsla()
684
775
  elif Color.is_valid_hsla(color):
685
- return color if isinstance(color, hsla) else hsla(*color) if Color.has_alpha(color) else hsla(color[0], color[1], color[2])
776
+ return (
777
+ color
778
+ if isinstance(color, hsla)
779
+ else (hsla(*color) if Color.has_alpha(color) else hsla(color[0], color[1], color[2]))
780
+ )
686
781
  raise ValueError(f"Invalid color format '{color}'")
687
782
 
688
783
  @staticmethod
689
- def to_hexa(color:rgba|hsla) -> hexa:
784
+ def to_hexa(color: rgba | hsla) -> hexa:
690
785
  """Will try to convert any color type to a color of type HEXA."""
691
786
  if isinstance(color, (rgba, hsla)):
692
787
  return color.to_hexa()
@@ -695,26 +790,43 @@ class Color:
695
790
  elif Color.is_valid_hsla(color):
696
791
  return hsla(*color).to_hexa() if Color.has_alpha(color) else hsla(color[0], color[1], color[2]).to_hexa()
697
792
  elif Color.is_valid_hexa(color):
698
- return color if isinstance(color, hexa) else hexa(f'#{color}')
793
+ return color if isinstance(color, hexa) else hexa(f"#{color}")
699
794
  raise ValueError(f"Invalid color format '{color}'")
700
795
 
701
796
  @staticmethod
702
- def str_to_rgba(string:str, only_first:bool = False) -> rgba|list[rgba]|None:
797
+ def str_to_rgba(string: str, only_first: bool = False) -> rgba | list[rgba] | None:
703
798
  """Will try to recognize RGBA colors inside a string and output the found ones as RGBA objects.<br>
704
- If `only_first` is `True` only the first found color will be returned (not as a list)."""
799
+ If `only_first` is `True` only the first found color will be returned (not as a list).
800
+ """
705
801
  matches = _re.findall(Regex.rgb_str(allow_alpha=True), string)
706
- if not matches: return None
707
- result = [rgba(int(m[0]), int(m[1]), int(m[2]), ((int(m[3]) if '.' not in m[3] else float(m[3])) if m[3] else None)) for m in matches]
802
+ if not matches:
803
+ return None
804
+ result = [
805
+ rgba(
806
+ int(m[0]),
807
+ int(m[1]),
808
+ int(m[2]),
809
+ ((int(m[3]) if "." not in m[3] else float(m[3])) if m[3] else None),
810
+ )
811
+ for m in matches
812
+ ]
708
813
  return result[0] if len(result) == 1 or only_first else result
709
814
 
710
815
  @staticmethod
711
- def rgba_to_hex(r:int, g:int, b:int, a:float = None, _preserve_original:bool = False) -> int:
816
+ def rgba_to_hex_int(
817
+ r: int,
818
+ g: int,
819
+ b: int,
820
+ a: float = None,
821
+ _preserve_original: bool = False,
822
+ ) -> int:
712
823
  """Convert RGBA channels to a HEXA integer (alpha is optional).\n
713
824
  -------------------------------------------------------------------------------------------------------------------------
714
825
  To preserve leading zeros, the function will add a `1` at the beginning, if the HEX value would start with a `0`.<br>
715
826
  This could affect the color a little bit, but will make sure, that it won't be interpreted as a completely different<br>
716
827
  color, when initializing it as a `hexa()` color or changing it back to RGBA using `Color.hex_to_rgba()`.\n
717
- ⇾ **You can disable this behavior by setting `_preserve_original` to `True`**"""
828
+ ⇾ **You can disable this behavior by setting `_preserve_original` to `True`**
829
+ """
718
830
  r = max(0, min(255, int(r)))
719
831
  g = max(0, min(255, int(g)))
720
832
  b = max(0, min(255, int(b)))
@@ -732,21 +844,31 @@ class Color:
732
844
  return hex_int
733
845
 
734
846
  @staticmethod
735
- def hex_to_rgba(hex_int:int) -> tuple[int,int,int,float|int|None]:
847
+ def hex_int_to_rgba(hex_int: int) -> tuple[int, int, int, float | int | None]:
736
848
  if not isinstance(hex_int, int):
737
- raise ValueError('Input must be an integer (hex value)')
738
- hex_str = f'{hex_int:x}'
849
+ raise ValueError("Input must be an integer (hex value)")
850
+ hex_str = f"{hex_int:x}"
739
851
  if len(hex_str) <= 6:
740
852
  hex_str = hex_str.zfill(6)
741
- return int(hex_str[0:2], 16), int(hex_str[2:4], 16), int(hex_str[4:6], 16), None
853
+ return (
854
+ int(hex_str[0:2], 16),
855
+ int(hex_str[2:4], 16),
856
+ int(hex_str[4:6], 16),
857
+ None,
858
+ )
742
859
  elif len(hex_str) <= 8:
743
860
  hex_str = hex_str.zfill(8)
744
- return int(hex_str[0:2], 16), int(hex_str[2:4], 16), int(hex_str[4:6], 16), int(hex_str[6:8], 16) / 255.0
861
+ return (
862
+ int(hex_str[0:2], 16),
863
+ int(hex_str[2:4], 16),
864
+ int(hex_str[4:6], 16),
865
+ int(hex_str[6:8], 16) / 255.0,
866
+ )
745
867
  else:
746
868
  raise ValueError(f"Invalid HEX integer '0x{hex_str}': expected in range [0x000000, 0xFFFFFF]")
747
869
 
748
870
  @staticmethod
749
- def luminance(r:int, g:int, b:int, output_type:type = None) -> int|float:
871
+ def luminance(r: int, g: int, b: int, output_type: type = None) -> int | float:
750
872
  """Gets the colors luminance using the luminance formula.\n
751
873
  ------------------------------------------------------------
752
874
  The param `output_type` can be set to:<br>
@@ -754,46 +876,72 @@ class Color:
754
876
  *`float`* =⠀float in [0.0, 1.0]<br>
755
877
  `None` =⠀integer in [0, 255]"""
756
878
  r, g, b = r / 255.0, g / 255.0, b / 255.0
757
- if r < 0.03928: r = r / 12.92
758
- else: r = ((r + 0.055) / 1.055) ** 2.4
759
- if g < 0.03928: g = g / 12.92
760
- else: g = ((g + 0.055) / 1.055) ** 2.4
761
- if b < 0.03928: b = b / 12.92
762
- else: b = ((b + 0.055) / 1.055) ** 2.4
879
+ if r < 0.03928:
880
+ r = r / 12.92
881
+ else:
882
+ r = ((r + 0.055) / 1.055) ** 2.4
883
+ if g < 0.03928:
884
+ g = g / 12.92
885
+ else:
886
+ g = ((g + 0.055) / 1.055) ** 2.4
887
+ if b < 0.03928:
888
+ b = b / 12.92
889
+ else:
890
+ b = ((b + 0.055) / 1.055) ** 2.4
763
891
  l = 0.2126 * r + 0.7152 * g + 0.0722 * b
764
892
  return round(l * 100) if isinstance(output_type, int) else round(l * 255) if output_type is None else l
765
893
 
766
894
  @staticmethod
767
- def text_color_for_on_bg(title_bg_color:rgba|hexa = 0xFFF) -> rgba|hexa:
768
- (was_hexa, hexa_prefix), was_int = Color.is_valid_hexa(title_bg_color, get_prefix=True), isinstance(title_bg_color, int)
895
+ def text_color_for_on_bg(
896
+ title_bg_color: rgba | hexa = 0xFFF,
897
+ ) -> rgba | hexa:
898
+ (was_hexa, hexa_prefix), was_int = Color.is_valid_hexa(title_bg_color, get_prefix=True), isinstance(
899
+ title_bg_color, int
900
+ )
769
901
  title_bg_color = Color.to_rgba(title_bg_color)
770
902
  brightness = 0.2126 * title_bg_color[0] + 0.7152 * title_bg_color[1] + 0.0722 * title_bg_color[2]
771
- return (hexa(f'{hexa_prefix}FFF') if was_hexa else rgba(255, 255, 255)) if brightness < 128 else ((0x000 if was_int else hexa(f'{hexa_prefix}000')) if was_hexa else rgba(0, 0, 0))
903
+ return (
904
+ (hexa(f"{hexa_prefix}FFF") if was_hexa else rgba(255, 255, 255))
905
+ if brightness < 128
906
+ else ((0x000 if was_int else hexa(f"{hexa_prefix}000")) if was_hexa else rgba(0, 0, 0))
907
+ )
772
908
 
773
909
  @staticmethod
774
- def adjust_lightness(color:rgba|hexa, brightness_change:float) -> rgba|hexa:
910
+ def adjust_lightness(color: rgba | hexa, brightness_change: float) -> rgba | hexa:
775
911
  """In- or decrease the lightness of the input color.\n
776
912
  ----------------------------------------------------------------------------------------------------
777
913
  **color** (rgba|hexa): HEX or RGBA color<br>
778
914
  **brightness_change** (float): float between -1.0 (darken by `100%`) and 1.0 (lighten by `100%`)\n
779
915
  ----------------------------------------------------------------------------------------------------
780
- **returns** (rgba|hexa): the adjusted color in the format of the input color"""
916
+ **returns** (rgba|hexa): the adjusted color in the format of the input color
917
+ """
781
918
  was_hexa = Color.is_valid_hexa(color)
782
919
  color = Color.to_hsla(color)
783
- h, s, l, a = color[0], color[1], color[2], color[3] if Color.has_alpha(color) else None
920
+ h, s, l, a = (
921
+ color[0],
922
+ color[1],
923
+ color[2],
924
+ color[3] if Color.has_alpha(color) else None,
925
+ )
784
926
  l = int(max(0, min(100, l + brightness_change * 100)))
785
927
  return Color.to_hexa((h, s, l, a)) if was_hexa else Color.to_rgba((h, s, l, a))
786
928
 
787
929
  @staticmethod
788
- def adjust_saturation(color:rgba|hexa, saturation_change:float) -> rgba|hexa:
930
+ def adjust_saturation(color: rgba | hexa, saturation_change: float) -> rgba | hexa:
789
931
  """In- or decrease the saturation of the input color.\n
790
932
  ---------------------------------------------------------------------------------------------------------
791
933
  **color** (rgba|hexa): HEX or RGBA color<br>
792
934
  **saturation_change** (float): float between -1.0 (saturate by `100%`) and 1.0 (desaturate by `100%`)\n
793
935
  ---------------------------------------------------------------------------------------------------------
794
- **returns** (rgba|hexa): the adjusted color in the format of the input color"""
936
+ **returns** (rgba|hexa): the adjusted color in the format of the input color
937
+ """
795
938
  was_hexa = Color.is_valid_hexa(color)
796
939
  color = Color.to_hsla(color)
797
- h, s, l, a = color[0], color[1], color[2], color[3] if Color.has_alpha(color) else None
940
+ h, s, l, a = (
941
+ color[0],
942
+ color[1],
943
+ color[2],
944
+ color[3] if Color.has_alpha(color) else None,
945
+ )
798
946
  s = int(max(0, min(100, s + saturation_change * 100)))
799
947
  return Color.to_hexa((h, s, l, a)) if was_hexa else Color.to_rgba((h, s, l, a))