Skip to content

Colors Class#

The Colors class provides functionality for working with colors, including converting between different color formats (hex, RGB), validating color values, and getting the type of color.

Class Documentation#

cleopatra.colors.Colors #

A class for handling and converting between different color formats.

The Colors class provides functionality for working with different color formats including hexadecimal colors, RGB colors (normalized between 0 and 1), and RGB colors (with values between 0 and 255). It supports validation, conversion, and manipulation of colors.

Attributes:

Name Type Description
color_value Union[List[str], List[Tuple[float, float, float]]]

The color values stored in the class, can be hex strings or RGB tuples.

Methods:

Name Description
get_type

Determine the type of each color (hex, rgb, rgb-normalized).

to_hex

Convert all colors to hexadecimal format.

to_rgb

Convert all colors to RGB format.

is_valid_hex

Check if each color is a valid hex color.

is_valid_rgb

Check if each color is a valid RGB color.

Examples:

Create a Colors object with a hex color:

>>> from cleopatra.colors import Colors
>>> hex_color = Colors("#ff0000")
>>> hex_color.color_value
['#ff0000']
>>> hex_color.get_type()
['hex']
Create a Colors object with an RGB color (values between 0 and 1):
>>> rgb_norm = Colors((0.5, 0.2, 0.8))
>>> rgb_norm.color_value
[(0.5, 0.2, 0.8)]
>>> rgb_norm.get_type()
['rgb-normalized']

Create a Colors object with an RGB color (values between 0 and 255):

>>> rgb_255 = Colors((128, 51, 204))
>>> rgb_255.color_value
[(128, 51, 204)]
>>> rgb_255.get_type()
['rgb']
Convert between color formats:
>>> hex_color.to_rgb()  # Convert hex to RGB (normalized)
[(1.0, 0.0, 0.0)]
>>> rgb_norm.to_hex()  # Convert RGB to hex
['#8033cc']

