如何在Python中将彩色文本输出到终端?


当前回答

这是我的现代(2021)解决方案:yachalk

它是少数正确支持嵌套样式的库之一:

除此之外,yachalk是自动完成友好的,具有256/真彩色支持,带有终端功能检测,并且是全类型的。

以下是您在选择解决方案时可能考虑的一些设计决策。

高级库与低级库/手动样式处理?

这个问题的许多答案都演示了如何直接使用ANSI转义代码,或者建议使用需要手动启用/禁用样式的低级库。

这些方法有一些微妙的问题:手动插入打开/关闭样式

语法上更详细,因为必须显式指定重置,更容易出错,因为您可能会意外忘记重置样式,无法正确处理边缘情况:例如,在某些终端中,需要在换行之前重置样式,并在换行后重新激活它们。此外,一些终端在简单地覆盖互斥样式方面存在问题,需要插入“不必要的”重置代码。如果开发者的本地终端没有这些怪癖,开发者不会立即发现这些怪癖。该问题将仅由其他人稍后报告,或导致问题,例如CI终端。

因此,如果要实现与许多终端的兼容性,最好使用提供自动处理样式重置的高级库。这允许库通过在需要时插入“伪”ANSI转义码来处理所有边缘情况。

为什么还有另一个图书馆?

在JavaScript中,任务的实际标准库是粉笔,在JS项目中使用了一段时间后,Python世界中可用的解决方案缺乏可比性。粉笔API不仅使用起来更方便(完全自动完成兼容),它还可以正确处理所有边缘情况。

yachalk的想法是为Python生态系统带来同样的便利。如果您对与其他库的比较感兴趣,我已经在项目页面上开始了功能比较。此外,这里有一长串(但仍然不完整)的备选方案,这些方案是在我的研究中提出的——有很多可供选择:)

有色的安西科色术语颜色科罗拉多州猪圈,猪圈祝福富有的彩色照相机彩色印刷品控制台颜色皮凡奇库勒尔牌手表样式(以前称为clr)皮白垩简单粉笔克洛克白垩质的(古希腊神话中的)古希腊神话

其他回答

class ColorText:
    """
    Use ANSI escape sequences to print colors +/- bold/underline to bash terminal.

    Examples
    --------
    >>> ColorText('HelloWorld').bold()
    >>> ColorText('HelloWorld').blue()
    >>> ColorText('HelloWorld').bold().custom("#bebebe")
    >>> ColorText('HelloWorld').underline().custom('dodgerblue')
    >>> ColorText.demo()

    Notes
    -----
    - execute ColorText.demo() for a printout of colors.
    """

    @classmethod
    def demo(cls):
        """Prints examples of all colors in normal, bold, underline, bold+underline."""
        for color in dir(ColorText):
            if all([color.startswith("_") is False,
                    color not in ["bold", "underline", "demo", "custom"],
                    callable(getattr(ColorText, color))]):
                print(getattr(ColorText(color), color)(),
                      "\t",
                      getattr(ColorText(f"bold {color}").bold(), color)(),
                      "\t",
                      getattr(ColorText(f"underline {color}").underline(), color)(),
                      "\t",
                      getattr(ColorText(f"bold underline {color}").underline().bold(), color)())
        print(ColorText("Input can also be color hex or R,G,B with ColorText.custom()").bold())
        pass

    def __init__(self, text: str = ""):
        self.text = text
        self.ending = "\033[0m"
        self.colors = []
        pass

    def __repr__(self):
        return self.text

    def __str__(self):
        return self.text

    def bold(self):
        self.text = "\033[1m" + self.text + self.ending
        return self

    def underline(self):
        self.text = "\033[4m" + self.text + self.ending
        return self

    def green(self):
        self.text = "\033[92m" + self.text + self.ending
        self.colors.append("green")
        return self

    def purple(self):
        self.text = "\033[95m" + self.text + self.ending
        self.colors.append("purple")
        return self

    def blue(self):
        self.text = "\033[94m" + self.text + self.ending
        self.colors.append("blue")
        return self

    def ltblue(self):
        self.text = "\033[34m" + self.text + self.ending
        self.colors.append("lightblue")
        return self

    def pink(self):
        self.text = "\033[35m" + self.text + self.ending
        self.colors.append("pink")
        return self

    def gray(self):
        self.text = "\033[30m" + self.text + self.ending
        self.colors.append("gray")
        return self

    def ltgray(self):
        self.text = "\033[37m" + self.text + self.ending
        self.colors.append("ltgray")
        return self

    def warn(self):
        self.text = "\033[93m" + self.text + self.ending
        self.colors.append("yellow")
        return self

    def fail(self):
        self.text = "\033[91m" + self.text + self.ending
        self.colors.append("red")
        return self

    def ltred(self):
        self.text = "\033[31m" + self.text + self.ending
        self.colors.append("lightred")
        return self

    def cyan(self):
        self.text = "\033[36m" + self.text + self.ending
        self.colors.append("cyan")
        return self

    def custom(self, *color_hex):
        """Print in custom color, `color_hex` - either actual hex, or tuple(r,g,b)"""
        if color_hex != (None, ):  # allows printing white on black background, black otherwise
            if len(color_hex) == 1:
                c = rgb2hex(colorConverter.to_rgb(color_hex[0]))
                rgb = ImageColor.getcolor(c, "RGB")
            else:
                assert (
                    len(color_hex) == 3
                ), "If not a color hex, ColorText.custom should have R,G,B as input"
                rgb = color_hex
            self.text = "\033[{};2;{};{};{}m".format(38, *rgb) + self.text + self.ending
            self.colors.append(rgb)
        return self

    pass

