[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Convert between RGB and HSL color models in Python

[This program converts between the RGB and HSL color models in Python]

My post Prevent event cascades and easily update read-only Edit widgets in tkinter and Python explains how you can easily update linked widgets without causing an event cascade. (Spoiler Alert: Update them using tkinter variables instead of setting their values directly.)

This example uses that technique to let you select colors using either RGB values or HSL values. When you adjust the color using one set of Scale widgets, the other widgets update to show the same color in the other color system.

Color Components

Most computer graphics systems, including tkinter, use RGB (red, green, and blue) component values to specify colors where each component value is between 0 and 255. There are other versions of the RGB system, for example using more or fewer bits per color component, but those are increasingly rare these days.

The HSL system uses the values hue, saturation, and lightness where:

  • Hue determines the color with a 0 to 360 degree direction on a color wheel.
  • Saturation indicates the amount of color added. You can think of this as the opposite of "grayness." When saturation = 0, the color is pure gray; if lightness = 0.5 you get a neutral color; and when saturation is 1, the color is "pure."
  • Lightness indicates how much light is in the color. When lightness = 0, the color is black. When lightness = 1, the color is white. When lightness = 0.5, the color is as "pure" as possible.

For more information about the HSL color model, see the HSL and HSV Wikipedia entry.

Converting From RGB to HSL

To see formulas that convert from RGB to HSL, see the From RGB section in the Wikipedia post. The following function implements those functions.

def rgb_to_hsl(r, g, b): '''Convert an RGB value into an HSL value.''' # Convert RGB to a 0.0 to 1.0 range. r /= 255 g /= 255 b /= 255 # Get the maximum and minimum RGB components. max_rgb = max(r, g, b) min_rgb = min(r, g, b) diff = max_rgb - min_rgb l = (max_rgb + min_rgb) / 2 if math.isclose(diff, 0, abs_tol=0.001): s = 0 h = 0 # H is really undefined. else: if l <= 0.5: s = diff / (max_rgb + min_rgb) else: s = diff / (2 - max_rgb - min_rgb) r_dist = (max_rgb - r) / diff g_dist = (max_rgb - g) / diff b_dist = (max_rgb - b) / diff if r == max_rgb: h = b_dist - g_dist elif g == max_rgb: h = 2 + r_dist - b_dist else: h = 4 + g_dist - r_dist h *= 60 if h < 0: h += 360 return h, s, l

This code simply converts the RGB values from the 0 to 255 range into the 0.0 to 1.0 range. It then uses the formulas from the Wikipedia post to convert the RB values into HSL values and returns the results.

Converting From HSL to RGB

To see formulas that convert from HSL to RGB, see the To RGB section in the Wikipedia post. The following function implements those functions.

def hsl_to_rgb(h, s, l): '''Convert an HSL value into an RGB value.''' if l <= 0.5: p2 = l * (1 + s) else: p2 = l + s - l * s p1 = 2 * l - p2 if math.isclose(s, 0): r = l g = l b = l else: r = qqh_to_rgb(p1, p2, h + 120) g = qqh_to_rgb(p1, p2, h) b = qqh_to_rgb(p1, p2, h - 120) # Convert RGB into the 0 to 255 range. r = round(r * 255) g = round(g * 255) b = round(b * 255) return r, g, b def qqh_to_rgb(q1, q2, hue): hue = hue % 360 if hue < 60: return q1 + (q2 - q1) * hue / 60 if hue < 180: return q2 if hue < 240: return q1 + (q2 - q1) * (240 - hue) / 60 return q1

The hsl_to_rgb function does most of the work, but it needs the qqh_to_rgb helper function to do its job. Again, see the Wikipedia post for details.

Getting Changes

The program stores its color value in HSL because that gives more resolution than RGB. The RGB components are integer values between 0 and 255, but the HSL values are floating point values so they can have much greater precision.

When the program starts, it uses the following statement to initialize the HSL values.

# Start with HSL for black. self.H = self.S = self.L = 0

When you change the value of any of the program's six Scale widgets, the following code executes.

def scale_changed(self, value, i): '''Update this scale's entry.''' # Update the HSL values. if i < 3: # This is an RGB value. Update the HSL values. self.H, self.S, self.L = rgb_to_hsl(self.scale_vars[0].get(), self.scale_vars[1].get(), self.scale_vars[2].get()) elif i == 3: self.H = self.scale_vars[3].get() elif i == 4: self.S = self.scale_vars[4].get() elif i == 5: self.L = self.scale_vars[5].get() # Display all values. self.show_values()

The parameter i gives the index of the Scale that was updated.

If you updated one of the RGB widgets, the method calls rgb_to_hsl, passing it the red, green, and blue component values. It saves the results in the program's self.H, self.S, and self.L values.

Otherwise, if you updated one of the HSL widgets, the code updates the corresponding saved HSL value.

The method finishes by calling the show_values method to update the other widgets.

Showing Results

The following show_values method updates all of the Scale widgets to show the current HSL values.

def show_values(self): '''Display all valeus.''' # Get the RGB values. r, g, b = hsl_to_rgb(self.H, self.S, self.L) # Display the values. self.scale_vars[0].set(r) self.scale_vars[1].set(g) self.scale_vars[2].set(b) self.scale_vars[3].set(self.H) self.scale_vars[4].set(self.S) self.scale_vars[5].set(self.L) self.entry_vars[0].set(r) self.entry_vars[1].set(g) self.entry_vars[2].set(b) self.entry_vars[3].set(f'{self.H:.2f}') self.entry_vars[4].set(f'{self.S:.4f}') self.entry_vars[5].set(f'{self.L:.4f}') # Display a color sample. color = f'#{r:02x}{g:02x}{b:02x}' self.swatch.configure(background=color)

This code first calls hsl_to_rgb to convert the saved self.H, self.S, and self.L values into RGB values. It then uses the tkinter variables attached to the program's widgets to set their values. The widgets update so you can see the current color's component values.

The method finishes by converting the RGB color components into a string of the form #rrggbb that tkinter can understand. It uses that color to set the background color of the self.swatch Label widget so you can see a sample of the color.

Conclusion

Give the program a try. I think it's interesting to adjust the RGB values and watch the HSL values jump all over the place. Or adjust the lightness value and watch the RGB values go crazy. (But then again, I'm easily amused.)

Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.