Source code in cleopatra/colors.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
class Colors:
    """A class for handling and converting between different color formats.

    The Colors class provides functionality for working with different color formats
    including hexadecimal colors, RGB colors (normalized between 0 and 1), and
    RGB colors (with values between 0 and 255). It supports validation, conversion,
    and manipulation of colors.

    Attributes
    ----------
    color_value : Union[List[str], List[Tuple[float, float, float]]]
        The color values stored in the class, can be hex strings or RGB tuples.

    Methods
    -------
    get_type()
        Determine the type of each color (hex, rgb, rgb-normalized).
    to_hex()
        Convert all colors to hexadecimal format.
    to_rgb(normalized=True)
        Convert all colors to RGB format.
    is_valid_hex()
        Check if each color is a valid hex color.
    is_valid_rgb()
        Check if each color is a valid RGB color.

    Examples
    --------
    Create a Colors object with a hex color:
    ```python
    >>> from cleopatra.colors import Colors
    >>> hex_color = Colors("#ff0000")
    >>> hex_color.color_value
    ['#ff0000']
    >>> hex_color.get_type()
    ['hex']

    ```
    Create a Colors object with an RGB color (values between 0 and 1):
    ```python
    >>> rgb_norm = Colors((0.5, 0.2, 0.8))
    >>> rgb_norm.color_value
    [(0.5, 0.2, 0.8)]
    >>> rgb_norm.get_type()
    ['rgb-normalized']

    ```

    Create a Colors object with an RGB color (values between 0 and 255):
    ```python
    >>> rgb_255 = Colors((128, 51, 204))
    >>> rgb_255.color_value
    [(128, 51, 204)]
    >>> rgb_255.get_type()
    ['rgb']

    ```
    Convert between color formats:
    ```python
    >>> hex_color.to_rgb()  # Convert hex to RGB (normalized)
    [(1.0, 0.0, 0.0)]
    >>> rgb_norm.to_hex()  # Convert RGB to hex
    ['#8033cc']

    ```
    """

    def __init__(
        self,
        color_value: Union[
            List[str], str, Tuple[float, float, float], List[Tuple[float, float, float]]
        ],
    ):
        """Initialize a Colors object with the given color value(s).

        Parameters
        ----------
        color_value : Union[List[str], str, Tuple[float, float, float], List[Tuple[float, float, float]]]
            The color value(s) to initialize the object with. Can be:
            - A single hex color string (e.g., "#ff0000" or "ff0000")
            - A single RGB tuple with values between 0-1 (e.g., (1.0, 0.0, 0.0))
            - A single RGB tuple with values between 0-255 (e.g., (255, 0, 0))
            - A list of hex color strings
            - A list of RGB tuples

        Raises
        ------
        ValueError
            If the color_value is not a string, tuple, or list of strings/tuples.

        Notes
        -----
        - Hex colors can be provided with or without the leading "#"
        - RGB tuples with float values between 0-1 are treated as normalized RGB
        - RGB tuples with integer values between 0-255 are treated as standard RGB
        - The class automatically detects the type of color format provided

        Examples
        --------
        - Initialize with a hex color:

            ```python
            >>> from cleopatra.colors import Colors
            >>> # With hash symbol
            >>> color1 = Colors("#ff0000")
            >>> color1.color_value
            ['#ff0000']
            >>> # Without hash symbol
            >>> color2 = Colors("ff0000")
            >>> color2.color_value
            ['ff0000']

            ```

        - Initialize with an RGB color (normalized, values between 0 and 1):

            ```python
            >>> rgb_norm = Colors((1.0, 0.0, 0.0))
            >>> rgb_norm.color_value
            [(1.0, 0.0, 0.0)]
            >>> rgb_norm.get_type()
            ['rgb-normalized']

            ```

        - Initialize with an RGB color (values between 0 and 255):

            ```python
            >>> rgb_255 = Colors((255, 0, 0))
            >>> rgb_255.color_value
            [(255, 0, 0)]
            >>> rgb_255.get_type()
            ['rgb']

            ```

        - Initialize with a list of colors:

            ```python
            >>> mixed_colors = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
            >>> mixed_colors.color_value
            ['#ff0000', (0, 255, 0), (0.0, 0.0, 1.0)]
            >>> mixed_colors.get_type()
            ['hex', 'rgb', 'rgb-normalized']

            ```
        """
        # convert the hex color to a list if it is a string
        if isinstance(color_value, str) or isinstance(color_value, tuple):
            color_value = [color_value]
        elif not isinstance(color_value, list):
            raise ValueError(
                "The color_value must be a list of hex colors, list of tuples (RGB color), a single hex "
                "or single RGB tuple color."
            )

        self._color_value = color_value

    @classmethod
    def create_from_image(cls, path: str) -> "Colors":
        """Create a color object from an image.

        if you have an image of a color ramp, and you want to extract the colors from it, you can use this method.

        ![color-ramp](./../_images/colors/color-ramp.png)

        Parameters
        ----------
        path : str
            The path to the image file.

        Returns
        -------
        Colors
            A color object.

        Raises
        ------
        FileNotFoundError
            If the file does not exist.

        Examples
        --------
        ```python
        >>> path = "examples/data/colors/color-ramp.png"
        >>> colors = Colors.create_from_image(path)
        >>> print(colors.color_value) # doctest: +SKIP
        [(9, 63, 8), (8, 68, 9), (5, 78, 7), (1, 82, 3), (0, 84, 0), (0, 85, 0), (1, 83, 0), (1, 81, 0), (1, 80, 1)

        ```
        """
        if not Path(path).exists():
            raise FileNotFoundError(f"The file {path} does not exist.")
        try:
            image = Image.open(path).convert("RGB")
        except UnidentifiedImageError:
            raise ValueError(f"The file {path} is not a valid image.")
        width, height = image.size
        color_values = [image.getpixel((x, int(height / 2))) for x in range(width)]

        return cls(color_values)

    def get_type(self) -> List[str]:
        """Determine the type of each color value.

        This method analyzes each color value stored in the object and determines
        its type: hex, rgb (values 0-255), or rgb-normalized (values 0-1).

        Returns
        -------
        List[str]
            A list of strings indicating the type of each color value.
            Possible values are:
            - 'hex': Hexadecimal color string
            - 'rgb': RGB tuple with values between 0-255
            - 'rgb-normalized': RGB tuple with values between 0-1

        Notes
        -----
        The method uses the following criteria to determine color types:
        - If the value is a string and is a valid hex color, it's classified as 'hex'
        - If the value is a tuple of 3 floats between 0-1, it's classified as 'rgb-normalized'
        - If the value is a tuple of 3 integers between 0-255, it's classified as 'rgb'

        Examples
        --------
        - Determine the type of a hex color:

            ```python
            >>> from cleopatra.colors import Colors
            >>> hex_color = Colors("#23a9dd")
            >>> hex_color.get_type()
            ['hex']

            ```

        - Determine the type of an RGB color with normalized values (0-1):

            ```python
            >>> rgb_norm = Colors((0.5, 0.2, 0.8))
            >>> rgb_norm.get_type()
            ['rgb-normalized']

            ```

        - Determine the type of an RGB color with values between 0-255:

            ```python
            >>> rgb_255 = Colors((128, 51, 204))
            >>> rgb_255.get_type()
            ['rgb']

            ```

        - Determine types of mixed color formats:

            ```python
            >>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
            >>> mixed.get_type()
            ['hex', 'rgb', 'rgb-normalized']

            ```
        """
        color_type = []
        for color_i in self.color_value:
            if self._is_valid_rgb_norm(color_i):
                color_type.append("rgb-normalized")
            elif self._is_valid_rgb_255(color_i):
                color_type.append("rgb")
            elif self._is_valid_hex_i(color_i):
                color_type.append("hex")

        return color_type

    @property
    def color_value(self) -> Union[List[str], List[Tuple[float, float, float]]]:
        """Get the color values stored in the object.

        This property returns the color values that were provided when initializing
        the Colors object or set afterwards. The values can be hex color strings,
        RGB tuples with values between 0-255, or normalized RGB tuples with values
        between 0-1.

        Returns
        -------
        Union[List[str], List[Tuple[float, float, float]]]
            A list containing the color values. Each element can be:
            - A hex color string (e.g., "#ff0000" or "ff0000")
            - An RGB tuple with values between 0-255 (e.g., (255, 0, 0))
            - A normalized RGB tuple with values between 0-1 (e.g., (1.0, 0.0, 0.0))

        Examples
        --------
        Get color values from a Colors object with hex colors:
        ```python
        >>> from cleopatra.colors import Colors
        >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
        >>> hex_colors.color_value
        ['#ff0000', '#00ff00', '#0000ff']

        ```

        Get color values from a Colors object with RGB colors:
        ```python
        >>> rgb_colors = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
        >>> rgb_colors.color_value
        [(255, 0, 0), (0, 255, 0), (0, 0, 255)]

        ```
        Get color values from a Colors object with mixed color formats:
        ```python
        >>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
        >>> mixed.color_value
        ['#ff0000', (0, 255, 0), (0.0, 0.0, 1.0)]

        ```
        """
        return self._color_value

    def to_hex(self) -> List[str]:
        """Convert all color values to hexadecimal format.

        This method converts all color values stored in the object to hexadecimal format.
        RGB tuples (both normalized and 0-255 range) are converted to their hex equivalents.
        Hex colors remain unchanged.

        Returns
        -------
        List[str]
            A list of hexadecimal color strings. Each string is in the format '#RRGGBB'.

        Notes
        -----
        - RGB tuples with values between 0-255 are first normalized to 0-1 range before conversion
        - RGB tuples with values already between 0-1 are directly converted
        - Existing hex colors are returned as-is
        - All returned hex colors include the leading '#' character

        Examples
        --------
        Convert RGB colors to hex:
        ```python
        >>> from cleopatra.colors import Colors
        >>> # RGB colors (0-255 range)
        >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
        >>> rgb_255.to_hex()
        ['#ff0000', '#00ff00', '#0000ff']

        ```
        >>> # RGB colors (normalized 0-1 range)
        >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)])
        >>> rgb_norm.to_hex()
        ['#ff0000', '#00ff00', '#0000ff']

        ```
        Convert a mix of color formats to hex:
        ```python
        >>> mixed = Colors([(128, 51, 204), "#23a9dd", (0.5, 0.2, 0.8)])
        >>> mixed.to_hex()
        ['#8033cc', '#23a9dd', '#8033cc']

        ```
        Hex colors are returned as-is:
        ```python
        >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
        >>> hex_colors.to_hex()
        ['#ff0000', '#00ff00', '#0000ff']

        ```
        """
        converted_color = []
        color_type = self.get_type()
        for ind, color_i in enumerate(self.color_value):
            if color_type[ind] == "hex":
                converted_color.append(color_i)
            elif color_type[ind] == "rgb":
                # Normalize the RGB values to be between 0 and 1
                rgb_color_normalized = tuple(value / 255 for value in color_i)
                converted_color.append(mcolors.to_hex(rgb_color_normalized))
            else:
                converted_color.append(mcolors.to_hex(color_i))
        return converted_color

    def is_valid_hex(self) -> List[bool]:
        """Check if each color value is a valid hexadecimal color.

        This method checks each color value stored in the object to determine
        if it is a valid hexadecimal color string.

        Returns
        -------
        List[bool]
            A list of boolean values, one for each color value in the object.
            True indicates the color is a valid hex color, False otherwise.

        Notes
        -----
        - The method uses matplotlib's is_color_like function to validate hex colors
        - Both formats with and without the leading '#' are supported
        - RGB tuples will return False as they are not hex colors

        Examples
        --------
        Check if hex colors are valid:
        ```python
        >>> from cleopatra.colors import Colors
        >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
        >>> hex_colors.is_valid_hex()
        [True, True, True]

        ```
        Check if RGB colors are valid hex colors (they're not):
        ```python
        >>> rgb_colors = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
        >>> rgb_colors.is_valid_hex()
        [False, False, False]

        ```
        Check a mix of color formats:
        ```python
        >>> mixed = Colors(["#ff0000", (0, 255, 0), "not-a-color"])
        >>> mixed.is_valid_hex()
        [True, False, False]

        ```
        """
        return [self._is_valid_hex_i(col) for col in self.color_value]

    @staticmethod
    def _is_valid_hex_i(hex_color: str) -> bool:
        """Check if a single color value is a valid hexadecimal color.

        This static method checks if the provided color value is a valid
        hexadecimal color string.

        Parameters
        ----------
        hex_color : str
            A color string to validate as a hexadecimal color.
            Can be in the format "#RRGGBB" or "RRGGBB".

        Returns
        -------
        bool
            True if the color is a valid hexadecimal color, False otherwise.

        Notes
        -----
        - The method uses matplotlib's is_color_like function to validate hex colors
        - Both formats with and without the leading '#' are supported
        - Non-string values will return False

        Examples
        --------
        Check valid hex colors:
        ```python
        >>> from cleopatra.colors import Colors
        >>> Colors._is_valid_hex_i("#ff0000")
        True
        >>> Colors._is_valid_hex_i("00ff00")
        False
        >>> Colors._is_valid_hex_i("#0000FF")
        True

        ```

        Check invalid hex colors:
        ```python
        >>> Colors._is_valid_hex_i("not-a-color")
        False
        >>> Colors._is_valid_hex_i("#12345")  # Too short
        False
        >>> Colors._is_valid_hex_i((255, 0, 0))  # doctest: +ELLIPSIS
        False

        ```
        """
        if not isinstance(hex_color, str):
            return False
        else:
            return True if mcolors.is_color_like(hex_color) else False

    def is_valid_rgb(self) -> List[bool]:
        """Check if each color value is a valid RGB color.

        This method checks each color value stored in the object to determine
        if it is a valid RGB color tuple (either with values between 0-255 or
        normalized values between 0-1).

        Returns
        -------
        List[bool]
            A list of boolean values, one for each color value in the object.
            True indicates the color is a valid RGB tuple, False otherwise.

        Notes
        -----
        - The method checks for both RGB formats: values between 0-255 and normalized values between 0-1
        - A valid RGB tuple must have exactly 3 values (R, G, B)
        - Hex color strings will return False as they are not RGB tuples

        Examples
        --------
        Check if RGB colors are valid:
        ```python
        >>> from cleopatra.colors import Colors
        >>> # RGB colors (0-255 range)
        >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
        >>> rgb_255.is_valid_rgb()
        [True, True, True]

        >>> # RGB colors (normalized 0-1 range)
        >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)])
        >>> rgb_norm.is_valid_rgb()
        [True, True, True]

        ```
        Check if hex colors are valid RGB colors (they're not):
        ```python
        >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
        >>> hex_colors.is_valid_rgb()
        [False, False, False]

        ```
        Check a mix of color formats:
        ```python
        >>> mixed = Colors([(255, 0, 0), "#00ff00", (0.0, 0.0, 1.0)])
        >>> mixed.is_valid_rgb()
        [True, False, True]

        ```
        """
        return [
            self._is_valid_rgb_norm(col) or self._is_valid_rgb_255(col)
            for col in self.color_value
        ]

    @staticmethod
    def _is_valid_rgb_255(rgb_tuple: Any) -> bool:
        """Check if a single color value is a valid RGB tuple with values between 0-255.

        This static method checks if the provided value is a valid RGB tuple with
        integer values between 0 and 255.

        Parameters
        ----------
        rgb_tuple : Any
            The value to check. Should be a tuple of 3 integers between 0 and 255
            to be considered valid.

        Returns
        -------
        bool
            True if the value is a valid RGB tuple with values between 0-255,
            False otherwise.

        Examples
        --------
        Check valid RGB tuples (0-255 range):
        ```python
        >>> from cleopatra.colors import Colors
        >>> Colors._is_valid_rgb_255((255, 0, 0))
        True
        >>> Colors._is_valid_rgb_255((128, 64, 32))
        True
        >>> Colors._is_valid_rgb_255((0, 0, 0))
        True

        ```
        Check invalid RGB tuples:
        ```python
        >>> Colors._is_valid_rgb_255((1.0, 0.0, 0.0))  # Floats, not integers
        False
        >>> Colors._is_valid_rgb_255((256, 0, 0))  # Value > 255
        False
        >>> Colors._is_valid_rgb_255((0, 0))  # Not 3 values
        False
        >>> Colors._is_valid_rgb_255("#ff0000")  # Not a tuple
        False

        ```
        """
        if isinstance(rgb_tuple, tuple) and len(rgb_tuple) == 3:
            if all(isinstance(value, int) for value in rgb_tuple):
                return all(0 <= value <= 255 for value in rgb_tuple)
        return False

    @staticmethod
    def _is_valid_rgb_norm(rgb_tuple: Any) -> bool:
        """Check if a single color value is a valid normalized RGB tuple with values between 0-1.

        This static method checks if the provided value is a valid RGB tuple with
        float values between 0.0 and 1.0.

        Parameters
        ----------
        rgb_tuple : Any
            The value to check. Should be a tuple of 3 floats between 0.0 and 1.0
            to be considered valid.

        Returns
        -------
        bool
            True if the value is a valid normalized RGB tuple with values between 0.0-1.0,
            False otherwise.

        Examples
        --------
        Check valid normalized RGB tuples:
        ```python
        >>> from cleopatra.colors import Colors
        >>> Colors._is_valid_rgb_norm((1.0, 0.0, 0.0))
        True
        >>> Colors._is_valid_rgb_norm((0.5, 0.5, 0.5))
        True
        >>> Colors._is_valid_rgb_norm((0.0, 0.0, 0.0))
        True

        ```
        Check invalid normalized RGB tuples:
        ```python
        >>> Colors._is_valid_rgb_norm((255, 0, 0))  # Integers, not floats
        False
        >>> Colors._is_valid_rgb_norm((1.2, 0.0, 0.0))  # Value > 1.0
        False
        >>> Colors._is_valid_rgb_norm((0.5, 0.5))  # Not 3 values
        False
        >>> Colors._is_valid_rgb_norm("#ff0000")  # Not a tuple
        False

        ```
        """
        if isinstance(rgb_tuple, tuple) and len(rgb_tuple) == 3:
            if all(isinstance(value, float) for value in rgb_tuple):
                return all(0.0 <= value <= 1.0 for value in rgb_tuple)
        return False

    def to_rgb(
        self, normalized: bool = True
    ) -> List[Tuple[Union[int, float], Union[int, float], Union[int, float]]]:
        """Convert all color values to RGB format.

        This method converts all color values stored in the object to RGB format.
        Hex colors are converted to their RGB equivalents. RGB colors remain unchanged
        but may be normalized or denormalized based on the 'normalized' parameter.

        Parameters
        ----------
        normalized : bool, optional
            Whether to return normalized RGB values (between 0 and 1) or standard RGB values
            (between 0 and 255), by default True.
            - If True, returns RGB values scaled between 0 and 1
            - If False, returns RGB values scaled between 0 and 255

        Returns
        -------
        List[Tuple[Union[int, float], Union[int, float], Union[int, float]]]
            A list of RGB tuples. Each tuple contains three values (R, G, B).
            - If normalized=True, values are floats between 0.0 and 1.0
            - If normalized=False, values are integers between 0 and 255

        Examples
        --------
        - Convert hex colors to normalized RGB (0-1 range):
            ```python
            >>> from cleopatra.colors import Colors
            >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
            >>> hex_colors.to_rgb(normalized=True)
            [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)]

            ```

        - Convert hex colors to standard RGB (0-255 range):
            ```python
            >>> hex_colors.to_rgb(normalized=False)
            [(255, 0, 0), (0, 255, 0), (0, 0, 255)]

            ```
        - Convert RGB colors and maintain their format:
            There are two types of RGB coor values (0-255), and (0-1), you can get the RGB values in any format, the
            default is the normalized format (0-1):

            ```python
            >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0)])
            >>> rgb_255.to_rgb(normalized=False)  # Keep as 0-255 range
            [(255, 0, 0), (0, 255, 0)]
            >>> rgb_255.to_rgb(normalized=True)  # Convert to 0-1 range
            [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]

            >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)])
            >>> rgb_norm.to_rgb(normalized=True)  # Keep as 0-1 range
            [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]
            >>> rgb_norm.to_rgb(normalized=False)  # Convert to 0-255 range
            [(255, 0, 0), (0, 255, 0)]

            ```

        Convert mixed color formats:
        ```python
        >>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
        >>> mixed.to_rgb(normalized=True)
        [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)]

        ```
        """
        color_type = self.get_type()
        rgb = []
        if normalized:
            for ind, color_i in enumerate(self.color_value):
                # if the color is in RGB format (0-255), normalize the values to be between 0 and 1
                if color_type[ind] == "rgb":
                    rgb_color_normalized = tuple(value / 255 for value in color_i)
                    rgb.append(rgb_color_normalized)
                else:
                    # any other format, just convert it to RGB
                    rgb.append(mcolors.to_rgb(color_i))
        else:
            for ind, color_i in enumerate(self.color_value):
                # if the color is in RGB format (0-255), normalize the values to be between 0 and 1
                if color_type[ind] == "rgb":
                    rgb.append(color_i)
                else:
                    # any other format, just convert it to RGB
                    rgb.append(tuple([int(c * 255) for c in mcolors.to_rgb(color_i)]))

        return rgb

    def get_color_map(self, name: str = None) -> Colormap:
        """Get color ramp from a color values in stored in the object.

        Parameters
        ----------
        name: str, Default is None.
            The name of the color ramp.

        Returns
        -------
        Colormap:
            A color map.

        Examples
        --------
        - Create a color object from an image and get the color ramp:
            ```python
            >>> path = "examples/data/colors/color-ramp.png"
            >>> colors = Colors.create_from_image(path)
            >>> color_ramp = colors.get_color_map()
            >>> print(color_ramp) # doctest: +SKIP
            <matplotlib.colors.LinearSegmentedColormap object at 0x7f8a2e1b5e50>

            ```
        """
        vals = self.to_rgb(normalized=True)
        name = "custom_color_map" if name is None else name
        return LinearSegmentedColormap.from_list(name, vals)

