Skip to content

Color

Module: terminaltexteffects.utils.graphics

Basic Usage

Color objects represent colors throughout TTE. They are immutable and hashable, so they can safely be reused as dictionary keys and configuration values.

Supports Multiple Specification Formats

from terminaltexteffects.utils.graphics import Color

red = Color("ff0000")
xterm_red = Color(9)
rgb_red_again = Color("#FF0000")

RGB strings must contain exactly six hexadecimal digits and may have one leading #. They are normalized to lowercase without the leading hash. XTerm colors must be non-boolean integers from 0 through 255. Invalid values raise ValueError.

rgb_color exposes the normalized six-digit string, rgb_ints exposes its three integer channels, and xterm_color contains the original XTerm index or None for an RGB specification.

Equality and Hashing

Equality preserves the normalized color specification, not only its displayed RGB value:

from terminaltexteffects.utils.graphics import Color

assert Color("#FF0000") == Color("ff0000")
assert Color(9) != Color("ff0000")

The second comparison is unequal even though XTerm color 9 converts to the same RGB value. Keeping XTerm provenance distinct lets terminal output continue to choose between XTerm and RGB escape sequences.

Printing Colors

Colors can be printed to show the code and resulting color appearance.

from terminaltexteffects.utils.graphics import Color

red = Color("#ff0000")
print(red)

Printed Color objects showing their codes and appearance

Using Colors to Build a Gradient

from terminaltexteffects.utils.graphics import Color, Gradient

rgb = Gradient(Color("#ff0000"), Color("#00ff00"), Color("#0000ff"), steps=5)
for color in rgb:
    assert isinstance(color, Color)
    print(color.rgb_color)

Shifting Between Colors

shift_color_towards() accepts a factor from 0 through 1 and uses the same nearest, ties-to-even channel rounding as gradient generation:

from terminaltexteffects.utils.graphics import Color, shift_color_towards

midpoint = shift_color_towards(Color("000000"), Color("ffffff"), 0.5)
assert midpoint == Color("808080")

An out-of-range or non-finite factor raises ValueError.

Passing Colors to Effect Configurations

from terminaltexteffects.effects.effect_colorshift import ColorShift
from terminaltexteffects.utils.graphics import Color

text = ("EXAMPLE" * 10 + "\n") * 10
red = Color("#ff0000")
green = Color("#00ff00")
blue = Color("#0000ff")
effect = ColorShift(text)
effect.effect_config.gradient_stops = (red, green, blue)
with effect.terminal_output() as terminal:
    for frame in effect:
        terminal.print(frame)

Color Reference

Represents a color in the RGB color space.

The color can be initialized with an XTerm-256 color code or an RGB hex color string. Can be printed to display the color code and appearance as a color block. RGB specifications are normalized to lowercase without a leading hash. Equality and hashing preserve the normalized specification: equivalent RGB spellings compare equal, while XTerm indices remain distinct from RGB specifications and from other indices. Instances are immutable.

Attributes:

Name Type Description
color_arg int | str

The color value as an XTerm-256 color code or an RGB hex color string.

xterm_color int | None

The XTerm-256 color code. None if the color is an RGB hex color string.

rgb_color str

The RGB hex color string.

Properties

rgb_ints (tuple[int, int, int]): Returns the RGB values as a tuple of integers.

Raises:

Type Description
ValueError

If the color value is not a valid XTerm-256 color code or an RGB hex color string.

