From d3efe7e8513785414a6c102f5b8079c8cdf9b2df Mon Sep 17 00:00:00 2001 From: nojhan Date: Fri, 25 Apr 2014 10:56:17 +0200 Subject: [PATCH] Make default colormaps work. Overwrite default colormaps only if asked. Correctly interpret integers in mode(). Test registered colormaps after specials. Silently ignore duplicated external palettes. Add a set_special_colormaps function. Do not sue ANSI_min/max as limits for the whole ANSI range. Move global variables at the top of the file. --- README.md | 2 + colout/colout.py | 179 +++++++++++++++++++++++++++-------------------- 2 files changed, 104 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 80ac984..c49e5db 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,8 @@ Gentoo WARNING: be sure to specify a default colormap that is compatible with the special colormap's mode. For instance, if you indicate `scale`, you can use `-d red,green,blue`, but `-d 12,13,14` will fail. Also, if you specify `Scale`, you cannot use `-d red,green,blue`, but `-d Rainbow` will work. + Similarly, external palettes are converted from RGB to 256-ANSI and will thus not work if you use + them as default colormaps for a 8-colors mode special color. * `-r TYPE(S)`, `--resources TYPE(S)`: Print the names of available resources. Use a comma-separated list of resources names diff --git a/colout/colout.py b/colout/colout.py index 00c6b01..21a63ed 100755 --- a/colout/colout.py +++ b/colout/colout.py @@ -23,10 +23,77 @@ import functools signal.signal( signal.SIGPIPE, signal.SIG_DFL ) +############################################################################### +# Global variables +############################################################################### + +# Escaped end markers for given color modes +endmarks = {8: ";", 256: ";38;5;"} + +# Available styles +styles = { + "normal": 0, "bold": 1, "faint": 2, "italic": 3, "underline": 4, + "blink": 5, "rapid_blink": 6, + "reverse": 7, "conceal": 8 +} + +# Available color names in 8-colors mode +colors = { + "black": 0, "red": 1, "green": 2, "yellow": 3, "orange":3, "blue": 4, + "magenta": 5, "purple": 5, "cyan": 6, "white": 7, "none": -1 +} + +themes = {} + +# pre-defined colormaps +# 8-colors mode should start with a lower-case letter (and can contains either named or indexed colors) +# 256-colors mode should start with an upper-case letter (and should contains indexed colors) +colormaps = { + # Rainbows + "rainbow" : ["magenta", "blue", "cyan", "green", "yellow", "red"], + "Rainbow" : [92, 93, 57, 21, 27, 33, 39, 45, 51, 50, 49, 48, 47, 46, 82, 118, 154, 190, 226, 220, 214, 208, 202, 196], + + # from magenta to red, with white in the middle + "spectrum" : ["magenta", "blue", "cyan", "white", "green", "yellow", "red"], + "Spectrum" : [91, 92, 56, 57, 21, 27, 26, 32, 31, 37, 36, 35, 41, 40, 41, 77, 83, 84, 120, 121, 157, 194, 231, 254, 255, 231, 230, 229, 228, 227, 226, 220, 214, 208, 202, 196] +} # colormaps + +colormaps["scale"] = colormaps["spectrum"] +colormaps["Scale"] = colormaps["Spectrum"] +colormaps["hash"] = colormaps["rainbow"] +colormaps["Hash"] = colormaps["Rainbow"] +colormaps["default"] = colormaps["spectrum"] +colormaps["Default"] = colormaps["Spectrum"] + +colormap_idx = 0 + +scale = (0,100) + +class UnknownColor(Exception): + pass + +class DuplicatedPalette(Exception): + pass + +class DuplicatedTheme(Exception): + pass + + ############################################################################### # Ressource parsing helpers ############################################################################### + +def set_special_colormaps( cmap ): + """Change all the special colors to a single colormap (which must be a list of colors).""" + colormaps["scale"] = cmap + colormaps["Scale"] = cmap + colormaps["hash"] = cmap + colormaps["Hash"] = cmap + colormaps["default"] = cmap + colormaps["Default"] = cmap + + def parse_gimp_palette( filename ): """ Parse the given filename as a GIMP palette (.gpl) @@ -90,7 +157,13 @@ def uniq( lst ): def rgb_to_ansi( r, g, b ): """Convert a RGB color to its closest 256-colors ANSI index""" - # ansi_max is the higher possible RGB value for ANSI colors + + # Range limits for the *colored* section of ANSI, + # this does not include the *gray* section. + ansi_min = 16 + ansi_max = 234 + + # ansi_max is the higher possible RGB value for ANSI *colors* # limit RGB values to ansi_max red,green,blue = tuple([ansi_max if c>ansi_max else c for c in (r,g,b)]) @@ -121,65 +194,6 @@ def hex_to_rgb(h): return tuple( int(h[i:i+lh//3], 16) for i in range(0, lh, lh//3) ) -############################################################################### -# Global variables -############################################################################### - -# Escaped end markers for given color modes -endmarks = {8: ";", 256: ";38;5;"} - -ansi_min = 16 -ansi_max = 234 - -# Available styles -styles = { - "normal": 0, "bold": 1, "faint": 2, "italic": 3, "underline": 4, - "blink": 5, "rapid_blink": 6, - "reverse": 7, "conceal": 8 -} - -# Available color names in 8-colors mode -colors = { - "black": 0, "red": 1, "green": 2, "yellow": 3, "orange":3, "blue": 4, - "magenta": 5, "purple": 5, "cyan": 6, "white": 7, "none": -1 -} - -themes = {} - -# pre-defined colormaps -# 8-colors mode should start with a lower-case letter (and can contains either named or indexed colors) -# 256-colors mode should start with an upper-case letter (and should contains indexed colors) -colormaps = { - # Rainbows - "rainbow" : ["magenta", "blue", "cyan", "green", "yellow", "red"], - "Rainbow" : [92, 93, 57, 21, 27, 33, 39, 45, 51, 50, 49, 48, 47, 46, 82, 118, 154, 190, 226, 220, 214, 208, 202, 196], - - # from magenta to red, with white in the middle - "spectrum" : ["magenta", "blue", "cyan", "white", "green", "yellow", "red"], - "Spectrum" : [91, 92, 56, 57, 21, 27, 26, 32, 31, 37, 36, 35, 41, 40, 41, 77, 83, 84, 120, 121, 157, 194, 231, 254, 255, 231, 230, 229, 228, 227, 226, 220, 214, 208, 202, 196] -} # colormaps - -colormaps["scale"] = colormaps["spectrum"] -colormaps["Scale"] = colormaps["Spectrum"] -colormaps["hash"] = colormaps["rainbow"] -colormaps["Hash"] = colormaps["Rainbow"] -colormaps["default"] = colormaps["spectrum"] -colormaps["Default"] = colormaps["Spectrum"] - -colormap_idx = 0 - -scale = (0,100) - -class UnknownColor(Exception): - pass - -class DuplicatedPalette(Exception): - pass - -class DuplicatedTheme(Exception): - pass - - ############################################################################### # Load available extern resources ############################################################################### @@ -199,7 +213,7 @@ def load_themes( themes_dir): themes[name] = importlib.import_module(module) -def load_palettes( palettes_dir ): +def load_palettes( palettes_dir, ignore_duplicates = True ): global colormaps logging.debug("search for palettes in: %s" % palettes_dir) os.chdir( palettes_dir ) @@ -212,7 +226,10 @@ def load_palettes( palettes_dir ): logging.warning("error while parsing palette %s: %s" % ( p,e ) ) continue if name in colormaps: - raise DuplicatedPalette(name) + if ignore_duplicates: + logging.warning("ignore this duplicated palette name: %s" % name) + else: + raise DuplicatedPalette(name) # Convert the palette to ANSI ansi_palette = [ rgb_to_ansi(r,g,b) for r,g,b in palette ] # Compress it so that there isn't two consecutive identical colors @@ -265,7 +282,12 @@ def load_resources( themes_dir, palettes_dir ): ############################################################################### def mode( color ): - if color in colors: + if type(color) is int: + if 0 <= color and color <= 255 : + return 256 + else: + raise UnknownColor(color) + elif color in colors: return 8 elif color in colormaps.keys(): if color[0].islower(): @@ -279,7 +301,7 @@ def mode( color ): return 256 elif color[0] == "#": return 256 - elif color.isdigit() and (ansi_min < int(color) and int(color) < ansi_max) : + elif color.isdigit() and (0 <= int(color) and int(color) <= 255) : return 256 else: raise UnknownColor(color) @@ -402,7 +424,7 @@ def color_map(name): color_code = str(30 + colors[color]) else: color_nb = int(color) - assert( ansi_min <= color_nb <= ansi_max ) + assert( 0 <= color_nb <= 255 ) color_code = str(color_nb) colormap_idx = next_in_map(color) @@ -477,9 +499,6 @@ def colorin(text, color="red", style="normal"): elif color.lower() == "random": color_code = color_random( color ) - elif color in colormaps.keys(): - color_code = color_in_colormaps( color ) - elif color.lower() == "scale": # "scale" or "Scale" color_code = color_scale( color, text ) @@ -492,6 +511,12 @@ def colorin(text, color="red", style="normal"): elif color == "colormap": color_code = color_map(color) + # Registered colormaps should be tested after special colors, + # because special tags are also registered as colormaps, + # but do not have the same simple behavior. + elif color in colormaps.keys(): + color_code = color_in_colormaps( color ) + # 8 colors modes elif color in colors: color_code = str(30 + colors[color]) @@ -818,7 +843,7 @@ def __args_parse__(argv, usage=""): parser.add_argument("-P", "--palettes-dir", metavar="DIR", action="append", help="Search for additional palettes (*.gpl files) in the given directory") - parser.add_argument("-d", "--default", metavar="COLORMAP", default="spectrum", + parser.add_argument("-d", "--default", metavar="COLORMAP", default=None, help="When using special colormaps (`scale` or `hash`), use this COLORMAP. \ This can be either one of the available colormaps or a comma-separated list of colors. \ WARNING: be sure to specify a default colormap that is compatible with the special colormap's mode.") @@ -982,22 +1007,22 @@ if __name__ == "__main__": logging.debug("user-defined scale: %f,%f" % scale) # Default color maps - if default_colormap not in colormaps: - cmap = default_colormap.split(",") + if default_colormap: + if default_colormap not in colormaps: + cmap = default_colormap.split(",") - elif default_colormap in colormaps: - cmap = colormaps[default_colormap] - - colormaps[color] = cmap - logging.debug("used-defined default colormap: %s" % ",".join([str(i) for i in cmap]) ) + elif default_colormap in colormaps: + cmap = colormaps[default_colormap] + set_special_colormaps( cmap ) + logging.debug("user-defined special colormap: %s" % ",".join([str(i) for i in cmap]) ) # explicit color map if as_colormap is True and color not in colormaps: colormaps["Default"] = color.split(",") # replace the colormap by the given colors colormaps["default"] = color.split(",") # replace the colormap by the given colors color = "colormap" # use the keyword to switch to colormap instead of list of colors - logging.debug("used-defined colormap: %s" % ",".join(colormaps["Default"]) ) + logging.debug("used-defined default colormap: %s" % ",".join(colormaps["Default"]) ) # if theme if as_theme: