Skip to content

Spectrum Reference

Create a list of concurrent Color and/or Style instances. Args: hues (int): Number of colors to generate. Defaults to 17. invert (bool, optional): If True, reverse the generated list. Defaults to False. seed (Optional[int], optional): If provided, sets the random seed for deterministic color order. Raises: ValueError: If hues < 2. ValueError: If seed is not None and not an integer. Properties: colors (List[Color]): List of Color instances. names (List[str]): List of color names.

Source code in src/rich_gradient/spectrum.py
class Spectrum:
    """Create a list of concurrent Color and/or Style instances.
    Args:
        hues (int): Number of colors to generate. Defaults to 17.
        invert (bool, optional): If True, reverse the generated list.
            Defaults to False.
        seed (Optional[int], optional): If provided, sets the random \
seed for deterministic color order.
    Raises:
        ValueError: If hues < 2.
        ValueError: If seed is not None and not an integer.
    Properties:
        colors (List[Color]): List of Color instances.
        names (List[str]): List of color names.

    """

    def __init__(
        self, hues: int = 17, invert: bool = False, seed: Optional[int] = None
    ) -> None:
        """Initialize the Spectrum with a specified number of hues and optional \
inversion and seed.
        Args:
            hues (int): Number of colors to generate. Defaults to 17.
            invert (bool, optional): If True, reverse the generated list.
                Defaults to False.
            seed (Optional[int], optional): If provided, sets the random seed for \
deterministic color order.
        Raises:
            ValueError: If hues < 2.
            ValueError: If seed is not None and not an integer.
        """
        if hues < 2:
            raise ValueError("hues must be at least 2")
        if hues > len(COLOR_STOPS):
            raise ValueError(f"hues must be at most {len(COLOR_STOPS)}")
        if seed is not None and not isinstance(seed, int):
            raise ValueError("seed must be an integer or None")

        # Use a dedicated RNG to avoid mutating global random state
        rng = Random(seed)

        # Generate a random cycle of colors from the spectrum
        colors: List[Color] = [Color.parse(color) for color in COLOR_STOPS.values()]
        color_cycle = cycle(colors)

        # Skip a pseudo-random number of colors to add variability, deterministically per seed
        for _ in range(rng.randint(1, 18)):
            next(color_cycle)

        # Create a list of colors based on the specified number of hues
        colors = [next(color_cycle) for _ in range(hues)]
        self.colors = colors
        if invert:
            self.colors.reverse()

        # Set names based on COLOR_STOPS mapping
        # Build a reverse map from normalized hex -> name and assign names for the selected colors

        hex_to_name = {
            Color.parse(value).get_truecolor().hex.upper(): name
            for name, value in COLOR_STOPS.items()
        }
        self.names = [
            hex_to_name.get(color.get_truecolor().hex.upper(), color.get_truecolor().hex.upper())
            for color in self.colors
        ]


        self.styles = [
            Style(color=color, bold=False, italic=False, underline=False)
            for color in self.colors
        ]
        self.hex = [color.get_truecolor().hex.upper() for color in self.colors]

    @property
    def colors(self) -> List[Color]:
        """Return the list of Color instances."""
        return self._colors

    @colors.setter
    def colors(self, value: List[Color]) -> None:
        """Set the list of Color instances."""
        if not isinstance(value, list) or not all(isinstance(c, Color) for c in value):
            raise ValueError("colors must be a list of Color instances")
        if len(value) < 2:
            raise ValueError("colors must contain at least two Color instances")
        self._colors = value

    @property
    def triplets(self) -> List[ColorTriplet]:
        """Return the list of ColorTriplet instances."""
        return [color.get_truecolor() for color in self._colors]

    @property
    def styles(self) -> List[Style]:
        """Return the list of Style instances."""
        return self._styles

    @styles.setter
    def styles(self, styles: List[StyleType]) -> None:
        """Set the list of Style instances."""
        if not isinstance(styles, list) or not all(
            isinstance(s, Style) for s in styles
        ):
            raise ValueError("styles must be a list of Style instances")
        if len(styles) != len(self.colors):
            raise ValueError("styles length must match colors length")
        parsed_styles: List[Style] = []
        for style in styles:
            if isinstance(style, (str)):
                parsed_styles.append(Style.parse(style))
            if isinstance(style, Style):
                parsed_styles.append(style)
        self._styles = parsed_styles

    @property
    def names(self) -> List[str]:
        """Return the list of color names."""
        return self._names

    @names.setter
    def names(self, value: List[str]) -> None:
        """Set the list of color names."""
        if not isinstance(value, list) or not all(isinstance(n, str) for n in value):
            raise ValueError("names must be a list of strings")
        if len(value) != len(self._colors):
            raise ValueError("names length must match colors length")
        self._names = value

    def __repr__(self) -> str:
        """Return a string representation of the Spectrum."""
        colors = [f"{name}" for name in self.names]
        colors_str = ", ".join(colors)
        return f"Spectrum({colors_str})"

    def __len__(self) -> int:
        """Return the number of colors in the Spectrum."""
        return len(self.colors)

    def __getitem__(self, index: int) -> Color:
        """Return the Color at the specified index."""
        if not isinstance(index, int):
            raise TypeError("Index must be an integer")
        if index < 0 or index >= len(self.colors):
            raise IndexError("Index out of range")
        return self.colors[index]

    def __iter__(self):
        """Return an iterator over the colors in the Spectrum."""
        return iter(self.colors)

    def __rich__(self) -> Table:
        """Return a rich Table representation of the Spectrum."""

        def rainbow_title(text: str) -> Text:
            chunks = [text[i : i + 2] for i in range(0, len(text), 2)]
            pieces: List[Text] = []
            for idx, chunk in enumerate(chunks):
                color = self.colors[idx % len(self.colors)]
                hex_code = color.get_truecolor().hex
                pieces.append(Text(chunk, style=f"b u {hex_code}"))
            return Text.assemble(*pieces)

        table = Table(title=rainbow_title("Spectrum Colors"))
        table.add_column(
            rainbow_title("Sample"), justify="center")
        table.add_column(rainbow_title("Color"), style="bold")
        table.add_column(rainbow_title("Hex"), style="bold")
        table.add_column(rainbow_title("Name"), style="bold")

        for color, name in zip(self.colors, self.names):
            hex_code = color.get_truecolor().hex
            red = color.get_truecolor().red
            green = color.get_truecolor().green
            blue = color.get_truecolor().blue

            name_text = Text(
                name.capitalize(),
                Style(color=hex_code, bold=True),
                no_wrap=True,
                justify="left",
            )
            hex_text = Text(
                f" {hex_code.upper()} ",
                Style(bgcolor=hex_code, color="#000000", bold=True),
                no_wrap=True,
                justify="center",
            )
            rgb_text = Text.assemble(*[
                Text("rgb", style=f"bold {hex_code}"),
                Text("(", style="i white"),
                Text(f"{red:>3}", style="#FF0000"),
                Text(",", style="i #555"),
                Text(f"{green:>3}", style="#00FF00"),
                Text(",", style="i #555"),
                Text(f"{blue:>3}", style="#00AAFF"),
                Text(")", style="i white"),
            ])
            sample = Text("█" * 10, style=Style(color=hex_code, bold=True))
            table.add_row(sample, name_text, hex_text, rgb_text)
        return table

    @property
    def rich(self) -> Table:
        """Return the rich Table representation of the Spectrum."""
        return self.__rich__()