Source code in terminaltexteffects/utils/graphics.py
@dataclass(frozen=True, init=False, repr=False, eq=False)
class Color:
    """Represents a color in the RGB color space.

    The color can be initialized with an XTerm-256 color code or an RGB hex color string. Can be printed
    to display the color code and appearance as a color block. RGB specifications are normalized to lowercase without
    a leading hash. Equality and hashing preserve the normalized specification: equivalent RGB spellings compare
    equal, while XTerm indices remain distinct from RGB specifications and from other indices. Instances are immutable.

    Attributes:
        color_arg (int | str): The color value as an XTerm-256 color code or an RGB hex color string.
        xterm_color (int | None): The XTerm-256 color code. None if the color is an RGB hex color string.
        rgb_color (str): The RGB hex color string.

    Properties:
        rgb_ints (tuple[int, int, int]): Returns the RGB values as a tuple of integers.

    Raises:
        ValueError: If the color value is not a valid XTerm-256 color code or an RGB hex color string.

    """

    color_arg: int | str
    xterm_color: int | None
    rgb_color: str
    _rgb_ints: tuple[int, int, int] = field(init=False, repr=False, compare=False)

    def __init__(self, color_value: int | str) -> None:
        """Initialize a Color object.

        Args:
            color_value (int | str): The color value as an XTerm-256 color code or an RGB hex color string.
                Example: 255 or 'ffffff' or '#ffffff'

        Raises:
            ValueError: If the color value is not a valid XTerm-256 color code or an RGB hex color string.

        """
        if not hexterm.is_valid_color(color_value):
            msg = (
                "Invalid color value. Color must be an XTerm-256 color code or an RGB hex color string. "
                "Example: 255 or 'ffffff' or '#ffffff'"
            )
            raise ValueError(
                msg,
            )
        if isinstance(color_value, str):
            color_value = color_value.removeprefix("#").lower()
        object.__setattr__(self, "color_arg", color_value)
        if isinstance(color_value, int):
            object.__setattr__(self, "xterm_color", color_value)
            object.__setattr__(self, "rgb_color", hexterm.xterm_to_hex(color_value))
        else:
            object.__setattr__(self, "rgb_color", color_value)
            object.__setattr__(self, "xterm_color", None)
        object.__setattr__(self, "_rgb_ints", colorterm._hex_to_int(self.rgb_color))

    @classmethod
    def _from_rgb_ints(cls, rgb_ints: tuple[int, int, int]) -> Color:
        """Create an RGB `Color` from channels already validated by internal calculations."""
        color = cls.__new__(cls)
        rgb_color = f"{rgb_ints[0]:02x}{rgb_ints[1]:02x}{rgb_ints[2]:02x}"
        object.__setattr__(color, "color_arg", rgb_color)
        object.__setattr__(color, "xterm_color", None)
        object.__setattr__(color, "rgb_color", rgb_color)
        object.__setattr__(color, "_rgb_ints", rgb_ints)
        return color

    @property
    def rgb_ints(self) -> tuple[int, int, int]:
        """Returns the RGB values as a tuple of integers.

        Returns:
            tuple[int, int, int]: The RGB values as a tuple of integers.

        """
        return self._rgb_ints

    def __repr__(self) -> str:
        """Return a constructor-compatible representation of the `Color`."""
        return f"Color({self.color_arg!r})"

    def __str__(self) -> str:
        """Return a string representation of the Color object."""
        color_block = f"{colorterm.fg(self.rgb_color)}█████{ansitools.reset_all()}"
        xterm_display = f" | XTerm Color: {self.xterm_color}" if self.xterm_color is not None else ""
        return f"Color Code: {self.rgb_color}{xterm_display}\nColor Appearance: {color_block}"

    def __eq__(self, other: object) -> bool:
        """Return whether this color has the same normalized specification as another `Color`.

        Returns `NotImplemented` when `other` is not a `Color`.
        """
        if not isinstance(other, Color):
            return NotImplemented
        return self.color_arg == other.color_arg

    def __ne__(self, other: object) -> bool:
        """Return whether this color is not equal to another `Color`.

        Returns `NotImplemented` when `other` is not a `Color`.
        """
        if not isinstance(other, Color):
            return NotImplemented
        return self.color_arg != other.color_arg

    def __hash__(self) -> int:
        """Return the hash of this color's normalized specification."""
        return hash(self.color_arg)

    def __iter__(self) -> Iterator[Color]:
        """Return an iterator yielding this `Color` instance once."""
        return iter((self,))

