Skip to content

utils

The utils module provides utility functions for color conversion and validation.

Module Overview

This module provides:

  • Color format conversions (hex ↔ RGB)
  • Color validation
  • Hex color normalization
  • Support for alpha channel

Functions

hex_to_rgb(hex_color)

Convert hex color to RGB tuple.

Args: hex_color: Hex color string (e.g., "#18453B" or "18453B")

Returns: Tuple of (R, G, B) values (0-255)

Raises: ValueError: If hex_color is not a valid hex color

Examples: >>> hex_to_rgb("#18453B") (24, 69, 59) >>> hex_to_rgb("FFFFFF") (255, 255, 255)

Source code in msuthemes/utils.py
def hex_to_rgb(hex_color: str) -> Tuple[int, int, int]:
    """Convert hex color to RGB tuple.

    Args:
        hex_color: Hex color string (e.g., "#18453B" or "18453B")

    Returns:
        Tuple of (R, G, B) values (0-255)

    Raises:
        ValueError: If hex_color is not a valid hex color

    Examples:
        >>> hex_to_rgb("#18453B")
        (24, 69, 59)
        >>> hex_to_rgb("FFFFFF")
        (255, 255, 255)
    """
    # Normalize the color first
    hex_color = normalize_hex(hex_color)

    # Remove the # prefix
    hex_color = hex_color.lstrip('#')

    # Convert to RGB
    r = int(hex_color[0:2], 16)
    g = int(hex_color[2:4], 16)
    b = int(hex_color[4:6], 16)

    return (r, g, b)

rgb_to_hex(r, g, b)

Convert RGB tuple to hex color.

Args: r: Red value (0-255) g: Green value (0-255) b: Blue value (0-255)

Returns: Hex color string with # prefix

Raises: ValueError: If RGB values are out of range

Examples: >>> rgb_to_hex(24, 69, 59) '#18453B' >>> rgb_to_hex(255, 255, 255) '#FFFFFF'

Source code in msuthemes/utils.py
def rgb_to_hex(r: int, g: int, b: int) -> str:
    """Convert RGB tuple to hex color.

    Args:
        r: Red value (0-255)
        g: Green value (0-255)
        b: Blue value (0-255)

    Returns:
        Hex color string with # prefix

    Raises:
        ValueError: If RGB values are out of range

    Examples:
        >>> rgb_to_hex(24, 69, 59)
        '#18453B'
        >>> rgb_to_hex(255, 255, 255)
        '#FFFFFF'
    """
    # Validate RGB values
    for val, name in [(r, 'R'), (g, 'G'), (b, 'B')]:
        if not isinstance(val, int) or not 0 <= val <= 255:
            raise ValueError(f"{name} value must be an integer between 0 and 255, got {val}")

    return f'#{r:02X}{g:02X}{b:02X}'

hex_to_rgba(hex_color, alpha=1.0)

Convert hex color to RGBA tuple (normalized 0-1).

Useful for matplotlib which often expects RGBA values in 0-1 range.

Args: hex_color: Hex color string (e.g., "#18453B" or "18453B") alpha: Alpha/opacity value (0-1, default 1.0)

Returns: Tuple of (R, G, B, A) values normalized to 0-1

Raises: ValueError: If hex_color is not valid or alpha out of range

Examples: >>> hex_to_rgba("#18453B") (0.09411764705882353, 0.27058823529411763, 0.23137254901960785, 1.0) >>> hex_to_rgba("#18453B", alpha=0.5) (0.09411764705882353, 0.27058823529411763, 0.23137254901960785, 0.5)

