|
|
#-*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2014 Calculate Ltd. http://www.calculate-linux.org
|
|
|
#
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
# You may obtain a copy of the License at
|
|
|
#
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
#
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
# See the License for the specific language governing permissions and
|
|
|
# limitations under the License.
|
|
|
|
|
|
import os
|
|
|
import sys
|
|
|
import re
|
|
|
from itertools import chain,ifilter,tee
|
|
|
from os import path
|
|
|
|
|
|
class ConsoleCodesInfo:
|
|
|
"""
|
|
|
Коды цветов
|
|
|
"""
|
|
|
COLORCOUNT = 8
|
|
|
BLACK, RED, GREEN, BROWN, BLUE, PURPLE, CYAN, WHITE = range(0,COLORCOUNT)
|
|
|
DEFAULT = 9
|
|
|
FOREGROUND = 30
|
|
|
FOREGROUND_DEFAULT = 39
|
|
|
FOREGROUND_END = 37
|
|
|
BACKGROUND = 40
|
|
|
BACKGROUND_DEFAULT = 49
|
|
|
BACKGROUND_END = 47
|
|
|
BOLD = 1
|
|
|
HALFBRIGHT = 2
|
|
|
UNDERLINE = 4
|
|
|
NOUNDERLINE = 24
|
|
|
NORMAL = 22
|
|
|
RESET = 0
|
|
|
FOREGROUND256 = 38
|
|
|
COLOR256 = 5
|
|
|
BACKGROUND256 = 48
|
|
|
INVERT = 7
|
|
|
NOINVERT = 27
|
|
|
|
|
|
class ConsoleColor256:
|
|
|
"""Объект перевода RGB цветов в консоль 256color"""
|
|
|
|
|
|
colorList = (0,95,135,175,215,255)
|
|
|
colorMatch = [colorList[x]+(colorList[x+1]-colorList[x])/2
|
|
|
for x in range(0,5)] + [255]
|
|
|
colorHex = ["%02x"%val for val in colorList]
|
|
|
webMatch = re.compile("^#[0-9A-Fa-f]{6}$")
|
|
|
grayMatch = [6 + x * 11 for x in xrange(0,23)] + [255]
|
|
|
|
|
|
@staticmethod
|
|
|
def rgbToConsole(color):
|
|
|
"""Перевести RGB в 256 консоль
|
|
|
|
|
|
>>> ConsoleColor256.rgbToConsole("#5f00ff")
|
|
|
57
|
|
|
>>> ConsoleColor256.rgbToConsole("not #rrggbb") is None
|
|
|
True
|
|
|
"""
|
|
|
if color and ConsoleColor256.webMatch.match(color):
|
|
|
color = [int(x,base=16) for x in (color[5:7],color[3:5],color[1:3])]
|
|
|
# grayscale match
|
|
|
if abs(color[0]-color[1]) + abs(color[0]-color[2]) < 5:
|
|
|
for j,matchGray in enumerate(ConsoleColor256.grayMatch):
|
|
|
if color[0] <= matchGray:
|
|
|
return 232+j
|
|
|
# color match
|
|
|
colorNumber = 16
|
|
|
for i,_color in enumerate(color):
|
|
|
for j,halfColor in enumerate(ConsoleColor256.colorMatch):
|
|
|
if _color <= halfColor:
|
|
|
colorNumber += j * 6 ** i
|
|
|
break
|
|
|
return colorNumber
|
|
|
return None
|
|
|
|
|
|
@staticmethod
|
|
|
def consoleToRgb(color):
|
|
|
"""
|
|
|
Перевести 256 консоль в #RGB
|
|
|
|
|
|
|
|
|
>>> ConsoleColor256.consoleToRgb(216)
|
|
|
'#ffaf87'
|
|
|
>>> ConsoleColor256.consoleToRgb(15) is None
|
|
|
True
|
|
|
"""
|
|
|
if color >= 255:
|
|
|
return "#ffffff"
|
|
|
if color >= 232:
|
|
|
return "#{0:02x}{0:02x}{0:02x}".format((color-232)*11)
|
|
|
elif color >=16:
|
|
|
color-= 16
|
|
|
return "#%s"%"".join(ConsoleColor256.colorHex[color/x%6]
|
|
|
for x in (36,6,1))
|
|
|
else:
|
|
|
return None
|
|
|
|
|
|
class TextState(object):
|
|
|
"""
|
|
|
Параметры текста
|
|
|
|
|
|
>>> ts = TextState()
|
|
|
>>> ts.attr = TextState.Attributes.BOLD | TextState.Attributes.UNDERLINE
|
|
|
>>> ts.bold
|
|
|
True
|
|
|
>>> ts.underline
|
|
|
True
|
|
|
>>> ts.halfbright
|
|
|
False
|
|
|
"""
|
|
|
class Attributes:
|
|
|
NONE = 0
|
|
|
BOLD = 1
|
|
|
HALFBRIGHT = 2
|
|
|
UNDERLINE = 4
|
|
|
INVERT = 8
|
|
|
|
|
|
class Colors:
|
|
|
# обычные цвета
|
|
|
BLACK = "black"
|
|
|
RED = "red"
|
|
|
GREEN = "green"
|
|
|
BROWN = "brown"
|
|
|
BLUE = "blue"
|
|
|
PURPLE = "purple"
|
|
|
CYAN = "cyan"
|
|
|
GRAY = "gray"
|
|
|
# яркие цвета
|
|
|
DARK = "dark"
|
|
|
LIGHT_RED = "lightred"
|
|
|
LIGHT_GREEN = "lightgreen"
|
|
|
YELLOW = "yellow"
|
|
|
LIGHT_BLUE = "lightblue"
|
|
|
LIGHT_PURPLE = "lightpurple"
|
|
|
LIGHT_CYAN = "lightcyan"
|
|
|
WHITE = "white"
|
|
|
# темные цвета (консоль пониженной яркости)
|
|
|
DARK_BLACK = "darkblack"
|
|
|
DARK_RED = "darkred"
|
|
|
DARK_GREEN = "darkgreen"
|
|
|
DARK_BROWN = "darkbrown"
|
|
|
DARK_BLUE = "darkblue"
|
|
|
DARK_PURPLE = "darkpurple"
|
|
|
DARK_CYAN = "darkcyan"
|
|
|
DARK_GRAY = "darkgray"
|
|
|
DEFAULT = None
|
|
|
|
|
|
normalColors = [Colors.BLACK,Colors.RED,Colors.GREEN,Colors.BROWN,
|
|
|
Colors.BLUE,Colors.PURPLE,Colors.CYAN,Colors.GRAY]
|
|
|
lightColors = [Colors.DARK,Colors.LIGHT_RED,Colors.LIGHT_GREEN,
|
|
|
Colors.YELLOW,Colors.LIGHT_BLUE,Colors.LIGHT_PURPLE,
|
|
|
Colors.LIGHT_CYAN,Colors.WHITE]
|
|
|
darkColors = [Colors.DARK_BLACK,Colors.DARK_RED,Colors.DARK_GREEN,
|
|
|
Colors.DARK_BROWN,Colors.DARK_BLUE,Colors.DARK_PURPLE,
|
|
|
Colors.DARK_CYAN,Colors.DARK_GRAY]
|
|
|
|
|
|
def bitProperty(bit):
|
|
|
def set(self,value):
|
|
|
self.attr&=~bit
|
|
|
self.attr|=bit if value else 0
|
|
|
def get(self):
|
|
|
return bool(self.attr & bit)
|
|
|
return property(get,set)
|
|
|
|
|
|
# текст с подчеркиванием
|
|
|
underline = bitProperty(Attributes.UNDERLINE)
|
|
|
# текст жирный
|
|
|
bold = bitProperty(Attributes.BOLD)
|
|
|
# пониженная яркость
|
|
|
halfbright = bitProperty(Attributes.HALFBRIGHT)
|
|
|
# инверсия
|
|
|
invert = bitProperty(Attributes.INVERT)
|
|
|
|
|
|
def __init__(self,foreground=Colors.DEFAULT,
|
|
|
background=Colors.DEFAULT,
|
|
|
attr=Attributes.NONE):
|
|
|
self.foreground = foreground
|
|
|
self.background = background
|
|
|
self.attr = attr
|
|
|
|
|
|
def clone(self):
|
|
|
return self.__class__(self.foreground,self.background,
|
|
|
self.attr)
|
|
|
|
|
|
def __cmp__(self,other):
|
|
|
for i in ["foreground","background","attr"]:
|
|
|
cmp_res = cmp(getattr(self,i),getattr(other,i))
|
|
|
if cmp_res:
|
|
|
return cmp_res
|
|
|
return 0
|
|
|
|
|
|
@classmethod
|
|
|
def colors(cls):
|
|
|
return chain(xrange(0,8),[None])
|
|
|
|
|
|
class BaseColorMapping:
|
|
|
# соответствие внутренних цветов консольным
|
|
|
mapConsole_TS = {ConsoleCodesInfo.BLACK: TextState.Colors.BLACK,
|
|
|
ConsoleCodesInfo.RED: TextState.Colors.RED,
|
|
|
ConsoleCodesInfo.GREEN: TextState.Colors.GREEN,
|
|
|
ConsoleCodesInfo.BROWN: TextState.Colors.BROWN,
|
|
|
ConsoleCodesInfo.BLUE: TextState.Colors.BLUE,
|
|
|
ConsoleCodesInfo.PURPLE:TextState.Colors.PURPLE,
|
|
|
ConsoleCodesInfo.CYAN: TextState.Colors.CYAN,
|
|
|
ConsoleCodesInfo.WHITE: TextState.Colors.GRAY}
|
|
|
|
|
|
mapTS_Console = {v:k for k,v in mapConsole_TS.items()}
|
|
|
|
|
|
def __init__(self,base):
|
|
|
self.mapConsole_TS = self.mapConsole_TS.copy()
|
|
|
self.mapConsole_TS.update(base.mapConsole_TS)
|
|
|
self.mapTS_Console = {v:k for k,v in self.mapConsole_TS.items()}
|
|
|
|
|
|
class LightColorMapping(BaseColorMapping):
|
|
|
# соответствие внутренних цветов консольным
|
|
|
offset = ConsoleCodesInfo.COLORCOUNT
|
|
|
mapConsole_TS = {ConsoleCodesInfo.BLACK+offset: TextState.Colors.DARK,
|
|
|
ConsoleCodesInfo.RED+offset: TextState.Colors.LIGHT_RED,
|
|
|
ConsoleCodesInfo.GREEN+offset: TextState.Colors.LIGHT_GREEN,
|
|
|
ConsoleCodesInfo.BROWN+offset: TextState.Colors.YELLOW,
|
|
|
ConsoleCodesInfo.BLUE+offset: TextState.Colors.LIGHT_BLUE,
|
|
|
ConsoleCodesInfo.PURPLE+offset:TextState.Colors.LIGHT_PURPLE,
|
|
|
ConsoleCodesInfo.CYAN+offset: TextState.Colors.LIGHT_CYAN,
|
|
|
ConsoleCodesInfo.WHITE+offset: TextState.Colors.WHITE}
|
|
|
|
|
|
mapTS_Console = {v:k for k,v in mapConsole_TS.items()}
|
|
|
|
|
|
class ConsoleCodeMapping(BaseColorMapping):
|
|
|
"""
|
|
|
Декоратор для преобразования кодов цвета в код цвета тона или фона
|
|
|
|
|
|
Добавляет смещение к коду (3 -> 3 + codeoffset)
|
|
|
"""
|
|
|
def __init__(self,codeoffset,base):
|
|
|
self.mapConsole_TS = {codeoffset+k:v for k,v in base.mapConsole_TS.items()}
|
|
|
self.mapTS_Console = {v:k for k,v in self.mapConsole_TS.items()}
|
|
|
|
|
|
class SpanPalette:
|
|
|
"""
|
|
|
Палитра для SpanCssOutput (черное на белом)
|
|
|
"""
|
|
|
LOW_BRIGHT, NORMAL_BRIGHT, HIGH_BRIGHT = 0, 1, 2
|
|
|
|
|
|
defaultColor = ["Black","Black","DarkGrey"]
|
|
|
defaultBackground = "White"
|
|
|
|
|
|
normalBright = dict(zip(TextState.normalColors,
|
|
|
["Black","DarkRed","DarkGreen",
|
|
|
"Sienna","DarkBlue","DarkViolet",
|
|
|
"LightSeaGreen","Grey"]))
|
|
|
|
|
|
highBright = dict(zip(TextState.lightColors,
|
|
|
["DarkGrey","Red","Green",
|
|
|
"Yellow","RoyalBlue","Magenta",
|
|
|
"Cyan","White"]))
|
|
|
|
|
|
lowBright = dict(zip(TextState.darkColors,
|
|
|
["Black","Maroon","DarkGreen",
|
|
|
"SaddleBrown","DarkBlue","DarkViolet",
|
|
|
"LightSeaGreen","Grey"]))
|
|
|
|
|
|
|
|
|
mapHighNormal = dict(zip(TextState.normalColors,
|
|
|
TextState.lightColors))
|
|
|
mapLowNormal = dict(zip(TextState.normalColors,
|
|
|
TextState.darkColors))
|
|
|
|
|
|
def __init__(self):
|
|
|
self.colorMap = dict(self.normalBright.items()+
|
|
|
self.highBright.items()+
|
|
|
self.lowBright.items())
|
|
|
self.brightMap = {self.NORMAL_BRIGHT:{},
|
|
|
self.HIGH_BRIGHT:self.mapHighNormal,
|
|
|
self.LOW_BRIGHT:self.mapLowNormal}
|
|
|
|
|
|
def brightTransform(self,color,bright):
|
|
|
"""
|
|
|
Преобразовать основной цвет в зависимости от установленной яркости
|
|
|
"""
|
|
|
mapper = self.brightMap.get(bright,{})
|
|
|
return mapper.get(color,color)
|
|
|
|
|
|
def getBackgroundColor(self,color):
|
|
|
if not color:
|
|
|
return self.defaultBackground
|
|
|
return self.colorMap.get(color,color)
|
|
|
|
|
|
def getTextColor(self,color,bright):
|
|
|
"""
|
|
|
Получить соответствие цвета строкой
|
|
|
"""
|
|
|
if not color:
|
|
|
return self.defaultColor[bright]
|
|
|
color = self.brightTransform(color,bright)
|
|
|
return self.colorMap.get(color,color)
|
|
|
|
|
|
class DarkPastelsPalette(SpanPalette):
|
|
|
"""
|
|
|
Палитра идентичная Calculate в консоли (Dark Pastels)
|
|
|
"""
|
|
|
defaultColor = ["#DCDCDC","#DCDCDC","#709080"]
|
|
|
defaultBackground = "#2C2C2C"
|
|
|
|
|
|
normalBright = dict(zip(TextState.normalColors,
|
|
|
["#2C2C2C","#705050","#60B48A", "#DFAF8F",
|
|
|
"#9AB8D7","#DC8CC3","#8CD0D3","#DCDCDC"]))
|
|
|
highBright = dict(zip(TextState.lightColors,
|
|
|
["#709080","#DCA3A3","#72D5A3", "#F0DFAF",
|
|
|
"#94BFF3","#EC93D3","#93E0E3","#FFFFFF"]))
|
|
|
|