Module Color

Expand source code
# Copyright (C) 2024
# Wassim Jabi <wassim.jabi@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with
# this program. If not, see <https://www.gnu.org/licenses/>.

import plotly.colors
import math

class Color:
    @staticmethod
    def ByCSSNamedColor(color, alpha: float = None):
        """
        Creates a Color from a CSS named color string. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

        Parameters
        ----------
        color : str
            A CSS named color.
        alpha : float , optional
            THe desired alpha (transparency value). The default is None which means no alpha value will be included in the returned list.

        Returns
        -------
        list
            The color expressed as an [r, g, b] or an [r, g, b, a] list.
        """
        import warnings
        import os
        try:
            import webcolors
        except:
            print("Color.ByCSSNamedColor - Information: Installing required webcolors library.")
            try:
                os.system("pip install webcolors")
            except:
                os.system("pip install webcolors --user")
            try:
                import webcolors
                print("Color.ByCSSNamedColor - Information: webcolors library installed correctly.")
            except:
                warnings.warn("Color.ByCSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
                return None

        if not alpha == None:
            if not 0.0 <= alpha <= 1.0:
                print("Color.ByCSSNamedColor - Error: alpha is not within the valid range of 0 to 1. Returning None.")
                return None
        try:
            # Get RGB values from the named CSS color
            rgbList = list(webcolors.name_to_rgb(color))
            if not alpha == None:
                rgbList.append(alpha)
            return rgbList

        except ValueError:
            print(f"Color.ByCSSNamedColor - Error: '{color}' is not a valid named CSS color. Returning None.")
            return None
    
    @staticmethod
    def ByHEX(hex: str, alpha: float = None):
        """
        Converts a hexadecimal color string to RGB color values.

        Parameters
        ----------
        hex : str
            A hexadecimal color string in the format '#RRGGBB'.
        alpha : float , optional
            The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is None
            which means no transparency value will be included in the returned color.
        Returns
        -------
        list
            The color expressed as an [r, g, b] or an [r, g, b, a] list.

        """
        if not isinstance(hex, str):
            print("Color.HEXtoRGB - Error: The input hex parameter is not a valid string. Returning None.")
            return None
        if not alpha == None:
            if not 0.0 <= alpha <= 1.0:
                print("Color.ByHEX - Error: alpha is not within the valid range of 0 to 1. Returning None.")
                return None
        hex = hex.lstrip('#')
        if len(hex) != 6:
            print("Color.HEXtoRGB - Error: Invalid hexadecimal color format. It should be a 6-digit hex value. Returning None.")
            return None
        r = int(hex[0:2], 16)
        g = int(hex[2:4], 16)
        b = int(hex[4:6], 16)
        rgbList = [r, g, b]
        if not alpha == None:
            rgbList.append(alpha)
        return rgbList

    @staticmethod
    def ByValueInRange(value: float = 0.5, minValue: float = 0.0, maxValue: float = 1.0, alpha: float = None, colorScale="viridis"):
        """
        Returns the r, g, b, (and optionally) a list of numbers representing the red, green, blue and alpha color elements.
        
        Parameters
        ----------
        value : float , optional
            The input value. The default is 0.5.
        minValue : float , optional
            the input minimum value. The default is 0.0.
        maxValue : float , optional
            The input maximum value. The default is 1.0.
        alpha : float , optional
            The alpha (transparency) value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
        useAlpha : bool , optional
            If set to True, the returns list includes the alpha value as a fourth element in the list.
        colorScale : str , optional
            The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.

        Returns
        -------
        list
            The color expressed as an [r, g, b] or an [r, g, b, a] list.

        """
        if not alpha == None:
            if not 0.0 <= alpha <= 1.0:
                print("Color.ByValueInRange - Error: alpha is not within the valid range of 0 to 1. Returning None.")
                return None
        # Code based on: https://stackoverflow.com/questions/62710057/access-color-from-plotly-color-scale

        def hex_to_rgb(value):
            value = str(value)
            value = value.lstrip('#')
            lv = len(value)
            returnValue = tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
            return str(returnValue)

        def get_color(colorscale_name, loc):
            from _plotly_utils.basevalidators import ColorscaleValidator
            # first parameter: Name of the property being validated
            # second parameter: a string, doesn't really matter in our use case
            cv = ColorscaleValidator("colorscale", "")
            # colorscale will be a list of lists: [[loc1, "rgb1"], [loc2, "rgb2"], ...] 
            colorscale = cv.validate_coerce(colorscale_name)
            if hasattr(loc, "__iter__"):
                return [get_continuous_color(colorscale, x) for x in loc]
            color = get_continuous_color(colorscale, loc)
            color = color.replace("rgb", "")
            color = color.replace("(", "")
            color = color.replace(")", "")
            color = color.split(",")
            final_colors = []
            for c in color:
                final_colors.append(math.floor(float(c)))
            return final_colors

        def get_continuous_color(colorscale, intermed):
            """
            Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
            color for any value in that range.

            Plotly doesn't make the colorscales directly accessible in a common format.
            Some are ready to use:
            
                colorscale = plotly.colors.PLOTLY_SCALES["Greens"]

            Others are just swatches that need to be constructed into a colorscale:

                viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
                colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)

            :param colorscale: A plotly continuous colorscale defined with RGB string colors.
            :param intermed: value in the range [0, 1]
            :return: color in rgb string format
            :rtype: str
            """

            if len(colorscale) < 1:
                raise ValueError("colorscale must have at least one color")
            if intermed <= 0 or len(colorscale) == 1:
                c = colorscale[0][1]
                return c if c[0] != "#" else hex_to_rgb(c)
            if intermed >= 1:
                c = colorscale[-1][1]
                return c if c[0] != "#" else hex_to_rgb(c)
            for cutoff, color in colorscale:
                if intermed > cutoff:
                    low_cutoff, low_color = cutoff, color
                else:
                    high_cutoff, high_color = cutoff, color
                    break
            if (low_color[0] == "#") or (high_color[0] == "#"):
                # some color scale names (such as cividis) returns:
                # [[loc1, "hex1"], [loc2, "hex2"], ...]
                low_color = hex_to_rgb(low_color)
                high_color = hex_to_rgb(high_color)
            return plotly.colors.find_intermediate_color(
                lowcolor=low_color,
                highcolor=high_color,
                intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
                colortype="rgb",
            )
        
        def get_color_default(ratio):
            r = 0.0
            g = 0.0
            b = 0.0

            finalRatio = ratio;
            if (finalRatio < 0.0):
                finalRatio = 0.0
            elif(finalRatio > 1.0):
                finalRatio = 1.0

            if (finalRatio >= 0.0 and finalRatio <= 0.25):
                r = 0.0
                g = 4.0 * finalRatio
                b = 1.0
            elif (finalRatio > 0.25 and finalRatio <= 0.5):
                r = 0.0
                g = 1.0
                b = 1.0 - 4.0 * (finalRatio - 0.25)
            elif (finalRatio > 0.5 and finalRatio <= 0.75):
                r = 4.0*(finalRatio - 0.5);
                g = 1.0
                b = 0.0
            else:
                r = 1.0
                g = 1.0 - 4.0 * (finalRatio - 0.75)
                b = 0.0

            rcom =  (max(min(r, 1.0), 0.0))
            gcom =  (max(min(g, 1.0), 0.0))
            bcom =  (max(min(b, 1.0), 0.0))

            return [rcom,gcom,bcom]
        
        if minValue > maxValue:
            temp = minValue;
            maxValue = minValue
            maxValue = temp

        val = value
        val = max(min(val,maxValue), minValue) # bracket value to the min and max values
        if (maxValue - minValue) != 0:
            val = (val - minValue)/(maxValue - minValue)
        else:
            val = 0
        if not colorScale or colorScale.lower() == "default":
            rgbList = get_color_default(val)
        else:
            rgbList = get_color(colorScale, val)
        if not alpha == None:
            rgbList.append(alpha)
        return rgbList
    
    @staticmethod
    def CSSNamedColor(color):
        """
        Returns the CSS Named color that most closely matches the input color. The input color is assumed to be
        in the format [r, g, b]. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

        Parameters
        ----------
        color : list
            The input color. This is assumed to be in the format [r, g, b]
        
        Returns
        -------
        str
            The CSS named color that most closely matches the input color.
        """
        import numbers
        import warnings
        import os
        try:
            import webcolors
        except:
            print("Color.CSSNamedColor - Information: Installing required webcolors library.")
            try:
                os.system("pip install webcolors")
            except:
                os.system("pip install webcolors --user")
            try:
                import webcolors
                print("Color.CSSNamedColor - Information: webcolors library installed correctly.")
            except:
                warnings.warn("Color.CSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
                return None

        if not isinstance(color, list):
            print("Color.CSSNamedColor - Error: The input color parameter is not a valid list. Returning None.")
            return None
        color = [int(x) for x in color if isinstance(x, numbers.Real)]
        if len(color) < 3:
            print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
            return None
        color = color[0:3]
        for x in color:
            if not (0 <= x <= 255):
                print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
                return None

        def est_color(requested_color):
            min_colors = {}
            for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
                r_c, g_c, b_c = webcolors.hex_to_rgb(key)
                rd = (r_c - requested_color[0]) ** 2
                gd = (g_c - requested_color[1]) ** 2
                bd = (b_c - requested_color[2]) ** 2
                min_colors[(rd + gd + bd)] = name
            return min_colors[min(min_colors.keys())]

        try:
            closest_color_name = webcolors.rgb_to_name(color)
        except ValueError:
            closest_color_name = est_color(color)
        return closest_color_name

    @staticmethod
    def CSSNamedColors():
        """
        Returns a list of all CSS named colors. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

        Parameters
        ----------

        Returns
        -------
        list
            The list of all CSS named colors.

        """
        # List of CSS named colors
        css_named_colors = [
            "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond",
            "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
            "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey",
            "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
            "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
            "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia",
            "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "grey", "honeydew", "hotpink",
            "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
            "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
            "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue",
            "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue",
            "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
            "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
            "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
            "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple",
            "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
            "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
            "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
        ]

        return css_named_colors

    @staticmethod
    def PlotlyColor(color, alpha=1.0, useAlpha=False):
        """
        Returns a plotly color string based on the input list of [r, g, b] or [r, g, b, a]. If your list is [r, g, b], you can optionally specify an alpha value

        Parameters
        ----------
        color : list
            The input color list. This is assumed to be in the format [r, g, b] or [r, g, b, a]
        alpha : float , optional
            The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
        useAlpha : bool , optional
            If set to True, the returns list includes the alpha value as a fourth element in the list.

        Returns
        -------
        str
            The plotly color string.

        """
        if not isinstance(color, list):
            print("Color.PlotlyColor - Error: The input color parameter is not a valid list. Returning None.")
            return None
        if len(color) < 3:
            print("Color.PlotlyColor - Error: The input color parameter contains less than the minimum three elements. Returning None.")
            return None
        if len(color) == 4:
            alpha = color[3]
        alpha = min(max(alpha, 0), 1)
        if alpha < 1:
            useAlpha = True
        if useAlpha:
            return "rgba("+str(color[0])+","+str(color[1])+","+str(color[2])+","+str(alpha)+")"
        return "rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+")"
    
    @staticmethod
    def RGBToHex(rgb):
        """
        Converts RGB color values to a hexadecimal color string.

        Parameters
        ----------
        rgb : tuple
            A tuple containing three integers representing the RGB values.

        Returns
        -------
        str
            A hexadecimal color string in the format '#RRGGBB'.
        """
        if not isinstance(rgb, list):
            print("Color.RGBToHex - Error: The input rgb parameter is not a valid list. Returning None.")
            return None
        r, g, b = rgb
        hex_value = "#{:02x}{:02x}{:02x}".format(r, g, b)
        return hex_value.upper()
    
    

Classes

class Color
Expand source code
class Color:
    @staticmethod
    def ByCSSNamedColor(color, alpha: float = None):
        """
        Creates a Color from a CSS named color string. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

        Parameters
        ----------
        color : str
            A CSS named color.
        alpha : float , optional
            THe desired alpha (transparency value). The default is None which means no alpha value will be included in the returned list.

        Returns
        -------
        list
            The color expressed as an [r, g, b] or an [r, g, b, a] list.
        """
        import warnings
        import os
        try:
            import webcolors
        except:
            print("Color.ByCSSNamedColor - Information: Installing required webcolors library.")
            try:
                os.system("pip install webcolors")
            except:
                os.system("pip install webcolors --user")
            try:
                import webcolors
                print("Color.ByCSSNamedColor - Information: webcolors library installed correctly.")
            except:
                warnings.warn("Color.ByCSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
                return None

        if not alpha == None:
            if not 0.0 <= alpha <= 1.0:
                print("Color.ByCSSNamedColor - Error: alpha is not within the valid range of 0 to 1. Returning None.")
                return None
        try:
            # Get RGB values from the named CSS color
            rgbList = list(webcolors.name_to_rgb(color))
            if not alpha == None:
                rgbList.append(alpha)
            return rgbList

        except ValueError:
            print(f"Color.ByCSSNamedColor - Error: '{color}' is not a valid named CSS color. Returning None.")
            return None
    
    @staticmethod
    def ByHEX(hex: str, alpha: float = None):
        """
        Converts a hexadecimal color string to RGB color values.

        Parameters
        ----------
        hex : str
            A hexadecimal color string in the format '#RRGGBB'.
        alpha : float , optional
            The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is None
            which means no transparency value will be included in the returned color.
        Returns
        -------
        list
            The color expressed as an [r, g, b] or an [r, g, b, a] list.

        """
        if not isinstance(hex, str):
            print("Color.HEXtoRGB - Error: The input hex parameter is not a valid string. Returning None.")
            return None
        if not alpha == None:
            if not 0.0 <= alpha <= 1.0:
                print("Color.ByHEX - Error: alpha is not within the valid range of 0 to 1. Returning None.")
                return None
        hex = hex.lstrip('#')
        if len(hex) != 6:
            print("Color.HEXtoRGB - Error: Invalid hexadecimal color format. It should be a 6-digit hex value. Returning None.")
            return None
        r = int(hex[0:2], 16)
        g = int(hex[2:4], 16)
        b = int(hex[4:6], 16)
        rgbList = [r, g, b]
        if not alpha == None:
            rgbList.append(alpha)
        return rgbList

    @staticmethod
    def ByValueInRange(value: float = 0.5, minValue: float = 0.0, maxValue: float = 1.0, alpha: float = None, colorScale="viridis"):
        """
        Returns the r, g, b, (and optionally) a list of numbers representing the red, green, blue and alpha color elements.
        
        Parameters
        ----------
        value : float , optional
            The input value. The default is 0.5.
        minValue : float , optional
            the input minimum value. The default is 0.0.
        maxValue : float , optional
            The input maximum value. The default is 1.0.
        alpha : float , optional
            The alpha (transparency) value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
        useAlpha : bool , optional
            If set to True, the returns list includes the alpha value as a fourth element in the list.
        colorScale : str , optional
            The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.

        Returns
        -------
        list
            The color expressed as an [r, g, b] or an [r, g, b, a] list.

        """
        if not alpha == None:
            if not 0.0 <= alpha <= 1.0:
                print("Color.ByValueInRange - Error: alpha is not within the valid range of 0 to 1. Returning None.")
                return None
        # Code based on: https://stackoverflow.com/questions/62710057/access-color-from-plotly-color-scale

        def hex_to_rgb(value):
            value = str(value)
            value = value.lstrip('#')
            lv = len(value)
            returnValue = tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
            return str(returnValue)

        def get_color(colorscale_name, loc):
            from _plotly_utils.basevalidators import ColorscaleValidator
            # first parameter: Name of the property being validated
            # second parameter: a string, doesn't really matter in our use case
            cv = ColorscaleValidator("colorscale", "")
            # colorscale will be a list of lists: [[loc1, "rgb1"], [loc2, "rgb2"], ...] 
            colorscale = cv.validate_coerce(colorscale_name)
            if hasattr(loc, "__iter__"):
                return [get_continuous_color(colorscale, x) for x in loc]
            color = get_continuous_color(colorscale, loc)
            color = color.replace("rgb", "")
            color = color.replace("(", "")
            color = color.replace(")", "")
            color = color.split(",")
            final_colors = []
            for c in color:
                final_colors.append(math.floor(float(c)))
            return final_colors

        def get_continuous_color(colorscale, intermed):
            """
            Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
            color for any value in that range.

            Plotly doesn't make the colorscales directly accessible in a common format.
            Some are ready to use:
            
                colorscale = plotly.colors.PLOTLY_SCALES["Greens"]

            Others are just swatches that need to be constructed into a colorscale:

                viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
                colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)

            :param colorscale: A plotly continuous colorscale defined with RGB string colors.
            :param intermed: value in the range [0, 1]
            :return: color in rgb string format
            :rtype: str
            """

            if len(colorscale) < 1:
                raise ValueError("colorscale must have at least one color")
            if intermed <= 0 or len(colorscale) == 1:
                c = colorscale[0][1]
                return c if c[0] != "#" else hex_to_rgb(c)
            if intermed >= 1:
                c = colorscale[-1][1]
                return c if c[0] != "#" else hex_to_rgb(c)
            for cutoff, color in colorscale:
                if intermed > cutoff:
                    low_cutoff, low_color = cutoff, color
                else:
                    high_cutoff, high_color = cutoff, color
                    break
            if (low_color[0] == "#") or (high_color[0] == "#"):
                # some color scale names (such as cividis) returns:
                # [[loc1, "hex1"], [loc2, "hex2"], ...]
                low_color = hex_to_rgb(low_color)
                high_color = hex_to_rgb(high_color)
            return plotly.colors.find_intermediate_color(
                lowcolor=low_color,
                highcolor=high_color,
                intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
                colortype="rgb",
            )
        
        def get_color_default(ratio):
            r = 0.0
            g = 0.0
            b = 0.0

            finalRatio = ratio;
            if (finalRatio < 0.0):
                finalRatio = 0.0
            elif(finalRatio > 1.0):
                finalRatio = 1.0

            if (finalRatio >= 0.0 and finalRatio <= 0.25):
                r = 0.0
                g = 4.0 * finalRatio
                b = 1.0
            elif (finalRatio > 0.25 and finalRatio <= 0.5):
                r = 0.0
                g = 1.0
                b = 1.0 - 4.0 * (finalRatio - 0.25)
            elif (finalRatio > 0.5 and finalRatio <= 0.75):
                r = 4.0*(finalRatio - 0.5);
                g = 1.0
                b = 0.0
            else:
                r = 1.0
                g = 1.0 - 4.0 * (finalRatio - 0.75)
                b = 0.0

            rcom =  (max(min(r, 1.0), 0.0))
            gcom =  (max(min(g, 1.0), 0.0))
            bcom =  (max(min(b, 1.0), 0.0))

            return [rcom,gcom,bcom]
        
        if minValue > maxValue:
            temp = minValue;
            maxValue = minValue
            maxValue = temp

        val = value
        val = max(min(val,maxValue), minValue) # bracket value to the min and max values
        if (maxValue - minValue) != 0:
            val = (val - minValue)/(maxValue - minValue)
        else:
            val = 0
        if not colorScale or colorScale.lower() == "default":
            rgbList = get_color_default(val)
        else:
            rgbList = get_color(colorScale, val)
        if not alpha == None:
            rgbList.append(alpha)
        return rgbList
    
    @staticmethod
    def CSSNamedColor(color):
        """
        Returns the CSS Named color that most closely matches the input color. The input color is assumed to be
        in the format [r, g, b]. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

        Parameters
        ----------
        color : list
            The input color. This is assumed to be in the format [r, g, b]
        
        Returns
        -------
        str
            The CSS named color that most closely matches the input color.
        """
        import numbers
        import warnings
        import os
        try:
            import webcolors
        except:
            print("Color.CSSNamedColor - Information: Installing required webcolors library.")
            try:
                os.system("pip install webcolors")
            except:
                os.system("pip install webcolors --user")
            try:
                import webcolors
                print("Color.CSSNamedColor - Information: webcolors library installed correctly.")
            except:
                warnings.warn("Color.CSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
                return None

        if not isinstance(color, list):
            print("Color.CSSNamedColor - Error: The input color parameter is not a valid list. Returning None.")
            return None
        color = [int(x) for x in color if isinstance(x, numbers.Real)]
        if len(color) < 3:
            print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
            return None
        color = color[0:3]
        for x in color:
            if not (0 <= x <= 255):
                print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
                return None

        def est_color(requested_color):
            min_colors = {}
            for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
                r_c, g_c, b_c = webcolors.hex_to_rgb(key)
                rd = (r_c - requested_color[0]) ** 2
                gd = (g_c - requested_color[1]) ** 2
                bd = (b_c - requested_color[2]) ** 2
                min_colors[(rd + gd + bd)] = name
            return min_colors[min(min_colors.keys())]

        try:
            closest_color_name = webcolors.rgb_to_name(color)
        except ValueError:
            closest_color_name = est_color(color)
        return closest_color_name

    @staticmethod
    def CSSNamedColors():
        """
        Returns a list of all CSS named colors. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

        Parameters
        ----------

        Returns
        -------
        list
            The list of all CSS named colors.

        """
        # List of CSS named colors
        css_named_colors = [
            "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond",
            "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
            "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey",
            "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
            "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
            "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia",
            "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "grey", "honeydew", "hotpink",
            "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
            "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
            "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue",
            "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue",
            "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
            "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
            "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
            "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple",
            "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
            "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
            "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
        ]

        return css_named_colors

    @staticmethod
    def PlotlyColor(color, alpha=1.0, useAlpha=False):
        """
        Returns a plotly color string based on the input list of [r, g, b] or [r, g, b, a]. If your list is [r, g, b], you can optionally specify an alpha value

        Parameters
        ----------
        color : list
            The input color list. This is assumed to be in the format [r, g, b] or [r, g, b, a]
        alpha : float , optional
            The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
        useAlpha : bool , optional
            If set to True, the returns list includes the alpha value as a fourth element in the list.

        Returns
        -------
        str
            The plotly color string.

        """
        if not isinstance(color, list):
            print("Color.PlotlyColor - Error: The input color parameter is not a valid list. Returning None.")
            return None
        if len(color) < 3:
            print("Color.PlotlyColor - Error: The input color parameter contains less than the minimum three elements. Returning None.")
            return None
        if len(color) == 4:
            alpha = color[3]
        alpha = min(max(alpha, 0), 1)
        if alpha < 1:
            useAlpha = True
        if useAlpha:
            return "rgba("+str(color[0])+","+str(color[1])+","+str(color[2])+","+str(alpha)+")"
        return "rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+")"
    
    @staticmethod
    def RGBToHex(rgb):
        """
        Converts RGB color values to a hexadecimal color string.

        Parameters
        ----------
        rgb : tuple
            A tuple containing three integers representing the RGB values.

        Returns
        -------
        str
            A hexadecimal color string in the format '#RRGGBB'.
        """
        if not isinstance(rgb, list):
            print("Color.RGBToHex - Error: The input rgb parameter is not a valid list. Returning None.")
            return None
        r, g, b = rgb
        hex_value = "#{:02x}{:02x}{:02x}".format(r, g, b)
        return hex_value.upper()

Static methods

def ByCSSNamedColor(color, alpha: float = None)

Creates a Color from a CSS named color string. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

Parameters

color : str
A CSS named color.
alpha : float , optional
THe desired alpha (transparency value). The default is None which means no alpha value will be included in the returned list.

Returns

list
The color expressed as an [r, g, b] or an [r, g, b, a] list.
Expand source code
@staticmethod
def ByCSSNamedColor(color, alpha: float = None):
    """
    Creates a Color from a CSS named color string. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

    Parameters
    ----------
    color : str
        A CSS named color.
    alpha : float , optional
        THe desired alpha (transparency value). The default is None which means no alpha value will be included in the returned list.

    Returns
    -------
    list
        The color expressed as an [r, g, b] or an [r, g, b, a] list.
    """
    import warnings
    import os
    try:
        import webcolors
    except:
        print("Color.ByCSSNamedColor - Information: Installing required webcolors library.")
        try:
            os.system("pip install webcolors")
        except:
            os.system("pip install webcolors --user")
        try:
            import webcolors
            print("Color.ByCSSNamedColor - Information: webcolors library installed correctly.")
        except:
            warnings.warn("Color.ByCSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
            return None

    if not alpha == None:
        if not 0.0 <= alpha <= 1.0:
            print("Color.ByCSSNamedColor - Error: alpha is not within the valid range of 0 to 1. Returning None.")
            return None
    try:
        # Get RGB values from the named CSS color
        rgbList = list(webcolors.name_to_rgb(color))
        if not alpha == None:
            rgbList.append(alpha)
        return rgbList

    except ValueError:
        print(f"Color.ByCSSNamedColor - Error: '{color}' is not a valid named CSS color. Returning None.")
        return None
def ByHEX(hex: str, alpha: float = None)

Converts a hexadecimal color string to RGB color values.

Parameters

hex : str
A hexadecimal color string in the format '#RRGGBB'.
alpha : float , optional
The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is None which means no transparency value will be included in the returned color.

Returns

list
The color expressed as an [r, g, b] or an [r, g, b, a] list.
Expand source code
@staticmethod
def ByHEX(hex: str, alpha: float = None):
    """
    Converts a hexadecimal color string to RGB color values.

    Parameters
    ----------
    hex : str
        A hexadecimal color string in the format '#RRGGBB'.
    alpha : float , optional
        The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is None
        which means no transparency value will be included in the returned color.
    Returns
    -------
    list
        The color expressed as an [r, g, b] or an [r, g, b, a] list.

    """
    if not isinstance(hex, str):
        print("Color.HEXtoRGB - Error: The input hex parameter is not a valid string. Returning None.")
        return None
    if not alpha == None:
        if not 0.0 <= alpha <= 1.0:
            print("Color.ByHEX - Error: alpha is not within the valid range of 0 to 1. Returning None.")
            return None
    hex = hex.lstrip('#')
    if len(hex) != 6:
        print("Color.HEXtoRGB - Error: Invalid hexadecimal color format. It should be a 6-digit hex value. Returning None.")
        return None
    r = int(hex[0:2], 16)
    g = int(hex[2:4], 16)
    b = int(hex[4:6], 16)
    rgbList = [r, g, b]
    if not alpha == None:
        rgbList.append(alpha)
    return rgbList
def ByValueInRange(value: float = 0.5, minValue: float = 0.0, maxValue: float = 1.0, alpha: float = None, colorScale='viridis')

Returns the r, g, b, (and optionally) a list of numbers representing the red, green, blue and alpha color elements.

Parameters

value : float , optional
The input value. The default is 0.5.
minValue : float , optional
the input minimum value. The default is 0.0.
maxValue : float , optional
The input maximum value. The default is 1.0.
alpha : float , optional
The alpha (transparency) value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
useAlpha : bool , optional
If set to True, the returns list includes the alpha value as a fourth element in the list.
colorScale : str , optional
The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.

Returns

list
The color expressed as an [r, g, b] or an [r, g, b, a] list.
Expand source code
@staticmethod
def ByValueInRange(value: float = 0.5, minValue: float = 0.0, maxValue: float = 1.0, alpha: float = None, colorScale="viridis"):
    """
    Returns the r, g, b, (and optionally) a list of numbers representing the red, green, blue and alpha color elements.
    
    Parameters
    ----------
    value : float , optional
        The input value. The default is 0.5.
    minValue : float , optional
        the input minimum value. The default is 0.0.
    maxValue : float , optional
        The input maximum value. The default is 1.0.
    alpha : float , optional
        The alpha (transparency) value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
    useAlpha : bool , optional
        If set to True, the returns list includes the alpha value as a fourth element in the list.
    colorScale : str , optional
        The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.

    Returns
    -------
    list
        The color expressed as an [r, g, b] or an [r, g, b, a] list.

    """
    if not alpha == None:
        if not 0.0 <= alpha <= 1.0:
            print("Color.ByValueInRange - Error: alpha is not within the valid range of 0 to 1. Returning None.")
            return None
    # Code based on: https://stackoverflow.com/questions/62710057/access-color-from-plotly-color-scale

    def hex_to_rgb(value):
        value = str(value)
        value = value.lstrip('#')
        lv = len(value)
        returnValue = tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
        return str(returnValue)

    def get_color(colorscale_name, loc):
        from _plotly_utils.basevalidators import ColorscaleValidator
        # first parameter: Name of the property being validated
        # second parameter: a string, doesn't really matter in our use case
        cv = ColorscaleValidator("colorscale", "")
        # colorscale will be a list of lists: [[loc1, "rgb1"], [loc2, "rgb2"], ...] 
        colorscale = cv.validate_coerce(colorscale_name)
        if hasattr(loc, "__iter__"):
            return [get_continuous_color(colorscale, x) for x in loc]
        color = get_continuous_color(colorscale, loc)
        color = color.replace("rgb", "")
        color = color.replace("(", "")
        color = color.replace(")", "")
        color = color.split(",")
        final_colors = []
        for c in color:
            final_colors.append(math.floor(float(c)))
        return final_colors

    def get_continuous_color(colorscale, intermed):
        """
        Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
        color for any value in that range.

        Plotly doesn't make the colorscales directly accessible in a common format.
        Some are ready to use:
        
            colorscale = plotly.colors.PLOTLY_SCALES["Greens"]

        Others are just swatches that need to be constructed into a colorscale:

            viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
            colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)

        :param colorscale: A plotly continuous colorscale defined with RGB string colors.
        :param intermed: value in the range [0, 1]
        :return: color in rgb string format
        :rtype: str
        """

        if len(colorscale) < 1:
            raise ValueError("colorscale must have at least one color")
        if intermed <= 0 or len(colorscale) == 1:
            c = colorscale[0][1]
            return c if c[0] != "#" else hex_to_rgb(c)
        if intermed >= 1:
            c = colorscale[-1][1]
            return c if c[0] != "#" else hex_to_rgb(c)
        for cutoff, color in colorscale:
            if intermed > cutoff:
                low_cutoff, low_color = cutoff, color
            else:
                high_cutoff, high_color = cutoff, color
                break
        if (low_color[0] == "#") or (high_color[0] == "#"):
            # some color scale names (such as cividis) returns:
            # [[loc1, "hex1"], [loc2, "hex2"], ...]
            low_color = hex_to_rgb(low_color)
            high_color = hex_to_rgb(high_color)
        return plotly.colors.find_intermediate_color(
            lowcolor=low_color,
            highcolor=high_color,
            intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
            colortype="rgb",
        )
    
    def get_color_default(ratio):
        r = 0.0
        g = 0.0
        b = 0.0

        finalRatio = ratio;
        if (finalRatio < 0.0):
            finalRatio = 0.0
        elif(finalRatio > 1.0):
            finalRatio = 1.0

        if (finalRatio >= 0.0 and finalRatio <= 0.25):
            r = 0.0
            g = 4.0 * finalRatio
            b = 1.0
        elif (finalRatio > 0.25 and finalRatio <= 0.5):
            r = 0.0
            g = 1.0
            b = 1.0 - 4.0 * (finalRatio - 0.25)
        elif (finalRatio > 0.5 and finalRatio <= 0.75):
            r = 4.0*(finalRatio - 0.5);
            g = 1.0
            b = 0.0
        else:
            r = 1.0
            g = 1.0 - 4.0 * (finalRatio - 0.75)
            b = 0.0

        rcom =  (max(min(r, 1.0), 0.0))
        gcom =  (max(min(g, 1.0), 0.0))
        bcom =  (max(min(b, 1.0), 0.0))

        return [rcom,gcom,bcom]
    
    if minValue > maxValue:
        temp = minValue;
        maxValue = minValue
        maxValue = temp

    val = value
    val = max(min(val,maxValue), minValue) # bracket value to the min and max values
    if (maxValue - minValue) != 0:
        val = (val - minValue)/(maxValue - minValue)
    else:
        val = 0
    if not colorScale or colorScale.lower() == "default":
        rgbList = get_color_default(val)
    else:
        rgbList = get_color(colorScale, val)
    if not alpha == None:
        rgbList.append(alpha)
    return rgbList
def CSSNamedColor(color)

Returns the CSS Named color that most closely matches the input color. The input color is assumed to be in the format [r, g, b]. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

Parameters

color : list
The input color. This is assumed to be in the format [r, g, b]

Returns

str
The CSS named color that most closely matches the input color.
Expand source code
@staticmethod
def CSSNamedColor(color):
    """
    Returns the CSS Named color that most closely matches the input color. The input color is assumed to be
    in the format [r, g, b]. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

    Parameters
    ----------
    color : list
        The input color. This is assumed to be in the format [r, g, b]
    
    Returns
    -------
    str
        The CSS named color that most closely matches the input color.
    """
    import numbers
    import warnings
    import os
    try:
        import webcolors
    except:
        print("Color.CSSNamedColor - Information: Installing required webcolors library.")
        try:
            os.system("pip install webcolors")
        except:
            os.system("pip install webcolors --user")
        try:
            import webcolors
            print("Color.CSSNamedColor - Information: webcolors library installed correctly.")
        except:
            warnings.warn("Color.CSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
            return None

    if not isinstance(color, list):
        print("Color.CSSNamedColor - Error: The input color parameter is not a valid list. Returning None.")
        return None
    color = [int(x) for x in color if isinstance(x, numbers.Real)]
    if len(color) < 3:
        print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
        return None
    color = color[0:3]
    for x in color:
        if not (0 <= x <= 255):
            print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
            return None

    def est_color(requested_color):
        min_colors = {}
        for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
            r_c, g_c, b_c = webcolors.hex_to_rgb(key)
            rd = (r_c - requested_color[0]) ** 2
            gd = (g_c - requested_color[1]) ** 2
            bd = (b_c - requested_color[2]) ** 2
            min_colors[(rd + gd + bd)] = name
        return min_colors[min(min_colors.keys())]

    try:
        closest_color_name = webcolors.rgb_to_name(color)
    except ValueError:
        closest_color_name = est_color(color)
    return closest_color_name
def CSSNamedColors()

Returns a list of all CSS named colors. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

Parameters

Returns

list
The list of all CSS named colors.
Expand source code
@staticmethod
def CSSNamedColors():
    """
    Returns a list of all CSS named colors. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color

    Parameters
    ----------

    Returns
    -------
    list
        The list of all CSS named colors.

    """
    # List of CSS named colors
    css_named_colors = [
        "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond",
        "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
        "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey",
        "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
        "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
        "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia",
        "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "grey", "honeydew", "hotpink",
        "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
        "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
        "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue",
        "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue",
        "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
        "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
        "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
        "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple",
        "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
        "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
        "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
    ]

    return css_named_colors
def PlotlyColor(color, alpha=1.0, useAlpha=False)

Returns a plotly color string based on the input list of [r, g, b] or [r, g, b, a]. If your list is [r, g, b], you can optionally specify an alpha value

Parameters

color : list
The input color list. This is assumed to be in the format [r, g, b] or [r, g, b, a]
alpha : float , optional
The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
useAlpha : bool , optional
If set to True, the returns list includes the alpha value as a fourth element in the list.

Returns

str
The plotly color string.
Expand source code
@staticmethod
def PlotlyColor(color, alpha=1.0, useAlpha=False):
    """
    Returns a plotly color string based on the input list of [r, g, b] or [r, g, b, a]. If your list is [r, g, b], you can optionally specify an alpha value

    Parameters
    ----------
    color : list
        The input color list. This is assumed to be in the format [r, g, b] or [r, g, b, a]
    alpha : float , optional
        The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
    useAlpha : bool , optional
        If set to True, the returns list includes the alpha value as a fourth element in the list.

    Returns
    -------
    str
        The plotly color string.

    """
    if not isinstance(color, list):
        print("Color.PlotlyColor - Error: The input color parameter is not a valid list. Returning None.")
        return None
    if len(color) < 3:
        print("Color.PlotlyColor - Error: The input color parameter contains less than the minimum three elements. Returning None.")
        return None
    if len(color) == 4:
        alpha = color[3]
    alpha = min(max(alpha, 0), 1)
    if alpha < 1:
        useAlpha = True
    if useAlpha:
        return "rgba("+str(color[0])+","+str(color[1])+","+str(color[2])+","+str(alpha)+")"
    return "rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+")"
def RGBToHex(rgb)

Converts RGB color values to a hexadecimal color string.

Parameters

rgb : tuple
A tuple containing three integers representing the RGB values.

Returns

str
A hexadecimal color string in the format '#RRGGBB'.
Expand source code
@staticmethod
def RGBToHex(rgb):
    """
    Converts RGB color values to a hexadecimal color string.

    Parameters
    ----------
    rgb : tuple
        A tuple containing three integers representing the RGB values.

    Returns
    -------
    str
        A hexadecimal color string in the format '#RRGGBB'.
    """
    if not isinstance(rgb, list):
        print("Color.RGBToHex - Error: The input rgb parameter is not a valid list. Returning None.")
        return None
    r, g, b = rgb
    hex_value = "#{:02x}{:02x}{:02x}".format(r, g, b)
    return hex_value.upper()