Build a User-Defined Function to Return a Color Based on a Value and Range
By: Siddhant Sadangi
Tags: Code, Programming, Software Engineering, Python, UI

There are often times when you need the color of a UI element to be in sync with a value. For example, when predicting employee attrition, a higher percentage being denoted with a redder font or background color and a lower one with a greener font/background color might convey the message more effectively.

tkinter, the de-facto standard GUI package for Python, lets you play around with a lot of stuff, including the color of the UI elements. However, the color has to be supplied and there’s no option for dynamic coloring within tkinter.

Here, we will build a useful little user-defined function (UDF) that will return a hex color from the traffic light gradient color palette based on a supplied possible range of values, the actual value, and the direction of the palette.

First, we need to understand what a hex color code is.

Hex code is a string in the format #RRGGBB, where RR, GG, and BB denote the intensity of the Red, Green, and Blue components, respective of the color, in hexadecimal format. The decimal color intensities vary from 0 (for darkest) to 255 (for lightest). This translates to a range of 00 to FF when converted to hexadecimal. So, the hex code for black is #000000 (signifying all the three components are at their maximum darkness). Similarly, for white, it is #FFFFFF. For pure red, green, and blue, the codes are #FF0000, #00FF00, and #0000FF, respectively.

You can play around with Google’s color picker to get an idea of how this works for composite colors like yellow, brown, etc.

Now on to the code.

I’ve created a minimal tkinter UI that consists of a label within a window:

Upon running this, you’ll get the window below:

Here, we can see that the value is 0 and the background is grey. Since we haven’t supplied a background color, it’s defaulted to grey.

To add a background color, we just need to supply it to Label using the bg argument. Let’s add black:

pred = Label(mainframe, width=25, text=f"{value}%", bg='#000000', anchor='c',padx=5, font=('TkTextFont',12))

Now we’ll create a function to supply the color to the label based on the value and the palette.

First, for the purpose of simplicity, let us define our range to be 0 to 100, with 0 being bad (red) and 100 being good (green). 50 will be neutral (yellow). In this case, the codes for bad, neutral, and good should be #ff0000, #ffff00, and #00ff00. From 0 to 50, red is 255 (ff) and green gradually increases from 0 (00) to 255 (ff). From 50 to 100, red decreases from 255 to 0, while green remains 255. Blue stays 0 in both cases.

This can be represented as:

This RGB code will now need to be converted to hex color code. Python has a built-in integer-to-hexadecimal converter.

print(hex(0)) returns 0x0 , where 0x is the prefix that identifies the base of the number. print(hex(255)) returns 0xff . We just need to ignore the first two characters and right-pad the output with a 0 so that it has a length of 2. This is done for all three RGB codes, and the resulting string is prefixed with # to identify it as a hex color code. This one-liner does it:

"#%s%s%s" % tuple([hex(c)[2:].rjust(2, "0") for c in (r, g, b)])

Let’s test this logic!

I’ve modified the label text to display both the value and the hex code. Let’s see how the labels look for 0, 25, 50, 75, and 100:

Perfect!

Now, what if we need to reverse the direction? That is, 0 is now good, while 100 is bad? We just interchange the values of red and green!