Source code in msuthemes/utils.py
def hex_to_rgba(hex_color: str, alpha: float = 1.0) -> Tuple[float, float, float, float]:
    """Convert hex color to RGBA tuple (normalized 0-1).

    Useful for matplotlib which often expects RGBA values in 0-1 range.

    Args:
        hex_color: Hex color string (e.g., "#18453B" or "18453B")
        alpha: Alpha/opacity value (0-1, default 1.0)

    Returns:
        Tuple of (R, G, B, A) values normalized to 0-1

    Raises:
        ValueError: If hex_color is not valid or alpha out of range

    Examples:
        >>> hex_to_rgba("#18453B")
        (0.09411764705882353, 0.27058823529411763, 0.23137254901960785, 1.0)
        >>> hex_to_rgba("#18453B", alpha=0.5)
        (0.09411764705882353, 0.27058823529411763, 0.23137254901960785, 0.5)
    """
    if not 0.0 <= alpha <= 1.0:
        raise ValueError(f"Alpha must be between 0 and 1, got {alpha}")

    r, g, b = hex_to_rgb(hex_color)

    return (r / 255.0, g / 255.0, b / 255.0, alpha)

validate_hex_color(color)

Validate if a string is a valid hex color code.

Args: color: String to validate as hex color (must start with #)

Returns: True if valid hex color, False otherwise

Examples: >>> validate_hex_color("#18453B") True >>> validate_hex_color("18453B") False >>> validate_hex_color("#ZZZ") False

Source code in msuthemes/utils.py
def validate_hex_color(color: str) -> bool:
    """Validate if a string is a valid hex color code.

    Args:
        color: String to validate as hex color (must start with #)

    Returns:
        True if valid hex color, False otherwise

    Examples:
        >>> validate_hex_color("#18453B")
        True
        >>> validate_hex_color("18453B")
        False
        >>> validate_hex_color("#ZZZ")
        False
    """
    if not isinstance(color, str):
        return False

    # Must start with #
    if not color.startswith('#'):
        return False

    # Remove # for validation
    hex_part = color[1:]

    # Check if it's exactly 6 hex digits (strict validation)
    if len(hex_part) != 6:
        return False

    # Check if all characters are valid hex
    return bool(re.match(r'^[0-9A-Fa-f]{6}$', hex_part))

normalize_hex(color)

Normalize hex color to uppercase with # prefix.

Args: color: Hex color string

Returns: Normalized hex color (uppercase, with #)

Raises: ValueError: If color is not a valid hex color

Examples: >>> normalize_hex("18453b") '#18453B' >>> normalize_hex("#18453b") '#18453B'

Source code in msuthemes/utils.py
def normalize_hex(color: str) -> str:
    """Normalize hex color to uppercase with # prefix.

    Args:
        color: Hex color string

    Returns:
        Normalized hex color (uppercase, with #)

    Raises:
        ValueError: If color is not a valid hex color

    Examples:
        >>> normalize_hex("18453b")
        '#18453B'
        >>> normalize_hex("#18453b")
        '#18453B'
    """
    if not validate_hex_color(color):
        raise ValueError(f"Invalid hex color: {color}")

    # Remove # if present and convert to uppercase
    color = color.lstrip('#').upper()

    # Expand 3-digit hex to 6-digit
    if len(color) == 3:
        color = ''.join([c*2 for c in color])

    return f'#{color}'

Usage Examples

Hex to RGB Conversion

from msuthemes.utils import hex_to_rgb

# Convert hex to RGB
rgb = hex_to_rgb('#18453b')
print(rgb)  # (24, 69, 59)

# Works with or without '#'
rgb = hex_to_rgb('18453b')
print(rgb)  # (24, 69, 59)

RGB to Hex Conversion

from msuthemes.utils import rgb_to_hex

# Convert RGB to hex
hex_color = rgb_to_hex(24, 69, 59)
print(hex_color)  # '#18453b'

# Pass as tuple
rgb_tuple = (24, 69, 59)
hex_color = rgb_to_hex(*rgb_tuple)
print(hex_color)  # '#18453b'

Hex to RGBA Conversion

from msuthemes.utils import hex_to_rgba

# Convert with default alpha
rgba = hex_to_rgba('#18453b')
print(rgba)  # (24, 69, 59, 255)

# Convert with custom alpha
rgba = hex_to_rgba('#18453b', alpha=128)
print(rgba)  # (24, 69, 59, 128)

# Convert with normalized alpha
rgba = hex_to_rgba('#18453b', alpha=0.5, normalize=True)
print(rgba)  # (24, 69, 59, 0.5)

Color Validation

from msuthemes.utils import validate_hex_color

# Validate hex colors
is_valid = validate_hex_color('#18453b')
print(is_valid)  # True

is_valid = validate_hex_color('18453b')
print(is_valid)  # True

is_valid = validate_hex_color('#gggggg')
print(is_valid)  # False

is_valid = validate_hex_color('#12345')
print(is_valid)  # False

Hex Normalization

from msuthemes.utils import normalize_hex

# Normalize hex colors (ensure '#' prefix, lowercase)
normalized = normalize_hex('18453B')
print(normalized)  # '#18453b'

normalized = normalize_hex('#18453B')
print(normalized)  # '#18453b'

# Invalid colors raise ValueError
try:
    normalized = normalize_hex('invalid')
except ValueError as e:
    print(f"Error: {e}")

Complete Conversion Pipeline

from msuthemes.utils import (
    hex_to_rgb,
    rgb_to_hex,
    validate_hex_color,
    normalize_hex
)

# Start with a hex color
original = '#18453b'

# Validate
if validate_hex_color(original):
    # Normalize
    normalized = normalize_hex(original)
    print(f"Normalized: {normalized}")

    # Convert to RGB
    rgb = hex_to_rgb(normalized)
    print(f"RGB: {rgb}")

    # Convert back to hex
    hex_again = rgb_to_hex(*rgb)
    print(f"Back to hex: {hex_again}")

    # Verify round-trip
    print(f"Round-trip successful: {normalized == hex_again}")

Working with MSU Colors

from msuthemes import MSU_GREEN, MSU_ORANGE
from msuthemes.utils import hex_to_rgb, hex_to_rgba

# Get RGB values for MSU colors
msu_green_rgb = hex_to_rgb(MSU_GREEN)
print(f"MSU Green RGB: {msu_green_rgb}")

msu_orange_rgb = hex_to_rgb(MSU_ORANGE)
print(f"MSU Orange RGB: {msu_orange_rgb}")

# Get RGBA with transparency
msu_green_rgba = hex_to_rgba(MSU_GREEN, alpha=0.5, normalize=True)
print(f"MSU Green RGBA (50% opacity): {msu_green_rgba}")

Integration with Matplotlib

from msuthemes import MSU_GREEN
from msuthemes.utils import hex_to_rgb
import matplotlib.pyplot as plt

# Matplotlib accepts various color formats
rgb = hex_to_rgb(MSU_GREEN)
rgb_normalized = tuple(c/255 for c in rgb)

# All of these work:
plt.plot([1, 2, 3], [1, 4, 2], color=MSU_GREEN)  # hex string
plt.plot([1, 2, 3], [1, 4, 2], color=rgb_normalized)  # normalized RGB tuple
plt.show()

Batch Conversion

from msuthemes import msu_qual1
from msuthemes.utils import hex_to_rgb

# Convert all colors in a palette
palette = msu_qual1.as_hex()
rgb_palette = [hex_to_rgb(color) for color in palette]

for hex_color, rgb_color in zip(palette, rgb_palette):
    print(f"{hex_color} -> {rgb_color}")

Error Handling

from msuthemes.utils import (
    hex_to_rgb,
    rgb_to_hex,
    validate_hex_color
)

# Validate before conversion
color = '#18453b'
if validate_hex_color(color):
    rgb = hex_to_rgb(color)
    print(f"Valid color: {rgb}")
else:
    print("Invalid color")

# Handle invalid RGB values
try:
    hex_color = rgb_to_hex(256, 0, 0)  # 256 is out of range
except ValueError as e:
    print(f"Error: {e}")

# Handle invalid hex strings
try:
    rgb = hex_to_rgb('#gggggg')
except ValueError as e:
    print(f"Error: {e}")

See Also