Color manipulation tool. Extension to colorsys.
Project description
hcolor - A color manipulation tool
The goal of this package is to provide a color manipulation tool for use in the Python environments.
The original intention was to bring mathematics to color space generation and to provide a set of
useful functions that are missing from the standard colorsys library.
The package provides the HColor class, which represents a color by RGBA components, stored as
floats (0-to-1) and provides handy manipulation methods for the object.
The main features of the HColor class are:
- instant cast to HTML color definition,
- creation from HSL parameters (hue/saturation/lightness),
- manipulation of HLS parameters,
- creation of color palettes as a series of HLS variations
- creation from HTML color definition (from string).
Usage basics
Installation
pip install hcolor
The package does not require any dependencies and weights just around 10kB.
Crating color objects
There are several ways to create a color object.
from hcolor import HColor
# Create color knowing RGB components, integers
red_from_rgb_1 = HColor(255, 0, 0)
# Create color knowing RGB components, floats
red_from_rgb_2 = HColor(1.0, 0, 0)
# Create color knowing HLS components, only floats
red_from_hls = HColor.from_hls(0, 0.5, 1)
# Create color from HTML color code
red_from_long_code = HColor("#ff0000")
red_from_short_code = HColor("#f00")
# So all the above give exactly the same color
assert red_from_rgb_1 == red_from_rgb_2 == red_from_hls == red_from_long_code == red_from_short_code
Before going further let's write a method to show the new color in this Jupyter Notebook, since it's a tool that we use to write this documentation. It will generate a html to show the resulting colors.
from IPython.display import display, HTML
def show_color(color_obj: HColor, name: str = None):
"""This code is only for showing the color in this readme, it's specific to Jupyter Notebooks."""
# create a new color with inverted lightness
opposite_color = color_obj.ch_light(1 - color_obj.lightness)
display(HTML(
'<div style="margin: 0.2em;">'
f'<span style="max-width: 850px; padding: .3em 1em; color: {opposite_color}; background-color: {color_obj}; '
'font-family: monospace; display:inline-block;">'
f'<span style="font-size: 120%; font-family: monospace">{color_obj}</span></span> '
f'<span style="color: {color_obj}">{name}</span></div>'
))
You don't need to bother with the code of show_color function.
It's here only for the sake of color's presentation and can work only in Jupyter Notebooks.
Creating color objects from hue names
There is an effective method for creating your own color using color names. A namespace class Colors has been provided for this purpose. Its methods create color objects based on the hue name. The saturation, lightness, and alpha can be customized according to the user's preferences. The base colors are:
redorange_redorangeyellow_orangeyellowyellow_greengreengreen_cyancyansky_blueblueindigovioletpurplemagentapink_magentarosered_rose
The default values are: light = 0.5, sat = 0.5, alpha = 1.0.
The object returned is a new instance of HColor class.
from hcolor import Colors
# Create basic color palette in sky_blue tones
light_bluish = Colors.sky_blue(light=0.92)
bluish_1 = Colors.sky_blue(light=0.65)
bluish_2 = Colors.sky_blue(light=0.35)
dark_bluish = Colors.sky_blue(light=0.18)
show_color(light_bluish, "light bluish")
show_color(bluish_1, "bluish 1")
show_color(bluish_2, "bluish 2")
show_color(dark_bluish, "dark bluish")
Check out how it differs when we crank up the saturation. Of course the saturation can be set to any float value between 0 and 1.
# Create basic color palette in sky_blue tones
show_color(Colors.sky_blue(sat=1.0, light=0.92), "100% saturated light bluish")
show_color(Colors.sky_blue(sat=1.0, light=0.65), "100% saturated bluish 1")
show_color(Colors.sky_blue(sat=1.0, light=0.35), "100% saturated bluish 2")
show_color(Colors.sky_blue(sat=1.0, light=0.18), "100% saturated dark bluish")
from airium import Airium
# Prodice a table presenting all the base colors
all_colors = [
"red", "orange_red", "orange", "yellow_orange", "yellow", "yellow_green", "green", "green_cyan",
"cyan", "sky_blue", "blue", "indigo", "violet", "purple", "magenta", "pink_magenta", "rose", "red_rose"
]
params_list = [{}, {"sat": 1.0}, {'light': 0.35}, {'light': 0.6, 'sat': 0.8}]
print(f"This is a presentation of all the {len(all_colors)} base colors created with different saturation and lightness.")
a = Airium()
with a.table(style="font-family: monospace; border-spacing: 30px;"):
for color_name in all_colors:
with a.tr():
hue = getattr(Colors, color_name)().hue
a.td().small(_t=f"hue={hue:.3f}")
for params in params_list:
bg_color = getattr(Colors, color_name)(**params)
fg_color = bg_color.ch_light(0.9 if bg_color.lightness < 0.5 else 0.1)
colors_info = ", ".join(f"{k}={v!r}" for k, v in params.items())
a.td().div(style=f"padding: 4px; color:{fg_color}; background-color: {bg_color}", _t=f"Colors.{color_name}({colors_info})")
display(HTML(str(a)))
This is a presentation of all the 18 base colors created with different saturation and lightness.
| hue=0.000 |
Colors.red()
|
Colors.red(sat=1.0)
|
Colors.red(light=0.35)
|
Colors.red(light=0.6, sat=0.8)
|
| hue=0.050 |
Colors.orange_red()
|
Colors.orange_red(sat=1.0)
|
Colors.orange_red(light=0.35)
|
Colors.orange_red(light=0.6, sat=0.8)
|
| hue=0.083 |
Colors.orange()
|
Colors.orange(sat=1.0)
|
Colors.orange(light=0.35)
|
Colors.orange(light=0.6, sat=0.8)
|
| hue=0.125 |
Colors.yellow_orange()
|
Colors.yellow_orange(sat=1.0)
|
Colors.yellow_orange(light=0.35)
|
Colors.yellow_orange(light=0.6, sat=0.8)
|
| hue=0.167 |
Colors.yellow()
|
Colors.yellow(sat=1.0)
|
Colors.yellow(light=0.35)
|
Colors.yellow(light=0.6, sat=0.8)
|
| hue=0.250 |
Colors.yellow_green()
|
Colors.yellow_green(sat=1.0)
|
Colors.yellow_green(light=0.35)
|
Colors.yellow_green(light=0.6, sat=0.8)
|
| hue=0.333 |
Colors.green()
|
Colors.green(sat=1.0)
|
Colors.green(light=0.35)
|
Colors.green(light=0.6, sat=0.8)
|
| hue=0.417 |
Colors.green_cyan()
|
Colors.green_cyan(sat=1.0)
|
Colors.green_cyan(light=0.35)
|
Colors.green_cyan(light=0.6, sat=0.8)
|
| hue=0.500 |
Colors.cyan()
|
Colors.cyan(sat=1.0)
|
Colors.cyan(light=0.35)
|
Colors.cyan(light=0.6, sat=0.8)
|
| hue=0.583 |
Colors.sky_blue()
|
Colors.sky_blue(sat=1.0)
|
Colors.sky_blue(light=0.35)
|
Colors.sky_blue(light=0.6, sat=0.8)
|
| hue=0.667 |
Colors.blue()
|
Colors.blue(sat=1.0)
|
Colors.blue(light=0.35)
|
Colors.blue(light=0.6, sat=0.8)
|
| hue=0.708 |
Colors.indigo()
|
Colors.indigo(sat=1.0)
|
Colors.indigo(light=0.35)
|
Colors.indigo(light=0.6, sat=0.8)
|
| hue=0.750 |
Colors.violet()
|
Colors.violet(sat=1.0)
|
Colors.violet(light=0.35)
|
Colors.violet(light=0.6, sat=0.8)
|
| hue=0.792 |
Colors.purple()
|
Colors.purple(sat=1.0)
|
Colors.purple(light=0.35)
|
Colors.purple(light=0.6, sat=0.8)
|
| hue=0.833 |
Colors.magenta()
|
Colors.magenta(sat=1.0)
|
Colors.magenta(light=0.35)
|
Colors.magenta(light=0.6, sat=0.8)
|
| hue=0.875 |
Colors.pink_magenta()
|
Colors.pink_magenta(sat=1.0)
|
Colors.pink_magenta(light=0.35)
|
Colors.pink_magenta(light=0.6, sat=0.8)
|
| hue=0.917 |
Colors.rose()
|
Colors.rose(sat=1.0)
|
Colors.rose(light=0.35)
|
Colors.rose(light=0.6, sat=0.8)
|
| hue=0.958 |
Colors.red_rose()
|
Colors.red_rose(sat=1.0)
|
Colors.red_rose(light=0.35)
|
Colors.red_rose(light=0.6, sat=0.8)
|
Color object's attributes
The HColor object exposes direct attributes for RGB and alpha components and HLS components
as properties, so that those are dynamically computed.
the_greenest = Colors.green(sat=0.97, light=0.4)
show_color(the_greenest, "the_greenest")
print("Its RGBA components are:")
print(f"{the_greenest.red = }")
print(f"{the_greenest.green = }")
print(f"{the_greenest.blue = }")
print(f"{the_greenest.alpha = }")
print("\nIts HLS components are:")
print(f"{the_greenest.hue = }")
print(f"{the_greenest.lightness = }")
print(f"{the_greenest.saturation = }")
Its RGBA components are:
the_greenest.red = 0.01355199999999984
the_greenest.green = 0.788
the_greenest.blue = 0.01200000000000001
the_greenest.alpha = 1.0
Its HLS components are:
the_greenest.hue = 0.333
the_greenest.lightness = 0.4
the_greenest.saturation = 0.97
Explicit type of color notation
By default, calling str() on HColor object will render as hexadecimal color code or in case if there is transparency set, as rgba(..) notation.
If we want to use explicit notation, we can use those methods:
print(f"{the_greenest.as_rgb() = }")
print(f"{the_greenest.as_rgba() = }")
print(f"{the_greenest.as_hex() = }")
print(f"{the_greenest.as_shex() = }")
the_greenest.as_rgb() = 'rgb(3, 201, 3)'
the_greenest.as_rgba() = 'rgba(3, 201, 3, 1.00)'
the_greenest.as_hex() = '#03c903'
the_greenest.as_shex() = '#0c0'
Opacity
To add a transparency, we add additional alpha parameter to the constructor call.
# This is how to construct a transparent color using RGBA components
from_rgba = HColor(0.62888, 0.63, 0.06999999999999995, 0.6)
# This is how to construct a transparent color using its name
yellowish = Colors.yellow(sat=0.8, light=0.35, alpha=0.6)
show_color(yellowish, "yellow-fish")
assert yellowish == from_rgba, "Result should be the same"
print(f"Yellowish casted to string is: {yellowish}")
Yellowish casted to string is: rgba(160, 161, 18, 0.60)
As soon as we set alpha component set to 1.0 it behaves naturally to HTML rules, gives just hexadecimal code.
# Create a new object from existing one - but with changed alpha
opaque_yellowish = yellowish.ch_alpha(1.0)
show_color(opaque_yellowish, "opaque yellow-fish")
print(f"Opaque yellowish casted to string is: {opaque_yellowish}")
Opaque yellowish casted to string is: #a0a112
Pseudo-random color
Another nice feature of HColor is when we want to create a unique color from any object, without caring what the actual hue should be, but are more interested in its lightness and saturation.
my_names = ["Peter", "Lois", "Stewie", "Brian", "Joe Swanson"]
for name in my_names:
its_color = Colors.from_hash(name, light=0.34567, sat=0.58)
show_color(its_color, f'Pseudo-random color for the string "{name}"')
The argument passed to the .from_hash() method must be a string or bytes object. It's then used to compute a sha1 hash from its value, which is then converted to a float (in a crude, but reproducible way). You can expect it to produce the same color on different machines.
Modifying existing colors
There are two mechanisms for changing the RGBA and HLS values of a color. Use one depending on the needs of your use case.
Modifying color attributes
One method works "in place", i.e. it modifies the object parameters and happens while assigning new values to any of its attributes: red, green, blue, alpha, hue, saturation and lightness.
the_color = HColor("#258b33")
show_color(the_color, "Original color")
the_color.saturation = 0.2
show_color(the_color, "The color after assigning new saturation value")
the_color.alpha = 0.3
the_color.lightness = 0.6
show_color(the_color, "The color after assigning new alpha and lightness value")
Cloning colors with changing the attributes
The second method creates a copy of the base object and happens when using one of HColor.ch_{attribute} methods. I.e.: ch_hue(), ch_light(), ch_sat() or ch_alpha().
old_color = HColor("#258b33")
show_color(old_color, "Original color")
saturated_color = old_color.ch_sat(0.2)
show_color(saturated_color, "A new color with changed saturation value")
transparent_color = saturated_color.ch_alpha(0.3).ch_light(0.6)
show_color(transparent_color, "Another new color object with changed alpha value")
Note that unlike the previous example, we must assign the method result to a new variable.
This is because the ch_* methods don't change the source color parameters.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
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 hcolor-0.0.7-py3-none-any.whl.
File metadata
- Download URL: hcolor-0.0.7-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c81d26dc08f5d8c7336674e5c420790b52843ec52ed29ef8277cc949e9c02f15
|
|
| MD5 |
2d494b64137293a140556e6aed0e2ec7
|
|
| BLAKE2b-256 |
db4433461097cd60af0843540985f88490a08159bc7f2deedc09e2d19a46dbe3
|