You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/calculate/lib/utils/color/output.py

397 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#-*- 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.
from calculate.lib.utils.tools import SavableIterator
from palette import (TextState, BaseColorMapping,
ConsoleCodeMapping, LightColorMapping, ConsoleColor256,
ConsoleCodesInfo, SpanPalette)
class BaseOutput:
"""
Базовый вывод текста.
Вывод просто текста без изменения шрифта
"""
def __init__(self,state=None):
pass
def setBold(self):
"""
Выводимый текст будет жирным
"""
pass
def resetBold(self):
"""
Выводимый текст будет нежирным
"""
pass
def setUnderline(self):
"""
Выводимый текст будет подчеркнутым
"""
pass
def resetUnderline(self):
"""
Выводимый текст не будет подчеркнутым
"""
pass
def setHalfbright(self):
"""
Цвет выводимого текста использует полутона
"""
pass
def resetHalfbright(self):
"""
Цвет выводимого текста не использует полутона
"""
pass
def reset(self):
"""
Использовать шрифт по умолчанию
"""
pass
def endText(self):
"""
Обработка текста завершена
"""
return ""
def outputText(self,text):
"""
Вывести текст с установленными настройками
"""
return text
def setForeground(self,color):
"""
Установить цвет шрифта
"""
pass
def setBackground(self,color):
"""
Установить цвет фона
"""
pass
def resetForeground(self,color):
"""
Использовать цвет шрифта по умолчанию
"""
pass
def resetBackground(self,color):
"""
Использовать цвет фона по умолчанию
"""
pass
def setInvert(self):
"""
Включить инверсию
"""
def resetInvert(self):
"""
Выключить инверсию
"""
class SaveAttrOutput(BaseOutput):
"""
Базовый класс с сохранением атрибутов
"""
def __init__(self,state=None):
self.prev_state = state.clone() if state else TextState()
self.current_state = self.prev_state.clone()
def setBold(self):
self.current_state.bold = True
def resetBold(self):
self.current_state.bold = False
def setUnderline(self):
self.current_state.underline = True
def resetUnderline(self):
self.current_state.underline = False
def setHalfbright(self):
self.current_state.halfbright = True
def resetHalfbright(self):
self.current_state.halfbright = False
def reset(self):
self.resetBold()
self.resetHalfbright()
self.resetUnderline()
self.resetForeground()
self.resetBackground()
self.resetInvert()
def setForeground(self,color):
self.current_state.foreground = color
def resetForeground(self):
self.current_state.foreground = TextState.Colors.DEFAULT
def setBackground(self,color):
self.current_state.background = color
def resetBackground(self):
self.current_state.background = TextState.Colors.DEFAULT
def resetInvert(self):
self.current_state.invert = False
def setInvert(self):
self.current_state.invert = True
class ColorTerminalOutput(SaveAttrOutput):
"""
Форматирует текст для вывода в консоль
"""
mapColors = ConsoleCodeMapping(ConsoleCodesInfo.FOREGROUND,
BaseColorMapping).mapTS_Console
mapLightColors = ConsoleCodeMapping(ConsoleCodesInfo.FOREGROUND-
LightColorMapping.offset,
LightColorMapping).mapTS_Console
mapBackgroundColors = ConsoleCodeMapping(ConsoleCodesInfo.BACKGROUND,
BaseColorMapping).mapTS_Console
def setBold(self):
self.resetHalfbright()
SaveAttrOutput.setBold(self)
def setHalfbright(self):
self.resetBold()
SaveAttrOutput.setHalfbright(self)
def handleForeground(self,prevstate,curstate,attrs):
cci = ConsoleCodesInfo
color = curstate.foreground
if color in self.mapColors:
attrs.append(self.mapColors[color])
elif color in self.mapLightColors:
if prevstate.bold == curstate.bold and curstate.bold == False:
attrs.append(cci.BOLD)
attrs.append(self.mapLightColors[color])
else:
attrs.append(cci.FOREGROUND_DEFAULT)
self.resetForeground()
def handleBackground(self,prevstate,curstate,attrs):
color = curstate.background
if color in self.mapBackgroundColors:
attrs.append(self.mapBackgroundColors[color])
else:
attrs.append(ConsoleCodesInfo.BACKGROUND_DEFAULT)
def handleIntensity(self,prevstate,curstate,attrs):
if curstate.bold and curstate.bold != prevstate.bold:
attrs.append(ConsoleCodesInfo.BOLD)
elif (curstate.halfbright and
prevstate.halfbright != curstate.halfbright):
attrs.append(ConsoleCodesInfo.HALFBRIGHT)
else:
attrs.append(ConsoleCodesInfo.NORMAL)
def handleUnderline(self,prevstate,curstate,attrs):
if curstate.underline:
attrs.append(ConsoleCodesInfo.UNDERLINE)
else:
attrs.append(ConsoleCodesInfo.NOUNDERLINE)
def handleInvert(self,prevstate,curstate,attrs):
if curstate.invert:
attrs.append(ConsoleCodesInfo.INVERT)
else:
attrs.append(ConsoleCodesInfo.NOINVERT)
def _createAttrs(self,prevstate,curstate):
"""
Создать ESC последовательность для установки параметров текста
"""
attrs = []
# получить интенсивность (полутон и жирность относятся к интенсивности)
intensity = lambda x:x & (TextState.Attributes.HALFBRIGHT |
TextState.Attributes.BOLD)
if (prevstate.attr != curstate.attr and
curstate.attr == TextState.Attributes.NONE and
curstate.foreground is TextState.Colors.DEFAULT and
curstate.background is TextState.Colors.DEFAULT):
attrs.append(ConsoleCodesInfo.RESET)
else:
if intensity(prevstate.attr) != intensity(curstate.attr):
self.handleIntensity(prevstate,curstate,attrs)
if prevstate.underline != curstate.underline:
self.handleUnderline(prevstate,curstate,attrs)
if prevstate.invert != curstate.invert:
self.handleInvert(prevstate,curstate,attrs)
if prevstate.foreground != curstate.foreground:
self.handleForeground(prevstate,curstate,attrs)
if prevstate.background != curstate.background:
self.handleBackground(prevstate,curstate,attrs)
return attrs
def handlePostAttr(self,prevstate,curstate):
"""
Добавление аттрибутов после выведенного текста
"""
if prevstate.foreground != curstate.foreground:
color = curstate.foreground
if (color in self.mapLightColors and
prevstate.bold == curstate.bold and curstate.bold == False):
return [ConsoleCodesInfo.NORMAL]
def _createEscCode(self,attrs):
"""
Создать ESC строку
"""
attrs = map(str,['\033['] + attrs + ['m'])
return "%s%s%s"%(attrs[0],";".join(attrs[1:-1]),attrs[-1])
def outputText(self,s):
"""
Задание параметров текста и вывод его
"""
if self.prev_state != self.current_state:
attr = self._createAttrs(self.prev_state,self.current_state)
attr = self._createEscCode(attr)
postattr = self.handlePostAttr(self.prev_state,self.current_state)
if postattr:
postattr = self._createEscCode(postattr)
else:
postattr = ""
self.prev_state = self.current_state.clone()
else:
attr = ""
postattr = ""
return attr + s + postattr
def endText(self):
self.reset()
return self.outputText("")
class ColorTerminal256Output(ColorTerminalOutput):
"""
Вывод на 256 цветный терминал
"""
mapLightColors = LightColorMapping.mapTS_Console
def handleForeground(self,prevstate,curstate,attrs):
color = curstate.foreground
color256 = ConsoleColor256.rgbToConsole(color)
if not color256 and color in self.mapLightColors:
color256 = self.mapLightColors[color]
if color256:
attrs.extend([ConsoleCodesInfo.FOREGROUND256,
ConsoleCodesInfo.COLOR256,
color256])
else:
ColorTerminalOutput.handleForeground(self,prevstate,curstate,attrs)
def handleBackground(self,prevstate,curstate,attrs):
color = curstate.background
color256 = ConsoleColor256.rgbToConsole(color)
if not color256 and color in self.mapLightColors:
color256 = self.mapLightColors[color]
if color256:
attrs.extend([ConsoleCodesInfo.BACKGROUND256,
ConsoleCodesInfo.COLOR256,
color256])
else:
ColorTerminalOutput.handleBackground(self,prevstate,curstate,attrs)
def handlePostAttr(self,prevstate,curstate):
pass
class SpanCssOutput(SaveAttrOutput):
"""
Форматирует текст для вывода в консоль
"""
def __init__(self,state=None,palette=SpanPalette()):
SaveAttrOutput.__init__(self,state=state)
self.palette = palette
def getStringColor(self,color,bold=False,halfbright=False,background=False):
"""
Получить название цвета по номеру и состоянию текста
"""
if halfbright:
bright = SpanPalette.LOW_BRIGHT
elif bold:
bright = SpanPalette.HIGH_BRIGHT
else:
bright = SpanPalette.NORMAL_BRIGHT
if background:
return self.palette.getBackgroundColor(color)
else:
return self.palette.getTextColor(color,bright)
def getTags(self,prevstate,curstate):
"""
Создать ESC последовательность для установки параметров текста
"""
style = []
colorAttr = ["color","background"]
if curstate.invert:
colorAttr = colorAttr[1],colorAttr[0]
if (prevstate.foreground != curstate.foreground or
prevstate.bold != curstate.bold or
curstate.invert or
prevstate.halfbright != curstate.halfbright):
sColor = self.getStringColor(curstate.foreground,
curstate.bold,
curstate.halfbright,
background=False)
style.append("%s:%s;"%(colorAttr[0],sColor))
if prevstate.background != curstate.background or curstate.invert:
sColor = self.getStringColor(curstate.background,background=True)
style.append("%s:%s;"%(colorAttr[1],sColor))
if prevstate.underline != curstate.underline:
if curstate.underline:
style.append("text-decoration:underline;")
else:
style.append("text-decoration:none;")
if prevstate.bold != curstate.bold:
if curstate.bold:
style.append("font-weight:bold;")
else:
style.append("font-weight:normal;")
return '<span style="%s">'%"".join(style),'</span>'
def outputText(self,s):
if self.prev_state != self.current_state:
lattr, rattr = self.getTags(self.prev_state,self.current_state)
else:
lattr = rattr = ""
return lattr + s + rattr
def endText(self):
self.reset()
return ""