color_value property #

Get the color values stored in the object.

This property returns the color values that were provided when initializing the Colors object or set afterwards. The values can be hex color strings, RGB tuples with values between 0-255, or normalized RGB tuples with values between 0-1.

Returns:

Type Description
Union[List[str], List[Tuple[float, float, float]]]

A list containing the color values. Each element can be: - A hex color string (e.g., "#ff0000" or "ff0000") - An RGB tuple with values between 0-255 (e.g., (255, 0, 0)) - A normalized RGB tuple with values between 0-1 (e.g., (1.0, 0.0, 0.0))

Examples:

Get color values from a Colors object with hex colors:

>>> from cleopatra.colors import Colors
>>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
>>> hex_colors.color_value
['#ff0000', '#00ff00', '#0000ff']

Get color values from a Colors object with RGB colors:

>>> rgb_colors = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
>>> rgb_colors.color_value
[(255, 0, 0), (0, 255, 0), (0, 0, 255)]
Get color values from a Colors object with mixed color formats:
>>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
>>> mixed.color_value
['#ff0000', (0, 255, 0), (0.0, 0.0, 1.0)]

__init__(color_value) #

Initialize a Colors object with the given color value(s).

Parameters:

Name Type Description Default
color_value Union[List[str], str, Tuple[float, float, float], List[Tuple[float, float, float]]]

The color value(s) to initialize the object with. Can be: - A single hex color string (e.g., "#ff0000" or "ff0000") - A single RGB tuple with values between 0-1 (e.g., (1.0, 0.0, 0.0)) - A single RGB tuple with values between 0-255 (e.g., (255, 0, 0)) - A list of hex color strings - A list of RGB tuples

required

Raises:

Type Description
ValueError

If the color_value is not a string, tuple, or list of strings/tuples.

Notes
  • Hex colors can be provided with or without the leading "#"
  • RGB tuples with float values between 0-1 are treated as normalized RGB
  • RGB tuples with integer values between 0-255 are treated as standard RGB
  • The class automatically detects the type of color format provided

Examples:

  • Initialize with a hex color:

    >>> from cleopatra.colors import Colors
    >>> # With hash symbol
    >>> color1 = Colors("#ff0000")
    >>> color1.color_value
    ['#ff0000']
    >>> # Without hash symbol
    >>> color2 = Colors("ff0000")
    >>> color2.color_value
    ['ff0000']
    
  • Initialize with an RGB color (normalized, values between 0 and 1):

    >>> rgb_norm = Colors((1.0, 0.0, 0.0))
    >>> rgb_norm.color_value
    [(1.0, 0.0, 0.0)]
    >>> rgb_norm.get_type()
    ['rgb-normalized']
    
  • Initialize with an RGB color (values between 0 and 255):

    >>> rgb_255 = Colors((255, 0, 0))
    >>> rgb_255.color_value
    [(255, 0, 0)]
    >>> rgb_255.get_type()
    ['rgb']
    
  • Initialize with a list of colors:

    >>> mixed_colors = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
    >>> mixed_colors.color_value
    ['#ff0000', (0, 255, 0), (0.0, 0.0, 1.0)]
    >>> mixed_colors.get_type()
    ['hex', 'rgb', 'rgb-normalized']
    
