Skip to content

Instantly share code, notes, and snippets.

@jsbueno
Created October 22, 2017 14:27
Show Gist options
  • Save jsbueno/6577d5514f43f128af826148605f2a2a to your computer and use it in GitHub Desktop.
Save jsbueno/6577d5514f43f128af826148605f2a2a to your computer and use it in GitHub Desktop.
Terminal colors and gradients in Python
import io
import sys
ANSI_SEQ = "\x1b[{}m"
FG_RGB = "38;2;{};{};{}"
BG_RGB = "48;2;{};{};{}"
RESET="0"
FRAMED="51"
ATTRS = {key:code for key, code in [line.strip().split() for line in """\
bold 1
faint 2
italic 3
underline 4
blink 5
fast_blink 6
reverse 7
conceal 8
crossed 9
fraktur 20
double_underline 21
framed 51
encircled 52
overlined 54\
""".split("\n")]}
def printc(*args, **kw):
"""Extends print function to add color printing parameters in compatible terminals.
Use the optional bg= or fg= parameters to pass colors for foreground or background
respectively. Parameters should be a 3-sequence with RGB numbers from 0 to 255
as string or decimal.
Use the optional end_fg and end_bg parameters to create color gradients
from the starting color to the ending ones.
Other attributes can be accepted as "True" according the keys in ATTRS
"""
extra_options = {}
for argname, value in list(kw.items()):
if argname in ATTRS:
extra_options[argname] = kw.pop(argname)
fg_param = kw.pop("start_fg", kw.pop("fg", None))
bg_param = kw.pop("start_bg", kw.pop("bg", None))
start_fg = fg = tuple(int(comp) for comp in (fg_param if fg_param else (0, 0, 0)))
start_bg = bg = tuple(int(comp) for comp in (bg_param if bg_param else (0, 0, 0)))
end_fg = tuple(int(comp) for comp in kw.pop("end_fg", fg))
end_bg = tuple(int(comp) for comp in kw.pop("end_bg", bg))
original_file = kw.pop("file", sys.stdout)
original_flush = kw.pop("flush", False)
original_end = kw.pop("end", None)
text_io = io.StringIO()
print(*args, **kw, file=text_io, end="")
text = text_io.getvalue()
if not fg_param and not bg_param or not len(text):
return print(*args, **kw)
fg_gradient = start_fg != end_fg
bg_gradient = start_bg != end_bg
text_io = io.StringIO()
extra_attrs = ";".join(ATTRS[key] for key in extra_options)
if extra_attrs:
text_io.write(ANSI_SEQ.format(extra_attrs))
for i, character in enumerate(text):
ansi_str = ""
if start_fg and (i == 0 or fg_gradient):
ansi_str = FG_RGB.format(*fg)
if start_bg and (i == 0 or bg_gradient):
ansi_str += (";" if ansi_str else "") + BG_RGB.format(*bg)
if ansi_str:
text_io.write(ANSI_SEQ.format(ansi_str))
text_io.write(character)
fg = tuple(int(comp + i * (end_comp - comp) / len(text)) for comp, end_comp in zip(fg, end_fg))
bg = tuple(int(comp + i * (end_comp - comp) / len(text)) for comp, end_comp in zip(bg, end_bg))
text_io.write(ANSI_SEQ.format(RESET))
return print(text_io.getvalue(), end=original_end, flush=original_flush, file=original_file)
Copy link

ghost commented Aug 26, 2022

doesnt work :(

@jsbueno
Copy link
Author

jsbueno commented Aug 26, 2022

This needs a compatible and configured terminal - this includes a terminal with support for 16M colors, which does not come by default eihter on Windows or Mac OS. For Windows use the "terminal" app in the microsoft store, and for Mac OS the "iterm2" program. Anyway, after creating this, I authored the "terminedia" package which features a print function with similar capabilities and can take tokens on the form terminidia.print("[color: red][effects: blink] This text in blinking red")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment