2 Commits

Author SHA1 Message Date
Wladimir van der Laan 4a06f19e4f fix bug when using normal color syntax 2011-04-10 15:50:51 +02:00
Wladimir J. van der Laan a841043b56 RGB color support 2011-04-10 14:52:22 +02:00
10 changed files with 526 additions and 26 deletions
+1
View File
@@ -2,3 +2,4 @@
MANIFEST
*.pyc
.tox
*~
+36 -24
View File
@@ -2,11 +2,13 @@
This module generates ANSI character codes to printing colors to terminals.
See: http://en.wikipedia.org/wiki/ANSI_escape_code
'''
from . import caps
CSI = '\033['
def code_to_chars(code):
return CSI + str(code) + 'm'
def code_to_chars(codes):
codes = [str(code) for code in codes]
return CSI + (";".join(codes)) + 'm'
class AnsiCodes(object):
def __init__(self, codes):
@@ -14,34 +16,44 @@ class AnsiCodes(object):
if not name.startswith('_'):
value = getattr(codes, name)
setattr(self, name, code_to_chars(value))
self._method = codes._caps_method
def __call__(self, code):
if isinstance(code, basestring):
return getattr(self, code)
else: # extended (pass to capability)
return code_to_chars(getattr(caps.capability, self._method)(code))
class AnsiFore:
BLACK = 30
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
MAGENTA = 35
CYAN = 36
WHITE = 37
RESET = 39
_caps_method = 'fg'
BLACK = (30,)
RED = (31,)
GREEN = (32,)
YELLOW = (33,)
BLUE = (34,)
MAGENTA = (35,)
CYAN = (36,)
WHITE = (37,)
RESET = (39,)
class AnsiBack:
BLACK = 40
RED = 41
GREEN = 42
YELLOW = 43
BLUE = 44
MAGENTA = 45
CYAN = 46
WHITE = 47
RESET = 49
_caps_method = 'bg'
BLACK = (40,)
RED = (41,)
GREEN = (42,)
YELLOW = (43,)
BLUE = (44,)
MAGENTA = (45,)
CYAN = (46,)
WHITE = (47,)
RESET = (49,)
class AnsiStyle:
BRIGHT = 1
DIM = 2
NORMAL = 22
RESET_ALL = 0
_caps_method = 'style'
BRIGHT = (1,)
DIM = (2,)
NORMAL = (22,)
RESET_ALL = (0,)
Fore = AnsiCodes( AnsiFore )
Back = AnsiCodes( AnsiBack )
+59
View File
@@ -0,0 +1,59 @@
from .conv import xterm256
from .conv import ansi
class _Base(object):
"""
Base 16-color ANSI.
All ANSI terminals as well as Windows support this.
"""
def fg(self, rgb):
(colid, bright) = ansi.from_rgb(rgb)
if bright:
return (1, 30 + colid)
else:
return (30 + colid,)
def bg(self, rgb):
(colid, bright) = ansi.from_rgb(rgb)
# Ignore brightness for background?
return (40 + colid)
def __str__():
return "BASE"
class _XTerm256(object):
"""
256-color terminal. Most modern terminal emulators support this.
Putty, Gnome Terminal, and even oldies like (m)rxvt, xterm.
"""
def fg(self, rgb):
return (38, 5, xterm256.from_rgb(rgb))
def bg(self, rgb):
return (48, 5, xterm256.from_rgb(rgb))
def __str__():
return "XTERM256"
class _RGB(object):
"""
Full 24-bit RGB support.
As far as I know, only Konsole can do this.
"""
def fg(self, rgb):
return (38, 2) + rgb
def bg(self, rgb):
return (48, 2) + rgb
def __str__():
return "RGB"
class ColorCapability:
ANSI = _Base() # Base 16-color ANSI support
XTERM256 = _XTerm256() # xterm256 (GNOME terminal / Putty)
RGB = _RGB() # Full RGB (Konsole)
# Current terminal caps, default to ANSI
capability = ColorCapability.ANSI
+69
View File
@@ -0,0 +1,69 @@
"""
ANSI 16-color conversion utilities.
"""
# Wladimir van der Laan, 2011
#
# The original ansi colors are defined as follows
#
# 0 black
# 1 red
# 2 green
# 3 brown (green+red)
# 4 blue
# 5 magenta (blue+red)
# 6 cyan (blue+green)
# 7 white
#
# However, the exact RGB mappings depend on the terminal that
# is used.
# In addition to these colors, nearly all terminals support 8
# colors that are simply brighter versions of the defined colors.
_colors = [
(0,0,0),
(128,0,0),
(0,128,0),
(128,128,0),
(0,0,128),
(128,0,128),
(0,128,128),
(192,192,192),
(128,128,128),
(255,0,0),
(0,255,0),
(255,255,0),
(0,0,255),
(255,0,255),
(0,255,255),
(255,255,255)
]
def to_rgb(color):
"""
Convert ANSI color tuple (color_id, bright) to RGB tuple.
Raise `ValueError` in case of invalid color spec.
"""
color = (color[1]<<3)|color[0]
try:
return _colors[color]
except IndexError:
raise ValueError("Color %i out of range" % color)
def from_rgb(rgb):
"""
Convert RGB tuple to ANSI color tuple (color_id, bright).
Raise `ValueError` in case of invalid color spec.
"""
min_d = 1000000
closest = None
for idx,col in enumerate(_colors):
dr = rgb[0]-col[0]
dg = rgb[1]-col[1]
db = rgb[2]-col[2]
d = dr*dr + dg*dg + db*db
if d < min_d:
min_d = d
closest = idx
return (closest&7, closest>>3)
+184
View File
@@ -0,0 +1,184 @@
"""
HTML color conversion utilities.
"""
# Wladimir van der Laan, 2011
colors = {
'aliceblue': '#F0F8FF',
'antiquewhite': '#FAEBD7',
'aqua': '#00FFFF',
'aquamarine': '#7FFFD4',
'azure': '#F0FFFF',
'beige': '#F5F5DC',
'bisque': '#FFE4C4',
'black': '#000000',
'blanchedalmond': '#FFEBCD',
'blue': '#0000FF',
'blueviolet': '#8A2BE2',
'brown': '#A52A2A',
'burlywood': '#DEB887',
'cadetblue': '#5F9EA0',
'chartreuse': '#7FFF00',
'chocolate': '#D2691E',
'coral': '#FF7F50',
'cornflowerblue': '#6495ED',
'cornsilk': '#FFF8DC',
'crimson': '#DC143C',
'cyan': '#00FFFF',
'darkblue': '#00008B',
'darkcyan': '#008B8B',
'darkgoldenrod': '#B8860B',
'darkgray': '#A9A9A9',
'darkgreen': '#006400',
'darkgrey': '#A9A9A9',
'darkkhaki': '#BDB76B',
'darkmagenta': '#8B008B',
'darkolivegreen': '#556B2F',
'darkorange': '#FF8C00',
'darkorchid': '#9932CC',
'darkred': '#8B0000',
'darksalmon': '#E9967A',
'darkseagreen': '#8FBC8F',
'darkslateblue': '#483D8B',
'darkslategray': '#2F4F4F',
'darkslategrey': '#2F4F4F',
'darkturquoise': '#00CED1',
'darkviolet': '#9400D3',
'deeppink': '#FF1493',
'deepskyblue': '#00BFFF',
'dimgray': '#696969',
'dimgrey': '#696969',
'dodgerblue': '#1E90FF',
'firebrick': '#B22222',
'floralwhite': '#FFFAF0',
'forestgreen': '#228B22',
'fuchsia': '#FF00FF',
'gainsboro': '#DCDCDC',
'ghostwhite': '#F8F8FF',
'gold': '#FFD700',
'goldenrod': '#DAA520',
'gray': '#808080',
'green': '#008000',
'greenyellow': '#ADFF2F',
'grey': '#808080',
'honeydew': '#F0FFF0',
'hotpink': '#FF69B4',
'indianred ': '#CD5C5C',
'indigo ': '#4B0082',
'ivory': '#FFFFF0',
'khaki': '#F0E68C',
'lavender': '#E6E6FA',
'lavenderblush': '#FFF0F5',
'lawngreen': '#7CFC00',
'lemonchiffon': '#FFFACD',
'lightblue': '#ADD8E6',
'lightcoral': '#F08080',
'lightcyan': '#E0FFFF',
'lightgoldenrodyellow': '#FAFAD2',
'lightgray': '#D3D3D3',
'lightgreen': '#90EE90',
'lightgrey': '#D3D3D3',
'lightpink': '#FFB6C1',
'lightsalmon': '#FFA07A',
'lightseagreen': '#20B2AA',
'lightskyblue': '#87CEFA',
'lightslategray': '#778899',
'lightslategrey': '#778899',
'lightsteelblue': '#B0C4DE',
'lightyellow': '#FFFFE0',
'lime': '#00FF00',
'limegreen': '#32CD32',
'linen': '#FAF0E6',
'magenta': '#FF00FF',
'maroon': '#800000',
'mediumaquamarine': '#66CDAA',
'mediumblue': '#0000CD',
'mediumorchid': '#BA55D3',
'mediumpurple': '#9370D8',
'mediumseagreen': '#3CB371',
'mediumslateblue': '#7B68EE',
'mediumspringgreen': '#00FA9A',
'mediumturquoise': '#48D1CC',
'mediumvioletred': '#C71585',
'midnightblue': '#191970',
'mintcream': '#F5FFFA',
'mistyrose': '#FFE4E1',
'moccasin': '#FFE4B5',
'navajowhite': '#FFDEAD',
'navy': '#000080',
'oldlace': '#FDF5E6',
'olive': '#808000',
'olivedrab': '#6B8E23',
'orange': '#FFA500',
'orangered': '#FF4500',
'orchid': '#DA70D6',
'palegoldenrod': '#EEE8AA',
'palegreen': '#98FB98',
'paleturquoise': '#AFEEEE',
'palevioletred': '#D87093',
'papayawhip': '#FFEFD5',
'peachpuff': '#FFDAB9',
'peru': '#CD853F',
'pink': '#FFC0CB',
'plum': '#DDA0DD',
'powderblue': '#B0E0E6',
'purple': '#800080',
'red': '#FF0000',
'rosybrown': '#BC8F8F',
'royalblue': '#4169E1',
'saddlebrown': '#8B4513',
'salmon': '#FA8072',
'sandybrown': '#F4A460',
'seagreen': '#2E8B57',
'seashell': '#FFF5EE',
'sienna': '#A0522D',
'silver': '#C0C0C0',
'skyblue': '#87CEEB',
'slateblue': '#6A5ACD',
'slategray': '#708090',
'slategrey': '#708090',
'snow': '#FFFAFA',
'springgreen': '#00FF7F',
'steelblue': '#4682B4',
'tan': '#D2B48C',
'teal': '#008080',
'thistle': '#D8BFD8',
'tomato': '#FF6347',
'turquoise': '#40E0D0',
'violet': '#EE82EE',
'wheat': '#F5DEB3',
'white': '#FFFFFF',
'whitesmoke': '#F5F5F5',
'yellow': '#FFFF00',
'yellowgreen': '#9ACD32'}
def to_rgb(color):
"""
Parse HTML color specification to RGB tuple.
Raise `ValueError` in case of invalid color spec.
"""
if color in colors:
color = colors[color]
if color.startswith("#"):
color = color[1:]
if len(color) == 6: #RRGGBB
rgb = (int(color[0:2],16),
int(color[2:4],16),
int(color[4:6],16))
elif len(color) == 3: #RGB
rgb = (int(color[0],16),
int(color[1],16),
int(color[2],16))
rgb = tuple([(x<<4)|x for x in rgb])
else:
raise ValueError("Invalid color %s" % color)
return rgb
def from_rgb(rgb):
"""
Convert RGB tuple to HTML color specification.
"""
return "#%02x%02x%02x" % rgb
+115
View File
@@ -0,0 +1,115 @@
"""
256-color xterm conversion utilities.
"""
# Wladimir van der Laan, 2011
# whole colortable, filled in later
colortable = None
# the 6 value iterations in the xterm color cube
valuerange = [ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF ]
# 16 basic ANSI colors + lighter variants
# these can be different depending on the terminal settings,
# so do not use them when converting from RGB.
basic16 = [
[ 0x00, 0x00, 0x00 ], # 0
[ 0xCD, 0x00, 0x00 ], # 1
[ 0x00, 0xCD, 0x00 ], # 2
[ 0xCD, 0xCD, 0x00 ], # 3
[ 0x00, 0x00, 0xEE ], # 4
[ 0xCD, 0x00, 0xCD ], # 5
[ 0x00, 0xCD, 0xCD ], # 6
[ 0xE5, 0xE5, 0xE5 ], # 7
[ 0x7F, 0x7F, 0x7F ], # 8
[ 0xFF, 0x00, 0x00 ], # 9
[ 0x00, 0xFF, 0x00 ], # 10
[ 0xFF, 0xFF, 0x00 ], # 11
[ 0x5C, 0x5C, 0xFF ], # 12
[ 0xFF, 0x00, 0xFF ], # 13
[ 0x00, 0xFF, 0xFF ], # 14
[ 0xFF, 0xFF, 0xFF ] # 15
]
# Closest color on RGB color cube (from valuerange)
closest6 = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
def _color_dist(c, rgb):
"""
Return squared Euclidian distance of rgb to color `c`
in the color table.
"""
d0 = colortable[c][0] - rgb[0]
d1 = colortable[c][1] - rgb[1]
d2 = colortable[c][2] - rgb[2]
return d0*d0 + d1*d1 + d2*d2
def to_rgb(color):
"""
Convert a xterm256 color value (0-255) to 3 unsigned chars RGB
tuple.
"""
rgb = [0,0,0]
if color < 16:
# 16 basic colors
rgb[0] = basic16[color][0]
rgb[1] = basic16[color][1]
rgb[2] = basic16[color][2]
elif color >= 16 and color <= 231:
# color cube color
color -= 16
rgb[0] = valuerange[(color/36)%6]
rgb[1] = valuerange[(color/6)%6]
rgb[2] = valuerange[color%6]
elif color >= 232 and color <= 255:
# gray tone
rgb[0] = rgb[1] = rgb[2] = 8+(color-232)*0x0a
return rgb
def from_rgb(rgb):
"""
Convert RGB tuple to xterm256 color value (0-255).
"""
# Optimized algorithm to find the xterm256 color with Euclidian closest distance to a
# provided RGB value: as the palette consists of an independent 6x6x6 RGB cube
# and a 24 grey tone scale, determine the closest color in both palettes, then
# from these two return the on that's closest to the requested RGB color.
# Compute closest color on 6x6x6 RGB cube
r = [closest6[x] for x in rgb]
b = 16 + r[0]*36 + r[1]*6 + r[2]
# Compute closest point on greyscale line
greyscale = sum(rgb)
r = (greyscale-9) / 30
c = 232 + max(min(r,23),0)
# Return RGB cube or greyscale color depending
# on which is closest.
distb = _color_dist(b, rgb)
distc = _color_dist(c, rgb)
if distb <= distc:
color = b
else:
color = c
return color
colortable = [to_rgb(c) for c in xrange(0, 256)]
+21 -2
View File
@@ -15,6 +15,7 @@ import re
import sys
from ..packages import colorama
from ..packages.colorama.conv import html
__all__ = (
'red', 'green', 'yellow', 'blue',
@@ -41,7 +42,7 @@ class ColoredString(object):
if sys.stdout.isatty() and not DISABLE_COLOR:
colorama.init(autoreset=True)
return '%s%s%s' % (
getattr(colorama.Fore, self.color), self.s, colorama.Fore.RESET)
colorama.Fore(self.color), self.s, colorama.Fore.RESET)
else:
return self.s
@@ -78,7 +79,7 @@ def clean(s):
strip = re.compile("([^-_a-zA-Z0-9!@#%&=,/'\";:~`\$\^\*\(\)\+\[\]\.\{\}\|\?\<\>\\]+|[^\s]+)")
txt = strip.sub('', str(s))
strip = re.compile(r'\[\d+m')
strip = re.compile(r'\[[\d;]+m')
txt = strip.sub('', txt)
return txt
@@ -108,6 +109,24 @@ def cyan(string):
def white(string):
return ColoredString('WHITE', string)
def rgb(color, string):
"""
RGB-colored text.
The `color` argument can have the following formats:
- #3f3f3f
- 3f3f3f
- ('3f', '3f', '3f')
- (100, 24, 244)
"""
if isinstance(color, basestring):
color = html.to_rgb(color)
else:
if isinstance(color[0], basestring):
color = (int(color[0],16), int(color[1],16), int(color[2],16))
return ColoredString(color, string)
def disable():
"""Disables colors."""
global DISABLE_COLOR
+1
View File
@@ -94,6 +94,7 @@ def columns(*cols, **kwargs):
cols[_big_col][1] = (cwidth - _total_cols) - len(cols)
cols[_big_col][0] = max_width(cols[_big_col][0], cols[_big_col][1]).split('\n')
height = len(max([c[0] for c in cols], key=len))
for i, (strings, width) in enumerate(cols):
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
sys.path.insert(0, os.path.abspath('..'))
from clint.textui import colored
from clint.packages.colorama.conv import html
from clint.packages.colorama import caps
from clint.textui import columns
# Force capability to XTERM256 for now, otherwise it's boring
# I'm not sure a reliable detection mechanism even exists...
caps.capability = caps.ColorCapability.XTERM256
def colorizer(colors):
for color in colors:
yield colored.rgb(color, color)
if __name__ == '__main__':
i = iter(colorizer(html.colors))
while True:
try:
a = i.next()
except StopIteration:
break
try:
b = i.next()
except StopIteration:
b = ""
try:
c = i.next()
except StopIteration:
c = ""
print columns([a,30],[b,30],[c,30])