如果你正在编程一个游戏,也许你想改变背景颜色,只使用空格?例如:

print " "+ "\033[01;41m" + " " +"\033[01;46m"  + "  " + "\033[01;42m"

下面是一个诅咒示例:

import curses

def main(stdscr):
    stdscr.clear()
    if curses.has_colors():
        for i in xrange(1, curses.COLORS):
            curses.init_pair(i, i, curses.COLOR_BLACK)
            stdscr.addstr("COLOR %d! " % i, curses.color_pair(i))
            stdscr.addstr("BOLD! ", curses.color_pair(i) | curses.A_BOLD)
            stdscr.addstr("STANDOUT! ", curses.color_pair(i) | curses.A_STANDOUT)
            stdscr.addstr("UNDERLINE! ", curses.color_pair(i) | curses.A_UNDERLINE)
            stdscr.addstr("BLINK! ", curses.color_pair(i) | curses.A_BLINK)
            stdscr.addstr("DIM! ", curses.color_pair(i) | curses.A_DIM)
            stdscr.addstr("REVERSE! ", curses.color_pair(i) | curses.A_REVERSE)
    stdscr.refresh()
    stdscr.getch()

if __name__ == '__main__':
    print "init..."
    curses.wrapper(main)

定义开始颜色的字符串和结束颜色的字符串。然后打印文本,开始字符串在前面,结束字符串在结尾。

CRED = '\033[91m'
CEND = '\033[0m'
print(CRED + "Error, does not compute!" + CEND)

这将在Bash和urxvt中使用Zenburn风格的配色方案生成以下内容:

通过实验,我们可以获得更多的颜色:

注意:\33[5m和\33[6m闪烁。

这样我们可以创建一个完整的颜色集合:

CEND      = '\33[0m'
CBOLD     = '\33[1m'
CITALIC   = '\33[3m'
CURL      = '\33[4m'
CBLINK    = '\33[5m'
CBLINK2   = '\33[6m'
CSELECTED = '\33[7m'

CBLACK  = '\33[30m'
CRED    = '\33[31m'
CGREEN  = '\33[32m'
CYELLOW = '\33[33m'
CBLUE   = '\33[34m'
CVIOLET = '\33[35m'
CBEIGE  = '\33[36m'
CWHITE  = '\33[37m'

CBLACKBG  = '\33[40m'
CREDBG    = '\33[41m'
CGREENBG  = '\33[42m'
CYELLOWBG = '\33[43m'
CBLUEBG   = '\33[44m'
CVIOLETBG = '\33[45m'
CBEIGEBG  = '\33[46m'
CWHITEBG  = '\33[47m'

CGREY    = '\33[90m'
CRED2    = '\33[91m'
CGREEN2  = '\33[92m'
CYELLOW2 = '\33[93m'
CBLUE2   = '\33[94m'
CVIOLET2 = '\33[95m'
CBEIGE2  = '\33[96m'
CWHITE2  = '\33[97m'

CGREYBG    = '\33[100m'
CREDBG2    = '\33[101m'
CGREENBG2  = '\33[102m'
CYELLOWBG2 = '\33[103m'
CBLUEBG2   = '\33[104m'
CVIOLETBG2 = '\33[105m'
CBEIGEBG2  = '\33[106m'
CWHITEBG2  = '\33[107m'

以下是生成测试的代码:

x = 0
for i in range(24):
  colors = ""
  for j in range(5):
    code = str(x+j)
    colors = colors + "\33[" + code + "m\\33[" + code + "m\033[0m "
  print(colors)
  x = x + 5

我最终做了这个,我觉得这是最干净的:

formatters = {
    'RED': '\033[91m',
    'GREEN': '\033[92m',
    'END': '\033[0m',
}

print 'Master is currently {RED}red{END}!'.format(**formatters)
print 'Help make master {GREEN}green{END} again!'.format(**formatters)