rgb_ints property

Returns the RGB values as a tuple of integers.

Returns:

Type Description
tuple[int, int, int]

tuple[int, int, int]: The RGB values as a tuple of integers.

__eq__(other)

Return whether this color has the same normalized specification as another Color.

Returns NotImplemented when other is not a Color.

Source code in terminaltexteffects/utils/graphics.py
def __eq__(self, other: object) -> bool:
    """Return whether this color has the same normalized specification as another `Color`.

    Returns `NotImplemented` when `other` is not a `Color`.
    """
    if not isinstance(other, Color):
        return NotImplemented
    return self.color_arg == other.color_arg

__hash__()

Return the hash of this color's normalized specification.

Source code in terminaltexteffects/utils/graphics.py
def __hash__(self) -> int:
    """Return the hash of this color's normalized specification."""
    return hash(self.color_arg)

__init__(color_value)

Initialize a Color object.

Parameters:

Name Type Description Default
color_value int | str

The color value as an XTerm-256 color code or an RGB hex color string. Example: 255 or 'ffffff' or '#ffffff'

required

Raises:

Type Description
ValueError

If the color value is not a valid XTerm-256 color code or an RGB hex color string.

Source code in terminaltexteffects/utils/graphics.py
def __init__(self, color_value: int | str) -> None:
    """Initialize a Color object.

    Args:
        color_value (int | str): The color value as an XTerm-256 color code or an RGB hex color string.
            Example: 255 or 'ffffff' or '#ffffff'

    Raises:
        ValueError: If the color value is not a valid XTerm-256 color code or an RGB hex color string.

    """
    if not hexterm.is_valid_color(color_value):
        msg = (
            "Invalid color value. Color must be an XTerm-256 color code or an RGB hex color string. "
            "Example: 255 or 'ffffff' or '#ffffff'"
        )
        raise ValueError(
            msg,
        )
    if isinstance(color_value, str):
        color_value = color_value.removeprefix("#").lower()
    object.__setattr__(self, "color_arg", color_value)
    if isinstance(color_value, int):
        object.__setattr__(self, "xterm_color", color_value)
        object.__setattr__(self, "rgb_color", hexterm.xterm_to_hex(color_value))
    else:
        object.__setattr__(self, "rgb_color", color_value)
        object.__setattr__(self, "xterm_color", None)
    object.__setattr__(self, "_rgb_ints", colorterm._hex_to_int(self.rgb_color))

__iter__()

Return an iterator yielding this Color instance once.

Source code in terminaltexteffects/utils/graphics.py
def __iter__(self) -> Iterator[Color]:
    """Return an iterator yielding this `Color` instance once."""
    return iter((self,))

__ne__(other)

Return whether this color is not equal to another Color.

Returns NotImplemented when other is not a Color.

Source code in terminaltexteffects/utils/graphics.py
def __ne__(self, other: object) -> bool:
    """Return whether this color is not equal to another `Color`.

    Returns `NotImplemented` when `other` is not a `Color`.
    """
    if not isinstance(other, Color):
        return NotImplemented
    return self.color_arg != other.color_arg

__repr__()

Return a constructor-compatible representation of the Color.

Source code in terminaltexteffects/utils/graphics.py
def __repr__(self) -> str:
    """Return a constructor-compatible representation of the `Color`."""
    return f"Color({self.color_arg!r})"

__str__()

Return a string representation of the Color object.

Source code in terminaltexteffects/utils/graphics.py
def __str__(self) -> str:
    """Return a string representation of the Color object."""
    color_block = f"{colorterm.fg(self.rgb_color)}█████{ansitools.reset_all()}"
    xterm_display = f" | XTerm Color: {self.xterm_color}" if self.xterm_color is not None else ""
    return f"Color Code: {self.rgb_color}{xterm_display}\nColor Appearance: {color_block}"