Source code in cleopatra/colors.py
def __init__(
    self,
    color_value: Union[
        List[str], str, Tuple[float, float, float], List[Tuple[float, float, float]]
    ],
):
    """Initialize a Colors object with the given color value(s).

    Parameters
    ----------
    color_value : Union[List[str], str, Tuple[float, float, float], List[Tuple[float, float, float]]]
        The color value(s) to initialize the object with. Can be:
        - A single hex color string (e.g., "#ff0000" or "ff0000")
        - A single RGB tuple with values between 0-1 (e.g., (1.0, 0.0, 0.0))
        - A single RGB tuple with values between 0-255 (e.g., (255, 0, 0))
        - A list of hex color strings
        - A list of RGB tuples

    Raises
    ------
    ValueError
        If the color_value is not a string, tuple, or list of strings/tuples.

    Notes
    -----
    - Hex colors can be provided with or without the leading "#"
    - RGB tuples with float values between 0-1 are treated as normalized RGB
    - RGB tuples with integer values between 0-255 are treated as standard RGB
    - The class automatically detects the type of color format provided

    Examples
    --------
    - Initialize with a hex color:

        ```python
        >>> from cleopatra.colors import Colors
        >>> # With hash symbol
        >>> color1 = Colors("#ff0000")
        >>> color1.color_value
        ['#ff0000']
        >>> # Without hash symbol
        >>> color2 = Colors("ff0000")
        >>> color2.color_value
        ['ff0000']

        ```

    - Initialize with an RGB color (normalized, values between 0 and 1):

        ```python
        >>> rgb_norm = Colors((1.0, 0.0, 0.0))
        >>> rgb_norm.color_value
        [(1.0, 0.0, 0.0)]
        >>> rgb_norm.get_type()
        ['rgb-normalized']

        ```

    - Initialize with an RGB color (values between 0 and 255):

        ```python
        >>> rgb_255 = Colors((255, 0, 0))
        >>> rgb_255.color_value
        [(255, 0, 0)]
        >>> rgb_255.get_type()
        ['rgb']

        ```

    - Initialize with a list of colors:

        ```python
        >>> mixed_colors = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
        >>> mixed_colors.color_value
        ['#ff0000', (0, 255, 0), (0.0, 0.0, 1.0)]
        >>> mixed_colors.get_type()
        ['hex', 'rgb', 'rgb-normalized']

        ```
    """
    # convert the hex color to a list if it is a string
    if isinstance(color_value, str) or isinstance(color_value, tuple):
        color_value = [color_value]
    elif not isinstance(color_value, list):
        raise ValueError(
            "The color_value must be a list of hex colors, list of tuples (RGB color), a single hex "
            "or single RGB tuple color."
        )

    self._color_value = color_value

create_from_image(path) classmethod #

Create a color object from an image.

if you have an image of a color ramp, and you want to extract the colors from it, you can use this method.

color-ramp

Parameters:

Name Type Description Default
path str

The path to the image file.

required

Returns:

Type Description
Colors

A color object.

Raises:

Type Description
FileNotFoundError

If the file does not exist.

Examples:

>>> path = "examples/data/colors/color-ramp.png"
>>> colors = Colors.create_from_image(path)
>>> print(colors.color_value) # doctest: +SKIP
[(9, 63, 8), (8, 68, 9), (5, 78, 7), (1, 82, 3), (0, 84, 0), (0, 85, 0), (1, 83, 0), (1, 81, 0), (1, 80, 1)
Source code in cleopatra/colors.py
@classmethod
def create_from_image(cls, path: str) -> "Colors":
    """Create a color object from an image.

    if you have an image of a color ramp, and you want to extract the colors from it, you can use this method.

    ![color-ramp](./../_images/colors/color-ramp.png)

    Parameters
    ----------
    path : str
        The path to the image file.

    Returns
    -------
    Colors
        A color object.

    Raises
    ------
    FileNotFoundError
        If the file does not exist.

    Examples
    --------
    ```python
    >>> path = "examples/data/colors/color-ramp.png"
    >>> colors = Colors.create_from_image(path)
    >>> print(colors.color_value) # doctest: +SKIP
    [(9, 63, 8), (8, 68, 9), (5, 78, 7), (1, 82, 3), (0, 84, 0), (0, 85, 0), (1, 83, 0), (1, 81, 0), (1, 80, 1)

    ```
    """
    if not Path(path).exists():
        raise FileNotFoundError(f"The file {path} does not exist.")
    try:
        image = Image.open(path).convert("RGB")
    except UnidentifiedImageError:
        raise ValueError(f"The file {path} is not a valid image.")
    width, height = image.size
    color_values = [image.getpixel((x, int(height / 2))) for x in range(width)]

    return cls(color_values)

get_color_map(name=None) #

Get color ramp from a color values in stored in the object.

Parameters:

Name Type Description Default
name str

The name of the color ramp.

None

Returns:

Name Type Description
Colormap Colormap

A color map.

Examples:

  • Create a color object from an image and get the color ramp:
    >>> path = "examples/data/colors/color-ramp.png"
    >>> colors = Colors.create_from_image(path)
    >>> color_ramp = colors.get_color_map()
    >>> print(color_ramp) # doctest: +SKIP
    <matplotlib.colors.LinearSegmentedColormap object at 0x7f8a2e1b5e50>
    
Source code in cleopatra/colors.py
def get_color_map(self, name: str = None) -> Colormap:
    """Get color ramp from a color values in stored in the object.

    Parameters
    ----------
    name: str, Default is None.
        The name of the color ramp.

    Returns
    -------
    Colormap:
        A color map.

    Examples
    --------
    - Create a color object from an image and get the color ramp:
        ```python
        >>> path = "examples/data/colors/color-ramp.png"
        >>> colors = Colors.create_from_image(path)
        >>> color_ramp = colors.get_color_map()
        >>> print(color_ramp) # doctest: +SKIP
        <matplotlib.colors.LinearSegmentedColormap object at 0x7f8a2e1b5e50>

        ```
    """
    vals = self.to_rgb(normalized=True)
    name = "custom_color_map" if name is None else name
    return LinearSegmentedColormap.from_list(name, vals)

get_type() #

Determine the type of each color value.

This method analyzes each color value stored in the object and determines its type: hex, rgb (values 0-255), or rgb-normalized (values 0-1).

Returns:

Type Description
List[str]

A list of strings indicating the type of each color value. Possible values are: - 'hex': Hexadecimal color string - 'rgb': RGB tuple with values between 0-255 - 'rgb-normalized': RGB tuple with values between 0-1

Notes

The method uses the following criteria to determine color types: - If the value is a string and is a valid hex color, it's classified as 'hex' - If the value is a tuple of 3 floats between 0-1, it's classified as 'rgb-normalized' - If the value is a tuple of 3 integers between 0-255, it's classified as 'rgb'

Examples:

  • Determine the type of a hex color:

    >>> from cleopatra.colors import Colors
    >>> hex_color = Colors("#23a9dd")
    >>> hex_color.get_type()
    ['hex']
    
  • Determine the type of an RGB color with normalized values (0-1):

    >>> rgb_norm = Colors((0.5, 0.2, 0.8))
    >>> rgb_norm.get_type()
    ['rgb-normalized']
    
  • Determine the type of an RGB color with values between 0-255:

    >>> rgb_255 = Colors((128, 51, 204))
    >>> rgb_255.get_type()
    ['rgb']
    
  • Determine types of mixed color formats:

    >>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
    >>> mixed.get_type()
    ['hex', 'rgb', 'rgb-normalized']
    
Source code in cleopatra/colors.py
def get_type(self) -> List[str]:
    """Determine the type of each color value.

    This method analyzes each color value stored in the object and determines
    its type: hex, rgb (values 0-255), or rgb-normalized (values 0-1).

    Returns
    -------
    List[str]
        A list of strings indicating the type of each color value.
        Possible values are:
        - 'hex': Hexadecimal color string
        - 'rgb': RGB tuple with values between 0-255
        - 'rgb-normalized': RGB tuple with values between 0-1

    Notes
    -----
    The method uses the following criteria to determine color types:
    - If the value is a string and is a valid hex color, it's classified as 'hex'
    - If the value is a tuple of 3 floats between 0-1, it's classified as 'rgb-normalized'
    - If the value is a tuple of 3 integers between 0-255, it's classified as 'rgb'

    Examples
    --------
    - Determine the type of a hex color:

        ```python
        >>> from cleopatra.colors import Colors
        >>> hex_color = Colors("#23a9dd")
        >>> hex_color.get_type()
        ['hex']

        ```

    - Determine the type of an RGB color with normalized values (0-1):

        ```python
        >>> rgb_norm = Colors((0.5, 0.2, 0.8))
        >>> rgb_norm.get_type()
        ['rgb-normalized']

        ```

    - Determine the type of an RGB color with values between 0-255:

        ```python
        >>> rgb_255 = Colors((128, 51, 204))
        >>> rgb_255.get_type()
        ['rgb']

        ```

    - Determine types of mixed color formats:

        ```python
        >>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
        >>> mixed.get_type()
        ['hex', 'rgb', 'rgb-normalized']

        ```
    """
    color_type = []
    for color_i in self.color_value:
        if self._is_valid_rgb_norm(color_i):
            color_type.append("rgb-normalized")
        elif self._is_valid_rgb_255(color_i):
            color_type.append("rgb")
        elif self._is_valid_hex_i(color_i):
            color_type.append("hex")

    return color_type

is_valid_hex() #

Check if each color value is a valid hexadecimal color.

This method checks each color value stored in the object to determine if it is a valid hexadecimal color string.

Returns:

Type Description
List[bool]

A list of boolean values, one for each color value in the object. True indicates the color is a valid hex color, False otherwise.

Notes
  • The method uses matplotlib's is_color_like function to validate hex colors
  • Both formats with and without the leading '#' are supported
  • RGB tuples will return False as they are not hex colors

Examples:

Check if hex colors are valid:

>>> from cleopatra.colors import Colors
>>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
>>> hex_colors.is_valid_hex()
[True, True, True]
Check if RGB colors are valid hex colors (they're not):
>>> rgb_colors = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
>>> rgb_colors.is_valid_hex()
[False, False, False]
Check a mix of color formats:
>>> mixed = Colors(["#ff0000", (0, 255, 0), "not-a-color"])
>>> mixed.is_valid_hex()
[True, False, False]

Source code in cleopatra/colors.py
def is_valid_hex(self) -> List[bool]:
    """Check if each color value is a valid hexadecimal color.

    This method checks each color value stored in the object to determine
    if it is a valid hexadecimal color string.

    Returns
    -------
    List[bool]
        A list of boolean values, one for each color value in the object.
        True indicates the color is a valid hex color, False otherwise.

    Notes
    -----
    - The method uses matplotlib's is_color_like function to validate hex colors
    - Both formats with and without the leading '#' are supported
    - RGB tuples will return False as they are not hex colors

    Examples
    --------
    Check if hex colors are valid:
    ```python
    >>> from cleopatra.colors import Colors
    >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
    >>> hex_colors.is_valid_hex()
    [True, True, True]

    ```
    Check if RGB colors are valid hex colors (they're not):
    ```python
    >>> rgb_colors = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
    >>> rgb_colors.is_valid_hex()
    [False, False, False]

    ```
    Check a mix of color formats:
    ```python
    >>> mixed = Colors(["#ff0000", (0, 255, 0), "not-a-color"])
    >>> mixed.is_valid_hex()
    [True, False, False]

    ```
    """
    return [self._is_valid_hex_i(col) for col in self.color_value]

is_valid_rgb() #

Check if each color value is a valid RGB color.

This method checks each color value stored in the object to determine if it is a valid RGB color tuple (either with values between 0-255 or normalized values between 0-1).

Returns:

Type Description
List[bool]

A list of boolean values, one for each color value in the object. True indicates the color is a valid RGB tuple, False otherwise.

Notes
  • The method checks for both RGB formats: values between 0-255 and normalized values between 0-1
  • A valid RGB tuple must have exactly 3 values (R, G, B)
  • Hex color strings will return False as they are not RGB tuples

Examples:

Check if RGB colors are valid:

>>> from cleopatra.colors import Colors
>>> # RGB colors (0-255 range)
>>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
>>> rgb_255.is_valid_rgb()
[True, True, True]

>>> # RGB colors (normalized 0-1 range)
>>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)])
>>> rgb_norm.is_valid_rgb()
[True, True, True]
Check if hex colors are valid RGB colors (they're not):
>>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
>>> hex_colors.is_valid_rgb()
[False, False, False]
Check a mix of color formats:
>>> mixed = Colors([(255, 0, 0), "#00ff00", (0.0, 0.0, 1.0)])
>>> mixed.is_valid_rgb()
[True, False, True]

Source code in cleopatra/colors.py
def is_valid_rgb(self) -> List[bool]:
    """Check if each color value is a valid RGB color.

    This method checks each color value stored in the object to determine
    if it is a valid RGB color tuple (either with values between 0-255 or
    normalized values between 0-1).

    Returns
    -------
    List[bool]
        A list of boolean values, one for each color value in the object.
        True indicates the color is a valid RGB tuple, False otherwise.

    Notes
    -----
    - The method checks for both RGB formats: values between 0-255 and normalized values between 0-1
    - A valid RGB tuple must have exactly 3 values (R, G, B)
    - Hex color strings will return False as they are not RGB tuples

    Examples
    --------
    Check if RGB colors are valid:
    ```python
    >>> from cleopatra.colors import Colors
    >>> # RGB colors (0-255 range)
    >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
    >>> rgb_255.is_valid_rgb()
    [True, True, True]

    >>> # RGB colors (normalized 0-1 range)
    >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)])
    >>> rgb_norm.is_valid_rgb()
    [True, True, True]

    ```
    Check if hex colors are valid RGB colors (they're not):
    ```python
    >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
    >>> hex_colors.is_valid_rgb()
    [False, False, False]

    ```
    Check a mix of color formats:
    ```python
    >>> mixed = Colors([(255, 0, 0), "#00ff00", (0.0, 0.0, 1.0)])
    >>> mixed.is_valid_rgb()
    [True, False, True]

    ```
    """
    return [
        self._is_valid_rgb_norm(col) or self._is_valid_rgb_255(col)
        for col in self.color_value
    ]

to_hex() #

Convert all color values to hexadecimal format.

This method converts all color values stored in the object to hexadecimal format. RGB tuples (both normalized and 0-255 range) are converted to their hex equivalents. Hex colors remain unchanged.

Returns:

Type Description
List[str]

A list of hexadecimal color strings. Each string is in the format '#RRGGBB'.

Notes
  • RGB tuples with values between 0-255 are first normalized to 0-1 range before conversion
  • RGB tuples with values already between 0-1 are directly converted
  • Existing hex colors are returned as-is
  • All returned hex colors include the leading '#' character

Examples:

Convert RGB colors to hex:

>>> from cleopatra.colors import Colors
>>> # RGB colors (0-255 range)
>>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
>>> rgb_255.to_hex()
['#ff0000', '#00ff00', '#0000ff']

>>> # RGB colors (normalized 0-1 range)
>>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)])
>>> rgb_norm.to_hex()
['#ff0000', '#00ff00', '#0000ff']

