Material Color Utilities Python Library
Project description
PyMCUlib - Material Color Utilities Lib for Python (Ported from Official MCU of TypeScript version)
Based on official MCU-TypeScript 2025 most updated version
Python version: >=3.12.0
Note: In 2025, the official Material Color Utilities library was updated to version 2025 as TypeScript & Java & Dart implementation. This Python package has since been reimplemented based on that latest TypeScript version (main-ts) and is published on PyPI as the default PyMCUlib package (install via pip install PyMCUlib). The original C++-based Python port (main-cpp) has been renamed PyMCUlib-cpp and is available via pip install PyMCUlib-cpp. Please choose the package that best fits your needs.
Overview
This project is a Python port of Google's Material Color Utilities library, with this version being specifically ported from the official TypeScript implementation. The Material Color Utilities (MCU) library provides algorithms and utilities that power the dynamic color system introduced in Material Design 3.
Introduction
Material Design 3 introduces a dynamic color system that generates beautiful, accessible color schemes based on dynamic inputs like a user's wallpaper. This Python port aims to faithfully mirror the components, modules, parameters, outputs, and dependencies of the TypeScript MCU library, making these powerful color tools accessible to Python developers.
Port Information
This port maintains the spirit and functionality of the original TypeScript implementation while adapting to Python's conventions and best practices:
-
Naming Conventions:
- Function and method names follow Python's
snake_caseconvention (e.g., TypeScript'sgetRotatedHue()becomes Python'sget_rotated_hue()). - Class names maintain
PascalCaseas in the original TypeScript (e.g.,DynamicSchemeremainsDynamicScheme).
- Function and method names follow Python's
-
TypeScript Fidelity: The port strives to be a close mirror of the TypeScript codebase in terms of logic and algorithms.
-
Pythonic Adaptations: Due to inherent differences between TypeScript and Python, some adaptations were necessary to align with Python's language features and common practices:
- Circular Dependency Management:
- In the
PyMCUlib.utilsmodule, components like those fromtheme_utilsare imported dynamically within the__init__.pyusing__getattr__to prevent circular import issues that can arise from complex interdependencies. - In the
PyMCUlib.dynamiccolormodule,ColorSpecDelegatewas extracted into its own file (color_spec_delegate.py) fromcolor_spec.pyto resolve circular dependencies between dynamic color specification and its implementations.
- In the
- Properties: Where appropriate, getter and setter methods from TypeScript have been converted to Python properties for more idiomatic access (e.g.,
hct.getHue()andhct.setHue()are accessed viahct.hueandhct.hue = new_value).
- Circular Dependency Management:
-
Documentation: Docstrings have been added for components and modules to aid understanding and usage.
While this port aims for high fidelity to the TypeScript source, it cannot be a 1:1 replication. However, it faithfully reproduces the ideas, logic, and algorithms.
Project Structure
Following the organization of the original MCU library, this Python project is structured into several modules, each handling specific aspects of color processing:
PyMCUlib/
├── __init__.py # Package entry point with public APIs
├── blend/ # Color blending utilities
├── contrast/ # Contrast ratio calculation and accessibility tools
├── dislike/ # Detection and fixing of universally disliked colors
├── dynamiccolor/ # Dynamic color system for Material Design 3
├── hct/ # HCT (Hue, Chroma, Tone) color space & CAM16 model
├── palettes/ # Color palette generation
├── quantize/ # Color extraction from images
├── scheme/ # Material Design color schemes
├── score/ # Color ranking and evaluation
├── temperature/ # Color temperature theory implementation
└── utils/ # Common utility functions
(Note: test files also exist but are not part of the installable package)
Component Descriptions
blend: Provides utilities for blending colors, primarily in the HCT color space, to create harmonious combinations.contrast: Contains tools for calculating contrast ratios between colors and generating accessible color combinations.dislike: Implements logic to identify and adjust colors that are generally perceived as unpleasant (e.g., dark yellow-greens).dynamiccolor: The core of Material Design 3's adaptive color system. It generates colors that respond to UI states (like dark/light mode) and context.hct: Implements the HCT (Hue, Chroma, Tone) color space and the CAM16 color appearance model, which are foundational to Material Design's color system. This module combines concepts that might be separate (e.g., CAM16 and HCT details) in other MCU ports.palettes: Creates tonal palettes (variations of a color at different tones) and core palettes, which serve as building blocks for color schemes.quantize: Implements algorithms (like Wu's algorithm and K-Means) to extract key colors from images, crucial for theme generation from user content.scheme: Generates complete Material Design color schemes (e.g., Vibrant, Neutral, Tonal Spot) from a source color.score: Evaluates and ranks colors for their suitability in theming, considering perceptual qualities and potential usage.temperature: Implements color temperature theory to find complementary and analogous colors for harmonious designs.utils: Provides core utility functions for color conversions, mathematical operations, image processing for color extraction, and other common tasks.
Installation
Install via pip (recommended):
pip install PyMCUlib
Alternatively, install from source:
git clone https://github.com/yourname/PyMCUlib.git
cd PyMCUlib
pip install .
Quick Start
The example below demonstrates the minimal workflow from import to obtaining an ARGB color value:
from PyMCUlib.dynamiccolor import DynamicScheme, DynamicColor, Variant
from PyMCUlib.hct import Hct
# 1. Create an HCT source color
source = Hct.from_int(0xFF6750A4) # Material Purple
# 2. Construct a DynamicScheme
# spec_version defaults to '2021', platform defaults to 'phone'
scheme = DynamicScheme({
'source_color_hct': source,
'variant': Variant.VIBRANT,
'is_dark': False,
'contrast_level': 0.0,
'spec_version': '2025', # <-- explicit use of 2025 spec
'platform': 'phone' # (optional, defaults to 'phone')
})
# 3. (Standard) Access predefined theme colors
primary_color = scheme.primary
print(f"Scheme Primary Color: {hex(primary_color)}")
# 4. (Advanced) Define and use a custom DynamicColor
custom_accent = DynamicColor.from_palette({
'name': 'custom_accent',
'palette': lambda s: s.primary_palette, # Uses the scheme's primary palette
'tone': lambda s: 40.0,
})
# 5. Get the ARGB value for the custom color
custom_argb = accent.get_argb(scheme)
print(f"Custom Accent Color: {hex(custom_argb)}")
Components & Usage Examples
The library consists of various components, designed to be modular. Here are some examples of how to use them:
Blend
Utilities for blending colors.
from PyMCUlib.blend import Blend
# Harmonize one color with another (colors as ARGB integers)
design_color_argb = 0xFFFF0000 # Red
key_color_argb = 0xFF0000FF # Blue
harmonized_color = Blend.harmonize(design_color_argb, key_color_argb)
# print(f"Harmonized Color: {hex(harmonized_color)}")
# Blend hue between two colors (amount 0.0-1.0)
from_color_argb = 0xFFFF0000 # Red
to_color_argb = 0xFF00FF00 # Green
mixed_hue_color = Blend.hct_hue(from_color_argb, to_color_argb, 0.5)
# print(f"Mixed Hue Color: {hex(mixed_hue_color)}")
Contrast
Tools for measuring contrast and finding colors that meet accessibility requirements.
from PyMCUlib.contrast import Contrast
# Calculate contrast ratio between two tones (0-100)
tone_a = 10.0
tone_b = 90.0
contrast_ratio = Contrast.ratio_of_tones(tone_a, tone_b)
# print(f"Contrast Ratio: {contrast_ratio}")
# Find a lighter tone with a specific contrast ratio
original_tone = 20.0
desired_ratio = 4.5
lighter_tone = Contrast.lighter(original_tone, desired_ratio) # Returns -1.0 if not possible
# if lighter_tone != -1.0:
# print(f"Lighter Tone for ratio {desired_ratio}: {lighter_tone}")
Dislike
Identifies and fixes universally disliked colors.
from PyMCUlib.dislike import DislikeAnalyzer
from PyMCUlib.hct import Hct
# Example disliked color (dark olive green)
disliked_argb_color = 0xFF8F7C00
hct_color = Hct.from_int(disliked_argb_color)
# Check if a color is disliked
is_disliked = DislikeAnalyzer.is_disliked(hct_color)
# print(f"Is color disliked? {is_disliked}")
# Fix a disliked color
fixed_hct_color = DislikeAnalyzer.fix_if_disliked(hct_color)
# print(f"Original ARGB: {hex(hct_color.to_int())}, Fixed ARGB: {hex(fixed_hct_color.to_int())}")
DynamicColor
Provides colors that adjust based on UI states (dark theme, contrast levels) via DynamicScheme.
from PyMCUlib.dynamiccolor import DynamicColor, DynamicScheme, Variant
from PyMCUlib.hct import Hct
from PyMCUlib.palettes import TonalPalette # For palette lambda example
# Define a source color for the scheme
source_hct = Hct.from_int(0xFF3F51B5) # Indigo
# Create a dynamic scheme (e.g., TonalSpot variant)
scheme_options = {
'source_color_hct': source_hct,
'variant': Variant.TONAL_SPOT,
'is_dark': False,
'contrast_level': 0.0
}
my_scheme = DynamicScheme(scheme_options)
# Example of creating a custom DynamicColor (conceptual)
# In practice, you'd often use predefined MaterialDynamicColors
custom_dynamic_color = DynamicColor.from_palette({
'name': "custom_accent",
'palette': lambda s: s.primary_palette, # Use primary palette from scheme
'tone': lambda s: 40.0 if not s.is_dark else 80.0
})
# Get the ARGB value of this custom color for the specific scheme
argb_value = custom_dynamic_color.get_argb(my_scheme)
# print(f"Custom Dynamic Color ARGB: {hex(argb_value)}")
# Accessing predefined Material Design colors
primary_color_argb = my_scheme.primary
# print(f"Scheme Primary Color ARGB: {hex(primary_color_argb)}")
HCT
The HCT color space (Hue, Chroma, Tone) accounts for viewing conditions and provides a perceptually accurate color model.
from PyMCUlib.hct import Hct
# Create HCT color from an ARGB integer
hct_color = Hct.from_int(0xFF0000FF) # Blue
# Access HCT components using properties
hue = hct_color.hue
chroma = hct_color.chroma
tone = hct_color.tone
# print(f"HCT: H={hue:.2f}, C={chroma:.2f}, T={tone:.2f}")
# Modify HCT components using property setters
hct_color.tone = 80.0 # Make it a lighter blue
# print(f"New Tone: {hct_color.tone}, New ARGB: {hex(hct_color.to_int())}")
# Convert back to ARGB
rgb_int = hct_color.to_int()
Palettes
Creates tonal palettes (colors varying only in tone) and core palettes.
TonalPalette:
from PyMCUlib.palettes import TonalPalette
# Create a tonal palette from an ARGB integer
# TonalPalette.from_int() or TonalPalette.from_hue_and_chroma()
palette = TonalPalette.from_int(0xFF0000FF) # Blue
# Get different tones of the same color
light_blue_argb = palette.tone(90)
mid_blue_argb = palette.tone(50)
dark_blue_argb = palette.tone(10)
print(hex(light_blue_argb)) # e.g. 0xffe0e0ff
print(hex(mid_blue_argb)) # e.g. 0xff5a64ff
print(hex(dark_blue_argb)) # e.g. 0xff00006e
CorePalette:
from PyMCUlib.palettes import CorePalette
# Generate a core palette from an ARGB integer
core = CorePalette.of(0xFF0000FF) # Blue
# Access its A1 tonal palette
primary_tone = core.a1.tone(50) # mid-tone of the primary accent
on_primary = core.a1.tone(10) # very dark primary accent
print(hex(primary_tone)) # e.g. 0xff5a64ff
print(hex(on_primary)) # e.g. 0xff00006e
Quantize
Extracts dominant colors from images. QuantizerCelebi is one of the available quantizers.
from PyMCUlib.quantize import QuantizerCelebi
# Example image pixels (list of ARGB integers)
pixels = [0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFF00008, 0xFF0AAB00]
max_colors_to_extract = 3
# Quantize pixels to extract key colors and their population
# Returns a dict: {argb_color: population_count}
result = QuantizerCelebi.quantize(pixels, max_colors_to_extract)
# for color, count in result.items():
# print(f"Quantized Color: {hex(color)}, Count: {count}")
Scheme
Generates Material Design color schemes from a source color.
from PyMCUlib.hct import Hct
from PyMCUlib.scheme import SchemeVibrant # Example scheme, others include SchemeTonalSpot, SchemeNeutral etc.
# Source color for the scheme
source_color_hct = Hct.from_int(0xFF6750A4) # Material Purple
# Create a vibrant color scheme (defaults to spec_version='2021', platform='phone')
vibrant_scheme = SchemeVibrant(
source_color_hct,
is_dark=False,
contrast_level=0.0,
spec_version='2025', # optional: override to the 2025 spec
platform='phone' # optional, defaults to 'phone'
)
# Access colors from the scheme (as ARGB integers)
primary_color_argb = vibrant_scheme.primary
secondary_color_argb = vibrant_scheme.secondary
print(hex(primary_color_argb))
print(hex(secondary_color_argb))
Score
Ranks colors for suitability in theming, often used after quantization.
from PyMCUlib.score import Score
# Example: colors_with_counts from a quantizer
colors_with_counts = {
0xff4285f4: 100, # Google Blue
0xff34a853: 80, # Google Green
0xfffbbc05: 60, # Google Yellow
0xffea4335: 40 # Google Red
}
# ScoreOptions can be used to customize scoring behavior
options = {'desired': 3, 'filter': True}
ranked_colors_argb = Score.score(colors_with_counts, options=options) # Returns a list of ARGB integers
# print("Ranked Colors for Theming:")
# for color in ranked_colors_argb:
# print(hex(color))
Temperature
Provides utilities for finding analogous and complementary colors.
from PyMCUlib.temperature import TemperatureCache
from PyMCUlib.hct import Hct
# Create a temperature cache for a color
source_hct = Hct.from_int(0xFF008772) # A teal color
temp_cache = TemperatureCache(source_hct)
# Get complementary color (as HCT)
complement_hct = temp_cache.complement
# print(f"Complementary Color HCT: H={complement_hct.hue:.2f}, C={complement_hct.chroma:.2f}, T={complement_hct.tone:.2f}")
# print(f"Complementary Color ARGB: {hex(complement_hct.to_int())}")
# Get analogous colors (list of HCT objects)
# count = number of analogous colors, divisions = segments in color wheel
analogous_hcts = temp_cache.analogous(count=3, divisions=12)
# print("Analogous Colors:")
# for hct in analogous_hcts:
# print(f" HCT: H={hct.hue:.2f}, C={hct.chroma:.2f}, T={hct.tone:.2f}, ARGB: {hex(hct.to_int())}")
Utils
- Dynamic Import: In
PyMCUlib/utils/__init__.py, submodules are loaded on-demand via__getattr__to avoid circular dependencies. - Theme Generation:
theme_from_source_color(),theme_from_image(),custom_color(),apply_theme(),set_scheme_properties(), etc.
License
This project follows the original Material Color Utilities project's license, which is the Apache License 2.0.
Disclaimer
This is a personal port created for learning and usage in Python projects. While every effort has been made to faithfully reproduce the original TypeScript library's functionality, there may be errors or inconsistencies. Contributions and issue reports are welcome.
Acknowledgments
Special thanks to the Google Material Color Utilities team for creating this powerful library, for their original C++ and TypeScript implementations, and for making it openly available.
Very Important
I have adopted this lib in my own project and it works well. But I didn't verify all modules in my own project. Please report any issue via this repository issues to let me know bugs. Thank you very much!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymculib-1.0.3.tar.gz.
File metadata
- Download URL: pymculib-1.0.3.tar.gz
- Upload date:
- Size: 84.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9db417a2a31a512b5a85e0b834f26fd45d856ea1e00e607217f58b25eae0d5d7
|
|
| MD5 |
e004ba910d33ecb081256684be889a87
|
|
| BLAKE2b-256 |
48b91a920048619d7f88fd6f8eaa41bf56348c011ca895d00ffd2d7ad3078690
|
File details
Details for the file pymculib-1.0.3-py3-none-any.whl.
File metadata
- Download URL: pymculib-1.0.3-py3-none-any.whl
- Upload date:
- Size: 97.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c67e2561fc1ec81d8ca5101df50b2b182af13f0816a44f2d4246c7d9b3ad4df0
|
|
| MD5 |
67a0519b21bff4749ede8f588c0d22e6
|
|
| BLAKE2b-256 |
5eee7cc0a00a5976d18dc8110d0d60b8f2150a303f60787b47e3b9fe7bc61efe
|