colors property writable

Return the list of Color instances.

names property writable

Return the list of color names.

rich property

Return the rich Table representation of the Spectrum.

styles property writable

Return the list of Style instances.

triplets property

Return the list of ColorTriplet instances.

__getitem__(index)

Return the Color at the specified index.

Source code in src/rich_gradient/spectrum.py
def __getitem__(self, index: int) -> Color:
    """Return the Color at the specified index."""
    if not isinstance(index, int):
        raise TypeError("Index must be an integer")
    if index < 0 or index >= len(self.colors):
        raise IndexError("Index out of range")
    return self.colors[index]

__init__(hues=17, invert=False, seed=None)

Initialize the Spectrum with a specified number of hues and optional inversion and seed. Args: hues (int): Number of colors to generate. Defaults to 17. invert (bool, optional): If True, reverse the generated list. Defaults to False. seed (Optional[int], optional): If provided, sets the random seed for deterministic color order. Raises: ValueError: If hues < 2. ValueError: If seed is not None and not an integer.

Source code in src/rich_gradient/spectrum.py
    def __init__(
        self, hues: int = 17, invert: bool = False, seed: Optional[int] = None
    ) -> None:
        """Initialize the Spectrum with a specified number of hues and optional \
inversion and seed.
        Args:
            hues (int): Number of colors to generate. Defaults to 17.
            invert (bool, optional): If True, reverse the generated list.
                Defaults to False.
            seed (Optional[int], optional): If provided, sets the random seed for \
deterministic color order.
        Raises:
            ValueError: If hues < 2.
            ValueError: If seed is not None and not an integer.
        """
        if hues < 2:
            raise ValueError("hues must be at least 2")
        if hues > len(COLOR_STOPS):
            raise ValueError(f"hues must be at most {len(COLOR_STOPS)}")
        if seed is not None and not isinstance(seed, int):
            raise ValueError("seed must be an integer or None")

        # Use a dedicated RNG to avoid mutating global random state
        rng = Random(seed)

        # Generate a random cycle of colors from the spectrum
        colors: List[Color] = [Color.parse(color) for color in COLOR_STOPS.values()]
        color_cycle = cycle(colors)

        # Skip a pseudo-random number of colors to add variability, deterministically per seed
        for _ in range(rng.randint(1, 18)):
            next(color_cycle)

        # Create a list of colors based on the specified number of hues
        colors = [next(color_cycle) for _ in range(hues)]
        self.colors = colors
        if invert:
            self.colors.reverse()

        # Set names based on COLOR_STOPS mapping
        # Build a reverse map from normalized hex -> name and assign names for the selected colors

        hex_to_name = {
            Color.parse(value).get_truecolor().hex.upper(): name
            for name, value in COLOR_STOPS.items()
        }
        self.names = [
            hex_to_name.get(color.get_truecolor().hex.upper(), color.get_truecolor().hex.upper())
            for color in self.colors
        ]


        self.styles = [
            Style(color=color, bold=False, italic=False, underline=False)
            for color in self.colors
        ]
        self.hex = [color.get_truecolor().hex.upper() for color in self.colors]