Convert a mix of color formats to hex:python

>>> mixed = Colors([(128, 51, 204), "#23a9dd", (0.5, 0.2, 0.8)])
>>> mixed.to_hex()
['#8033cc', '#23a9dd', '#8033cc']

Hex colors are returned as-is:python

>>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
>>> hex_colors.to_hex()
['#ff0000', '#00ff00', '#0000ff']

```

Source code in cleopatra/colors.py
def to_hex(self) -> List[str]:
    """Convert all color values to hexadecimal format.

    This method converts all color values stored in the object to hexadecimal format.
    RGB tuples (both normalized and 0-255 range) are converted to their hex equivalents.
    Hex colors remain unchanged.

    Returns
    -------
    List[str]
        A list of hexadecimal color strings. Each string is in the format '#RRGGBB'.

    Notes
    -----
    - RGB tuples with values between 0-255 are first normalized to 0-1 range before conversion
    - RGB tuples with values already between 0-1 are directly converted
    - Existing hex colors are returned as-is
    - All returned hex colors include the leading '#' character

    Examples
    --------
    Convert RGB colors to hex:
    ```python
    >>> from cleopatra.colors import Colors
    >>> # RGB colors (0-255 range)
    >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0), (0, 0, 255)])
    >>> rgb_255.to_hex()
    ['#ff0000', '#00ff00', '#0000ff']

    ```
    >>> # RGB colors (normalized 0-1 range)
    >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)])
    >>> rgb_norm.to_hex()
    ['#ff0000', '#00ff00', '#0000ff']

    ```
    Convert a mix of color formats to hex:
    ```python
    >>> mixed = Colors([(128, 51, 204), "#23a9dd", (0.5, 0.2, 0.8)])
    >>> mixed.to_hex()
    ['#8033cc', '#23a9dd', '#8033cc']

    ```
    Hex colors are returned as-is:
    ```python
    >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
    >>> hex_colors.to_hex()
    ['#ff0000', '#00ff00', '#0000ff']

    ```
    """
    converted_color = []
    color_type = self.get_type()
    for ind, color_i in enumerate(self.color_value):
        if color_type[ind] == "hex":
            converted_color.append(color_i)
        elif color_type[ind] == "rgb":
            # Normalize the RGB values to be between 0 and 1
            rgb_color_normalized = tuple(value / 255 for value in color_i)
            converted_color.append(mcolors.to_hex(rgb_color_normalized))
        else:
            converted_color.append(mcolors.to_hex(color_i))
    return converted_color

to_rgb(normalized=True) #

Convert all color values to RGB format.

This method converts all color values stored in the object to RGB format. Hex colors are converted to their RGB equivalents. RGB colors remain unchanged but may be normalized or denormalized based on the 'normalized' parameter.

Parameters:

Name Type Description Default
normalized bool

Whether to return normalized RGB values (between 0 and 1) or standard RGB values (between 0 and 255), by default True. - If True, returns RGB values scaled between 0 and 1 - If False, returns RGB values scaled between 0 and 255

True

Returns:

Type Description
List[Tuple[Union[int, float], Union[int, float], Union[int, float]]]

A list of RGB tuples. Each tuple contains three values (R, G, B). - If normalized=True, values are floats between 0.0 and 1.0 - If normalized=False, values are integers between 0 and 255

Examples:

  • Convert hex colors to normalized RGB (0-1 range):

    >>> from cleopatra.colors import Colors
    >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
    >>> hex_colors.to_rgb(normalized=True)
    [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)]
    

  • Convert hex colors to standard RGB (0-255 range):

    >>> hex_colors.to_rgb(normalized=False)
    [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
    

  • Convert RGB colors and maintain their format: There are two types of RGB coor values (0-255), and (0-1), you can get the RGB values in any format, the default is the normalized format (0-1):

    >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0)])
    >>> rgb_255.to_rgb(normalized=False)  # Keep as 0-255 range
    [(255, 0, 0), (0, 255, 0)]
    >>> rgb_255.to_rgb(normalized=True)  # Convert to 0-1 range
    [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]
    
    >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)])
    >>> rgb_norm.to_rgb(normalized=True)  # Keep as 0-1 range
    [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]
    >>> rgb_norm.to_rgb(normalized=False)  # Convert to 0-255 range
    [(255, 0, 0), (0, 255, 0)]
    

Convert mixed color formats:

>>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
>>> mixed.to_rgb(normalized=True)
[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)]

Source code in cleopatra/colors.py
def to_rgb(
    self, normalized: bool = True
) -> List[Tuple[Union[int, float], Union[int, float], Union[int, float]]]:
    """Convert all color values to RGB format.

    This method converts all color values stored in the object to RGB format.
    Hex colors are converted to their RGB equivalents. RGB colors remain unchanged
    but may be normalized or denormalized based on the 'normalized' parameter.

    Parameters
    ----------
    normalized : bool, optional
        Whether to return normalized RGB values (between 0 and 1) or standard RGB values
        (between 0 and 255), by default True.
        - If True, returns RGB values scaled between 0 and 1
        - If False, returns RGB values scaled between 0 and 255

    Returns
    -------
    List[Tuple[Union[int, float], Union[int, float], Union[int, float]]]
        A list of RGB tuples. Each tuple contains three values (R, G, B).
        - If normalized=True, values are floats between 0.0 and 1.0
        - If normalized=False, values are integers between 0 and 255

    Examples
    --------
    - Convert hex colors to normalized RGB (0-1 range):
        ```python
        >>> from cleopatra.colors import Colors
        >>> hex_colors = Colors(["#ff0000", "#00ff00", "#0000ff"])
        >>> hex_colors.to_rgb(normalized=True)
        [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)]

        ```

    - Convert hex colors to standard RGB (0-255 range):
        ```python
        >>> hex_colors.to_rgb(normalized=False)
        [(255, 0, 0), (0, 255, 0), (0, 0, 255)]

        ```
    - Convert RGB colors and maintain their format:
        There are two types of RGB coor values (0-255), and (0-1), you can get the RGB values in any format, the
        default is the normalized format (0-1):

        ```python
        >>> rgb_255 = Colors([(255, 0, 0), (0, 255, 0)])
        >>> rgb_255.to_rgb(normalized=False)  # Keep as 0-255 range
        [(255, 0, 0), (0, 255, 0)]
        >>> rgb_255.to_rgb(normalized=True)  # Convert to 0-1 range
        [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]

        >>> rgb_norm = Colors([(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)])
        >>> rgb_norm.to_rgb(normalized=True)  # Keep as 0-1 range
        [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]
        >>> rgb_norm.to_rgb(normalized=False)  # Convert to 0-255 range
        [(255, 0, 0), (0, 255, 0)]

        ```

    Convert mixed color formats:
    ```python
    >>> mixed = Colors(["#ff0000", (0, 255, 0), (0.0, 0.0, 1.0)])
    >>> mixed.to_rgb(normalized=True)
    [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)]

    ```
    """
    color_type = self.get_type()
    rgb = []
    if normalized:
        for ind, color_i in enumerate(self.color_value):
            # if the color is in RGB format (0-255), normalize the values to be between 0 and 1
            if color_type[ind] == "rgb":
                rgb_color_normalized = tuple(value / 255 for value in color_i)
                rgb.append(rgb_color_normalized)
            else:
                # any other format, just convert it to RGB
                rgb.append(mcolors.to_rgb(color_i))
    else:
        for ind, color_i in enumerate(self.color_value):
            # if the color is in RGB format (0-255), normalize the values to be between 0 and 1
            if color_type[ind] == "rgb":
                rgb.append(color_i)
            else:
                # any other format, just convert it to RGB
                rgb.append(tuple([int(c * 255) for c in mcolors.to_rgb(color_i)]))

    return rgb

Examples#

Creating Color Objects#

from cleopatra.colors import Colors

# Create a Colors object with a hex color
hex_color = Colors("#FF5733")

# Create a Colors object with an RGB color (normalized)
rgb_color = Colors((1.0, 0.34, 0.2))

# Create a Colors object with an RGB color (0-255)
rgb_255_color = Colors((255, 87, 51))

# Create a Colors object with a named color
named_color = Colors("red")

# Create a Colors object with a list of colors
color_list = Colors(["red", "green", "blue"])

Converting Between Color Formats#

# Convert to hex
hex_value = rgb_color.to_hex()
print(hex_value)  # "#FF5733"

# Convert to RGB (normalized)
rgb_value = hex_color.to_rgb(normalized=True)
print(rgb_value)  # (1.0, 0.34, 0.2)

# Convert to RGB (0-255)
rgb_255_value = hex_color.to_rgb(normalized=False)
print(rgb_255_value)  # (255, 87, 51)

Validating Color Values#

# Check if a hex color is valid
is_valid = hex_color.is_valid_hex()
print(is_valid)  # True

# Check if an RGB color is valid
is_valid = rgb_color.is_valid_rgb()
print(is_valid)  # True