generated from mwc/lab_retro
When I run the game, it works for a bit but then when the first asteroid
gets to the bottom I get this:
Traceback (most recent call last):
File "/root/making_with_code/mwc1/unit3/lab_retro/nav_game.py", line 14, in <module>
game.play()
File "/root/making_with_code/mwc1/unit3/lab_retro/retro/game.py", line 80, in play
agent.play_turn(self)
File "/root/making_with_code/mwc1/unit3/lab_retro/asteroid.py", line 16, in play_turn
game.remove_agent_by_name(self.name)
AttributeError: 'Asteroid' object has no attribute 'name'
This commit is contained in:
23
blessed/__init__.py
Normal file
23
blessed/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
A thin, practical wrapper around terminal capabilities in Python.
|
||||
|
||||
http://pypi.python.org/pypi/blessed
|
||||
"""
|
||||
# std imports
|
||||
import sys as _sys
|
||||
import platform as _platform
|
||||
|
||||
# isort: off
|
||||
if _platform.system() == 'Windows':
|
||||
from blessed.win_terminal import Terminal
|
||||
else:
|
||||
from blessed.terminal import Terminal # type: ignore
|
||||
|
||||
if (3, 0, 0) <= _sys.version_info[:3] < (3, 2, 3):
|
||||
# Good till 3.2.10
|
||||
# Python 3.x < 3.2.3 has a bug in which tparm() erroneously takes a string.
|
||||
raise ImportError('Blessed needs Python 3.2.3 or greater for Python 3 '
|
||||
'support due to http://bugs.python.org/issue10570.')
|
||||
|
||||
__all__ = ('Terminal',)
|
||||
__version__ = "1.20.0"
|
||||
0
blessed/__init__.py:Zone.Identifier
Normal file
0
blessed/__init__.py:Zone.Identifier
Normal file
BIN
blessed/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/_capabilities.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/_capabilities.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/color.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/color.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/colorspace.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/colorspace.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/formatters.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/formatters.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/keyboard.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/keyboard.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/sequences.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/sequences.cpython-310.pyc
Normal file
Binary file not shown.
BIN
blessed/__pycache__/terminal.cpython-310.pyc
Normal file
BIN
blessed/__pycache__/terminal.cpython-310.pyc
Normal file
Binary file not shown.
168
blessed/_capabilities.py
Normal file
168
blessed/_capabilities.py
Normal file
@@ -0,0 +1,168 @@
|
||||
"""Terminal capability builder patterns."""
|
||||
# std imports
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
__all__ = (
|
||||
'CAPABILITY_DATABASE',
|
||||
'CAPABILITIES_RAW_MIXIN',
|
||||
'CAPABILITIES_ADDITIVES',
|
||||
'CAPABILITIES_CAUSE_MOVEMENT',
|
||||
)
|
||||
|
||||
CAPABILITY_DATABASE = OrderedDict((
|
||||
('bell', ('bel', {})),
|
||||
('carriage_return', ('cr', {})),
|
||||
('change_scroll_region', ('csr', {'nparams': 2})),
|
||||
('clear_all_tabs', ('tbc', {})),
|
||||
('clear_screen', ('clear', {})),
|
||||
('clr_bol', ('el1', {})),
|
||||
('clr_eol', ('el', {})),
|
||||
('clr_eos', ('clear_eos', {})),
|
||||
('column_address', ('hpa', {'nparams': 1})),
|
||||
('cursor_address', ('cup', {'nparams': 2, 'match_grouped': True})),
|
||||
('cursor_down', ('cud1', {})),
|
||||
('cursor_home', ('home', {})),
|
||||
('cursor_invisible', ('civis', {})),
|
||||
('cursor_left', ('cub1', {})),
|
||||
('cursor_normal', ('cnorm', {})),
|
||||
('cursor_report', ('u6', {'nparams': 2, 'match_grouped': True})),
|
||||
('cursor_right', ('cuf1', {})),
|
||||
('cursor_up', ('cuu1', {})),
|
||||
('cursor_visible', ('cvvis', {})),
|
||||
('delete_character', ('dch1', {})),
|
||||
('delete_line', ('dl1', {})),
|
||||
('enter_blink_mode', ('blink', {})),
|
||||
('enter_bold_mode', ('bold', {})),
|
||||
('enter_dim_mode', ('dim', {})),
|
||||
('enter_fullscreen', ('smcup', {})),
|
||||
('enter_standout_mode', ('standout', {})),
|
||||
('enter_superscript_mode', ('superscript', {})),
|
||||
('enter_susimpleript_mode', ('susimpleript', {})),
|
||||
('enter_underline_mode', ('underline', {})),
|
||||
('erase_chars', ('ech', {'nparams': 1})),
|
||||
('exit_alt_charset_mode', ('rmacs', {})),
|
||||
('exit_am_mode', ('rmam', {})),
|
||||
('exit_attribute_mode', ('sgr0', {})),
|
||||
('exit_ca_mode', ('rmcup', {})),
|
||||
('exit_fullscreen', ('rmcup', {})),
|
||||
('exit_insert_mode', ('rmir', {})),
|
||||
('exit_standout_mode', ('rmso', {})),
|
||||
('exit_underline_mode', ('rmul', {})),
|
||||
('flash_hook', ('hook', {})),
|
||||
('flash_screen', ('flash', {})),
|
||||
('insert_line', ('il1', {})),
|
||||
('keypad_local', ('rmkx', {})),
|
||||
('keypad_xmit', ('smkx', {})),
|
||||
('meta_off', ('rmm', {})),
|
||||
('meta_on', ('smm', {})),
|
||||
('orig_pair', ('op', {})),
|
||||
('parm_down_cursor', ('cud', {'nparams': 1})),
|
||||
('parm_left_cursor', ('cub', {'nparams': 1, 'match_grouped': True})),
|
||||
('parm_dch', ('dch', {'nparams': 1})),
|
||||
('parm_delete_line', ('dl', {'nparams': 1})),
|
||||
('parm_ich', ('ich', {'nparams': 1})),
|
||||
('parm_index', ('indn', {'nparams': 1})),
|
||||
('parm_insert_line', ('il', {'nparams': 1})),
|
||||
('parm_right_cursor', ('cuf', {'nparams': 1, 'match_grouped': True})),
|
||||
('parm_rindex', ('rin', {'nparams': 1})),
|
||||
('parm_up_cursor', ('cuu', {'nparams': 1})),
|
||||
('print_screen', ('mc0', {})),
|
||||
('prtr_off', ('mc4', {})),
|
||||
('prtr_on', ('mc5', {})),
|
||||
('reset_1string', ('r1', {})),
|
||||
('reset_2string', ('r2', {})),
|
||||
('reset_3string', ('r3', {})),
|
||||
('restore_cursor', ('rc', {})),
|
||||
('row_address', ('vpa', {'nparams': 1})),
|
||||
('save_cursor', ('sc', {})),
|
||||
('scroll_forward', ('ind', {})),
|
||||
('scroll_reverse', ('rev', {})),
|
||||
('set0_des_seq', ('s0ds', {})),
|
||||
('set1_des_seq', ('s1ds', {})),
|
||||
('set2_des_seq', ('s2ds', {})),
|
||||
('set3_des_seq', ('s3ds', {})),
|
||||
# this 'color' is deceiving, but often matching, and a better match
|
||||
# than set_a_attributes1 or set_a_foreground.
|
||||
('color', ('_foreground_color', {'nparams': 1, 'match_any': True,
|
||||
'numeric': 1})),
|
||||
('set_a_foreground', ('color', {'nparams': 1, 'match_any': True,
|
||||
'numeric': 1})),
|
||||
('set_a_background', ('on_color', {'nparams': 1, 'match_any': True,
|
||||
'numeric': 1})),
|
||||
('set_tab', ('hts', {})),
|
||||
('tab', ('ht', {})),
|
||||
('italic', ('sitm', {})),
|
||||
('no_italic', ('sitm', {})),
|
||||
))
|
||||
|
||||
CAPABILITIES_RAW_MIXIN = {
|
||||
'bell': re.escape('\a'),
|
||||
'carriage_return': re.escape('\r'),
|
||||
'cursor_left': re.escape('\b'),
|
||||
'cursor_report': re.escape('\x1b') + r'\[(\d+)\;(\d+)R',
|
||||
'cursor_right': re.escape('\x1b') + r'\[C',
|
||||
'exit_attribute_mode': re.escape('\x1b') + r'\[m',
|
||||
'parm_left_cursor': re.escape('\x1b') + r'\[(\d+)D',
|
||||
'parm_right_cursor': re.escape('\x1b') + r'\[(\d+)C',
|
||||
'restore_cursor': re.escape(r'\x1b\[u'),
|
||||
'save_cursor': re.escape(r'\x1b\[s'),
|
||||
'scroll_forward': re.escape('\n'),
|
||||
'set0_des_seq': re.escape('\x1b(B'),
|
||||
'tab': re.escape('\t'),
|
||||
}
|
||||
_ANY_NOTESC = '[^' + re.escape('\x1b') + ']*'
|
||||
|
||||
CAPABILITIES_ADDITIVES = {
|
||||
'link': ('link',
|
||||
re.escape('\x1b') + r'\]8;' + _ANY_NOTESC + ';' +
|
||||
_ANY_NOTESC + re.escape('\x1b') + '\\\\'),
|
||||
'color256': ('color', re.escape('\x1b') + r'\[38;5;\d+m'),
|
||||
'on_color256': ('on_color', re.escape('\x1b') + r'\[48;5;\d+m'),
|
||||
'color_rgb': ('color_rgb', re.escape('\x1b') + r'\[38;2;\d+;\d+;\d+m'),
|
||||
'on_color_rgb': ('on_color_rgb', re.escape('\x1b') + r'\[48;2;\d+;\d+;\d+m'),
|
||||
'shift_in': ('', re.escape('\x0f')),
|
||||
'shift_out': ('', re.escape('\x0e')),
|
||||
# sgr(...) outputs strangely, use the basic ANSI/EMCA-48 codes here.
|
||||
'set_a_attributes1': (
|
||||
'sgr', re.escape('\x1b') + r'\[\d+m'),
|
||||
'set_a_attributes2': (
|
||||
'sgr', re.escape('\x1b') + r'\[\d+\;\d+m'),
|
||||
'set_a_attributes3': (
|
||||
'sgr', re.escape('\x1b') + r'\[\d+\;\d+\;\d+m'),
|
||||
'set_a_attributes4': (
|
||||
'sgr', re.escape('\x1b') + r'\[\d+\;\d+\;\d+\;\d+m'),
|
||||
# this helps where xterm's sgr0 includes set0_des_seq, we'd
|
||||
# rather like to also match this immediate substring.
|
||||
'sgr0': ('sgr0', re.escape('\x1b') + r'\[m'),
|
||||
'backspace': ('', re.escape('\b')),
|
||||
'ascii_tab': ('', re.escape('\t')),
|
||||
'clr_eol': ('', re.escape('\x1b[K')),
|
||||
'clr_eol0': ('', re.escape('\x1b[0K')),
|
||||
'clr_bol': ('', re.escape('\x1b[1K')),
|
||||
'clr_eosK': ('', re.escape('\x1b[2K')),
|
||||
}
|
||||
|
||||
CAPABILITIES_CAUSE_MOVEMENT = (
|
||||
'ascii_tab',
|
||||
'backspace',
|
||||
'carriage_return',
|
||||
'clear_screen',
|
||||
'column_address',
|
||||
'cursor_address',
|
||||
'cursor_down',
|
||||
'cursor_home',
|
||||
'cursor_left',
|
||||
'cursor_right',
|
||||
'cursor_up',
|
||||
'enter_fullscreen',
|
||||
'exit_fullscreen',
|
||||
'parm_down_cursor',
|
||||
'parm_left_cursor',
|
||||
'parm_right_cursor',
|
||||
'parm_up_cursor',
|
||||
'restore_cursor',
|
||||
'row_address',
|
||||
'scroll_forward',
|
||||
'tab',
|
||||
)
|
||||
0
blessed/_capabilities.py:Zone.Identifier
Normal file
0
blessed/_capabilities.py:Zone.Identifier
Normal file
7
blessed/_capabilities.pyi
Normal file
7
blessed/_capabilities.pyi
Normal file
@@ -0,0 +1,7 @@
|
||||
# std imports
|
||||
from typing import Any, Dict, Tuple, OrderedDict
|
||||
|
||||
CAPABILITY_DATABASE: OrderedDict[str, Tuple[str, Dict[str, Any]]]
|
||||
CAPABILITIES_RAW_MIXIN: Dict[str, str]
|
||||
CAPABILITIES_ADDITIVES: Dict[str, Tuple[str, str]]
|
||||
CAPABILITIES_CAUSE_MOVEMENT: Tuple[str, ...]
|
||||
0
blessed/_capabilities.pyi:Zone.Identifier
Normal file
0
blessed/_capabilities.pyi:Zone.Identifier
Normal file
258
blessed/color.py
Normal file
258
blessed/color.py
Normal file
@@ -0,0 +1,258 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Sub-module providing color functions.
|
||||
|
||||
References,
|
||||
|
||||
- https://en.wikipedia.org/wiki/Color_difference
|
||||
- http://www.easyrgb.com/en/math.php
|
||||
- Measuring Colour by R.W.G. Hunt and M.R. Pointer
|
||||
"""
|
||||
|
||||
# std imports
|
||||
from math import cos, exp, sin, sqrt, atan2
|
||||
|
||||
# isort: off
|
||||
try:
|
||||
from functools import lru_cache
|
||||
except ImportError:
|
||||
# lru_cache was added in Python 3.2
|
||||
from backports.functools_lru_cache import lru_cache
|
||||
|
||||
|
||||
def rgb_to_xyz(red, green, blue):
|
||||
"""
|
||||
Convert standard RGB color to XYZ color.
|
||||
|
||||
:arg int red: RGB value of Red.
|
||||
:arg int green: RGB value of Green.
|
||||
:arg int blue: RGB value of Blue.
|
||||
:returns: Tuple (X, Y, Z) representing XYZ color
|
||||
:rtype: tuple
|
||||
|
||||
D65/2° standard illuminant
|
||||
"""
|
||||
rgb = []
|
||||
for val in red, green, blue:
|
||||
val /= 255.0
|
||||
if val > 0.04045:
|
||||
val = pow((val + 0.055) / 1.055, 2.4)
|
||||
else:
|
||||
val /= 12.92
|
||||
val *= 100
|
||||
rgb.append(val)
|
||||
|
||||
red, green, blue = rgb # pylint: disable=unbalanced-tuple-unpacking
|
||||
x_val = red * 0.4124 + green * 0.3576 + blue * 0.1805
|
||||
y_val = red * 0.2126 + green * 0.7152 + blue * 0.0722
|
||||
z_val = red * 0.0193 + green * 0.1192 + blue * 0.9505
|
||||
|
||||
return x_val, y_val, z_val
|
||||
|
||||
|
||||
def xyz_to_lab(x_val, y_val, z_val):
|
||||
"""
|
||||
Convert XYZ color to CIE-Lab color.
|
||||
|
||||
:arg float x_val: XYZ value of X.
|
||||
:arg float y_val: XYZ value of Y.
|
||||
:arg float z_val: XYZ value of Z.
|
||||
:returns: Tuple (L, a, b) representing CIE-Lab color
|
||||
:rtype: tuple
|
||||
|
||||
D65/2° standard illuminant
|
||||
"""
|
||||
xyz = []
|
||||
for val, ref in (x_val, 95.047), (y_val, 100.0), (z_val, 108.883):
|
||||
val /= ref
|
||||
val = pow(val, 1 / 3.0) if val > 0.008856 else 7.787 * val + 16 / 116.0
|
||||
xyz.append(val)
|
||||
|
||||
x_val, y_val, z_val = xyz # pylint: disable=unbalanced-tuple-unpacking
|
||||
cie_l = 116 * y_val - 16
|
||||
cie_a = 500 * (x_val - y_val)
|
||||
cie_b = 200 * (y_val - z_val)
|
||||
|
||||
return cie_l, cie_a, cie_b
|
||||
|
||||
|
||||
@lru_cache(maxsize=256)
|
||||
def rgb_to_lab(red, green, blue):
|
||||
"""
|
||||
Convert RGB color to CIE-Lab color.
|
||||
|
||||
:arg int red: RGB value of Red.
|
||||
:arg int green: RGB value of Green.
|
||||
:arg int blue: RGB value of Blue.
|
||||
:returns: Tuple (L, a, b) representing CIE-Lab color
|
||||
:rtype: tuple
|
||||
|
||||
D65/2° standard illuminant
|
||||
"""
|
||||
return xyz_to_lab(*rgb_to_xyz(red, green, blue))
|
||||
|
||||
|
||||
def dist_rgb(rgb1, rgb2):
|
||||
"""
|
||||
Determine distance between two rgb colors.
|
||||
|
||||
:arg tuple rgb1: RGB color definition
|
||||
:arg tuple rgb2: RGB color definition
|
||||
:returns: Square of the distance between provided colors
|
||||
:rtype: float
|
||||
|
||||
This works by treating RGB colors as coordinates in three dimensional
|
||||
space and finding the closest point within the configured color range
|
||||
using the formula::
|
||||
|
||||
d^2 = (r2 - r1)^2 + (g2 - g1)^2 + (b2 - b1)^2
|
||||
|
||||
For efficiency, the square of the distance is returned
|
||||
which is sufficient for comparisons
|
||||
"""
|
||||
return sum(pow(rgb1[idx] - rgb2[idx], 2) for idx in (0, 1, 2))
|
||||
|
||||
|
||||
def dist_rgb_weighted(rgb1, rgb2):
|
||||
"""
|
||||
Determine the weighted distance between two rgb colors.
|
||||
|
||||
:arg tuple rgb1: RGB color definition
|
||||
:arg tuple rgb2: RGB color definition
|
||||
:returns: Square of the distance between provided colors
|
||||
:rtype: float
|
||||
|
||||
Similar to a standard distance formula, the values are weighted
|
||||
to approximate human perception of color differences
|
||||
|
||||
For efficiency, the square of the distance is returned
|
||||
which is sufficient for comparisons
|
||||
"""
|
||||
red_mean = (rgb1[0] + rgb2[0]) / 2.0
|
||||
|
||||
return ((2 + red_mean / 256) * pow(rgb1[0] - rgb2[0], 2) +
|
||||
4 * pow(rgb1[1] - rgb2[1], 2) +
|
||||
(2 + (255 - red_mean) / 256) * pow(rgb1[2] - rgb2[2], 2))
|
||||
|
||||
|
||||
def dist_cie76(rgb1, rgb2):
|
||||
"""
|
||||
Determine distance between two rgb colors using the CIE94 algorithm.
|
||||
|
||||
:arg tuple rgb1: RGB color definition
|
||||
:arg tuple rgb2: RGB color definition
|
||||
:returns: Square of the distance between provided colors
|
||||
:rtype: float
|
||||
|
||||
For efficiency, the square of the distance is returned
|
||||
which is sufficient for comparisons
|
||||
"""
|
||||
l_1, a_1, b_1 = rgb_to_lab(*rgb1)
|
||||
l_2, a_2, b_2 = rgb_to_lab(*rgb2)
|
||||
return pow(l_1 - l_2, 2) + pow(a_1 - a_2, 2) + pow(b_1 - b_2, 2)
|
||||
|
||||
|
||||
def dist_cie94(rgb1, rgb2):
|
||||
# pylint: disable=too-many-locals
|
||||
"""
|
||||
Determine distance between two rgb colors using the CIE94 algorithm.
|
||||
|
||||
:arg tuple rgb1: RGB color definition
|
||||
:arg tuple rgb2: RGB color definition
|
||||
:returns: Square of the distance between provided colors
|
||||
:rtype: float
|
||||
|
||||
For efficiency, the square of the distance is returned
|
||||
which is sufficient for comparisons
|
||||
"""
|
||||
l_1, a_1, b_1 = rgb_to_lab(*rgb1)
|
||||
l_2, a_2, b_2 = rgb_to_lab(*rgb2)
|
||||
|
||||
s_l = k_l = k_c = k_h = 1
|
||||
k_1 = 0.045
|
||||
k_2 = 0.015
|
||||
|
||||
delta_l = l_1 - l_2
|
||||
delta_a = a_1 - a_2
|
||||
delta_b = b_1 - b_2
|
||||
c_1 = sqrt(a_1 ** 2 + b_1 ** 2)
|
||||
c_2 = sqrt(a_2 ** 2 + b_2 ** 2)
|
||||
delta_c = c_1 - c_2
|
||||
delta_h = sqrt(delta_a ** 2 + delta_b ** 2 + delta_c ** 2)
|
||||
s_c = 1 + k_1 * c_1
|
||||
s_h = 1 + k_2 * c_1
|
||||
|
||||
return ((delta_l / (k_l * s_l)) ** 2 + # pylint: disable=superfluous-parens
|
||||
(delta_c / (k_c * s_c)) ** 2 +
|
||||
(delta_h / (k_h * s_h)) ** 2)
|
||||
|
||||
|
||||
def dist_cie2000(rgb1, rgb2):
|
||||
# pylint: disable=too-many-locals
|
||||
"""
|
||||
Determine distance between two rgb colors using the CIE2000 algorithm.
|
||||
|
||||
:arg tuple rgb1: RGB color definition
|
||||
:arg tuple rgb2: RGB color definition
|
||||
:returns: Square of the distance between provided colors
|
||||
:rtype: float
|
||||
|
||||
For efficiency, the square of the distance is returned
|
||||
which is sufficient for comparisons
|
||||
"""
|
||||
s_l = k_l = k_c = k_h = 1
|
||||
|
||||
l_1, a_1, b_1 = rgb_to_lab(*rgb1)
|
||||
l_2, a_2, b_2 = rgb_to_lab(*rgb2)
|
||||
|
||||
delta_l = l_2 - l_1
|
||||
l_mean = (l_1 + l_2) / 2
|
||||
|
||||
c_1 = sqrt(a_1 ** 2 + b_1 ** 2)
|
||||
c_2 = sqrt(a_2 ** 2 + b_2 ** 2)
|
||||
c_mean = (c_1 + c_2) / 2
|
||||
delta_c = c_1 - c_2
|
||||
|
||||
g_x = sqrt(c_mean ** 7 / (c_mean ** 7 + 25 ** 7))
|
||||
h_1 = atan2(b_1, a_1 + (a_1 / 2) * (1 - g_x)) % 360
|
||||
h_2 = atan2(b_2, a_2 + (a_2 / 2) * (1 - g_x)) % 360
|
||||
|
||||
if 0 in (c_1, c_2):
|
||||
delta_h_prime = 0
|
||||
h_mean = h_1 + h_2
|
||||
else:
|
||||
delta_h_prime = h_2 - h_1
|
||||
if abs(delta_h_prime) <= 180:
|
||||
h_mean = (h_1 + h_2) / 2
|
||||
else:
|
||||
if h_2 <= h_1:
|
||||
delta_h_prime += 360
|
||||
else:
|
||||
delta_h_prime -= 360
|
||||
h_mean = (h_1 + h_2 + 360) / 2 if h_1 + h_2 < 360 else (h_1 + h_2 - 360) / 2
|
||||
|
||||
delta_h = 2 * sqrt(c_1 * c_2) * sin(delta_h_prime / 2)
|
||||
|
||||
t_x = (1 -
|
||||
0.17 * cos(h_mean - 30) +
|
||||
0.24 * cos(2 * h_mean) +
|
||||
0.32 * cos(3 * h_mean + 6) -
|
||||
0.20 * cos(4 * h_mean - 63))
|
||||
|
||||
s_l = 1 + (0.015 * (l_mean - 50) ** 2) / sqrt(20 + (l_mean - 50) ** 2)
|
||||
s_c = 1 + 0.045 * c_mean
|
||||
s_h = 1 + 0.015 * c_mean * t_x
|
||||
r_t = -2 * g_x * sin(abs(60 * exp(-1 * abs((delta_h - 275) / 25) ** 2)))
|
||||
|
||||
delta_l = delta_l / (k_l * s_l)
|
||||
delta_c = delta_c / (k_c * s_c)
|
||||
delta_h = delta_h / (k_h * s_h)
|
||||
|
||||
return delta_l ** 2 + delta_c ** 2 + delta_h ** 2 + r_t * delta_c * delta_h
|
||||
|
||||
|
||||
COLOR_DISTANCE_ALGORITHMS = {'rgb': dist_rgb,
|
||||
'rgb-weighted': dist_rgb_weighted,
|
||||
'cie76': dist_cie76,
|
||||
'cie94': dist_cie94,
|
||||
'cie2000': dist_cie2000}
|
||||
0
blessed/color.py:Zone.Identifier
Normal file
0
blessed/color.py:Zone.Identifier
Normal file
17
blessed/color.pyi
Normal file
17
blessed/color.pyi
Normal file
@@ -0,0 +1,17 @@
|
||||
# std imports
|
||||
from typing import Dict, Tuple, Callable
|
||||
|
||||
_RGB = Tuple[int, int, int]
|
||||
|
||||
def rgb_to_xyz(red: int, green: int, blue: int) -> Tuple[float, float, float]: ...
|
||||
def xyz_to_lab(
|
||||
x_val: float, y_val: float, z_val: float
|
||||
) -> Tuple[float, float, float]: ...
|
||||
def rgb_to_lab(red: int, green: int, blue: int) -> Tuple[float, float, float]: ...
|
||||
def dist_rgb(rgb1: _RGB, rgb2: _RGB) -> float: ...
|
||||
def dist_rgb_weighted(rgb1: _RGB, rgb2: _RGB) -> float: ...
|
||||
def dist_cie76(rgb1: _RGB, rgb2: _RGB) -> float: ...
|
||||
def dist_cie94(rgb1: _RGB, rgb2: _RGB) -> float: ...
|
||||
def dist_cie2000(rgb1: _RGB, rgb2: _RGB) -> float: ...
|
||||
|
||||
COLOR_DISTANCE_ALGORITHMS: Dict[str, Callable[[_RGB, _RGB], float]]
|
||||
0
blessed/color.pyi:Zone.Identifier
Normal file
0
blessed/color.pyi:Zone.Identifier
Normal file
973
blessed/colorspace.py
Normal file
973
blessed/colorspace.py
Normal file
@@ -0,0 +1,973 @@
|
||||
"""
|
||||
Color reference data.
|
||||
|
||||
References,
|
||||
|
||||
- https://github.com/freedesktop/xorg-rgb/blob/master/rgb.txt
|
||||
- https://github.com/ThomasDickey/xterm-snapshots/blob/master/256colres.h
|
||||
- https://github.com/ThomasDickey/xterm-snapshots/blob/master/XTerm-col.ad
|
||||
- https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
- https://gist.github.com/XVilka/8346728
|
||||
- https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/
|
||||
- http://jdebp.uk/Softwares/nosh/guide/TerminalCapabilities.html
|
||||
"""
|
||||
|
||||
# std imports
|
||||
import collections
|
||||
|
||||
__all__ = (
|
||||
'CGA_COLORS',
|
||||
'RGBColor',
|
||||
'RGB_256TABLE',
|
||||
'X11_COLORNAMES_TO_RGB',
|
||||
)
|
||||
|
||||
CGA_COLORS = {'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'}
|
||||
|
||||
|
||||
class RGBColor(collections.namedtuple("RGBColor", ["red", "green", "blue"])):
|
||||
"""Named tuple for an RGB color definition."""
|
||||
|
||||
def __str__(self):
|
||||
return '#{0:02x}{1:02x}{2:02x}'.format(*self)
|
||||
|
||||
|
||||
#: X11 Color names to (XTerm-defined) RGB values from xorg-rgb/rgb.txt
|
||||
X11_COLORNAMES_TO_RGB = {
|
||||
'aliceblue': RGBColor(240, 248, 255),
|
||||
'antiquewhite': RGBColor(250, 235, 215),
|
||||
'antiquewhite1': RGBColor(255, 239, 219),
|
||||
'antiquewhite2': RGBColor(238, 223, 204),
|
||||
'antiquewhite3': RGBColor(205, 192, 176),
|
||||
'antiquewhite4': RGBColor(139, 131, 120),
|
||||
'aqua': RGBColor(0, 255, 255),
|
||||
'aquamarine': RGBColor(127, 255, 212),
|
||||
'aquamarine1': RGBColor(127, 255, 212),
|
||||
'aquamarine2': RGBColor(118, 238, 198),
|
||||
'aquamarine3': RGBColor(102, 205, 170),
|
||||
'aquamarine4': RGBColor(69, 139, 116),
|
||||
'azure': RGBColor(240, 255, 255),
|
||||
'azure1': RGBColor(240, 255, 255),
|
||||
'azure2': RGBColor(224, 238, 238),
|
||||
'azure3': RGBColor(193, 205, 205),
|
||||
'azure4': RGBColor(131, 139, 139),
|
||||
'beige': RGBColor(245, 245, 220),
|
||||
'bisque': RGBColor(255, 228, 196),
|
||||
'bisque1': RGBColor(255, 228, 196),
|
||||
'bisque2': RGBColor(238, 213, 183),
|
||||
'bisque3': RGBColor(205, 183, 158),
|
||||
'bisque4': RGBColor(139, 125, 107),
|
||||
'black': RGBColor(0, 0, 0),
|
||||
'blanchedalmond': RGBColor(255, 235, 205),
|
||||
'blue': RGBColor(0, 0, 255),
|
||||
'blue1': RGBColor(0, 0, 255),
|
||||
'blue2': RGBColor(0, 0, 238),
|
||||
'blue3': RGBColor(0, 0, 205),
|
||||
'blue4': RGBColor(0, 0, 139),
|
||||
'blueviolet': RGBColor(138, 43, 226),
|
||||
'brown': RGBColor(165, 42, 42),
|
||||
'brown1': RGBColor(255, 64, 64),
|
||||
'brown2': RGBColor(238, 59, 59),
|
||||
'brown3': RGBColor(205, 51, 51),
|
||||
'brown4': RGBColor(139, 35, 35),
|
||||
'burlywood': RGBColor(222, 184, 135),
|
||||
'burlywood1': RGBColor(255, 211, 155),
|
||||
'burlywood2': RGBColor(238, 197, 145),
|
||||
'burlywood3': RGBColor(205, 170, 125),
|
||||
'burlywood4': RGBColor(139, 115, 85),
|
||||
'cadetblue': RGBColor(95, 158, 160),
|
||||
'cadetblue1': RGBColor(152, 245, 255),
|
||||
'cadetblue2': RGBColor(142, 229, 238),
|
||||
'cadetblue3': RGBColor(122, 197, 205),
|
||||
'cadetblue4': RGBColor(83, 134, 139),
|
||||
'chartreuse': RGBColor(127, 255, 0),
|
||||
'chartreuse1': RGBColor(127, 255, 0),
|
||||
'chartreuse2': RGBColor(118, 238, 0),
|
||||
'chartreuse3': RGBColor(102, 205, 0),
|
||||
'chartreuse4': RGBColor(69, 139, 0),
|
||||
'chocolate': RGBColor(210, 105, 30),
|
||||
'chocolate1': RGBColor(255, 127, 36),
|
||||
'chocolate2': RGBColor(238, 118, 33),
|
||||
'chocolate3': RGBColor(205, 102, 29),
|
||||
'chocolate4': RGBColor(139, 69, 19),
|
||||
'coral': RGBColor(255, 127, 80),
|
||||
'coral1': RGBColor(255, 114, 86),
|
||||
'coral2': RGBColor(238, 106, 80),
|
||||
'coral3': RGBColor(205, 91, 69),
|
||||
'coral4': RGBColor(139, 62, 47),
|
||||
'cornflowerblue': RGBColor(100, 149, 237),
|
||||
'cornsilk': RGBColor(255, 248, 220),
|
||||
'cornsilk1': RGBColor(255, 248, 220),
|
||||
'cornsilk2': RGBColor(238, 232, 205),
|
||||
'cornsilk3': RGBColor(205, 200, 177),
|
||||
'cornsilk4': RGBColor(139, 136, 120),
|
||||
'crimson': RGBColor(220, 20, 60),
|
||||
'cyan': RGBColor(0, 255, 255),
|
||||
'cyan1': RGBColor(0, 255, 255),
|
||||
'cyan2': RGBColor(0, 238, 238),
|
||||
'cyan3': RGBColor(0, 205, 205),
|
||||
'cyan4': RGBColor(0, 139, 139),
|
||||
'darkblue': RGBColor(0, 0, 139),
|
||||
'darkcyan': RGBColor(0, 139, 139),
|
||||
'darkgoldenrod': RGBColor(184, 134, 11),
|
||||
'darkgoldenrod1': RGBColor(255, 185, 15),
|
||||
'darkgoldenrod2': RGBColor(238, 173, 14),
|
||||
'darkgoldenrod3': RGBColor(205, 149, 12),
|
||||
'darkgoldenrod4': RGBColor(139, 101, 8),
|
||||
'darkgray': RGBColor(169, 169, 169),
|
||||
'darkgreen': RGBColor(0, 100, 0),
|
||||
'darkgrey': RGBColor(169, 169, 169),
|
||||
'darkkhaki': RGBColor(189, 183, 107),
|
||||
'darkmagenta': RGBColor(139, 0, 139),
|
||||
'darkolivegreen': RGBColor(85, 107, 47),
|
||||
'darkolivegreen1': RGBColor(202, 255, 112),
|
||||
'darkolivegreen2': RGBColor(188, 238, 104),
|
||||
'darkolivegreen3': RGBColor(162, 205, 90),
|
||||
'darkolivegreen4': RGBColor(110, 139, 61),
|
||||
'darkorange': RGBColor(255, 140, 0),
|
||||
'darkorange1': RGBColor(255, 127, 0),
|
||||
'darkorange2': RGBColor(238, 118, 0),
|
||||
'darkorange3': RGBColor(205, 102, 0),
|
||||
'darkorange4': RGBColor(139, 69, 0),
|
||||
'darkorchid': RGBColor(153, 50, 204),
|
||||
'darkorchid1': RGBColor(191, 62, 255),
|
||||
'darkorchid2': RGBColor(178, 58, 238),
|
||||
'darkorchid3': RGBColor(154, 50, 205),
|
||||
'darkorchid4': RGBColor(104, 34, 139),
|
||||
'darkred': RGBColor(139, 0, 0),
|
||||
'darksalmon': RGBColor(233, 150, 122),
|
||||
'darkseagreen': RGBColor(143, 188, 143),
|
||||
'darkseagreen1': RGBColor(193, 255, 193),
|
||||
'darkseagreen2': RGBColor(180, 238, 180),
|
||||
'darkseagreen3': RGBColor(155, 205, 155),
|
||||
'darkseagreen4': RGBColor(105, 139, 105),
|
||||
'darkslateblue': RGBColor(72, 61, 139),
|
||||
'darkslategray': RGBColor(47, 79, 79),
|
||||
'darkslategray1': RGBColor(151, 255, 255),
|
||||
'darkslategray2': RGBColor(141, 238, 238),
|
||||
'darkslategray3': RGBColor(121, 205, 205),
|
||||
'darkslategray4': RGBColor(82, 139, 139),
|
||||
'darkslategrey': RGBColor(47, 79, 79),
|
||||
'darkturquoise': RGBColor(0, 206, 209),
|
||||
'darkviolet': RGBColor(148, 0, 211),
|
||||
'deeppink': RGBColor(255, 20, 147),
|
||||
'deeppink1': RGBColor(255, 20, 147),
|
||||
'deeppink2': RGBColor(238, 18, 137),
|
||||
'deeppink3': RGBColor(205, 16, 118),
|
||||
'deeppink4': RGBColor(139, 10, 80),
|
||||
'deepskyblue': RGBColor(0, 191, 255),
|
||||
'deepskyblue1': RGBColor(0, 191, 255),
|
||||
'deepskyblue2': RGBColor(0, 178, 238),
|
||||
'deepskyblue3': RGBColor(0, 154, 205),
|
||||
'deepskyblue4': RGBColor(0, 104, 139),
|
||||
'dimgray': RGBColor(105, 105, 105),
|
||||
'dimgrey': RGBColor(105, 105, 105),
|
||||
'dodgerblue': RGBColor(30, 144, 255),
|
||||
'dodgerblue1': RGBColor(30, 144, 255),
|
||||
'dodgerblue2': RGBColor(28, 134, 238),
|
||||
'dodgerblue3': RGBColor(24, 116, 205),
|
||||
'dodgerblue4': RGBColor(16, 78, 139),
|
||||
'firebrick': RGBColor(178, 34, 34),
|
||||
'firebrick1': RGBColor(255, 48, 48),
|
||||
'firebrick2': RGBColor(238, 44, 44),
|
||||
'firebrick3': RGBColor(205, 38, 38),
|
||||
'firebrick4': RGBColor(139, 26, 26),
|
||||
'floralwhite': RGBColor(255, 250, 240),
|
||||
'forestgreen': RGBColor(34, 139, 34),
|
||||
'fuchsia': RGBColor(255, 0, 255),
|
||||
'gainsboro': RGBColor(220, 220, 220),
|
||||
'ghostwhite': RGBColor(248, 248, 255),
|
||||
'gold': RGBColor(255, 215, 0),
|
||||
'gold1': RGBColor(255, 215, 0),
|
||||
'gold2': RGBColor(238, 201, 0),
|
||||
'gold3': RGBColor(205, 173, 0),
|
||||
'gold4': RGBColor(139, 117, 0),
|
||||
'goldenrod': RGBColor(218, 165, 32),
|
||||
'goldenrod1': RGBColor(255, 193, 37),
|
||||
'goldenrod2': RGBColor(238, 180, 34),
|
||||
'goldenrod3': RGBColor(205, 155, 29),
|
||||
'goldenrod4': RGBColor(139, 105, 20),
|
||||
'gray': RGBColor(190, 190, 190),
|
||||
'gray0': RGBColor(0, 0, 0),
|
||||
'gray1': RGBColor(3, 3, 3),
|
||||
'gray10': RGBColor(26, 26, 26),
|
||||
'gray100': RGBColor(255, 255, 255),
|
||||
'gray11': RGBColor(28, 28, 28),
|
||||
'gray12': RGBColor(31, 31, 31),
|
||||
'gray13': RGBColor(33, 33, 33),
|
||||
'gray14': RGBColor(36, 36, 36),
|
||||
'gray15': RGBColor(38, 38, 38),
|
||||
'gray16': RGBColor(41, 41, 41),
|
||||
'gray17': RGBColor(43, 43, 43),
|
||||
'gray18': RGBColor(46, 46, 46),
|
||||
'gray19': RGBColor(48, 48, 48),
|
||||
'gray2': RGBColor(5, 5, 5),
|
||||
'gray20': RGBColor(51, 51, 51),
|
||||
'gray21': RGBColor(54, 54, 54),
|
||||
'gray22': RGBColor(56, 56, 56),
|
||||
'gray23': RGBColor(59, 59, 59),
|
||||
'gray24': RGBColor(61, 61, 61),
|
||||
'gray25': RGBColor(64, 64, 64),
|
||||
'gray26': RGBColor(66, 66, 66),
|
||||
'gray27': RGBColor(69, 69, 69),
|
||||
'gray28': RGBColor(71, 71, 71),
|
||||
'gray29': RGBColor(74, 74, 74),
|
||||
'gray3': RGBColor(8, 8, 8),
|
||||
'gray30': RGBColor(77, 77, 77),
|
||||
'gray31': RGBColor(79, 79, 79),
|
||||
'gray32': RGBColor(82, 82, 82),
|
||||
'gray33': RGBColor(84, 84, 84),
|
||||
'gray34': RGBColor(87, 87, 87),
|
||||
'gray35': RGBColor(89, 89, 89),
|
||||
'gray36': RGBColor(92, 92, 92),
|
||||
'gray37': RGBColor(94, 94, 94),
|
||||
'gray38': RGBColor(97, 97, 97),
|
||||
'gray39': RGBColor(99, 99, 99),
|
||||
'gray4': RGBColor(10, 10, 10),
|
||||
'gray40': RGBColor(102, 102, 102),
|
||||
'gray41': RGBColor(105, 105, 105),
|
||||
'gray42': RGBColor(107, 107, 107),
|
||||
'gray43': RGBColor(110, 110, 110),
|
||||
'gray44': RGBColor(112, 112, 112),
|
||||
'gray45': RGBColor(115, 115, 115),
|
||||
'gray46': RGBColor(117, 117, 117),
|
||||
'gray47': RGBColor(120, 120, 120),
|
||||
'gray48': RGBColor(122, 122, 122),
|
||||
'gray49': RGBColor(125, 125, 125),
|
||||
'gray5': RGBColor(13, 13, 13),
|
||||
'gray50': RGBColor(127, 127, 127),
|
||||
'gray51': RGBColor(130, 130, 130),
|
||||
'gray52': RGBColor(133, 133, 133),
|
||||
'gray53': RGBColor(135, 135, 135),
|
||||
'gray54': RGBColor(138, 138, 138),
|
||||
'gray55': RGBColor(140, 140, 140),
|
||||
'gray56': RGBColor(143, 143, 143),
|
||||
'gray57': RGBColor(145, 145, 145),
|
||||
'gray58': RGBColor(148, 148, 148),
|
||||
'gray59': RGBColor(150, 150, 150),
|
||||
'gray6': RGBColor(15, 15, 15),
|
||||
'gray60': RGBColor(153, 153, 153),
|
||||
'gray61': RGBColor(156, 156, 156),
|
||||
'gray62': RGBColor(158, 158, 158),
|
||||
'gray63': RGBColor(161, 161, 161),
|
||||
'gray64': RGBColor(163, 163, 163),
|
||||
'gray65': RGBColor(166, 166, 166),
|
||||
'gray66': RGBColor(168, 168, 168),
|
||||
'gray67': RGBColor(171, 171, 171),
|
||||
'gray68': RGBColor(173, 173, 173),
|
||||
'gray69': RGBColor(176, 176, 176),
|
||||
'gray7': RGBColor(18, 18, 18),
|
||||
'gray70': RGBColor(179, 179, 179),
|
||||
'gray71': RGBColor(181, 181, 181),
|
||||
'gray72': RGBColor(184, 184, 184),
|
||||
'gray73': RGBColor(186, 186, 186),
|
||||
'gray74': RGBColor(189, 189, 189),
|
||||
'gray75': RGBColor(191, 191, 191),
|
||||
'gray76': RGBColor(194, 194, 194),
|
||||
'gray77': RGBColor(196, 196, 196),
|
||||
'gray78': RGBColor(199, 199, 199),
|
||||
'gray79': RGBColor(201, 201, 201),
|
||||
'gray8': RGBColor(20, 20, 20),
|
||||
'gray80': RGBColor(204, 204, 204),
|
||||
'gray81': RGBColor(207, 207, 207),
|
||||
'gray82': RGBColor(209, 209, 209),
|
||||
'gray83': RGBColor(212, 212, 212),
|
||||
'gray84': RGBColor(214, 214, 214),
|
||||
'gray85': RGBColor(217, 217, 217),
|
||||
'gray86': RGBColor(219, 219, 219),
|
||||
'gray87': RGBColor(222, 222, 222),
|
||||
'gray88': RGBColor(224, 224, 224),
|
||||
'gray89': RGBColor(227, 227, 227),
|
||||
'gray9': RGBColor(23, 23, 23),
|
||||
'gray90': RGBColor(229, 229, 229),
|
||||
'gray91': RGBColor(232, 232, 232),
|
||||
'gray92': RGBColor(235, 235, 235),
|
||||
'gray93': RGBColor(237, 237, 237),
|
||||
'gray94': RGBColor(240, 240, 240),
|
||||
'gray95': RGBColor(242, 242, 242),
|
||||
'gray96': RGBColor(245, 245, 245),
|
||||
'gray97': RGBColor(247, 247, 247),
|
||||
'gray98': RGBColor(250, 250, 250),
|
||||
'gray99': RGBColor(252, 252, 252),
|
||||
'green': RGBColor(0, 255, 0),
|
||||
'green1': RGBColor(0, 255, 0),
|
||||
'green2': RGBColor(0, 238, 0),
|
||||
'green3': RGBColor(0, 205, 0),
|
||||
'green4': RGBColor(0, 139, 0),
|
||||
'greenyellow': RGBColor(173, 255, 47),
|
||||
'grey': RGBColor(190, 190, 190),
|
||||
'grey0': RGBColor(0, 0, 0),
|
||||
'grey1': RGBColor(3, 3, 3),
|
||||
'grey10': RGBColor(26, 26, 26),
|
||||
'grey100': RGBColor(255, 255, 255),
|
||||
'grey11': RGBColor(28, 28, 28),
|
||||
'grey12': RGBColor(31, 31, 31),
|
||||
'grey13': RGBColor(33, 33, 33),
|
||||
'grey14': RGBColor(36, 36, 36),
|
||||
'grey15': RGBColor(38, 38, 38),
|
||||
'grey16': RGBColor(41, 41, 41),
|
||||
'grey17': RGBColor(43, 43, 43),
|
||||
'grey18': RGBColor(46, 46, 46),
|
||||
'grey19': RGBColor(48, 48, 48),
|
||||
'grey2': RGBColor(5, 5, 5),
|
||||
'grey20': RGBColor(51, 51, 51),
|
||||
'grey21': RGBColor(54, 54, 54),
|
||||
'grey22': RGBColor(56, 56, 56),
|
||||
'grey23': RGBColor(59, 59, 59),
|
||||
'grey24': RGBColor(61, 61, 61),
|
||||
'grey25': RGBColor(64, 64, 64),
|
||||
'grey26': RGBColor(66, 66, 66),
|
||||
'grey27': RGBColor(69, 69, 69),
|
||||
'grey28': RGBColor(71, 71, 71),
|
||||
'grey29': RGBColor(74, 74, 74),
|
||||
'grey3': RGBColor(8, 8, 8),
|
||||
'grey30': RGBColor(77, 77, 77),
|
||||
'grey31': RGBColor(79, 79, 79),
|
||||
'grey32': RGBColor(82, 82, 82),
|
||||
'grey33': RGBColor(84, 84, 84),
|
||||
'grey34': RGBColor(87, 87, 87),
|
||||
'grey35': RGBColor(89, 89, 89),
|
||||
'grey36': RGBColor(92, 92, 92),
|
||||
'grey37': RGBColor(94, 94, 94),
|
||||
'grey38': RGBColor(97, 97, 97),
|
||||
'grey39': RGBColor(99, 99, 99),
|
||||
'grey4': RGBColor(10, 10, 10),
|
||||
'grey40': RGBColor(102, 102, 102),
|
||||
'grey41': RGBColor(105, 105, 105),
|
||||
'grey42': RGBColor(107, 107, 107),
|
||||
'grey43': RGBColor(110, 110, 110),
|
||||
'grey44': RGBColor(112, 112, 112),
|
||||
'grey45': RGBColor(115, 115, 115),
|
||||
'grey46': RGBColor(117, 117, 117),
|
||||
'grey47': RGBColor(120, 120, 120),
|
||||
'grey48': RGBColor(122, 122, 122),
|
||||
'grey49': RGBColor(125, 125, 125),
|
||||
'grey5': RGBColor(13, 13, 13),
|
||||
'grey50': RGBColor(127, 127, 127),
|
||||
'grey51': RGBColor(130, 130, 130),
|
||||
'grey52': RGBColor(133, 133, 133),
|
||||
'grey53': RGBColor(135, 135, 135),
|
||||
'grey54': RGBColor(138, 138, 138),
|
||||
'grey55': RGBColor(140, 140, 140),
|
||||
'grey56': RGBColor(143, 143, 143),
|
||||
'grey57': RGBColor(145, 145, 145),
|
||||
'grey58': RGBColor(148, 148, 148),
|
||||
'grey59': RGBColor(150, 150, 150),
|
||||
'grey6': RGBColor(15, 15, 15),
|
||||
'grey60': RGBColor(153, 153, 153),
|
||||
'grey61': RGBColor(156, 156, 156),
|
||||
'grey62': RGBColor(158, 158, 158),
|
||||
'grey63': RGBColor(161, 161, 161),
|
||||
'grey64': RGBColor(163, 163, 163),
|
||||
'grey65': RGBColor(166, 166, 166),
|
||||
'grey66': RGBColor(168, 168, 168),
|
||||
'grey67': RGBColor(171, 171, 171),
|
||||
'grey68': RGBColor(173, 173, 173),
|
||||
'grey69': RGBColor(176, 176, 176),
|
||||
'grey7': RGBColor(18, 18, 18),
|
||||
'grey70': RGBColor(179, 179, 179),
|
||||
'grey71': RGBColor(181, 181, 181),
|
||||
'grey72': RGBColor(184, 184, 184),
|
||||
'grey73': RGBColor(186, 186, 186),
|
||||
'grey74': RGBColor(189, 189, 189),
|
||||
'grey75': RGBColor(191, 191, 191),
|
||||
'grey76': RGBColor(194, 194, 194),
|
||||
'grey77': RGBColor(196, 196, 196),
|
||||
'grey78': RGBColor(199, 199, 199),
|
||||
'grey79': RGBColor(201, 201, 201),
|
||||
'grey8': RGBColor(20, 20, 20),
|
||||
'grey80': RGBColor(204, 204, 204),
|
||||
'grey81': RGBColor(207, 207, 207),
|
||||
'grey82': RGBColor(209, 209, 209),
|
||||
'grey83': RGBColor(212, 212, 212),
|
||||
'grey84': RGBColor(214, 214, 214),
|
||||
'grey85': RGBColor(217, 217, 217),
|
||||
'grey86': RGBColor(219, 219, 219),
|
||||
'grey87': RGBColor(222, 222, 222),
|
||||
'grey88': RGBColor(224, 224, 224),
|
||||
'grey89': RGBColor(227, 227, 227),
|
||||
'grey9': RGBColor(23, 23, 23),
|
||||
'grey90': RGBColor(229, 229, 229),
|
||||
'grey91': RGBColor(232, 232, 232),
|
||||
'grey92': RGBColor(235, 235, 235),
|
||||
'grey93': RGBColor(237, 237, 237),
|
||||
'grey94': RGBColor(240, 240, 240),
|
||||
'grey95': RGBColor(242, 242, 242),
|
||||
'grey96': RGBColor(245, 245, 245),
|
||||
'grey97': RGBColor(247, 247, 247),
|
||||
'grey98': RGBColor(250, 250, 250),
|
||||
'grey99': RGBColor(252, 252, 252),
|
||||
'honeydew': RGBColor(240, 255, 240),
|
||||
'honeydew1': RGBColor(240, 255, 240),
|
||||
'honeydew2': RGBColor(224, 238, 224),
|
||||
'honeydew3': RGBColor(193, 205, 193),
|
||||
'honeydew4': RGBColor(131, 139, 131),
|
||||
'hotpink': RGBColor(255, 105, 180),
|
||||
'hotpink1': RGBColor(255, 110, 180),
|
||||
'hotpink2': RGBColor(238, 106, 167),
|
||||
'hotpink3': RGBColor(205, 96, 144),
|
||||
'hotpink4': RGBColor(139, 58, 98),
|
||||
'indianred': RGBColor(205, 92, 92),
|
||||
'indianred1': RGBColor(255, 106, 106),
|
||||
'indianred2': RGBColor(238, 99, 99),
|
||||
'indianred3': RGBColor(205, 85, 85),
|
||||
'indianred4': RGBColor(139, 58, 58),
|
||||
'indigo': RGBColor(75, 0, 130),
|
||||
'ivory': RGBColor(255, 255, 240),
|
||||
'ivory1': RGBColor(255, 255, 240),
|
||||
'ivory2': RGBColor(238, 238, 224),
|
||||
'ivory3': RGBColor(205, 205, 193),
|
||||
'ivory4': RGBColor(139, 139, 131),
|
||||
'khaki': RGBColor(240, 230, 140),
|
||||
'khaki1': RGBColor(255, 246, 143),
|
||||
'khaki2': RGBColor(238, 230, 133),
|
||||
'khaki3': RGBColor(205, 198, 115),
|
||||
'khaki4': RGBColor(139, 134, 78),
|
||||
'lavender': RGBColor(230, 230, 250),
|
||||
'lavenderblush': RGBColor(255, 240, 245),
|
||||
'lavenderblush1': RGBColor(255, 240, 245),
|
||||
'lavenderblush2': RGBColor(238, 224, 229),
|
||||
'lavenderblush3': RGBColor(205, 193, 197),
|
||||
'lavenderblush4': RGBColor(139, 131, 134),
|
||||
'lawngreen': RGBColor(124, 252, 0),
|
||||
'lemonchiffon': RGBColor(255, 250, 205),
|
||||
'lemonchiffon1': RGBColor(255, 250, 205),
|
||||
'lemonchiffon2': RGBColor(238, 233, 191),
|
||||
'lemonchiffon3': RGBColor(205, 201, 165),
|
||||
'lemonchiffon4': RGBColor(139, 137, 112),
|
||||
'lightblue': RGBColor(173, 216, 230),
|
||||
'lightblue1': RGBColor(191, 239, 255),
|
||||
'lightblue2': RGBColor(178, 223, 238),
|
||||
'lightblue3': RGBColor(154, 192, 205),
|
||||
'lightblue4': RGBColor(104, 131, 139),
|
||||
'lightcoral': RGBColor(240, 128, 128),
|
||||
'lightcyan': RGBColor(224, 255, 255),
|
||||
'lightcyan1': RGBColor(224, 255, 255),
|
||||
'lightcyan2': RGBColor(209, 238, 238),
|
||||
'lightcyan3': RGBColor(180, 205, 205),
|
||||
'lightcyan4': RGBColor(122, 139, 139),
|
||||
'lightgoldenrod': RGBColor(238, 221, 130),
|
||||
'lightgoldenrod1': RGBColor(255, 236, 139),
|
||||
'lightgoldenrod2': RGBColor(238, 220, 130),
|
||||
'lightgoldenrod3': RGBColor(205, 190, 112),
|
||||
'lightgoldenrod4': RGBColor(139, 129, 76),
|
||||
'lightgoldenrodyellow': RGBColor(250, 250, 210),
|
||||
'lightgray': RGBColor(211, 211, 211),
|
||||
'lightgreen': RGBColor(144, 238, 144),
|
||||
'lightgrey': RGBColor(211, 211, 211),
|
||||
'lightpink': RGBColor(255, 182, 193),
|
||||
'lightpink1': RGBColor(255, 174, 185),
|
||||
'lightpink2': RGBColor(238, 162, 173),
|
||||
'lightpink3': RGBColor(205, 140, 149),
|
||||
'lightpink4': RGBColor(139, 95, 101),
|
||||
'lightsalmon': RGBColor(255, 160, 122),
|
||||
'lightsalmon1': RGBColor(255, 160, 122),
|
||||
'lightsalmon2': RGBColor(238, 149, 114),
|
||||
'lightsalmon3': RGBColor(205, 129, 98),
|
||||
'lightsalmon4': RGBColor(139, 87, 66),
|
||||
'lightseagreen': RGBColor(32, 178, 170),
|
||||
'lightskyblue': RGBColor(135, 206, 250),
|
||||
'lightskyblue1': RGBColor(176, 226, 255),
|
||||
'lightskyblue2': RGBColor(164, 211, 238),
|
||||
'lightskyblue3': RGBColor(141, 182, 205),
|
||||
'lightskyblue4': RGBColor(96, 123, 139),
|
||||
'lightslateblue': RGBColor(132, 112, 255),
|
||||
'lightslategray': RGBColor(119, 136, 153),
|
||||
'lightslategrey': RGBColor(119, 136, 153),
|
||||
'lightsteelblue': RGBColor(176, 196, 222),
|
||||
'lightsteelblue1': RGBColor(202, 225, 255),
|
||||
'lightsteelblue2': RGBColor(188, 210, 238),
|
||||
'lightsteelblue3': RGBColor(162, 181, 205),
|
||||
'lightsteelblue4': RGBColor(110, 123, 139),
|
||||
'lightyellow': RGBColor(255, 255, 224),
|
||||
'lightyellow1': RGBColor(255, 255, 224),
|
||||
'lightyellow2': RGBColor(238, 238, 209),
|
||||
'lightyellow3': RGBColor(205, 205, 180),
|
||||
'lightyellow4': RGBColor(139, 139, 122),
|
||||
'lime': RGBColor(0, 255, 0),
|
||||
'limegreen': RGBColor(50, 205, 50),
|
||||
'linen': RGBColor(250, 240, 230),
|
||||
'magenta': RGBColor(255, 0, 255),
|
||||
'magenta1': RGBColor(255, 0, 255),
|
||||
'magenta2': RGBColor(238, 0, 238),
|
||||
'magenta3': RGBColor(205, 0, 205),
|
||||
'magenta4': RGBColor(139, 0, 139),
|
||||
'maroon': RGBColor(176, 48, 96),
|
||||
'maroon1': RGBColor(255, 52, 179),
|
||||
'maroon2': RGBColor(238, 48, 167),
|
||||
'maroon3': RGBColor(205, 41, 144),
|
||||
'maroon4': RGBColor(139, 28, 98),
|
||||
'mediumaquamarine': RGBColor(102, 205, 170),
|
||||
'mediumblue': RGBColor(0, 0, 205),
|
||||
'mediumorchid': RGBColor(186, 85, 211),
|
||||
'mediumorchid1': RGBColor(224, 102, 255),
|
||||
'mediumorchid2': RGBColor(209, 95, 238),
|
||||
'mediumorchid3': RGBColor(180, 82, 205),
|
||||
'mediumorchid4': RGBColor(122, 55, 139),
|
||||
'mediumpurple': RGBColor(147, 112, 219),
|
||||
'mediumpurple1': RGBColor(171, 130, 255),
|
||||
'mediumpurple2': RGBColor(159, 121, 238),
|
||||
'mediumpurple3': RGBColor(137, 104, 205),
|
||||
'mediumpurple4': RGBColor(93, 71, 139),
|
||||
'mediumseagreen': RGBColor(60, 179, 113),
|
||||
'mediumslateblue': RGBColor(123, 104, 238),
|
||||
'mediumspringgreen': RGBColor(0, 250, 154),
|
||||
'mediumturquoise': RGBColor(72, 209, 204),
|
||||
'mediumvioletred': RGBColor(199, 21, 133),
|
||||
'midnightblue': RGBColor(25, 25, 112),
|
||||
'mintcream': RGBColor(245, 255, 250),
|
||||
'mistyrose': RGBColor(255, 228, 225),
|
||||
'mistyrose1': RGBColor(255, 228, 225),
|
||||
'mistyrose2': RGBColor(238, 213, 210),
|
||||
'mistyrose3': RGBColor(205, 183, 181),
|
||||
'mistyrose4': RGBColor(139, 125, 123),
|
||||
'moccasin': RGBColor(255, 228, 181),
|
||||
'navajowhite': RGBColor(255, 222, 173),
|
||||
'navajowhite1': RGBColor(255, 222, 173),
|
||||
'navajowhite2': RGBColor(238, 207, 161),
|
||||
'navajowhite3': RGBColor(205, 179, 139),
|
||||
'navajowhite4': RGBColor(139, 121, 94),
|
||||
'navy': RGBColor(0, 0, 128),
|
||||
'navyblue': RGBColor(0, 0, 128),
|
||||
'oldlace': RGBColor(253, 245, 230),
|
||||
'olive': RGBColor(128, 128, 0),
|
||||
'olivedrab': RGBColor(107, 142, 35),
|
||||
'olivedrab1': RGBColor(192, 255, 62),
|
||||
'olivedrab2': RGBColor(179, 238, 58),
|
||||
'olivedrab3': RGBColor(154, 205, 50),
|
||||
'olivedrab4': RGBColor(105, 139, 34),
|
||||
'orange': RGBColor(255, 165, 0),
|
||||
'orange1': RGBColor(255, 165, 0),
|
||||
'orange2': RGBColor(238, 154, 0),
|
||||
'orange3': RGBColor(205, 133, 0),
|
||||
'orange4': RGBColor(139, 90, 0),
|
||||
'orangered': RGBColor(255, 69, 0),
|
||||
'orangered1': RGBColor(255, 69, 0),
|
||||
'orangered2': RGBColor(238, 64, 0),
|
||||
'orangered3': RGBColor(205, 55, 0),
|
||||
'orangered4': RGBColor(139, 37, 0),
|
||||
'orchid': RGBColor(218, 112, 214),
|
||||
'orchid1': RGBColor(255, 131, 250),
|
||||
'orchid2': RGBColor(238, 122, 233),
|
||||
'orchid3': RGBColor(205, 105, 201),
|
||||
'orchid4': RGBColor(139, 71, 137),
|
||||
'palegoldenrod': RGBColor(238, 232, 170),
|
||||
'palegreen': RGBColor(152, 251, 152),
|
||||
'palegreen1': RGBColor(154, 255, 154),
|
||||
'palegreen2': RGBColor(144, 238, 144),
|
||||
'palegreen3': RGBColor(124, 205, 124),
|
||||
'palegreen4': RGBColor(84, 139, 84),
|
||||
'paleturquoise': RGBColor(175, 238, 238),
|
||||
'paleturquoise1': RGBColor(187, 255, 255),
|
||||
'paleturquoise2': RGBColor(174, 238, 238),
|
||||
'paleturquoise3': RGBColor(150, 205, 205),
|
||||
'paleturquoise4': RGBColor(102, 139, 139),
|
||||
'palevioletred': RGBColor(219, 112, 147),
|
||||
'palevioletred1': RGBColor(255, 130, 171),
|
||||
'palevioletred2': RGBColor(238, 121, 159),
|
||||
'palevioletred3': RGBColor(205, 104, 137),
|
||||
'palevioletred4': RGBColor(139, 71, 93),
|
||||
'papayawhip': RGBColor(255, 239, 213),
|
||||
'peachpuff': RGBColor(255, 218, 185),
|
||||
'peachpuff1': RGBColor(255, 218, 185),
|
||||
'peachpuff2': RGBColor(238, 203, 173),
|
||||
'peachpuff3': RGBColor(205, 175, 149),
|
||||
'peachpuff4': RGBColor(139, 119, 101),
|
||||
'peru': RGBColor(205, 133, 63),
|
||||
'pink': RGBColor(255, 192, 203),
|
||||
'pink1': RGBColor(255, 181, 197),
|
||||
'pink2': RGBColor(238, 169, 184),
|
||||
'pink3': RGBColor(205, 145, 158),
|
||||
'pink4': RGBColor(139, 99, 108),
|
||||
'plum': RGBColor(221, 160, 221),
|
||||
'plum1': RGBColor(255, 187, 255),
|
||||
'plum2': RGBColor(238, 174, 238),
|
||||
'plum3': RGBColor(205, 150, 205),
|
||||
'plum4': RGBColor(139, 102, 139),
|
||||
'powderblue': RGBColor(176, 224, 230),
|
||||
'purple': RGBColor(160, 32, 240),
|
||||
'purple1': RGBColor(155, 48, 255),
|
||||
'purple2': RGBColor(145, 44, 238),
|
||||
'purple3': RGBColor(125, 38, 205),
|
||||
'purple4': RGBColor(85, 26, 139),
|
||||
'rebeccapurple': RGBColor(102, 51, 153),
|
||||
'red': RGBColor(255, 0, 0),
|
||||
'red1': RGBColor(255, 0, 0),
|
||||
'red2': RGBColor(238, 0, 0),
|
||||
'red3': RGBColor(205, 0, 0),
|
||||
'red4': RGBColor(139, 0, 0),
|
||||
'rosybrown': RGBColor(188, 143, 143),
|
||||
'rosybrown1': RGBColor(255, 193, 193),
|
||||
'rosybrown2': RGBColor(238, 180, 180),
|
||||
'rosybrown3': RGBColor(205, 155, 155),
|
||||
'rosybrown4': RGBColor(139, 105, 105),
|
||||
'royalblue': RGBColor(65, 105, 225),
|
||||
'royalblue1': RGBColor(72, 118, 255),
|
||||
'royalblue2': RGBColor(67, 110, 238),
|
||||
'royalblue3': RGBColor(58, 95, 205),
|
||||
'royalblue4': RGBColor(39, 64, 139),
|
||||
'saddlebrown': RGBColor(139, 69, 19),
|
||||
'salmon': RGBColor(250, 128, 114),
|
||||
'salmon1': RGBColor(255, 140, 105),
|
||||
'salmon2': RGBColor(238, 130, 98),
|
||||
'salmon3': RGBColor(205, 112, 84),
|
||||
'salmon4': RGBColor(139, 76, 57),
|
||||
'sandybrown': RGBColor(244, 164, 96),
|
||||
'seagreen': RGBColor(46, 139, 87),
|
||||
'seagreen1': RGBColor(84, 255, 159),
|
||||
'seagreen2': RGBColor(78, 238, 148),
|
||||
'seagreen3': RGBColor(67, 205, 128),
|
||||
'seagreen4': RGBColor(46, 139, 87),
|
||||
'seashell': RGBColor(255, 245, 238),
|
||||
'seashell1': RGBColor(255, 245, 238),
|
||||
'seashell2': RGBColor(238, 229, 222),
|
||||
'seashell3': RGBColor(205, 197, 191),
|
||||
'seashell4': RGBColor(139, 134, 130),
|
||||
'sienna': RGBColor(160, 82, 45),
|
||||
'sienna1': RGBColor(255, 130, 71),
|
||||
'sienna2': RGBColor(238, 121, 66),
|
||||
'sienna3': RGBColor(205, 104, 57),
|
||||
'sienna4': RGBColor(139, 71, 38),
|
||||
'silver': RGBColor(192, 192, 192),
|
||||
'skyblue': RGBColor(135, 206, 235),
|
||||
'skyblue1': RGBColor(135, 206, 255),
|
||||
'skyblue2': RGBColor(126, 192, 238),
|
||||
'skyblue3': RGBColor(108, 166, 205),
|
||||
'skyblue4': RGBColor(74, 112, 139),
|
||||
'slateblue': RGBColor(106, 90, 205),
|
||||
'slateblue1': RGBColor(131, 111, 255),
|
||||
'slateblue2': RGBColor(122, 103, 238),
|
||||
'slateblue3': RGBColor(105, 89, 205),
|
||||
'slateblue4': RGBColor(71, 60, 139),
|
||||
'slategray': RGBColor(112, 128, 144),
|
||||
'slategray1': RGBColor(198, 226, 255),
|
||||
'slategray2': RGBColor(185, 211, 238),
|
||||
'slategray3': RGBColor(159, 182, 205),
|
||||
'slategray4': RGBColor(108, 123, 139),
|
||||
'slategrey': RGBColor(112, 128, 144),
|
||||
'snow': RGBColor(255, 250, 250),
|
||||
'snow1': RGBColor(255, 250, 250),
|
||||
'snow2': RGBColor(238, 233, 233),
|
||||
'snow3': RGBColor(205, 201, 201),
|
||||
'snow4': RGBColor(139, 137, 137),
|
||||
'springgreen': RGBColor(0, 255, 127),
|
||||
'springgreen1': RGBColor(0, 255, 127),
|
||||
'springgreen2': RGBColor(0, 238, 118),
|
||||
'springgreen3': RGBColor(0, 205, 102),
|
||||
'springgreen4': RGBColor(0, 139, 69),
|
||||
'steelblue': RGBColor(70, 130, 180),
|
||||
'steelblue1': RGBColor(99, 184, 255),
|
||||
'steelblue2': RGBColor(92, 172, 238),
|
||||
'steelblue3': RGBColor(79, 148, 205),
|
||||
'steelblue4': RGBColor(54, 100, 139),
|
||||
'tan': RGBColor(210, 180, 140),
|
||||
'tan1': RGBColor(255, 165, 79),
|
||||
'tan2': RGBColor(238, 154, 73),
|
||||
'tan3': RGBColor(205, 133, 63),
|
||||
'tan4': RGBColor(139, 90, 43),
|
||||
'teal': RGBColor(0, 128, 128),
|
||||
'thistle': RGBColor(216, 191, 216),
|
||||
'thistle1': RGBColor(255, 225, 255),
|
||||
'thistle2': RGBColor(238, 210, 238),
|
||||
'thistle3': RGBColor(205, 181, 205),
|
||||
'thistle4': RGBColor(139, 123, 139),
|
||||
'tomato': RGBColor(255, 99, 71),
|
||||
'tomato1': RGBColor(255, 99, 71),
|
||||
'tomato2': RGBColor(238, 92, 66),
|
||||
'tomato3': RGBColor(205, 79, 57),
|
||||
'tomato4': RGBColor(139, 54, 38),
|
||||
'turquoise': RGBColor(64, 224, 208),
|
||||
'turquoise1': RGBColor(0, 245, 255),
|
||||
'turquoise2': RGBColor(0, 229, 238),
|
||||
'turquoise3': RGBColor(0, 197, 205),
|
||||
'turquoise4': RGBColor(0, 134, 139),
|
||||
'violet': RGBColor(238, 130, 238),
|
||||
'violetred': RGBColor(208, 32, 144),
|
||||
'violetred1': RGBColor(255, 62, 150),
|
||||
'violetred2': RGBColor(238, 58, 140),
|
||||
'violetred3': RGBColor(205, 50, 120),
|
||||
'violetred4': RGBColor(139, 34, 82),
|
||||
'webgray': RGBColor(128, 128, 128),
|
||||
'webgreen': RGBColor(0, 128, 0),
|
||||
'webgrey': RGBColor(128, 128, 128),
|
||||
'webmaroon': RGBColor(128, 0, 0),
|
||||
'webpurple': RGBColor(128, 0, 128),
|
||||
'wheat': RGBColor(245, 222, 179),
|
||||
'wheat1': RGBColor(255, 231, 186),
|
||||
'wheat2': RGBColor(238, 216, 174),
|
||||
'wheat3': RGBColor(205, 186, 150),
|
||||
'wheat4': RGBColor(139, 126, 102),
|
||||
'white': RGBColor(255, 255, 255),
|
||||
'whitesmoke': RGBColor(245, 245, 245),
|
||||
'x11gray': RGBColor(190, 190, 190),
|
||||
'x11green': RGBColor(0, 255, 0),
|
||||
'x11grey': RGBColor(190, 190, 190),
|
||||
'x11maroon': RGBColor(176, 48, 96),
|
||||
'x11purple': RGBColor(160, 32, 240),
|
||||
'yellow': RGBColor(255, 255, 0),
|
||||
'yellow1': RGBColor(255, 255, 0),
|
||||
'yellow2': RGBColor(238, 238, 0),
|
||||
'yellow3': RGBColor(205, 205, 0),
|
||||
'yellow4': RGBColor(139, 139, 0),
|
||||
'yellowgreen': RGBColor(154, 205, 50)
|
||||
}
|
||||
|
||||
#: Curses color indices of 8, 16, and 256-color terminals
|
||||
RGB_256TABLE = (
|
||||
RGBColor(0, 0, 0),
|
||||
RGBColor(205, 0, 0),
|
||||
RGBColor(0, 205, 0),
|
||||
RGBColor(205, 205, 0),
|
||||
RGBColor(0, 0, 238),
|
||||
RGBColor(205, 0, 205),
|
||||
RGBColor(0, 205, 205),
|
||||
RGBColor(229, 229, 229),
|
||||
RGBColor(127, 127, 127),
|
||||
RGBColor(255, 0, 0),
|
||||
RGBColor(0, 255, 0),
|
||||
RGBColor(255, 255, 0),
|
||||
RGBColor(92, 92, 255),
|
||||
RGBColor(255, 0, 255),
|
||||
RGBColor(0, 255, 255),
|
||||
RGBColor(255, 255, 255),
|
||||
RGBColor(0, 0, 0),
|
||||
RGBColor(0, 0, 95),
|
||||
RGBColor(0, 0, 135),
|
||||
RGBColor(0, 0, 175),
|
||||
RGBColor(0, 0, 215),
|
||||
RGBColor(0, 0, 255),
|
||||
RGBColor(0, 95, 0),
|
||||
RGBColor(0, 95, 95),
|
||||
RGBColor(0, 95, 135),
|
||||
RGBColor(0, 95, 175),
|
||||
RGBColor(0, 95, 215),
|
||||
RGBColor(0, 95, 255),
|
||||
RGBColor(0, 135, 0),
|
||||
RGBColor(0, 135, 95),
|
||||
RGBColor(0, 135, 135),
|
||||
RGBColor(0, 135, 175),
|
||||
RGBColor(0, 135, 215),
|
||||
RGBColor(0, 135, 255),
|
||||
RGBColor(0, 175, 0),
|
||||
RGBColor(0, 175, 95),
|
||||
RGBColor(0, 175, 135),
|
||||
RGBColor(0, 175, 175),
|
||||
RGBColor(0, 175, 215),
|
||||
RGBColor(0, 175, 255),
|
||||
RGBColor(0, 215, 0),
|
||||
RGBColor(0, 215, 95),
|
||||
RGBColor(0, 215, 135),
|
||||
RGBColor(0, 215, 175),
|
||||
RGBColor(0, 215, 215),
|
||||
RGBColor(0, 215, 255),
|
||||
RGBColor(0, 255, 0),
|
||||
RGBColor(0, 255, 95),
|
||||
RGBColor(0, 255, 135),
|
||||
RGBColor(0, 255, 175),
|
||||
RGBColor(0, 255, 215),
|
||||
RGBColor(0, 255, 255),
|
||||
RGBColor(95, 0, 0),
|
||||
RGBColor(95, 0, 95),
|
||||
RGBColor(95, 0, 135),
|
||||
RGBColor(95, 0, 175),
|
||||
RGBColor(95, 0, 215),
|
||||
RGBColor(95, 0, 255),
|
||||
RGBColor(95, 95, 0),
|
||||
RGBColor(95, 95, 95),
|
||||
RGBColor(95, 95, 135),
|
||||
RGBColor(95, 95, 175),
|
||||
RGBColor(95, 95, 215),
|
||||
RGBColor(95, 95, 255),
|
||||
RGBColor(95, 135, 0),
|
||||
RGBColor(95, 135, 95),
|
||||
RGBColor(95, 135, 135),
|
||||
RGBColor(95, 135, 175),
|
||||
RGBColor(95, 135, 215),
|
||||
RGBColor(95, 135, 255),
|
||||
RGBColor(95, 175, 0),
|
||||
RGBColor(95, 175, 95),
|
||||
RGBColor(95, 175, 135),
|
||||
RGBColor(95, 175, 175),
|
||||
RGBColor(95, 175, 215),
|
||||
RGBColor(95, 175, 255),
|
||||
RGBColor(95, 215, 0),
|
||||
RGBColor(95, 215, 95),
|
||||
RGBColor(95, 215, 135),
|
||||
RGBColor(95, 215, 175),
|
||||
RGBColor(95, 215, 215),
|
||||
RGBColor(95, 215, 255),
|
||||
RGBColor(95, 255, 0),
|
||||
RGBColor(95, 255, 95),
|
||||
RGBColor(95, 255, 135),
|
||||
RGBColor(95, 255, 175),
|
||||
RGBColor(95, 255, 215),
|
||||
RGBColor(95, 255, 255),
|
||||
RGBColor(135, 0, 0),
|
||||
RGBColor(135, 0, 95),
|
||||
RGBColor(135, 0, 135),
|
||||
RGBColor(135, 0, 175),
|
||||
RGBColor(135, 0, 215),
|
||||
RGBColor(135, 0, 255),
|
||||
RGBColor(135, 95, 0),
|
||||
RGBColor(135, 95, 95),
|
||||
RGBColor(135, 95, 135),
|
||||
RGBColor(135, 95, 175),
|
||||
RGBColor(135, 95, 215),
|
||||
RGBColor(135, 95, 255),
|
||||
RGBColor(135, 135, 0),
|
||||
RGBColor(135, 135, 95),
|
||||
RGBColor(135, 135, 135),
|
||||
RGBColor(135, 135, 175),
|
||||
RGBColor(135, 135, 215),
|
||||
RGBColor(135, 135, 255),
|
||||
RGBColor(135, 175, 0),
|
||||
RGBColor(135, 175, 95),
|
||||
RGBColor(135, 175, 135),
|
||||
RGBColor(135, 175, 175),
|
||||
RGBColor(135, 175, 215),
|
||||
RGBColor(135, 175, 255),
|
||||
RGBColor(135, 215, 0),
|
||||
RGBColor(135, 215, 95),
|
||||
RGBColor(135, 215, 135),
|
||||
RGBColor(135, 215, 175),
|
||||
RGBColor(135, 215, 215),
|
||||
RGBColor(135, 215, 255),
|
||||
RGBColor(135, 255, 0),
|
||||
RGBColor(135, 255, 95),
|
||||
RGBColor(135, 255, 135),
|
||||
RGBColor(135, 255, 175),
|
||||
RGBColor(135, 255, 215),
|
||||
RGBColor(135, 255, 255),
|
||||
RGBColor(175, 0, 0),
|
||||
RGBColor(175, 0, 95),
|
||||
RGBColor(175, 0, 135),
|
||||
RGBColor(175, 0, 175),
|
||||
RGBColor(175, 0, 215),
|
||||
RGBColor(175, 0, 255),
|
||||
RGBColor(175, 95, 0),
|
||||
RGBColor(175, 95, 95),
|
||||
RGBColor(175, 95, 135),
|
||||
RGBColor(175, 95, 175),
|
||||
RGBColor(175, 95, 215),
|
||||
RGBColor(175, 95, 255),
|
||||
RGBColor(175, 135, 0),
|
||||
RGBColor(175, 135, 95),
|
||||
RGBColor(175, 135, 135),
|
||||
RGBColor(175, 135, 175),
|
||||
RGBColor(175, 135, 215),
|
||||
RGBColor(175, 135, 255),
|
||||
RGBColor(175, 175, 0),
|
||||
RGBColor(175, 175, 95),
|
||||
RGBColor(175, 175, 135),
|
||||
RGBColor(175, 175, 175),
|
||||
RGBColor(175, 175, 215),
|
||||
RGBColor(175, 175, 255),
|
||||
RGBColor(175, 215, 0),
|
||||
RGBColor(175, 215, 95),
|
||||
RGBColor(175, 215, 135),
|
||||
RGBColor(175, 215, 175),
|
||||
RGBColor(175, 215, 215),
|
||||
RGBColor(175, 215, 255),
|
||||
RGBColor(175, 255, 0),
|
||||
RGBColor(175, 255, 95),
|
||||
RGBColor(175, 255, 135),
|
||||
RGBColor(175, 255, 175),
|
||||
RGBColor(175, 255, 215),
|
||||
RGBColor(175, 255, 255),
|
||||
RGBColor(215, 0, 0),
|
||||
RGBColor(215, 0, 95),
|
||||
RGBColor(215, 0, 135),
|
||||
RGBColor(215, 0, 175),
|
||||
RGBColor(215, 0, 215),
|
||||
RGBColor(215, 0, 255),
|
||||
RGBColor(215, 95, 0),
|
||||
RGBColor(215, 95, 95),
|
||||
RGBColor(215, 95, 135),
|
||||
RGBColor(215, 95, 175),
|
||||
RGBColor(215, 95, 215),
|
||||
RGBColor(215, 95, 255),
|
||||
RGBColor(215, 135, 0),
|
||||
RGBColor(215, 135, 95),
|
||||
RGBColor(215, 135, 135),
|
||||
RGBColor(215, 135, 175),
|
||||
RGBColor(215, 135, 215),
|
||||
RGBColor(215, 135, 255),
|
||||
RGBColor(215, 175, 0),
|
||||
RGBColor(215, 175, 95),
|
||||
RGBColor(215, 175, 135),
|
||||
RGBColor(215, 175, 175),
|
||||
RGBColor(215, 175, 215),
|
||||
RGBColor(215, 175, 255),
|
||||
RGBColor(215, 215, 0),
|
||||
RGBColor(215, 215, 95),
|
||||
RGBColor(215, 215, 135),
|
||||
RGBColor(215, 215, 175),
|
||||
RGBColor(215, 215, 215),
|
||||
RGBColor(215, 215, 255),
|
||||
RGBColor(215, 255, 0),
|
||||
RGBColor(215, 255, 95),
|
||||
RGBColor(215, 255, 135),
|
||||
RGBColor(215, 255, 175),
|
||||
RGBColor(215, 255, 215),
|
||||
RGBColor(215, 255, 255),
|
||||
RGBColor(255, 0, 0),
|
||||
RGBColor(255, 0, 135),
|
||||
RGBColor(255, 0, 95),
|
||||
RGBColor(255, 0, 175),
|
||||
RGBColor(255, 0, 215),
|
||||
RGBColor(255, 0, 255),
|
||||
RGBColor(255, 95, 0),
|
||||
RGBColor(255, 95, 95),
|
||||
RGBColor(255, 95, 135),
|
||||
RGBColor(255, 95, 175),
|
||||
RGBColor(255, 95, 215),
|
||||
RGBColor(255, 95, 255),
|
||||
RGBColor(255, 135, 0),
|
||||
RGBColor(255, 135, 95),
|
||||
RGBColor(255, 135, 135),
|
||||
RGBColor(255, 135, 175),
|
||||
RGBColor(255, 135, 215),
|
||||
RGBColor(255, 135, 255),
|
||||
RGBColor(255, 175, 0),
|
||||
RGBColor(255, 175, 95),
|
||||
RGBColor(255, 175, 135),
|
||||
RGBColor(255, 175, 175),
|
||||
RGBColor(255, 175, 215),
|
||||
RGBColor(255, 175, 255),
|
||||
RGBColor(255, 215, 0),
|
||||
RGBColor(255, 215, 95),
|
||||
RGBColor(255, 215, 135),
|
||||
RGBColor(255, 215, 175),
|
||||
RGBColor(255, 215, 215),
|
||||
RGBColor(255, 215, 255),
|
||||
RGBColor(255, 255, 0),
|
||||
RGBColor(255, 255, 95),
|
||||
RGBColor(255, 255, 135),
|
||||
RGBColor(255, 255, 175),
|
||||
RGBColor(255, 255, 215),
|
||||
RGBColor(255, 255, 255),
|
||||
RGBColor(8, 8, 8),
|
||||
RGBColor(18, 18, 18),
|
||||
RGBColor(28, 28, 28),
|
||||
RGBColor(38, 38, 38),
|
||||
RGBColor(48, 48, 48),
|
||||
RGBColor(58, 58, 58),
|
||||
RGBColor(68, 68, 68),
|
||||
RGBColor(78, 78, 78),
|
||||
RGBColor(88, 88, 88),
|
||||
RGBColor(98, 98, 98),
|
||||
RGBColor(108, 108, 108),
|
||||
RGBColor(118, 118, 118),
|
||||
RGBColor(128, 128, 128),
|
||||
RGBColor(138, 138, 138),
|
||||
RGBColor(148, 148, 148),
|
||||
RGBColor(158, 158, 158),
|
||||
RGBColor(168, 168, 168),
|
||||
RGBColor(178, 178, 178),
|
||||
RGBColor(188, 188, 188),
|
||||
RGBColor(198, 198, 198),
|
||||
RGBColor(208, 208, 208),
|
||||
RGBColor(218, 218, 218),
|
||||
RGBColor(228, 228, 228),
|
||||
RGBColor(238, 238, 238),
|
||||
)
|
||||
0
blessed/colorspace.py:Zone.Identifier
Normal file
0
blessed/colorspace.py:Zone.Identifier
Normal file
12
blessed/colorspace.pyi
Normal file
12
blessed/colorspace.pyi
Normal file
@@ -0,0 +1,12 @@
|
||||
# std imports
|
||||
from typing import Set, Dict, Tuple, NamedTuple
|
||||
|
||||
CGA_COLORS: Set[str]
|
||||
|
||||
class RGBColor(NamedTuple):
|
||||
red: int
|
||||
green: int
|
||||
blue: int
|
||||
|
||||
X11_COLORNAMES_TO_RGB: Dict[str, RGBColor]
|
||||
RGB_256TABLE: Tuple[RGBColor, ...]
|
||||
0
blessed/colorspace.pyi:Zone.Identifier
Normal file
0
blessed/colorspace.pyi:Zone.Identifier
Normal file
496
blessed/formatters.py
Normal file
496
blessed/formatters.py
Normal file
@@ -0,0 +1,496 @@
|
||||
"""Sub-module providing sequence-formatting functions."""
|
||||
# std imports
|
||||
import platform
|
||||
|
||||
# 3rd party
|
||||
import six
|
||||
|
||||
# local
|
||||
from blessed.colorspace import CGA_COLORS, X11_COLORNAMES_TO_RGB
|
||||
|
||||
# isort: off
|
||||
# curses
|
||||
if platform.system() == 'Windows':
|
||||
import jinxed as curses # pylint: disable=import-error
|
||||
else:
|
||||
import curses
|
||||
|
||||
|
||||
def _make_colors():
|
||||
"""
|
||||
Return set of valid colors and their derivatives.
|
||||
|
||||
:rtype: set
|
||||
:returns: Color names with prefixes
|
||||
"""
|
||||
colors = set()
|
||||
# basic CGA foreground color, background, high intensity, and bold
|
||||
# background ('iCE colors' in my day).
|
||||
for cga_color in CGA_COLORS:
|
||||
colors.add(cga_color)
|
||||
colors.add('on_' + cga_color)
|
||||
colors.add('bright_' + cga_color)
|
||||
colors.add('on_bright_' + cga_color)
|
||||
|
||||
# foreground and background VGA color
|
||||
for vga_color in X11_COLORNAMES_TO_RGB:
|
||||
colors.add(vga_color)
|
||||
colors.add('on_' + vga_color)
|
||||
return colors
|
||||
|
||||
|
||||
#: Valid colors and their background (on), bright, and bright-background
|
||||
#: derivatives.
|
||||
COLORS = _make_colors()
|
||||
|
||||
#: Attributes that may be compounded with colors, by underscore, such as
|
||||
#: 'reverse_indigo'.
|
||||
COMPOUNDABLES = set('bold underline reverse blink italic standout'.split())
|
||||
|
||||
|
||||
class ParameterizingString(six.text_type):
|
||||
r"""
|
||||
A Unicode string which can be called as a parameterizing termcap.
|
||||
|
||||
For example::
|
||||
|
||||
>>> from blessed import Terminal
|
||||
>>> term = Terminal()
|
||||
>>> color = ParameterizingString(term.color, term.normal, 'color')
|
||||
>>> color(9)('color #9')
|
||||
u'\x1b[91mcolor #9\x1b(B\x1b[m'
|
||||
"""
|
||||
|
||||
def __new__(cls, cap, normal=u'', name=u'<not specified>'):
|
||||
# pylint: disable = missing-return-doc, missing-return-type-doc
|
||||
"""
|
||||
Class constructor accepting 3 positional arguments.
|
||||
|
||||
:arg str cap: parameterized string suitable for curses.tparm()
|
||||
:arg str normal: terminating sequence for this capability (optional).
|
||||
:arg str name: name of this terminal capability (optional).
|
||||
"""
|
||||
new = six.text_type.__new__(cls, cap)
|
||||
new._normal = normal
|
||||
new._name = name
|
||||
return new
|
||||
|
||||
def __call__(self, *args):
|
||||
"""
|
||||
Returning :class:`FormattingString` instance for given parameters.
|
||||
|
||||
Return evaluated terminal capability (self), receiving arguments
|
||||
``*args``, followed by the terminating sequence (self.normal) into
|
||||
a :class:`FormattingString` capable of being called.
|
||||
|
||||
:raises TypeError: Mismatch between capability and arguments
|
||||
:raises curses.error: :func:`curses.tparm` raised an exception
|
||||
:rtype: :class:`FormattingString` or :class:`NullCallableString`
|
||||
:returns: Callable string for given parameters
|
||||
"""
|
||||
try:
|
||||
# Re-encode the cap, because tparm() takes a bytestring in Python
|
||||
# 3. However, appear to be a plain Unicode string otherwise so
|
||||
# concats work.
|
||||
attr = curses.tparm(self.encode('latin1'), *args).decode('latin1')
|
||||
return FormattingString(attr, self._normal)
|
||||
except TypeError as err:
|
||||
# If the first non-int (i.e. incorrect) arg was a string, suggest
|
||||
# something intelligent:
|
||||
if args and isinstance(args[0], six.string_types):
|
||||
raise TypeError(
|
||||
"Unknown terminal capability, %r, or, TypeError "
|
||||
"for arguments %r: %s" % (self._name, args, err))
|
||||
# Somebody passed a non-string; I don't feel confident
|
||||
# guessing what they were trying to do.
|
||||
raise
|
||||
except curses.error as err:
|
||||
# ignore 'tparm() returned NULL', you won't get any styling,
|
||||
# even if does_styling is True. This happens on win32 platforms
|
||||
# with http://www.lfd.uci.edu/~gohlke/pythonlibs/#curses installed
|
||||
if "tparm() returned NULL" not in six.text_type(err):
|
||||
raise
|
||||
return NullCallableString()
|
||||
|
||||
|
||||
class ParameterizingProxyString(six.text_type):
|
||||
r"""
|
||||
A Unicode string which can be called to proxy missing termcap entries.
|
||||
|
||||
This class supports the function :func:`get_proxy_string`, and mirrors
|
||||
the behavior of :class:`ParameterizingString`, except that instead of
|
||||
a capability name, receives a format string, and callable to filter the
|
||||
given positional ``*args`` of :meth:`ParameterizingProxyString.__call__`
|
||||
into a terminal sequence.
|
||||
|
||||
For example::
|
||||
|
||||
>>> from blessed import Terminal
|
||||
>>> term = Terminal('screen')
|
||||
>>> hpa = ParameterizingString(term.hpa, term.normal, 'hpa')
|
||||
>>> hpa(9)
|
||||
u''
|
||||
>>> fmt = u'\x1b[{0}G'
|
||||
>>> fmt_arg = lambda *arg: (arg[0] + 1,)
|
||||
>>> hpa = ParameterizingProxyString((fmt, fmt_arg), term.normal, 'hpa')
|
||||
>>> hpa(9)
|
||||
u'\x1b[10G'
|
||||
"""
|
||||
|
||||
def __new__(cls, fmt_pair, normal=u'', name=u'<not specified>'):
|
||||
# pylint: disable = missing-return-doc, missing-return-type-doc
|
||||
"""
|
||||
Class constructor accepting 4 positional arguments.
|
||||
|
||||
:arg tuple fmt_pair: Two element tuple containing:
|
||||
- format string suitable for displaying terminal sequences
|
||||
- callable suitable for receiving __call__ arguments for formatting string
|
||||
:arg str normal: terminating sequence for this capability (optional).
|
||||
:arg str name: name of this terminal capability (optional).
|
||||
"""
|
||||
assert isinstance(fmt_pair, tuple), fmt_pair
|
||||
assert callable(fmt_pair[1]), fmt_pair[1]
|
||||
new = six.text_type.__new__(cls, fmt_pair[0])
|
||||
new._fmt_args = fmt_pair[1]
|
||||
new._normal = normal
|
||||
new._name = name
|
||||
return new
|
||||
|
||||
def __call__(self, *args):
|
||||
"""
|
||||
Returning :class:`FormattingString` instance for given parameters.
|
||||
|
||||
Arguments are determined by the capability. For example, ``hpa``
|
||||
(move_x) receives only a single integer, whereas ``cup`` (move)
|
||||
receives two integers. See documentation in terminfo(5) for the
|
||||
given capability.
|
||||
|
||||
:rtype: FormattingString
|
||||
:returns: Callable string for given parameters
|
||||
"""
|
||||
return FormattingString(self.format(*self._fmt_args(*args)),
|
||||
self._normal)
|
||||
|
||||
|
||||
class FormattingString(six.text_type):
|
||||
r"""
|
||||
A Unicode string which doubles as a callable.
|
||||
|
||||
This is used for terminal attributes, so that it may be used both
|
||||
directly, or as a callable. When used directly, it simply emits
|
||||
the given terminal sequence. When used as a callable, it wraps the
|
||||
given (string) argument with the 2nd argument used by the class
|
||||
constructor::
|
||||
|
||||
>>> from blessed import Terminal
|
||||
>>> term = Terminal()
|
||||
>>> style = FormattingString(term.bright_blue, term.normal)
|
||||
>>> print(repr(style))
|
||||
u'\x1b[94m'
|
||||
>>> style('Big Blue')
|
||||
u'\x1b[94mBig Blue\x1b(B\x1b[m'
|
||||
"""
|
||||
|
||||
def __new__(cls, sequence, normal=u''):
|
||||
# pylint: disable = missing-return-doc, missing-return-type-doc
|
||||
"""
|
||||
Class constructor accepting 2 positional arguments.
|
||||
|
||||
:arg str sequence: terminal attribute sequence.
|
||||
:arg str normal: terminating sequence for this attribute (optional).
|
||||
"""
|
||||
new = six.text_type.__new__(cls, sequence)
|
||||
new._normal = normal
|
||||
return new
|
||||
|
||||
def __call__(self, *args):
|
||||
"""
|
||||
Return ``text`` joined by ``sequence`` and ``normal``.
|
||||
|
||||
:raises TypeError: Not a string type
|
||||
:rtype: str
|
||||
:returns: Arguments wrapped in sequence and normal
|
||||
"""
|
||||
# Jim Allman brings us this convenience of allowing existing
|
||||
# unicode strings to be joined as a call parameter to a formatting
|
||||
# string result, allowing nestation:
|
||||
#
|
||||
# >>> t.red('This is ', t.bold('extremely'), ' dangerous!')
|
||||
for idx, ucs_part in enumerate(args):
|
||||
if not isinstance(ucs_part, six.string_types):
|
||||
expected_types = ', '.join(_type.__name__ for _type in six.string_types)
|
||||
raise TypeError(
|
||||
"TypeError for FormattingString argument, "
|
||||
"%r, at position %s: expected type %s, "
|
||||
"got %s" % (ucs_part, idx, expected_types,
|
||||
type(ucs_part).__name__))
|
||||
postfix = u''
|
||||
if self and self._normal:
|
||||
postfix = self._normal
|
||||
_refresh = self._normal + self
|
||||
args = [_refresh.join(ucs_part.split(self._normal))
|
||||
for ucs_part in args]
|
||||
|
||||
return self + u''.join(args) + postfix
|
||||
|
||||
|
||||
class FormattingOtherString(six.text_type):
|
||||
r"""
|
||||
A Unicode string which doubles as a callable for another sequence when called.
|
||||
|
||||
This is used for the :meth:`~.Terminal.move_up`, ``down``, ``left``, and ``right()``
|
||||
family of functions::
|
||||
|
||||
>>> from blessed import Terminal
|
||||
>>> term = Terminal()
|
||||
>>> move_right = FormattingOtherString(term.cuf1, term.cuf)
|
||||
>>> print(repr(move_right))
|
||||
u'\x1b[C'
|
||||
>>> print(repr(move_right(666)))
|
||||
u'\x1b[666C'
|
||||
>>> print(repr(move_right()))
|
||||
u'\x1b[C'
|
||||
"""
|
||||
|
||||
def __new__(cls, direct, target):
|
||||
# pylint: disable = missing-return-doc, missing-return-type-doc
|
||||
"""
|
||||
Class constructor accepting 2 positional arguments.
|
||||
|
||||
:arg str direct: capability name for direct formatting, eg ``('x' + term.right)``.
|
||||
:arg str target: capability name for callable, eg ``('x' + term.right(99))``.
|
||||
"""
|
||||
new = six.text_type.__new__(cls, direct)
|
||||
new._callable = target
|
||||
return new
|
||||
|
||||
def __getnewargs__(self):
|
||||
# return arguments used for the __new__ method upon unpickling.
|
||||
return six.text_type.__new__(six.text_type, self), self._callable
|
||||
|
||||
def __call__(self, *args):
|
||||
"""Return ``text`` by ``target``."""
|
||||
return self._callable(*args) if args else self
|
||||
|
||||
|
||||
class NullCallableString(six.text_type):
|
||||
"""
|
||||
A dummy callable Unicode alternative to :class:`FormattingString`.
|
||||
|
||||
This is used for colors on terminals that do not support colors, it is just a basic form of
|
||||
unicode that may also act as a callable.
|
||||
"""
|
||||
|
||||
def __new__(cls):
|
||||
"""Class constructor."""
|
||||
return six.text_type.__new__(cls, u'')
|
||||
|
||||
def __call__(self, *args):
|
||||
"""
|
||||
Allow empty string to be callable, returning given string, if any.
|
||||
|
||||
When called with an int as the first arg, return an empty Unicode. An
|
||||
int is a good hint that I am a :class:`ParameterizingString`, as there
|
||||
are only about half a dozen string-returning capabilities listed in
|
||||
terminfo(5) which accept non-int arguments, they are seldom used.
|
||||
|
||||
When called with a non-int as the first arg (no no args at all), return
|
||||
the first arg, acting in place of :class:`FormattingString` without
|
||||
any attributes.
|
||||
"""
|
||||
if not args or isinstance(args[0], int):
|
||||
# As a NullCallableString, even when provided with a parameter,
|
||||
# such as t.color(5), we must also still be callable, fe:
|
||||
#
|
||||
# >>> t.color(5)('shmoo')
|
||||
#
|
||||
# is actually simplified result of NullCallable()() on terminals
|
||||
# without color support, so turtles all the way down: we return
|
||||
# another instance.
|
||||
return NullCallableString()
|
||||
return u''.join(args)
|
||||
|
||||
|
||||
def get_proxy_string(term, attr):
|
||||
"""
|
||||
Proxy and return callable string for proxied attributes.
|
||||
|
||||
:arg Terminal term: :class:`~.Terminal` instance.
|
||||
:arg str attr: terminal capability name that may be proxied.
|
||||
:rtype: None or :class:`ParameterizingProxyString`.
|
||||
:returns: :class:`ParameterizingProxyString` for some attributes
|
||||
of some terminal types that support it, where the terminfo(5)
|
||||
database would otherwise come up empty, such as ``move_x``
|
||||
attribute for ``term.kind`` of ``screen``. Otherwise, None.
|
||||
"""
|
||||
# normalize 'screen-256color', or 'ansi.sys' to its basic names
|
||||
term_kind = next(iter(_kind for _kind in ('screen', 'ansi',)
|
||||
if term.kind.startswith(_kind)), term)
|
||||
_proxy_table = { # pragma: no cover
|
||||
'screen': {
|
||||
# proxy move_x/move_y for 'screen' terminal type, used by tmux(1).
|
||||
'hpa': ParameterizingProxyString(
|
||||
(u'\x1b[{0}G', lambda *arg: (arg[0] + 1,)), term.normal, attr),
|
||||
'vpa': ParameterizingProxyString(
|
||||
(u'\x1b[{0}d', lambda *arg: (arg[0] + 1,)), term.normal, attr),
|
||||
},
|
||||
'ansi': {
|
||||
# proxy show/hide cursor for 'ansi' terminal type. There is some
|
||||
# demand for a richly working ANSI terminal type for some reason.
|
||||
'civis': ParameterizingProxyString(
|
||||
(u'\x1b[?25l', lambda *arg: ()), term.normal, attr),
|
||||
'cnorm': ParameterizingProxyString(
|
||||
(u'\x1b[?25h', lambda *arg: ()), term.normal, attr),
|
||||
'hpa': ParameterizingProxyString(
|
||||
(u'\x1b[{0}G', lambda *arg: (arg[0] + 1,)), term.normal, attr),
|
||||
'vpa': ParameterizingProxyString(
|
||||
(u'\x1b[{0}d', lambda *arg: (arg[0] + 1,)), term.normal, attr),
|
||||
'sc': '\x1b[s',
|
||||
'rc': '\x1b[u',
|
||||
}
|
||||
}
|
||||
return _proxy_table.get(term_kind, {}).get(attr, None)
|
||||
|
||||
|
||||
def split_compound(compound):
|
||||
"""
|
||||
Split compound formating string into segments.
|
||||
|
||||
>>> split_compound('bold_underline_bright_blue_on_red')
|
||||
['bold', 'underline', 'bright_blue', 'on_red']
|
||||
|
||||
:arg str compound: a string that may contain compounds, separated by
|
||||
underline (``_``).
|
||||
:rtype: list
|
||||
:returns: List of formating string segments
|
||||
"""
|
||||
merged_segs = []
|
||||
# These occur only as prefixes, so they can always be merged:
|
||||
mergeable_prefixes = ['on', 'bright', 'on_bright']
|
||||
for segment in compound.split('_'):
|
||||
if merged_segs and merged_segs[-1] in mergeable_prefixes:
|
||||
merged_segs[-1] += '_' + segment
|
||||
else:
|
||||
merged_segs.append(segment)
|
||||
return merged_segs
|
||||
|
||||
|
||||
def resolve_capability(term, attr):
|
||||
"""
|
||||
Resolve a raw terminal capability using :func:`tigetstr`.
|
||||
|
||||
:arg Terminal term: :class:`~.Terminal` instance.
|
||||
:arg str attr: terminal capability name.
|
||||
:returns: string of the given terminal capability named by ``attr``,
|
||||
which may be empty (u'') if not found or not supported by the
|
||||
given :attr:`~.Terminal.kind`.
|
||||
:rtype: str
|
||||
"""
|
||||
if not term.does_styling:
|
||||
return u''
|
||||
val = curses.tigetstr(term._sugar.get(attr, attr)) # pylint: disable=protected-access
|
||||
# Decode sequences as latin1, as they are always 8-bit bytes, so when
|
||||
# b'\xff' is returned, this is decoded as u'\xff'.
|
||||
return u'' if val is None else val.decode('latin1')
|
||||
|
||||
|
||||
def resolve_color(term, color):
|
||||
"""
|
||||
Resolve a simple color name to a callable capability.
|
||||
|
||||
This function supports :func:`resolve_attribute`.
|
||||
|
||||
:arg Terminal term: :class:`~.Terminal` instance.
|
||||
:arg str color: any string found in set :const:`COLORS`.
|
||||
:returns: a string class instance which emits the terminal sequence
|
||||
for the given color, and may be used as a callable to wrap the
|
||||
given string with such sequence.
|
||||
:returns: :class:`NullCallableString` when
|
||||
:attr:`~.Terminal.number_of_colors` is 0,
|
||||
otherwise :class:`FormattingString`.
|
||||
:rtype: :class:`NullCallableString` or :class:`FormattingString`
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
if term.number_of_colors == 0:
|
||||
return NullCallableString()
|
||||
|
||||
# fg/bg capabilities terminals that support 0-256+ colors.
|
||||
vga_color_cap = (term._background_color if 'on_' in color else
|
||||
term._foreground_color)
|
||||
|
||||
base_color = color.rsplit('_', 1)[-1]
|
||||
if base_color in CGA_COLORS:
|
||||
# curses constants go up to only 7, so add an offset to get at the
|
||||
# bright colors at 8-15:
|
||||
offset = 8 if 'bright_' in color else 0
|
||||
base_color = color.rsplit('_', 1)[-1]
|
||||
attr = 'COLOR_%s' % (base_color.upper(),)
|
||||
fmt_attr = vga_color_cap(getattr(curses, attr) + offset)
|
||||
return FormattingString(fmt_attr, term.normal)
|
||||
|
||||
assert base_color in X11_COLORNAMES_TO_RGB, (
|
||||
'color not known', base_color)
|
||||
rgb = X11_COLORNAMES_TO_RGB[base_color]
|
||||
|
||||
# downconvert X11 colors to CGA, EGA, or VGA color spaces
|
||||
if term.number_of_colors <= 256:
|
||||
fmt_attr = vga_color_cap(term.rgb_downconvert(*rgb))
|
||||
return FormattingString(fmt_attr, term.normal)
|
||||
|
||||
# Modern 24-bit color terminals are written pretty basically. The
|
||||
# foreground and background sequences are:
|
||||
# - ^[38;2;<r>;<g>;<b>m
|
||||
# - ^[48;2;<r>;<g>;<b>m
|
||||
fgbg_seq = ('48' if 'on_' in color else '38')
|
||||
assert term.number_of_colors == 1 << 24
|
||||
fmt_attr = u'\x1b[' + fgbg_seq + ';2;{0};{1};{2}m'
|
||||
return FormattingString(fmt_attr.format(*rgb), term.normal)
|
||||
|
||||
|
||||
def resolve_attribute(term, attr):
|
||||
"""
|
||||
Resolve a terminal attribute name into a capability class.
|
||||
|
||||
:arg Terminal term: :class:`~.Terminal` instance.
|
||||
:arg str attr: Sugary, ordinary, or compound formatted terminal
|
||||
capability, such as "red_on_white", "normal", "red", or
|
||||
"bold_on_black".
|
||||
:returns: a string class instance which emits the terminal sequence
|
||||
for the given terminal capability, or may be used as a callable to
|
||||
wrap the given string with such sequence.
|
||||
:returns: :class:`NullCallableString` when
|
||||
:attr:`~.Terminal.number_of_colors` is 0,
|
||||
otherwise :class:`FormattingString`.
|
||||
:rtype: :class:`NullCallableString` or :class:`FormattingString`
|
||||
"""
|
||||
if attr in COLORS:
|
||||
return resolve_color(term, attr)
|
||||
|
||||
# A direct compoundable, such as `bold' or `on_red'.
|
||||
if attr in COMPOUNDABLES:
|
||||
sequence = resolve_capability(term, attr)
|
||||
return FormattingString(sequence, term.normal)
|
||||
|
||||
# Given `bold_on_red', resolve to ('bold', 'on_red'), RECURSIVE
|
||||
# call for each compounding section, joined and returned as
|
||||
# a completed completed FormattingString.
|
||||
formatters = split_compound(attr)
|
||||
if all((fmt in COLORS or fmt in COMPOUNDABLES) for fmt in formatters):
|
||||
resolution = (resolve_attribute(term, fmt) for fmt in formatters)
|
||||
return FormattingString(u''.join(resolution), term.normal)
|
||||
|
||||
# otherwise, this is our end-game: given a sequence such as 'csr'
|
||||
# (change scrolling region), return a ParameterizingString instance,
|
||||
# that when called, performs and returns the final string after curses
|
||||
# capability lookup is performed.
|
||||
tparm_capseq = resolve_capability(term, attr)
|
||||
if not tparm_capseq:
|
||||
# and, for special terminals, such as 'screen', provide a Proxy
|
||||
# ParameterizingString for attributes they do not claim to support,
|
||||
# but actually do! (such as 'hpa' and 'vpa').
|
||||
proxy = get_proxy_string(term,
|
||||
term._sugar.get(attr, attr)) # pylint: disable=protected-access
|
||||
if proxy is not None:
|
||||
return proxy
|
||||
|
||||
return ParameterizingString(tparm_capseq, term.normal, attr)
|
||||
0
blessed/formatters.py:Zone.Identifier
Normal file
0
blessed/formatters.py:Zone.Identifier
Normal file
70
blessed/formatters.pyi
Normal file
70
blessed/formatters.pyi
Normal file
@@ -0,0 +1,70 @@
|
||||
# std imports
|
||||
from typing import (Any,
|
||||
Set,
|
||||
List,
|
||||
Type,
|
||||
Tuple,
|
||||
Union,
|
||||
TypeVar,
|
||||
Callable,
|
||||
NoReturn,
|
||||
Optional,
|
||||
overload)
|
||||
|
||||
# local
|
||||
from .terminal import Terminal
|
||||
|
||||
COLORS: Set[str]
|
||||
COMPOUNDABLES: Set[str]
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class ParameterizingString(str):
|
||||
def __new__(cls: Type[_T], cap: str, normal: str = ..., name: str = ...) -> _T: ...
|
||||
@overload
|
||||
def __call__(
|
||||
self, *args: int
|
||||
) -> Union["FormattingString", "NullCallableString"]: ...
|
||||
@overload
|
||||
def __call__(self, *args: str) -> NoReturn: ...
|
||||
|
||||
class ParameterizingProxyString(str):
|
||||
def __new__(
|
||||
cls: Type[_T],
|
||||
fmt_pair: Tuple[str, Callable[..., Tuple[object, ...]]],
|
||||
normal: str = ...,
|
||||
name: str = ...,
|
||||
) -> _T: ...
|
||||
def __call__(self, *args: Any) -> "FormattingString": ...
|
||||
|
||||
class FormattingString(str):
|
||||
def __new__(cls: Type[_T], sequence: str, normal: str = ...) -> _T: ...
|
||||
@overload
|
||||
def __call__(self, *args: int) -> NoReturn: ...
|
||||
@overload
|
||||
def __call__(self, *args: str) -> str: ...
|
||||
|
||||
class FormattingOtherString(str):
|
||||
def __new__(
|
||||
cls: Type[_T], direct: ParameterizingString, target: ParameterizingString = ...
|
||||
) -> _T: ...
|
||||
def __call__(self, *args: Union[int, str]) -> str: ...
|
||||
|
||||
class NullCallableString(str):
|
||||
def __new__(cls: Type[_T]) -> _T: ...
|
||||
@overload
|
||||
def __call__(self, *args: int) -> "NullCallableString": ...
|
||||
@overload
|
||||
def __call__(self, *args: str) -> str: ...
|
||||
|
||||
def get_proxy_string(
|
||||
term: Terminal, attr: str
|
||||
) -> Optional[ParameterizingProxyString]: ...
|
||||
def split_compound(compound: str) -> List[str]: ...
|
||||
def resolve_capability(term: Terminal, attr: str) -> str: ...
|
||||
def resolve_color(
|
||||
term: Terminal, color: str
|
||||
) -> Union[NullCallableString, FormattingString]: ...
|
||||
def resolve_attribute(
|
||||
term: Terminal, attr: str
|
||||
) -> Union[ParameterizingString, FormattingString]: ...
|
||||
0
blessed/formatters.pyi:Zone.Identifier
Normal file
0
blessed/formatters.pyi:Zone.Identifier
Normal file
451
blessed/keyboard.py
Normal file
451
blessed/keyboard.py
Normal file
@@ -0,0 +1,451 @@
|
||||
"""Sub-module providing 'keyboard awareness'."""
|
||||
|
||||
# std imports
|
||||
import re
|
||||
import time
|
||||
import platform
|
||||
from collections import OrderedDict
|
||||
|
||||
# 3rd party
|
||||
import six
|
||||
|
||||
# isort: off
|
||||
# curses
|
||||
if platform.system() == 'Windows':
|
||||
# pylint: disable=import-error
|
||||
import jinxed as curses
|
||||
from jinxed.has_key import _capability_names as capability_names
|
||||
else:
|
||||
import curses
|
||||
from curses.has_key import _capability_names as capability_names
|
||||
|
||||
|
||||
class Keystroke(six.text_type):
|
||||
"""
|
||||
A unicode-derived class for describing a single keystroke.
|
||||
|
||||
A class instance describes a single keystroke received on input,
|
||||
which may contain multiple characters as a multibyte sequence,
|
||||
which is indicated by properties :attr:`is_sequence` returning
|
||||
``True``.
|
||||
|
||||
When the string is a known sequence, :attr:`code` matches terminal
|
||||
class attributes for comparison, such as ``term.KEY_LEFT``.
|
||||
|
||||
The string-name of the sequence, such as ``u'KEY_LEFT'`` is accessed
|
||||
by property :attr:`name`, and is used by the :meth:`__repr__` method
|
||||
to display a human-readable form of the Keystroke this class
|
||||
instance represents. It may otherwise by joined, split, or evaluated
|
||||
just as as any other unicode string.
|
||||
"""
|
||||
|
||||
def __new__(cls, ucs='', code=None, name=None):
|
||||
"""Class constructor."""
|
||||
new = six.text_type.__new__(cls, ucs)
|
||||
new._name = name
|
||||
new._code = code
|
||||
return new
|
||||
|
||||
@property
|
||||
def is_sequence(self):
|
||||
"""Whether the value represents a multibyte sequence (bool)."""
|
||||
return self._code is not None
|
||||
|
||||
def __repr__(self):
|
||||
"""Docstring overwritten."""
|
||||
return (six.text_type.__repr__(self) if self._name is None else
|
||||
self._name)
|
||||
__repr__.__doc__ = six.text_type.__doc__
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""String-name of key sequence, such as ``u'KEY_LEFT'`` (str)."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def code(self):
|
||||
"""Integer keycode value of multibyte sequence (int)."""
|
||||
return self._code
|
||||
|
||||
|
||||
def get_curses_keycodes():
|
||||
"""
|
||||
Return mapping of curses key-names paired by their keycode integer value.
|
||||
|
||||
:rtype: dict
|
||||
:returns: Dictionary of (name, code) pairs for curses keyboard constant
|
||||
values and their mnemonic name. Such as code ``260``, with the value of
|
||||
its key-name identity, ``u'KEY_LEFT'``.
|
||||
"""
|
||||
_keynames = [attr for attr in dir(curses)
|
||||
if attr.startswith('KEY_')]
|
||||
return {keyname: getattr(curses, keyname) for keyname in _keynames}
|
||||
|
||||
|
||||
def get_keyboard_codes():
|
||||
"""
|
||||
Return mapping of keycode integer values paired by their curses key-name.
|
||||
|
||||
:rtype: dict
|
||||
:returns: Dictionary of (code, name) pairs for curses keyboard constant
|
||||
values and their mnemonic name. Such as key ``260``, with the value of
|
||||
its identity, ``u'KEY_LEFT'``.
|
||||
|
||||
These keys are derived from the attributes by the same of the curses module,
|
||||
with the following exceptions:
|
||||
|
||||
* ``KEY_DELETE`` in place of ``KEY_DC``
|
||||
* ``KEY_INSERT`` in place of ``KEY_IC``
|
||||
* ``KEY_PGUP`` in place of ``KEY_PPAGE``
|
||||
* ``KEY_PGDOWN`` in place of ``KEY_NPAGE``
|
||||
* ``KEY_ESCAPE`` in place of ``KEY_EXIT``
|
||||
* ``KEY_SUP`` in place of ``KEY_SR``
|
||||
* ``KEY_SDOWN`` in place of ``KEY_SF``
|
||||
|
||||
This function is the inverse of :func:`get_curses_keycodes`. With the
|
||||
given override "mixins" listed above, the keycode for the delete key will
|
||||
map to our imaginary ``KEY_DELETE`` mnemonic, effectively erasing the
|
||||
phrase ``KEY_DC`` from our code vocabulary for anyone that wishes to use
|
||||
the return value to determine the key-name by keycode.
|
||||
"""
|
||||
keycodes = OrderedDict(get_curses_keycodes())
|
||||
keycodes.update(CURSES_KEYCODE_OVERRIDE_MIXIN)
|
||||
# merge _CURSES_KEYCODE_ADDINS added to our module space
|
||||
keycodes.update(
|
||||
(name, value) for name, value in globals().copy().items() if name.startswith('KEY_')
|
||||
)
|
||||
|
||||
# invert dictionary (key, values) => (values, key), preferring the
|
||||
# last-most inserted value ('KEY_DELETE' over 'KEY_DC').
|
||||
return dict(zip(keycodes.values(), keycodes.keys()))
|
||||
|
||||
|
||||
def _alternative_left_right(term):
|
||||
r"""
|
||||
Determine and return mapping of left and right arrow keys sequences.
|
||||
|
||||
:arg blessed.Terminal term: :class:`~.Terminal` instance.
|
||||
:rtype: dict
|
||||
:returns: Dictionary of sequences ``term._cuf1``, and ``term._cub1``,
|
||||
valued as ``KEY_RIGHT``, ``KEY_LEFT`` (when appropriate).
|
||||
|
||||
This function supports :func:`get_terminal_sequences` to discover
|
||||
the preferred input sequence for the left and right application keys.
|
||||
|
||||
It is necessary to check the value of these sequences to ensure we do not
|
||||
use ``u' '`` and ``u'\b'`` for ``KEY_RIGHT`` and ``KEY_LEFT``,
|
||||
preferring their true application key sequence, instead.
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
keymap = {}
|
||||
if term._cuf1 and term._cuf1 != u' ':
|
||||
keymap[term._cuf1] = curses.KEY_RIGHT
|
||||
if term._cub1 and term._cub1 != u'\b':
|
||||
keymap[term._cub1] = curses.KEY_LEFT
|
||||
return keymap
|
||||
|
||||
|
||||
def get_keyboard_sequences(term):
|
||||
r"""
|
||||
Return mapping of keyboard sequences paired by keycodes.
|
||||
|
||||
:arg blessed.Terminal term: :class:`~.Terminal` instance.
|
||||
:returns: mapping of keyboard unicode sequences paired by keycodes
|
||||
as integer. This is used as the argument ``mapper`` to
|
||||
the supporting function :func:`resolve_sequence`.
|
||||
:rtype: OrderedDict
|
||||
|
||||
Initialize and return a keyboard map and sequence lookup table,
|
||||
(sequence, keycode) from :class:`~.Terminal` instance ``term``,
|
||||
where ``sequence`` is a multibyte input sequence of unicode
|
||||
characters, such as ``u'\x1b[D'``, and ``keycode`` is an integer
|
||||
value, matching curses constant such as term.KEY_LEFT.
|
||||
|
||||
The return value is an OrderedDict instance, with their keys
|
||||
sorted longest-first.
|
||||
"""
|
||||
# A small gem from curses.has_key that makes this all possible,
|
||||
# _capability_names: a lookup table of terminal capability names for
|
||||
# keyboard sequences (fe. kcub1, key_left), keyed by the values of
|
||||
# constants found beginning with KEY_ in the main curses module
|
||||
# (such as KEY_LEFT).
|
||||
#
|
||||
# latin1 encoding is used so that bytes in 8-bit range of 127-255
|
||||
# have equivalent chr() and unichr() values, so that the sequence
|
||||
# of a kermit or avatar terminal, for example, remains unchanged
|
||||
# in its byte sequence values even when represented by unicode.
|
||||
#
|
||||
sequence_map = dict((
|
||||
(seq.decode('latin1'), val)
|
||||
for (seq, val) in (
|
||||
(curses.tigetstr(cap), val)
|
||||
for (val, cap) in capability_names.items()
|
||||
) if seq
|
||||
) if term.does_styling else ())
|
||||
|
||||
sequence_map.update(_alternative_left_right(term))
|
||||
sequence_map.update(DEFAULT_SEQUENCE_MIXIN)
|
||||
|
||||
# This is for fast lookup matching of sequences, preferring
|
||||
# full-length sequence such as ('\x1b[D', KEY_LEFT)
|
||||
# over simple sequences such as ('\x1b', KEY_EXIT).
|
||||
return OrderedDict((
|
||||
(seq, sequence_map[seq]) for seq in sorted(
|
||||
sequence_map.keys(), key=len, reverse=True)))
|
||||
|
||||
|
||||
def get_leading_prefixes(sequences):
|
||||
"""
|
||||
Return a set of proper prefixes for given sequence of strings.
|
||||
|
||||
:arg iterable sequences
|
||||
:rtype: set
|
||||
:return: Set of all string prefixes
|
||||
|
||||
Given an iterable of strings, all textparts leading up to the final
|
||||
string is returned as a unique set. This function supports the
|
||||
:meth:`~.Terminal.inkey` method by determining whether the given
|
||||
input is a sequence that **may** lead to a final matching pattern.
|
||||
|
||||
>>> prefixes(['abc', 'abdf', 'e', 'jkl'])
|
||||
set([u'a', u'ab', u'abd', u'j', u'jk'])
|
||||
"""
|
||||
return {seq[:i] for seq in sequences for i in range(1, len(seq))}
|
||||
|
||||
|
||||
def resolve_sequence(text, mapper, codes):
|
||||
r"""
|
||||
Return a single :class:`Keystroke` instance for given sequence ``text``.
|
||||
|
||||
:arg str text: string of characters received from terminal input stream.
|
||||
:arg OrderedDict mapper: unicode multibyte sequences, such as ``u'\x1b[D'``
|
||||
paired by their integer value (260)
|
||||
:arg dict codes: a :type:`dict` of integer values (such as 260) paired
|
||||
by their mnemonic name, such as ``'KEY_LEFT'``.
|
||||
:rtype: Keystroke
|
||||
:returns: Keystroke instance for the given sequence
|
||||
|
||||
The given ``text`` may extend beyond a matching sequence, such as
|
||||
``u\x1b[Dxxx`` returns a :class:`Keystroke` instance of attribute
|
||||
:attr:`Keystroke.sequence` valued only ``u\x1b[D``. It is up to
|
||||
calls to determine that ``xxx`` remains unresolved.
|
||||
"""
|
||||
for sequence, code in mapper.items():
|
||||
if text.startswith(sequence):
|
||||
return Keystroke(ucs=sequence, code=code, name=codes[code])
|
||||
return Keystroke(ucs=text and text[0] or u'')
|
||||
|
||||
|
||||
def _time_left(stime, timeout):
|
||||
"""
|
||||
Return time remaining since ``stime`` before given ``timeout``.
|
||||
|
||||
This function assists determining the value of ``timeout`` for
|
||||
class method :meth:`~.Terminal.kbhit` and similar functions.
|
||||
|
||||
:arg float stime: starting time for measurement
|
||||
:arg float timeout: timeout period, may be set to None to
|
||||
indicate no timeout (where None is always returned).
|
||||
:rtype: float or int
|
||||
:returns: time remaining as float. If no time is remaining,
|
||||
then the integer ``0`` is returned.
|
||||
"""
|
||||
return max(0, timeout - (time.time() - stime)) if timeout else timeout
|
||||
|
||||
|
||||
def _read_until(term, pattern, timeout):
|
||||
"""
|
||||
Convenience read-until-pattern function, supporting :meth:`~.get_location`.
|
||||
|
||||
:arg blessed.Terminal term: :class:`~.Terminal` instance.
|
||||
:arg float timeout: timeout period, may be set to None to indicate no
|
||||
timeout (where 0 is always returned).
|
||||
:arg str pattern: target regular expression pattern to seek.
|
||||
:rtype: tuple
|
||||
:returns: tuple in form of ``(match, str)``, *match*
|
||||
may be :class:`re.MatchObject` if pattern is discovered
|
||||
in input stream before timeout has elapsed, otherwise
|
||||
None. ``str`` is any remaining text received exclusive
|
||||
of the matching pattern).
|
||||
|
||||
The reason a tuple containing non-matching data is returned, is that the
|
||||
consumer should push such data back into the input buffer by
|
||||
:meth:`~.Terminal.ungetch` if any was received.
|
||||
|
||||
For example, when a user is performing rapid input keystrokes while its
|
||||
terminal emulator surreptitiously responds to this in-band sequence, we
|
||||
must ensure any such keyboard data is well-received by the next call to
|
||||
term.inkey() without delay.
|
||||
"""
|
||||
stime = time.time()
|
||||
match, buf = None, u''
|
||||
|
||||
# first, buffer all pending data. pexpect library provides a
|
||||
# 'searchwindowsize' attribute that limits this memory region. We're not
|
||||
# concerned about OOM conditions: only (human) keyboard input and terminal
|
||||
# response sequences are expected.
|
||||
|
||||
while True: # pragma: no branch
|
||||
# block as long as necessary to ensure at least one character is
|
||||
# received on input or remaining timeout has elapsed.
|
||||
ucs = term.inkey(timeout=_time_left(stime, timeout))
|
||||
# while the keyboard buffer is "hot" (has input), we continue to
|
||||
# aggregate all awaiting data. We do this to ensure slow I/O
|
||||
# calls do not unnecessarily give up within the first 'while' loop
|
||||
# for short timeout periods.
|
||||
while ucs:
|
||||
buf += ucs
|
||||
ucs = term.inkey(timeout=0)
|
||||
|
||||
match = re.search(pattern=pattern, string=buf)
|
||||
if match is not None:
|
||||
# match
|
||||
break
|
||||
|
||||
if timeout is not None and not _time_left(stime, timeout):
|
||||
# timeout
|
||||
break
|
||||
|
||||
return match, buf
|
||||
|
||||
|
||||
#: Though we may determine *keynames* and codes for keyboard input that
|
||||
#: generate multibyte sequences, it is also especially useful to aliases
|
||||
#: a few basic ASCII characters such as ``KEY_TAB`` instead of ``u'\t'`` for
|
||||
#: uniformity.
|
||||
#:
|
||||
#: Furthermore, many key-names for application keys enabled only by context
|
||||
#: manager :meth:`~.Terminal.keypad` are surprisingly absent. We inject them
|
||||
#: here directly into the curses module.
|
||||
_CURSES_KEYCODE_ADDINS = (
|
||||
'TAB',
|
||||
'KP_MULTIPLY',
|
||||
'KP_ADD',
|
||||
'KP_SEPARATOR',
|
||||
'KP_SUBTRACT',
|
||||
'KP_DECIMAL',
|
||||
'KP_DIVIDE',
|
||||
'KP_EQUAL',
|
||||
'KP_0',
|
||||
'KP_1',
|
||||
'KP_2',
|
||||
'KP_3',
|
||||
'KP_4',
|
||||
'KP_5',
|
||||
'KP_6',
|
||||
'KP_7',
|
||||
'KP_8',
|
||||
'KP_9')
|
||||
|
||||
_LASTVAL = max(get_curses_keycodes().values())
|
||||
for keycode_name in _CURSES_KEYCODE_ADDINS:
|
||||
_LASTVAL += 1
|
||||
globals()['KEY_' + keycode_name] = _LASTVAL
|
||||
|
||||
#: In a perfect world, terminal emulators would always send exactly what
|
||||
#: the terminfo(5) capability database plans for them, accordingly by the
|
||||
#: value of the ``TERM`` name they declare.
|
||||
#:
|
||||
#: But this isn't a perfect world. Many vt220-derived terminals, such as
|
||||
#: those declaring 'xterm', will continue to send vt220 codes instead of
|
||||
#: their native-declared codes, for backwards-compatibility.
|
||||
#:
|
||||
#: This goes for many: rxvt, putty, iTerm.
|
||||
#:
|
||||
#: These "mixins" are used for *all* terminals, regardless of their type.
|
||||
#:
|
||||
#: Furthermore, curses does not provide sequences sent by the keypad,
|
||||
#: at least, it does not provide a way to distinguish between keypad 0
|
||||
#: and numeric 0.
|
||||
DEFAULT_SEQUENCE_MIXIN = (
|
||||
# these common control characters (and 127, ctrl+'?') mapped to
|
||||
# an application key definition.
|
||||
(six.unichr(10), curses.KEY_ENTER),
|
||||
(six.unichr(13), curses.KEY_ENTER),
|
||||
(six.unichr(8), curses.KEY_BACKSPACE),
|
||||
(six.unichr(9), KEY_TAB), # noqa # pylint: disable=undefined-variable
|
||||
(six.unichr(27), curses.KEY_EXIT),
|
||||
(six.unichr(127), curses.KEY_BACKSPACE),
|
||||
|
||||
(u"\x1b[A", curses.KEY_UP),
|
||||
(u"\x1b[B", curses.KEY_DOWN),
|
||||
(u"\x1b[C", curses.KEY_RIGHT),
|
||||
(u"\x1b[D", curses.KEY_LEFT),
|
||||
(u"\x1b[1;2A", curses.KEY_SR),
|
||||
(u"\x1b[1;2B", curses.KEY_SF),
|
||||
(u"\x1b[1;2C", curses.KEY_SRIGHT),
|
||||
(u"\x1b[1;2D", curses.KEY_SLEFT),
|
||||
(u"\x1b[F", curses.KEY_END),
|
||||
(u"\x1b[H", curses.KEY_HOME),
|
||||
# not sure where these are from .. please report
|
||||
(u"\x1b[K", curses.KEY_END),
|
||||
(u"\x1b[U", curses.KEY_NPAGE),
|
||||
(u"\x1b[V", curses.KEY_PPAGE),
|
||||
|
||||
# keys sent after term.smkx (keypad_xmit) is emitted, source:
|
||||
# http://www.xfree86.org/current/ctlseqs.html#PC-Style%20Function%20Keys
|
||||
# http://fossies.org/linux/rxvt/doc/rxvtRef.html#KeyCodes
|
||||
#
|
||||
# keypad, numlock on
|
||||
(u"\x1bOM", curses.KEY_ENTER), # noqa return
|
||||
(u"\x1bOj", KEY_KP_MULTIPLY), # noqa * # pylint: disable=undefined-variable
|
||||
(u"\x1bOk", KEY_KP_ADD), # noqa + # pylint: disable=undefined-variable
|
||||
(u"\x1bOl", KEY_KP_SEPARATOR), # noqa , # pylint: disable=undefined-variable
|
||||
(u"\x1bOm", KEY_KP_SUBTRACT), # noqa - # pylint: disable=undefined-variable
|
||||
(u"\x1bOn", KEY_KP_DECIMAL), # noqa . # pylint: disable=undefined-variable
|
||||
(u"\x1bOo", KEY_KP_DIVIDE), # noqa / # pylint: disable=undefined-variable
|
||||
(u"\x1bOX", KEY_KP_EQUAL), # noqa = # pylint: disable=undefined-variable
|
||||
(u"\x1bOp", KEY_KP_0), # noqa 0 # pylint: disable=undefined-variable
|
||||
(u"\x1bOq", KEY_KP_1), # noqa 1 # pylint: disable=undefined-variable
|
||||
(u"\x1bOr", KEY_KP_2), # noqa 2 # pylint: disable=undefined-variable
|
||||
(u"\x1bOs", KEY_KP_3), # noqa 3 # pylint: disable=undefined-variable
|
||||
(u"\x1bOt", KEY_KP_4), # noqa 4 # pylint: disable=undefined-variable
|
||||
(u"\x1bOu", KEY_KP_5), # noqa 5 # pylint: disable=undefined-variable
|
||||
(u"\x1bOv", KEY_KP_6), # noqa 6 # pylint: disable=undefined-variable
|
||||
(u"\x1bOw", KEY_KP_7), # noqa 7 # pylint: disable=undefined-variable
|
||||
(u"\x1bOx", KEY_KP_8), # noqa 8 # pylint: disable=undefined-variable
|
||||
(u"\x1bOy", KEY_KP_9), # noqa 9 # pylint: disable=undefined-variable
|
||||
|
||||
# keypad, numlock off
|
||||
(u"\x1b[1~", curses.KEY_FIND), # find
|
||||
(u"\x1b[2~", curses.KEY_IC), # insert (0)
|
||||
(u"\x1b[3~", curses.KEY_DC), # delete (.), "Execute"
|
||||
(u"\x1b[4~", curses.KEY_SELECT), # select
|
||||
(u"\x1b[5~", curses.KEY_PPAGE), # pgup (9)
|
||||
(u"\x1b[6~", curses.KEY_NPAGE), # pgdown (3)
|
||||
(u"\x1b[7~", curses.KEY_HOME), # home
|
||||
(u"\x1b[8~", curses.KEY_END), # end
|
||||
(u"\x1b[OA", curses.KEY_UP), # up (8)
|
||||
(u"\x1b[OB", curses.KEY_DOWN), # down (2)
|
||||
(u"\x1b[OC", curses.KEY_RIGHT), # right (6)
|
||||
(u"\x1b[OD", curses.KEY_LEFT), # left (4)
|
||||
(u"\x1b[OF", curses.KEY_END), # end (1)
|
||||
(u"\x1b[OH", curses.KEY_HOME), # home (7)
|
||||
|
||||
# The vt220 placed F1-F4 above the keypad, in place of actual
|
||||
# F1-F4 were local functions (hold screen, print screen,
|
||||
# set up, data/talk, break).
|
||||
(u"\x1bOP", curses.KEY_F1),
|
||||
(u"\x1bOQ", curses.KEY_F2),
|
||||
(u"\x1bOR", curses.KEY_F3),
|
||||
(u"\x1bOS", curses.KEY_F4),
|
||||
)
|
||||
|
||||
#: Override mixins for a few curses constants with easier
|
||||
#: mnemonics: there may only be a 1:1 mapping when only a
|
||||
#: keycode (int) is given, where these phrases are preferred.
|
||||
CURSES_KEYCODE_OVERRIDE_MIXIN = (
|
||||
('KEY_DELETE', curses.KEY_DC),
|
||||
('KEY_INSERT', curses.KEY_IC),
|
||||
('KEY_PGUP', curses.KEY_PPAGE),
|
||||
('KEY_PGDOWN', curses.KEY_NPAGE),
|
||||
('KEY_ESCAPE', curses.KEY_EXIT),
|
||||
('KEY_SUP', curses.KEY_SR),
|
||||
('KEY_SDOWN', curses.KEY_SF),
|
||||
('KEY_UP_LEFT', curses.KEY_A1),
|
||||
('KEY_UP_RIGHT', curses.KEY_A3),
|
||||
('KEY_CENTER', curses.KEY_B2),
|
||||
('KEY_BEGIN', curses.KEY_BEG),
|
||||
)
|
||||
|
||||
__all__ = ('Keystroke', 'get_keyboard_codes', 'get_keyboard_sequences',)
|
||||
0
blessed/keyboard.py:Zone.Identifier
Normal file
0
blessed/keyboard.py:Zone.Identifier
Normal file
28
blessed/keyboard.pyi
Normal file
28
blessed/keyboard.pyi
Normal file
@@ -0,0 +1,28 @@
|
||||
# std imports
|
||||
from typing import Set, Dict, Type, Mapping, TypeVar, Iterable, Optional, OrderedDict
|
||||
|
||||
# local
|
||||
from .terminal import Terminal
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class Keystroke(str):
|
||||
def __new__(
|
||||
cls: Type[_T],
|
||||
ucs: str = ...,
|
||||
code: Optional[int] = ...,
|
||||
name: Optional[str] = ...,
|
||||
) -> _T: ...
|
||||
@property
|
||||
def is_sequence(self) -> bool: ...
|
||||
@property
|
||||
def name(self) -> Optional[str]: ...
|
||||
@property
|
||||
def code(self) -> Optional[int]: ...
|
||||
|
||||
def get_keyboard_codes() -> Dict[int, str]: ...
|
||||
def get_keyboard_sequences(term: Terminal) -> OrderedDict[str, int]: ...
|
||||
def get_leading_prefixes(sequences: Iterable[str]) -> Set[str]: ...
|
||||
def resolve_sequence(
|
||||
text: str, mapper: Mapping[str, int], codes: Mapping[int, str]
|
||||
) -> Keystroke: ...
|
||||
0
blessed/keyboard.pyi:Zone.Identifier
Normal file
0
blessed/keyboard.pyi:Zone.Identifier
Normal file
0
blessed/py.typed
Normal file
0
blessed/py.typed
Normal file
0
blessed/py.typed:Zone.Identifier
Normal file
0
blessed/py.typed:Zone.Identifier
Normal file
461
blessed/sequences.py
Normal file
461
blessed/sequences.py
Normal file
@@ -0,0 +1,461 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Module providing 'sequence awareness'."""
|
||||
# std imports
|
||||
import re
|
||||
import math
|
||||
import textwrap
|
||||
|
||||
# 3rd party
|
||||
import six
|
||||
from wcwidth import wcwidth
|
||||
|
||||
# local
|
||||
from blessed._capabilities import CAPABILITIES_CAUSE_MOVEMENT
|
||||
|
||||
__all__ = ('Sequence', 'SequenceTextWrapper', 'iter_parse', 'measure_length')
|
||||
|
||||
|
||||
class Termcap(object):
|
||||
"""Terminal capability of given variable name and pattern."""
|
||||
|
||||
def __init__(self, name, pattern, attribute):
|
||||
"""
|
||||
Class initializer.
|
||||
|
||||
:arg str name: name describing capability.
|
||||
:arg str pattern: regular expression string.
|
||||
:arg str attribute: :class:`~.Terminal` attribute used to build
|
||||
this terminal capability.
|
||||
"""
|
||||
self.name = name
|
||||
self.pattern = pattern
|
||||
self.attribute = attribute
|
||||
self._re_compiled = None
|
||||
|
||||
def __repr__(self):
|
||||
# pylint: disable=redundant-keyword-arg
|
||||
return '<Termcap {self.name}:{self.pattern!r}>'.format(self=self)
|
||||
|
||||
@property
|
||||
def named_pattern(self):
|
||||
"""Regular expression pattern for capability with named group."""
|
||||
# pylint: disable=redundant-keyword-arg
|
||||
return '(?P<{self.name}>{self.pattern})'.format(self=self)
|
||||
|
||||
@property
|
||||
def re_compiled(self):
|
||||
"""Compiled regular expression pattern for capability."""
|
||||
if self._re_compiled is None:
|
||||
self._re_compiled = re.compile(self.pattern)
|
||||
return self._re_compiled
|
||||
|
||||
@property
|
||||
def will_move(self):
|
||||
"""Whether capability causes cursor movement."""
|
||||
return self.name in CAPABILITIES_CAUSE_MOVEMENT
|
||||
|
||||
def horizontal_distance(self, text):
|
||||
"""
|
||||
Horizontal carriage adjusted by capability, may be negative.
|
||||
|
||||
:rtype: int
|
||||
:arg str text: for capabilities *parm_left_cursor*,
|
||||
*parm_right_cursor*, provide the matching sequence
|
||||
text, its interpreted distance is returned.
|
||||
|
||||
:returns: 0 except for matching '
|
||||
"""
|
||||
value = {
|
||||
'cursor_left': -1,
|
||||
'backspace': -1,
|
||||
'cursor_right': 1,
|
||||
'tab': 8,
|
||||
'ascii_tab': 8,
|
||||
}.get(self.name)
|
||||
if value is not None:
|
||||
return value
|
||||
|
||||
unit = {
|
||||
'parm_left_cursor': -1,
|
||||
'parm_right_cursor': 1
|
||||
}.get(self.name)
|
||||
if unit is not None:
|
||||
value = int(self.re_compiled.match(text).group(1))
|
||||
return unit * value
|
||||
|
||||
return 0
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
@classmethod
|
||||
def build(cls, name, capability, attribute, nparams=0,
|
||||
numeric=99, match_grouped=False, match_any=False,
|
||||
match_optional=False):
|
||||
r"""
|
||||
Class factory builder for given capability definition.
|
||||
|
||||
:arg str name: Variable name given for this pattern.
|
||||
:arg str capability: A unicode string representing a terminal
|
||||
capability to build for. When ``nparams`` is non-zero, it
|
||||
must be a callable unicode string (such as the result from
|
||||
``getattr(term, 'bold')``.
|
||||
:arg str attribute: The terminfo(5) capability name by which this
|
||||
pattern is known.
|
||||
:arg int nparams: number of positional arguments for callable.
|
||||
:arg int numeric: Value to substitute into capability to when generating pattern
|
||||
:arg bool match_grouped: If the numeric pattern should be
|
||||
grouped, ``(\d+)`` when ``True``, ``\d+`` default.
|
||||
:arg bool match_any: When keyword argument ``nparams`` is given,
|
||||
*any* numeric found in output is suitable for building as
|
||||
pattern ``(\d+)``. Otherwise, only the first matching value of
|
||||
range *(numeric - 1)* through *(numeric + 1)* will be replaced by
|
||||
pattern ``(\d+)`` in builder.
|
||||
:arg bool match_optional: When ``True``, building of numeric patterns
|
||||
containing ``(\d+)`` will be built as optional, ``(\d+)?``.
|
||||
:rtype: blessed.sequences.Termcap
|
||||
:returns: Terminal capability instance for given capability definition
|
||||
"""
|
||||
_numeric_regex = r'\d+'
|
||||
if match_grouped:
|
||||
_numeric_regex = r'(\d+)'
|
||||
if match_optional:
|
||||
_numeric_regex = r'(\d+)?'
|
||||
numeric = 99 if numeric is None else numeric
|
||||
|
||||
# basic capability attribute, not used as a callable
|
||||
if nparams == 0:
|
||||
return cls(name, re.escape(capability), attribute)
|
||||
|
||||
# a callable capability accepting numeric argument
|
||||
_outp = re.escape(capability(*(numeric,) * nparams))
|
||||
if not match_any:
|
||||
for num in range(numeric - 1, numeric + 2):
|
||||
if str(num) in _outp:
|
||||
pattern = _outp.replace(str(num), _numeric_regex)
|
||||
return cls(name, pattern, attribute)
|
||||
|
||||
if match_grouped:
|
||||
pattern = re.sub(r'(\d+)', lambda x: _numeric_regex, _outp)
|
||||
else:
|
||||
pattern = re.sub(r'\d+', lambda x: _numeric_regex, _outp)
|
||||
return cls(name, pattern, attribute)
|
||||
|
||||
|
||||
class SequenceTextWrapper(textwrap.TextWrapper):
|
||||
"""Docstring overridden."""
|
||||
|
||||
def __init__(self, width, term, **kwargs):
|
||||
"""
|
||||
Class initializer.
|
||||
|
||||
This class supports the :meth:`~.Terminal.wrap` method.
|
||||
"""
|
||||
self.term = term
|
||||
textwrap.TextWrapper.__init__(self, width, **kwargs)
|
||||
|
||||
def _wrap_chunks(self, chunks):
|
||||
"""
|
||||
Sequence-aware variant of :meth:`textwrap.TextWrapper._wrap_chunks`.
|
||||
|
||||
:raises ValueError: ``self.width`` is not a positive integer
|
||||
:rtype: list
|
||||
:returns: text chunks adjusted for width
|
||||
|
||||
This simply ensures that word boundaries are not broken mid-sequence, as standard python
|
||||
textwrap would incorrectly determine the length of a string containing sequences, and may
|
||||
also break consider sequences part of a "word" that may be broken by hyphen (``-``), where
|
||||
this implementation corrects both.
|
||||
"""
|
||||
lines = []
|
||||
if self.width <= 0 or not isinstance(self.width, int):
|
||||
raise ValueError(
|
||||
"invalid width {0!r}({1!r}) (must be integer > 0)"
|
||||
.format(self.width, type(self.width)))
|
||||
|
||||
term = self.term
|
||||
drop_whitespace = not hasattr(self, 'drop_whitespace'
|
||||
) or self.drop_whitespace
|
||||
chunks.reverse()
|
||||
while chunks:
|
||||
cur_line = []
|
||||
cur_len = 0
|
||||
indent = self.subsequent_indent if lines else self.initial_indent
|
||||
width = self.width - len(indent)
|
||||
if drop_whitespace and (
|
||||
Sequence(chunks[-1], term).strip() == '' and lines):
|
||||
del chunks[-1]
|
||||
while chunks:
|
||||
chunk_len = Sequence(chunks[-1], term).length()
|
||||
if cur_len + chunk_len > width:
|
||||
break
|
||||
cur_line.append(chunks.pop())
|
||||
cur_len += chunk_len
|
||||
if chunks and Sequence(chunks[-1], term).length() > width:
|
||||
self._handle_long_word(chunks, cur_line, cur_len, width)
|
||||
if drop_whitespace and (
|
||||
cur_line and Sequence(cur_line[-1], term).strip() == ''):
|
||||
del cur_line[-1]
|
||||
if cur_line:
|
||||
lines.append(indent + u''.join(cur_line))
|
||||
return lines
|
||||
|
||||
def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
|
||||
"""
|
||||
Sequence-aware :meth:`textwrap.TextWrapper._handle_long_word`.
|
||||
|
||||
This simply ensures that word boundaries are not broken mid-sequence, as standard python
|
||||
textwrap would incorrectly determine the length of a string containing sequences, and may
|
||||
also break consider sequences part of a "word" that may be broken by hyphen (``-``), where
|
||||
this implementation corrects both.
|
||||
"""
|
||||
# Figure out when indent is larger than the specified width, and make
|
||||
# sure at least one character is stripped off on every pass
|
||||
space_left = 1 if width < 1 else width - cur_len
|
||||
# If we're allowed to break long words, then do so: put as much
|
||||
# of the next chunk onto the current line as will fit.
|
||||
|
||||
if self.break_long_words:
|
||||
term = self.term
|
||||
chunk = reversed_chunks[-1]
|
||||
idx = nxt = 0
|
||||
for text, _ in iter_parse(term, chunk):
|
||||
nxt += len(text)
|
||||
if Sequence(chunk[:nxt], term).length() > space_left:
|
||||
break
|
||||
idx = nxt
|
||||
cur_line.append(chunk[:idx])
|
||||
reversed_chunks[-1] = chunk[idx:]
|
||||
|
||||
# Otherwise, we have to preserve the long word intact. Only add
|
||||
# it to the current line if there's nothing already there --
|
||||
# that minimizes how much we violate the width constraint.
|
||||
elif not cur_line:
|
||||
cur_line.append(reversed_chunks.pop())
|
||||
|
||||
# If we're not allowed to break long words, and there's already
|
||||
# text on the current line, do nothing. Next time through the
|
||||
# main loop of _wrap_chunks(), we'll wind up here again, but
|
||||
# cur_len will be zero, so the next line will be entirely
|
||||
# devoted to the long word that we can't handle right now.
|
||||
|
||||
|
||||
SequenceTextWrapper.__doc__ = textwrap.TextWrapper.__doc__
|
||||
|
||||
|
||||
class Sequence(six.text_type):
|
||||
"""
|
||||
A "sequence-aware" version of the base :class:`str` class.
|
||||
|
||||
This unicode-derived class understands the effect of escape sequences
|
||||
of printable length, allowing a properly implemented :meth:`rjust`,
|
||||
:meth:`ljust`, :meth:`center`, and :meth:`length`.
|
||||
"""
|
||||
|
||||
def __new__(cls, sequence_text, term):
|
||||
# pylint: disable = missing-return-doc, missing-return-type-doc
|
||||
"""
|
||||
Class constructor.
|
||||
|
||||
:arg str sequence_text: A string that may contain sequences.
|
||||
:arg blessed.Terminal term: :class:`~.Terminal` instance.
|
||||
"""
|
||||
new = six.text_type.__new__(cls, sequence_text)
|
||||
new._term = term
|
||||
return new
|
||||
|
||||
def ljust(self, width, fillchar=u' '):
|
||||
"""
|
||||
Return string containing sequences, left-adjusted.
|
||||
|
||||
:arg int width: Total width given to left-adjust ``text``. If
|
||||
unspecified, the width of the attached terminal is used (default).
|
||||
:arg str fillchar: String for padding right-of ``text``.
|
||||
:returns: String of ``text``, left-aligned by ``width``.
|
||||
:rtype: str
|
||||
"""
|
||||
rightside = fillchar * int(
|
||||
(max(0.0, float(width.__index__() - self.length()))) / float(len(fillchar)))
|
||||
return u''.join((self, rightside))
|
||||
|
||||
def rjust(self, width, fillchar=u' '):
|
||||
"""
|
||||
Return string containing sequences, right-adjusted.
|
||||
|
||||
:arg int width: Total width given to right-adjust ``text``. If
|
||||
unspecified, the width of the attached terminal is used (default).
|
||||
:arg str fillchar: String for padding left-of ``text``.
|
||||
:returns: String of ``text``, right-aligned by ``width``.
|
||||
:rtype: str
|
||||
"""
|
||||
leftside = fillchar * int(
|
||||
(max(0.0, float(width.__index__() - self.length()))) / float(len(fillchar)))
|
||||
return u''.join((leftside, self))
|
||||
|
||||
def center(self, width, fillchar=u' '):
|
||||
"""
|
||||
Return string containing sequences, centered.
|
||||
|
||||
:arg int width: Total width given to center ``text``. If
|
||||
unspecified, the width of the attached terminal is used (default).
|
||||
:arg str fillchar: String for padding left and right-of ``text``.
|
||||
:returns: String of ``text``, centered by ``width``.
|
||||
:rtype: str
|
||||
"""
|
||||
split = max(0.0, float(width.__index__()) - self.length()) / 2
|
||||
leftside = fillchar * int(
|
||||
(max(0.0, math.floor(split))) / float(len(fillchar)))
|
||||
rightside = fillchar * int(
|
||||
(max(0.0, math.ceil(split))) / float(len(fillchar)))
|
||||
return u''.join((leftside, self, rightside))
|
||||
|
||||
def truncate(self, width):
|
||||
"""
|
||||
Truncate a string in a sequence-aware manner.
|
||||
|
||||
Any printable characters beyond ``width`` are removed, while all
|
||||
sequences remain in place. Horizontal Sequences are first expanded
|
||||
by :meth:`padd`.
|
||||
|
||||
:arg int width: The printable width to truncate the string to.
|
||||
:rtype: str
|
||||
:returns: String truncated to at most ``width`` printable characters.
|
||||
"""
|
||||
output = ""
|
||||
current_width = 0
|
||||
target_width = width.__index__()
|
||||
parsed_seq = iter_parse(self._term, self.padd())
|
||||
|
||||
# Retain all text until non-cap width reaches desired width
|
||||
for text, cap in parsed_seq:
|
||||
if not cap:
|
||||
# use wcwidth clipped to 0 because it can sometimes return -1
|
||||
current_width += max(wcwidth(text), 0)
|
||||
if current_width > target_width:
|
||||
break
|
||||
output += text
|
||||
|
||||
# Return with remaining caps appended
|
||||
return output + ''.join(text for text, cap in parsed_seq if cap)
|
||||
|
||||
def length(self):
|
||||
r"""
|
||||
Return the printable length of string containing sequences.
|
||||
|
||||
Strings containing ``term.left`` or ``\b`` will cause "overstrike",
|
||||
but a length less than 0 is not ever returned. So ``_\b+`` is a
|
||||
length of 1 (displays as ``+``), but ``\b`` alone is simply a
|
||||
length of 0.
|
||||
|
||||
Some characters may consume more than one cell, mainly those CJK
|
||||
Unified Ideographs (Chinese, Japanese, Korean) defined by Unicode
|
||||
as half or full-width characters.
|
||||
|
||||
For example:
|
||||
|
||||
>>> from blessed import Terminal
|
||||
>>> from blessed.sequences import Sequence
|
||||
>>> term = Terminal()
|
||||
>>> msg = term.clear + term.red(u'コンニチハ')
|
||||
>>> Sequence(msg, term).length()
|
||||
10
|
||||
|
||||
.. note:: Although accounted for, strings containing sequences such
|
||||
as ``term.clear`` will not give accurate returns, it is not
|
||||
considered lengthy (a length of 0).
|
||||
"""
|
||||
# because control characters may return -1, "clip" their length to 0.
|
||||
return sum(max(wcwidth(w_char), 0) for w_char in self.padd(strip=True))
|
||||
|
||||
def strip(self, chars=None):
|
||||
"""
|
||||
Return string of sequences, leading and trailing whitespace removed.
|
||||
|
||||
:arg str chars: Remove characters in chars instead of whitespace.
|
||||
:rtype: str
|
||||
:returns: string of sequences with leading and trailing whitespace removed.
|
||||
"""
|
||||
return self.strip_seqs().strip(chars)
|
||||
|
||||
def lstrip(self, chars=None):
|
||||
"""
|
||||
Return string of all sequences and leading whitespace removed.
|
||||
|
||||
:arg str chars: Remove characters in chars instead of whitespace.
|
||||
:rtype: str
|
||||
:returns: string of sequences with leading removed.
|
||||
"""
|
||||
return self.strip_seqs().lstrip(chars)
|
||||
|
||||
def rstrip(self, chars=None):
|
||||
"""
|
||||
Return string of all sequences and trailing whitespace removed.
|
||||
|
||||
:arg str chars: Remove characters in chars instead of whitespace.
|
||||
:rtype: str
|
||||
:returns: string of sequences with trailing removed.
|
||||
"""
|
||||
return self.strip_seqs().rstrip(chars)
|
||||
|
||||
def strip_seqs(self):
|
||||
"""
|
||||
Return ``text`` stripped of only its terminal sequences.
|
||||
|
||||
:rtype: str
|
||||
:returns: Text with terminal sequences removed
|
||||
"""
|
||||
return self.padd(strip=True)
|
||||
|
||||
def padd(self, strip=False):
|
||||
"""
|
||||
Return non-destructive horizontal movement as destructive spacing.
|
||||
|
||||
:arg bool strip: Strip terminal sequences
|
||||
:rtype: str
|
||||
:returns: Text adjusted for horizontal movement
|
||||
"""
|
||||
outp = ''
|
||||
for text, cap in iter_parse(self._term, self):
|
||||
if not cap:
|
||||
outp += text
|
||||
continue
|
||||
|
||||
value = cap.horizontal_distance(text)
|
||||
if value > 0:
|
||||
outp += ' ' * value
|
||||
elif value < 0:
|
||||
outp = outp[:value]
|
||||
elif not strip:
|
||||
outp += text
|
||||
return outp
|
||||
|
||||
|
||||
def iter_parse(term, text):
|
||||
"""
|
||||
Generator yields (text, capability) for characters of ``text``.
|
||||
|
||||
value for ``capability`` may be ``None``, where ``text`` is
|
||||
:class:`str` of length 1. Otherwise, ``text`` is a full
|
||||
matching sequence of given capability.
|
||||
"""
|
||||
for match in term._caps_compiled_any.finditer(text): # pylint: disable=protected-access
|
||||
name = match.lastgroup
|
||||
value = match.group(name)
|
||||
if name == 'MISMATCH':
|
||||
yield (value, None)
|
||||
else:
|
||||
yield value, term.caps[name]
|
||||
|
||||
|
||||
def measure_length(text, term):
|
||||
"""
|
||||
.. deprecated:: 1.12.0.
|
||||
|
||||
:rtype: int
|
||||
:returns: Length of the first sequence in the string
|
||||
"""
|
||||
try:
|
||||
text, capability = next(iter_parse(term, text))
|
||||
if capability:
|
||||
return len(text)
|
||||
except StopIteration:
|
||||
return 0
|
||||
return 0
|
||||
0
blessed/sequences.py:Zone.Identifier
Normal file
0
blessed/sequences.py:Zone.Identifier
Normal file
55
blessed/sequences.pyi
Normal file
55
blessed/sequences.pyi
Normal file
@@ -0,0 +1,55 @@
|
||||
# std imports
|
||||
import textwrap
|
||||
from typing import Any, Type, Tuple, Pattern, TypeVar, Iterator, Optional, SupportsIndex
|
||||
|
||||
# local
|
||||
from .terminal import Terminal
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class Termcap:
|
||||
name: str = ...
|
||||
pattern: str = ...
|
||||
attribute: str = ...
|
||||
def __init__(self, name: str, pattern: str, attribute: str) -> None: ...
|
||||
@property
|
||||
def named_pattern(self) -> str: ...
|
||||
@property
|
||||
def re_compiled(self) -> Pattern[str]: ...
|
||||
@property
|
||||
def will_move(self) -> bool: ...
|
||||
def horizontal_distance(self, text: str) -> int: ...
|
||||
@classmethod
|
||||
def build(
|
||||
cls,
|
||||
name: str,
|
||||
capability: str,
|
||||
attribute: str,
|
||||
nparams: int = ...,
|
||||
numeric: int = ...,
|
||||
match_grouped: bool = ...,
|
||||
match_any: bool = ...,
|
||||
match_optional: bool = ...,
|
||||
) -> "Termcap": ...
|
||||
|
||||
class SequenceTextWrapper(textwrap.TextWrapper):
|
||||
term: Terminal = ...
|
||||
def __init__(self, width: int, term: Terminal, **kwargs: Any) -> None: ...
|
||||
|
||||
class Sequence(str):
|
||||
def __new__(cls: Type[_T], sequence_text: str, term: Terminal) -> _T: ...
|
||||
def ljust(self, width: SupportsIndex, fillchar: str = ...) -> str: ...
|
||||
def rjust(self, width: SupportsIndex, fillchar: str = ...) -> str: ...
|
||||
def center(self, width: SupportsIndex, fillchar: str = ...) -> str: ...
|
||||
def truncate(self, width: SupportsIndex) -> str: ...
|
||||
def length(self) -> int: ...
|
||||
def strip(self, chars: Optional[str] = ...) -> str: ...
|
||||
def lstrip(self, chars: Optional[str] = ...) -> str: ...
|
||||
def rstrip(self, chars: Optional[str] = ...) -> str: ...
|
||||
def strip_seqs(self) -> str: ...
|
||||
def padd(self, strip: bool = ...) -> str: ...
|
||||
|
||||
def iter_parse(
|
||||
term: Terminal, text: str
|
||||
) -> Iterator[Tuple[str, Optional[Termcap]]]: ...
|
||||
def measure_length(text: str, term: Terminal) -> int: ...
|
||||
0
blessed/sequences.pyi:Zone.Identifier
Normal file
0
blessed/sequences.pyi:Zone.Identifier
Normal file
1552
blessed/terminal.py
Normal file
1552
blessed/terminal.py
Normal file
File diff suppressed because it is too large
Load Diff
0
blessed/terminal.py:Zone.Identifier
Normal file
0
blessed/terminal.py:Zone.Identifier
Normal file
108
blessed/terminal.pyi
Normal file
108
blessed/terminal.pyi
Normal file
@@ -0,0 +1,108 @@
|
||||
# std imports
|
||||
from typing import IO, Any, List, Tuple, Union, Optional, OrderedDict, SupportsIndex, ContextManager
|
||||
|
||||
# local
|
||||
from .keyboard import Keystroke
|
||||
from .sequences import Termcap
|
||||
from .formatters import (FormattingString,
|
||||
NullCallableString,
|
||||
ParameterizingString,
|
||||
FormattingOtherString)
|
||||
|
||||
HAS_TTY: bool
|
||||
|
||||
class Terminal:
|
||||
caps: OrderedDict[str, Termcap]
|
||||
errors: List[str] = ...
|
||||
def __init__(
|
||||
self,
|
||||
kind: Optional[str] = ...,
|
||||
stream: Optional[IO[str]] = ...,
|
||||
force_styling: bool = ...,
|
||||
) -> None: ...
|
||||
def __getattr__(
|
||||
self, attr: str
|
||||
) -> Union[NullCallableString, ParameterizingString, FormattingString]: ...
|
||||
@property
|
||||
def kind(self) -> str: ...
|
||||
@property
|
||||
def does_styling(self) -> bool: ...
|
||||
@property
|
||||
def is_a_tty(self) -> bool: ...
|
||||
@property
|
||||
def height(self) -> int: ...
|
||||
@property
|
||||
def width(self) -> int: ...
|
||||
@property
|
||||
def pixel_height(self) -> int: ...
|
||||
@property
|
||||
def pixel_width(self) -> int: ...
|
||||
def location(
|
||||
self, x: Optional[int] = ..., y: Optional[int] = ...
|
||||
) -> ContextManager[None]: ...
|
||||
def get_location(self, timeout: Optional[float] = ...) -> Tuple[int, int]: ...
|
||||
def get_fgcolor(self, timeout: Optional[float] = ...) -> Tuple[int, int, int]: ...
|
||||
def get_bgcolor(self, timeout: Optional[float] = ...) -> Tuple[int, int, int]: ...
|
||||
def fullscreen(self) -> ContextManager[None]: ...
|
||||
def hidden_cursor(self) -> ContextManager[None]: ...
|
||||
def move_xy(self, x: int, y: int) -> ParameterizingString: ...
|
||||
def move_yx(self, y: int, x: int) -> ParameterizingString: ...
|
||||
@property
|
||||
def move_left(self) -> FormattingOtherString: ...
|
||||
@property
|
||||
def move_right(self) -> FormattingOtherString: ...
|
||||
@property
|
||||
def move_up(self) -> FormattingOtherString: ...
|
||||
@property
|
||||
def move_down(self) -> FormattingOtherString: ...
|
||||
@property
|
||||
def color(self) -> Union[NullCallableString, ParameterizingString]: ...
|
||||
def color_rgb(self, red: int, green: int, blue: int) -> FormattingString: ...
|
||||
@property
|
||||
def on_color(self) -> Union[NullCallableString, ParameterizingString]: ...
|
||||
def on_color_rgb(self, red: int, green: int, blue: int) -> FormattingString: ...
|
||||
def formatter(self, value: str) -> Union[NullCallableString, FormattingString]: ...
|
||||
def rgb_downconvert(self, red: int, green: int, blue: int) -> int: ...
|
||||
@property
|
||||
def normal(self) -> str: ...
|
||||
def link(self, url: str, text: str, url_id: str = ...) -> str: ...
|
||||
@property
|
||||
def stream(self) -> IO[str]: ...
|
||||
@property
|
||||
def number_of_colors(self) -> int: ...
|
||||
@number_of_colors.setter
|
||||
def number_of_colors(self, value: int) -> None: ...
|
||||
@property
|
||||
def color_distance_algorithm(self) -> str: ...
|
||||
@color_distance_algorithm.setter
|
||||
def color_distance_algorithm(self, value: str) -> None: ...
|
||||
def ljust(
|
||||
self, text: str, width: Optional[SupportsIndex] = ..., fillchar: str = ...
|
||||
) -> str: ...
|
||||
def rjust(
|
||||
self, text: str, width: Optional[SupportsIndex] = ..., fillchar: str = ...
|
||||
) -> str: ...
|
||||
def center(
|
||||
self, text: str, width: Optional[SupportsIndex] = ..., fillchar: str = ...
|
||||
) -> str: ...
|
||||
def truncate(self, text: str, width: Optional[SupportsIndex] = ...) -> str: ...
|
||||
def length(self, text: str) -> int: ...
|
||||
def strip(self, text: str, chars: Optional[str] = ...) -> str: ...
|
||||
def rstrip(self, text: str, chars: Optional[str] = ...) -> str: ...
|
||||
def lstrip(self, text: str, chars: Optional[str] = ...) -> str: ...
|
||||
def strip_seqs(self, text: str) -> str: ...
|
||||
def split_seqs(self, text: str, maxsplit: int) -> List[str]: ...
|
||||
def wrap(
|
||||
self, text: str, width: Optional[int] = ..., **kwargs: Any
|
||||
) -> List[str]: ...
|
||||
def getch(self) -> str: ...
|
||||
def ungetch(self, text: str) -> None: ...
|
||||
def kbhit(self, timeout: Optional[float] = ...) -> bool: ...
|
||||
def cbreak(self) -> ContextManager[None]: ...
|
||||
def raw(self) -> ContextManager[None]: ...
|
||||
def keypad(self) -> ContextManager[None]: ...
|
||||
def inkey(
|
||||
self, timeout: Optional[float] = ..., esc_delay: float = ...
|
||||
) -> Keystroke: ...
|
||||
|
||||
class WINSZ: ...
|
||||
0
blessed/terminal.pyi:Zone.Identifier
Normal file
0
blessed/terminal.pyi:Zone.Identifier
Normal file
163
blessed/win_terminal.py
Normal file
163
blessed/win_terminal.py
Normal file
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Module containing Windows version of :class:`Terminal`."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
# std imports
|
||||
import time
|
||||
import msvcrt # pylint: disable=import-error
|
||||
import contextlib
|
||||
|
||||
# 3rd party
|
||||
from jinxed import win32 # pylint: disable=import-error
|
||||
|
||||
# local
|
||||
from .terminal import WINSZ
|
||||
from .terminal import Terminal as _Terminal
|
||||
|
||||
|
||||
class Terminal(_Terminal):
|
||||
"""Windows subclass of :class:`Terminal`."""
|
||||
|
||||
def getch(self):
|
||||
r"""
|
||||
Read, decode, and return the next byte from the keyboard stream.
|
||||
|
||||
:rtype: unicode
|
||||
:returns: a single unicode character, or ``u''`` if a multi-byte
|
||||
sequence has not yet been fully received.
|
||||
|
||||
For versions of Windows 10.0.10586 and later, the console is expected
|
||||
to be in ENABLE_VIRTUAL_TERMINAL_INPUT mode and the default method is
|
||||
called.
|
||||
|
||||
For older versions of Windows, msvcrt.getwch() is used. If the received
|
||||
character is ``\x00`` or ``\xe0``, the next character is
|
||||
automatically retrieved.
|
||||
"""
|
||||
if win32.VTMODE_SUPPORTED:
|
||||
return super(Terminal, self).getch()
|
||||
|
||||
rtn = msvcrt.getwch()
|
||||
if rtn in ('\x00', '\xe0'):
|
||||
rtn += msvcrt.getwch()
|
||||
return rtn
|
||||
|
||||
def kbhit(self, timeout=None):
|
||||
"""
|
||||
Return whether a keypress has been detected on the keyboard.
|
||||
|
||||
This method is used by :meth:`inkey` to determine if a byte may
|
||||
be read using :meth:`getch` without blocking. This is implemented
|
||||
by wrapping msvcrt.kbhit() in a timeout.
|
||||
|
||||
:arg float timeout: When ``timeout`` is 0, this call is
|
||||
non-blocking, otherwise blocking indefinitely until keypress
|
||||
is detected when None (default). When ``timeout`` is a
|
||||
positive number, returns after ``timeout`` seconds have
|
||||
elapsed (float).
|
||||
:rtype: bool
|
||||
:returns: True if a keypress is awaiting to be read on the keyboard
|
||||
attached to this terminal.
|
||||
"""
|
||||
end = time.time() + (timeout or 0)
|
||||
while True:
|
||||
|
||||
if msvcrt.kbhit():
|
||||
return True
|
||||
|
||||
if timeout is not None and end < time.time():
|
||||
break
|
||||
|
||||
time.sleep(0.01) # Sleep to reduce CPU load
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _winsize(fd):
|
||||
"""
|
||||
Return named tuple describing size of the terminal by ``fd``.
|
||||
|
||||
:arg int fd: file descriptor queries for its window size.
|
||||
:rtype: WINSZ
|
||||
:returns: named tuple describing size of the terminal
|
||||
|
||||
WINSZ is a :class:`collections.namedtuple` instance, whose structure
|
||||
directly maps to the return value of the :const:`termios.TIOCGWINSZ`
|
||||
ioctl return value. The return parameters are:
|
||||
|
||||
- ``ws_row``: width of terminal by its number of character cells.
|
||||
- ``ws_col``: height of terminal by its number of character cells.
|
||||
- ``ws_xpixel``: width of terminal by pixels (not accurate).
|
||||
- ``ws_ypixel``: height of terminal by pixels (not accurate).
|
||||
"""
|
||||
window = win32.get_terminal_size(fd)
|
||||
return WINSZ(ws_row=window.lines, ws_col=window.columns,
|
||||
ws_xpixel=0, ws_ypixel=0)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def cbreak(self):
|
||||
"""
|
||||
Allow each keystroke to be read immediately after it is pressed.
|
||||
|
||||
This is a context manager for ``jinxed.w32.setcbreak()``.
|
||||
|
||||
.. note:: You must explicitly print any user input you would like
|
||||
displayed. If you provide any kind of editing, you must handle
|
||||
backspace and other line-editing control functions in this mode
|
||||
as well!
|
||||
|
||||
**Normally**, characters received from the keyboard cannot be read
|
||||
by Python until the *Return* key is pressed. Also known as *cooked* or
|
||||
*canonical input* mode, it allows the tty driver to provide
|
||||
line-editing before shuttling the input to your program and is the
|
||||
(implicit) default terminal mode set by most unix shells before
|
||||
executing programs.
|
||||
"""
|
||||
if self._keyboard_fd is not None:
|
||||
|
||||
filehandle = msvcrt.get_osfhandle(self._keyboard_fd)
|
||||
|
||||
# Save current terminal mode:
|
||||
save_mode = win32.get_console_mode(filehandle)
|
||||
save_line_buffered = self._line_buffered
|
||||
win32.setcbreak(filehandle)
|
||||
try:
|
||||
self._line_buffered = False
|
||||
yield
|
||||
finally:
|
||||
win32.set_console_mode(filehandle, save_mode)
|
||||
self._line_buffered = save_line_buffered
|
||||
|
||||
else:
|
||||
yield
|
||||
|
||||
@contextlib.contextmanager
|
||||
def raw(self):
|
||||
"""
|
||||
A context manager for ``jinxed.w32.setcbreak()``.
|
||||
|
||||
Although both :meth:`break` and :meth:`raw` modes allow each keystroke
|
||||
to be read immediately after it is pressed, Raw mode disables
|
||||
processing of input and output.
|
||||
|
||||
In cbreak mode, special input characters such as ``^C`` are
|
||||
interpreted by the terminal driver and excluded from the stdin stream.
|
||||
In raw mode these values are receive by the :meth:`inkey` method.
|
||||
"""
|
||||
if self._keyboard_fd is not None:
|
||||
|
||||
filehandle = msvcrt.get_osfhandle(self._keyboard_fd)
|
||||
|
||||
# Save current terminal mode:
|
||||
save_mode = win32.get_console_mode(filehandle)
|
||||
save_line_buffered = self._line_buffered
|
||||
win32.setraw(filehandle)
|
||||
try:
|
||||
self._line_buffered = False
|
||||
yield
|
||||
finally:
|
||||
win32.set_console_mode(filehandle, save_mode)
|
||||
self._line_buffered = save_line_buffered
|
||||
|
||||
else:
|
||||
yield
|
||||
0
blessed/win_terminal.py:Zone.Identifier
Normal file
0
blessed/win_terminal.py:Zone.Identifier
Normal file
11
blessed/win_terminal.pyi
Normal file
11
blessed/win_terminal.pyi
Normal file
@@ -0,0 +1,11 @@
|
||||
# std imports
|
||||
from typing import Optional, ContextManager
|
||||
|
||||
# local
|
||||
from .terminal import Terminal as _Terminal
|
||||
|
||||
class Terminal(_Terminal):
|
||||
def getch(self) -> str: ...
|
||||
def kbhit(self, timeout: Optional[float] = ...) -> bool: ...
|
||||
def cbreak(self) -> ContextManager[None]: ...
|
||||
def raw(self) -> ContextManager[None]: ...
|
||||
0
blessed/win_terminal.pyi:Zone.Identifier
Normal file
0
blessed/win_terminal.pyi:Zone.Identifier
Normal file
Reference in New Issue
Block a user