"""The **Color** class and its implementation."""
# Daniel Fletcher
# Harvard CS50P 2024
# Final Project
# color.py
# Color class and related functionality
from .constants import ANSI_PREFIX
[docs]class Color:
"""A class for representing color in RGB format.
Represents any given color by storing its RGB values---that is, its
red level (0-255), green level (0-255), and blue level (0-255). Once
a **Color** object has its RGB values set, it can be used to add color
to strings via concatenation.
Also designed to be used in conjunction with other key components of the
package, like the **ColorStr** class or the ``colorize`` decorator function.
Example::
# Normal print statement (behaves as expected)
print(\"Not in Color.\")
my_color = rgb(128, 0, 128)
# Colorized print statement (prints string contents in purple)
print(my_color + \"Now in Color!\" + constants.RESET)
Note:
Make sure to use the ``RESET`` constant when combining strings with **Color**
objects; the color settings of your terminal environment may not revert to
defaults unless explicitly specified!
"""
[docs] def __init__(self):
"""Initialize a **Color** object with red, green, and blue values all set to ``0``.
Note that there are no parameterized ``__init___`` functions for the
``Color`` class; instead, it is recommended to use one of the associated
:ref:`core functions <core-functions-module-label>` that come with the package
as opposed to calling the initializer explicitly.
That said, the default initializer can still be called. Upon initialization,
each of the object properties (red, green, blue) can be tweaked individually from
their default values of ``0``.
Example::
# Normal print statement (behaves as expected)
print(\"Not in Color.\")
my_color = Color()
my_color.red = 128
my_color.green = 0
my_color.blue = 128
# Colorized print statement (prints string contents in purple)
print(my_color + \"Now in Color!\" + constants.RESET)
"""
self.red = 0
self.green = 0
self.blue = 0
[docs] def __str__(self):
"""Return a string representation of the color. Functionally
identical to calling the ``fg`` method.
Example::
my_color = rgb(0, 0, 255)
print(f\"{my_color}f-strings make me feel blue.{constants.RESET}\")
"""
return self.fg()
[docs] def __add__(self, string: str) -> str:
"""Support concatenation of **Color** and **str** objects.
Example::
my_color = rgb(0, 255, 0)
my_message = \"Hello, Green World!\"
print(my_color + my_message + constants.RESET)
Note:
Concatenation of **Color** and **str** objects only supports operations
where the first operand is a **Color**. This means that any operation
of the type ``Color + str`` is valid, whereas any operation of the type
``str + Color`` is *not*.
"""
if isinstance(string, str):
return str(self) + string
else:
return NotImplemented
[docs] def __eq__(self, other) -> bool:
"""Support equality comparisons of two **Color** objects.
Two objects of type **Color** are defined to be equal if
and only if:
- ``color1.red == color2.red`` is **True**,
- ``color1.green == color2.green`` is **True**,
- and ``color1.blue == color2.blue`` is **True**.
Both equality operators (``==`` and ``!=``) are supported.
Example::
color1 = rgb(255, 255, 0)
color2 = hexcode(\"#ffff00\")
color3 = css(\"blue\")
print(color1 == color2) # True
print(color2 == color3) # False
print(color1 == color3) # False
"""
return (isinstance(other, Color)
and self.red == other.red
and self.green == other.green
and self.blue == other.blue)
def __ne__(self, other) -> bool:
return not (self == other)
@property
def red(self):
"""int: A numeric value (0-255) representing the color's red levels.
Supports both get and set operations.
Raises:
TypeError:
If the property is set to a non-integer value.
ValueError:
If the property is set to an out-of-range integer (e.g., not in range 0-255).
"""
return self._red
@red.setter
def red(self, red: int):
if isinstance(red, int):
if 0 <= red <= 255:
self._red = red
else:
raise ValueError(f"Invalid rgb value, {red} not in range [0, 255]")
else:
raise TypeError(f"Expected rgb value as integer, got object of type {type(red)}")
@property
def green(self):
"""int: A numeric value (0-255) representing the color's green levels.
Supports both get and set operations.
Raises:
TypeError:
If the property is set to a non-integer value.
ValueError:
If the property is set to an out-of-range integer (e.g., not in range 0-255).
"""
return self._green
@green.setter
def green(self, green: int):
if isinstance(green, int):
if 0 <= green <= 255:
self._green = green
else:
raise ValueError(f"Invalid rgb value, {green} not in range [0, 255]")
else:
raise TypeError(f"Expected rgb value as integer, got object of type {type(green)}")
@property
def blue(self):
"""int: A numeric value (0-255) representing the color's blue levels.
Supports both get and set operations.
Raises:
TypeError:
If the property is set to a non-integer value.
ValueError:
If the property is set to an out-of-range integer (e.g., not in range 0-255).
"""
return self._blue
@blue.setter
def blue(self, blue: int):
if isinstance(blue, int):
if 0 <= blue <= 255:
self._blue = blue
else:
raise ValueError(f"Invalid rgb value, {blue} not in range [0, 255]")
else:
raise TypeError(f"Expected rgb value as integer, got object of type {type(blue)}")
[docs] def fg(self) -> str:
"""Return the stored color as a string representing its corresponding foreground
ANSI color code sequence.
The return value of this function is the default behavior of converting
a **Color** object to a string. The option to call ``fg`` explicitly
has been included for completeness, readability, and consistency.
Returns:
The ANSI color code sequence representation of the object, specifically
for use as a foreground color.
Example::
my_color = rgb(0, 128, 64)
print(my_color.fg() + "This text is so colorful!" + constants.RESET)
"""
rgb = f"{self.red};{self.green};{self.blue}"
return f"{ANSI_PREFIX}[38;2;{rgb}m"
[docs] def bg(self) -> str:
"""Return the stored color as a string representing its corresponding background
ANSI color code sequence.
Returns:
The ANSI color code sequence representation of the object, specifically
to use as a background color.
Example::
my_color = rgb(30, 120, 15)
print(my_color.bg() + "What a gorgeous background!" + constants.RESET)
"""
rgb = f"{self.red};{self.green};{self.blue}"
return f"{ANSI_PREFIX}[48;2;{rgb}m"