__iter__()

Return an iterator over the colors in the Spectrum.

Source code in src/rich_gradient/spectrum.py
def __iter__(self):
    """Return an iterator over the colors in the Spectrum."""
    return iter(self.colors)

__len__()

Return the number of colors in the Spectrum.

Source code in src/rich_gradient/spectrum.py
def __len__(self) -> int:
    """Return the number of colors in the Spectrum."""
    return len(self.colors)

__repr__()

Return a string representation of the Spectrum.

Source code in src/rich_gradient/spectrum.py
def __repr__(self) -> str:
    """Return a string representation of the Spectrum."""
    colors = [f"{name}" for name in self.names]
    colors_str = ", ".join(colors)
    return f"Spectrum({colors_str})"

__rich__()

Return a rich Table representation of the Spectrum.

Source code in src/rich_gradient/spectrum.py
def __rich__(self) -> Table:
    """Return a rich Table representation of the Spectrum."""

    def rainbow_title(text: str) -> Text:
        chunks = [text[i : i + 2] for i in range(0, len(text), 2)]
        pieces: List[Text] = []
        for idx, chunk in enumerate(chunks):
            color = self.colors[idx % len(self.colors)]
            hex_code = color.get_truecolor().hex
            pieces.append(Text(chunk, style=f"b u {hex_code}"))
        return Text.assemble(*pieces)

    table = Table(title=rainbow_title("Spectrum Colors"))
    table.add_column(
        rainbow_title("Sample"), justify="center")
    table.add_column(rainbow_title("Color"), style="bold")
    table.add_column(rainbow_title("Hex"), style="bold")
    table.add_column(rainbow_title("Name"), style="bold")

    for color, name in zip(self.colors, self.names):
        hex_code = color.get_truecolor().hex
        red = color.get_truecolor().red
        green = color.get_truecolor().green
        blue = color.get_truecolor().blue

        name_text = Text(
            name.capitalize(),
            Style(color=hex_code, bold=True),
            no_wrap=True,
            justify="left",
        )
        hex_text = Text(
            f" {hex_code.upper()} ",
            Style(bgcolor=hex_code, color="#000000", bold=True),
            no_wrap=True,
            justify="center",
        )
        rgb_text = Text.assemble(*[
            Text("rgb", style=f"bold {hex_code}"),
            Text("(", style="i white"),
            Text(f"{red:>3}", style="#FF0000"),
            Text(",", style="i #555"),
            Text(f"{green:>3}", style="#00FF00"),
            Text(",", style="i #555"),
            Text(f"{blue:>3}", style="#00AAFF"),
            Text(")", style="i white"),
        ])
        sample = Text("█" * 10, style=Style(color=hex_code, bold=True))
        table.add_row(sample, name_text, hex_text, rgb_text)
    return table