Skip to main content

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:

  • 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

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")
#e0ebf5 light bluish
#79a6d2 bluish 1
#2d5986 bluish 2
#172e45 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")
#d6ebff 100% saturated light bluish
#4da6ff 100% saturated bluish 1
#005ab2 100% saturated bluish 2
#002e5c 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 = }")
#03c903 the_greenest
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}")
rgba(160, 161, 18, 0.60) yellow-fish
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}")
#a0a112 opaque yellow-fish
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}"')
#256f8b Pseudo-random color for the string "Peter"
#8b7b25 Pseudo-random color for the string "Lois"
#258b69 Pseudo-random color for the string "Stewie"
#8b2544 Pseudo-random color for the string "Brian"
#258b33 Pseudo-random color for the string "Joe Swanson"

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")
#258b33 Original color
#466a4b The color after assigning new saturation value
rgba(133, 173, 138, 0.30) 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")
#258b33 Original color
#466a4b A new color with changed saturation value
rgba(133, 173, 138, 0.30) 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

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hcolor-0.0.7-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

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

Hashes for hcolor-0.0.7-py3-none-any.whl
Algorithm Hash digest
SHA256 c81d26dc08f5d8c7336674e5c420790b52843ec52ed29ef8277cc949e9c02f15
MD5 2d494b64137293a140556e6aed0e2ec7
BLAKE2b-256 db4433461097cd60af0843540985f88490a08159bc7f2deedc09e2d19a46dbe3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page