【问题标题】:How to use terminal color palette with curses如何使用带有诅咒的终端调色板
【发布时间】:2013-09-04 06:49:14
【问题描述】:

我无法让终端调色板与 curses 一起使用。

import curses

def main(stdscr):
    curses.use_default_colors()
    for i in range(0,7):
        stdscr.addstr("Hello", curses.color_pair(i))
    stdscr.getch()

curses.wrapper(main)

此 python 脚本产生以下屏幕:

但是,我的 gnome-terminal 调色板中确实有更多颜色。我如何在 curses 中访问它们?

【问题讨论】:

    标签: python colors curses


    【解决方案1】:

    curses.use_default_colors() 仅将默认 fg 或 bg 颜色设置为 -1,来自手册页“init_pair(x,COLOR_RED,-1) 将在默认背景上将 x 对初始化为红色,init_pair(x,-1,COLOR_BLUE) 将在蓝色背景上将 x 对初始化为默认前景。”

    我一直认为 curses 只支持名为“curses.COLOR_...”的 8 个,通常这已经足够了,但我想在我的应用程序中添加一些香料,所以很短的时间搜索就找到了我。大多数术语很可能支持 256 色,您可以使用上面的@Hristo Eftimov 的代码来打印所支持的内容。我决定制作一个备用颜色选择器,它将 x 颜色编号的示例显示为前景和背景。 方向键左/右或键 a/d 更改要更改的属性,+/- 增加/减少颜色编号,q 或 esc 退出。

    
        #!/usr/bin/python
        
        from traceback import format_exc
        import sys, os, time, re, curses
        import locale
        locale.setlocale(locale.LC_ALL, '')
        os.environ.setdefault('ESCDELAY', '250')
        os.environ["NCURSES_NO_UTF8_ACS"] = "1"
        
        move_dirs = {curses.KEY_DOWN : (1, 0), curses.KEY_UP : (-1, 0), curses.KEY_RIGHT : (0, 1), curses.KEY_LEFT : (0, -1),
                     ord('s') : (1, 0), ord('w') : (-1, 0), ord('d') : (0, 1), ord('a') : (0, -1)}
        
        colors = {'white': curses.COLOR_WHITE, 'red': curses.COLOR_RED, 'green': curses.COLOR_GREEN,
                  'yellow': curses.COLOR_YELLOW, 'blue': curses.COLOR_BLUE, 'magenta': curses.COLOR_MAGENTA,
                  'cyan': curses.COLOR_CYAN, 'black': curses.COLOR_BLACK}
        
        class SuspendCurses():
            def __enter__(self):
                curses.endwin()
            def __exit__(self, exc_type, exc_val, tb):
                newscr = curses.initscr()
                newscr.refresh()
                curses.doupdate()
        
        def cp(i):
            return curses.color_pair(i)
        
        def set_pairs(fg, bg):
            curses.init_pair(1, fg, colors['black'])
            curses.init_pair(2, fg, colors['yellow'])
            curses.init_pair(3, fg, colors['white'])
            curses.init_pair(4, fg, colors['red'])
            curses.init_pair(5, colors['black'], bg)
            curses.init_pair(6, colors['yellow'], bg)
            curses.init_pair(7, colors['white'], bg)
            curses.init_pair(8, colors['red'], bg)
        
        def main_loop(stdscr):
            ret = 0
            EXIT = False
            try:
                curses.curs_set(1) #set curses options and variables
                curses.noecho()
                curses.cbreak()
                maxc = curses.COLORS
                maxy, maxx = stdscr.getmaxyx()
                if maxy < 10 or maxx < 65:
                    with SuspendCurses():
                        print('Terminal window needs to be at least 10h by 65w')
                        print('Current h:{0}  and w:{1}'.format(maxy, maxx))
                    ret = 1
                    EXIT = True
                stdscr.refresh()
                h, w = 10, 65
                test_win = curses.newwin(h, w, 0, 0)
                stdscr.nodelay(1)
                test_win.leaveok(0)
                test_win.keypad(1)
                test_win.bkgd(' ', cp(0))
                test_win.box()
                cursor = [2, 0]
                test_win.move(2, 2+cursor[1]*20)
                fgcol, bgcol = 1, 1
                set_pairs(fgcol, bgcol)
                test_win.refresh()
                cursor_bounds = ((0,0),(0,1))
                teststr = '! @ # $ % ^ & *     _ + - = '
                k, newk = 1, 2
                while not EXIT:
                    if k > -1:
                        test_win.clear()
                        if k in move_dirs.keys():  #move cursor left or right with wrapping
                            cursor[1] += move_dirs[k][1]
                            if cursor[1] > cursor_bounds[1][1]: cursor[1] = cursor_bounds[1][0]
                            if cursor[1] < cursor_bounds[1][0]: cursor[1] = cursor_bounds[1][1]
                        if k == 45:  #decr currently selected attr
                            if cursor[1] == 0:
                                fgcol -= 1
                                if fgcol < 0: fgcol = maxc-1
                            else:
                                bgcol -= 1
                                if bgcol < 0: bgcol = maxc-1
                            set_pairs(fgcol, bgcol)
                        if k == 43:  #incr currently selected attr
                            if cursor[1] == 0:
                                fgcol += 1
                                if fgcol > maxc-1: fgcol = 0
                            else:
                                bgcol += 1
                                if bgcol > maxc-1: bgcol = 0
                            set_pairs(fgcol, bgcol)
                        if k in (ord('q'), 27):
                            EXIT = True
                        test_win.addstr(1, 10, '{0} colors supported'.format(maxc), cp(0))
                        test_win.addstr(2, 2, 'FG: {0}  '.format(fgcol), cp(0))
                        test_win.addstr(2, 32, 'BG: {0}  '.format(bgcol), cp(0))
                        for i in range(1,5):
                            test_win.addstr(3+i, 2, teststr, cp(i))
                            test_win.addstr(3+i, 32,teststr, cp(i+4))
                        test_win.move(1, 2+cursor[1]*30)
                        test_win.box()
                        test_win.refresh()
                        curses.napms(10)
                    newk = stdscr.getch()
                    if newk != k:
                        k = newk
            except KeyboardInterrupt:
                pass
            except:
                ret = 1
                with SuspendCurses():
                    print(format_exc())
            finally:
                return ret
        
        if __name__ == '__main__':
            try:
                _ret = curses.wrapper(main_loop)
            except Exception as e:
                print(e)
            finally:
                print('Exit status ' + str(_ret))
                sys.exit(_ret)
    

    截图:

    【讨论】:

      【解决方案2】:

      您可以通过以下方式安装使用culour 软件包:

      pip install culour
      

      然后你就可以用它来打印curses的颜色了:

      culour.addstr(window, "colored string")
      

      【讨论】:

        【解决方案3】:

        以下是我在自己的电脑(Ubuntu 14.04,python 3)上通过实验得出的。

        • 有 256 种颜色(由前 8 位定义)。
        • 其他位用于附加属性,例如突出显示。
        • 将数字 -1 作为颜色传递回默认背景和前景色。
        • 颜色对 0 (mod 256) 固定为 (-1, -1)。
        • 颜色 0 到 15 是终端调色板颜色。

        考虑以下测试代码。 将此添加到您的.bashrc

        # Set proper $TERM if we are running gnome-terminal
        if [ "$COLORTERM" == "gnome-terminal" ]
        then
            TERM=xterm-256color
        fi
        

        把它放在一个python文件中并运行它。

        import curses
        
        def main(stdscr):
            curses.start_color()
            curses.use_default_colors()
            for i in range(0, curses.COLORS):
                curses.init_pair(i + 1, i, -1)
            try:
                for i in range(0, 255):
                    stdscr.addstr(str(i), curses.color_pair(i))
            except curses.ERR:
                # End of screen reached
                pass
            stdscr.getch()
        
        curses.wrapper(main)
        

        运行它将产生以下输出。

        如您所见,颜色对 1-16 是前景色的终端调色板。

        【讨论】:

        • 你确定这是那个截图的代码吗?在我的系统(Ubuntu 12.04)中,curses.COLORS 是 8,而不是 256,任何尝试使用上面的颜色来初始化一对都会引发异常 _curses.error: init_pair() returned ERR。你使用的是什么curses 模块,Python 标准库中的默认模块?
        • 是的,我确定这是正确的屏幕截图。在我的 ubuntu 14.04(使用 python3)上再次运行它会产生相同的输出。 curses.COLORS 对我来说是 256。
        • Gnome 终端终于默认使用TERM=xterm-256color 了吗?伟大的!还是您手动将其添加到您的~/.profile / ~/.bashrc?想检查这些文件中是否有任何与TERM 相关的代码?
        • @ThomasDickey 如果您认为此堆栈上的信息可以改进,请提供您自己的答案。此外,信息不能不正确,因为它是对实验的描述以及发布此答案时我的机器上的观察结果。
        • 如果xterm-256color 不起作用,请使用ls /usr/share/terminfo/x 列出可用的终端。
        【解决方案4】:

        我没有代表点将此作为评论提交给 Chiel 10 Brinke 的出色答案,所以我将在这里提供他的颜色脚本的更有用的版本:

        import curses
        def main(stdscr):
            curses.start_color()
            curses.use_default_colors()
            for i in range(0, curses.COLORS):
                curses.init_pair(i + 1, i, -1)
            stdscr.addstr(0, 0, '{0} colors available'.format(curses.COLORS))
            maxy, maxx = stdscr.getmaxyx()
            maxx = maxx - maxx % 5
            x = 0
            y = 1
            try:
                for i in range(0, curses.COLORS):
                    stdscr.addstr(y, x, '{0:5}'.format(i), curses.color_pair(i))
                    x = (x + 5) % maxx
                    if x == 0:
                        y += 1
            except curses.ERR:
                pass
            stdscr.getch()
        curses.wrapper(main)
        

        【讨论】:

          【解决方案5】:

          终端“调色板”由终端应用程序本身设置,以将默认的curses颜色映射到特定于应用程序的“解释”。如果您使用红色,终端可以选择将其显示为酒红色或樱桃红色,或者如果用户愿意,可以选择完全不同的颜色。

          换句话说,只需使用诅咒颜色(结合或不结合明亮或闪烁修饰符),一切都应该正常工作。

          我相信curses.use_default_colors() 调用只是提供了透明度;这是对use_default_colors() ncurses API function 的直接调用。 ncurses 颜色是基于调色板的;您需要使用curses.init_pair() calls 为每个对号设置自己的颜色属性,然后从调色板中选择带有curses.color_pair() 的颜色对以显示具有该特定对的文本;或直接为给定的addstr() 调用构建文本属性。

          【讨论】:

          • 那么,就问题中的代码而言,我应该如何使用这些颜色?
          • @Chiel92:我可能错过了什么; stdscr.can_change_color() 是否返回 True
          • @Chiel92:它通常只在 Linux 终端中运行,大多数发行版都可以通过 CTRL+ALT+&lt;1...6&gt; 访问这些终端(7 是您的桌面环境)。在此类终端中,您可以为每种颜色分配一个 RGB 值。
          【解决方案6】:

          我目前将这些行放在我的脚本前面。

          curses.use_default_colors()
          for i in range(0, curses.COLORS):
              curses.init_pair(i, i, -1);
          

          我不知道这是否是最好的解决方案,但至少它会产生一些与终端调色板一致的颜色对。

          【讨论】:

          • 这是一个很好的解决方案,因为它使用默认(可能是透明的)背景将前 8 对分配给它们“匹配”的前景色。请注意,您可以拥有多于 8 对:这里curses.COLOR_PAIRS 返回64
          • 快速初始化的好选择..只是想知道是否有任何方法可以将颜色编号与此处的真实颜色名称(比如“红色”)相关联..或者只需要反复试验。 . 这些颜色出现的默认顺序是什么?
          • Afaik 你能做的最好的就是使用来自stackoverflow.com/a/22166613/1546844 的脚本查看颜色,并尝试找到让你做你想做的事情的模式。每个终端可能会有所不同,什么颜色对应什么数字,我不确定。
          • OSX 10.13.2 -- 我将您的代码放在包装器的 main() 函数的顶部,颜色开始为我工作。谢谢。
          猜你喜欢
          • 2015-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多