Как использовать цветовую палитру терминала с 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-терминала больше цветов. Как я могу получить к ним доступ в curses?


person Chiel ten Brinke    schedule 31.08.2013    source источник


Ответы (6)


Следующее я выяснил экспериментально на своем ПК (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 являются конечной цветовой палитрой для цветов переднего плана.

person Chiel ten Brinke    schedule 04.03.2014
comment
Вы уверены, что это был код для того снимка экрана? В моей системе (Ubuntu 12.04) curses.COLORS равно 8, а не 256, и любая попытка инициализировать пару с использованием цвета, указанного выше, вызывает исключение _curses.error: init_pair() returned ERR. Какой модуль curses вы используете, модуль по умолчанию из stdlib Python? - person MestreLion; 28.01.2015
comment
Да, я уверен, что это правильный скриншот. Запуск его снова на моей Ubuntu 14.04 (с использованием python3) дает тот же результат. А curses.COLORS для меня 256. - person Chiel ten Brinke; 28.01.2015
comment
Итак, Gnome Terminal, наконец, использует TERM=xterm-256color по умолчанию? Здорово! Или вы вручную добавили это в свой ~/.profile / ~/.bashrc? Хотите проверить эти файлы на наличие кода, связанного с TERM? - person MestreLion; 29.01.2015
comment
Ах, да, теперь, когда вы упомянули об этом, я вспомнил, что делал это. Я обновлю ответ, чтобы включить это. - person Chiel ten Brinke; 29.01.2015
comment
Больше половины этого ответа неверно. Ответ Мартина в порядке (может быть представлен лучше, но в любом случае вся необходимая информация находится на страницах руководства ncurses). - person Thomas Dickey; 07.01.2017
comment
@ThomasDickey, если вы считаете, что информацию в этом стеке можно улучшить, дайте свой собственный ответ. Кроме того, информация не может быть неверной, так как это описание эксперимента вместе с наблюдениями за моей машиной на момент публикации этого ответа. - person Chiel ten Brinke; 09.01.2017
comment
Если xterm-256color не работает, используйте ls /usr/share/terminfo/x, чтобы вывести список доступных терминалов. - person Serge Stroobandt; 11.07.2017

«Цветовая палитра» терминала устанавливается самим терминальным приложением для сопоставления цветов curses по умолчанию с «интерпретациями» приложения. Если вы используете красный цвет, терминал может отображать его как бордовый или вишнево-красный, или, по желанию пользователя, что-то совершенно другое.

Другими словами, просто используйте цвета curses (в сочетании с модификаторами Bright или Blink или без них), и все должно работать.

Я считаю, что вызов curses.use_default_colors() просто делает доступной прозрачность; это прямой вызов функции use_default_colors() API ncurses. цвета ncurses в остальном основаны на палитре; вам необходимо установить собственные цветовые атрибуты для каждого номера пары с помощью curses.init_pair() вызовов, затем выберите пара цветов с curses.color_pair() из палитры для отображения текста с этой конкретной парой; или создавать текстовые атрибуты непосредственно для данного вызова addstr().

person Martijn Pieters    schedule 08.09.2013
comment
Итак, как мне использовать эти цвета с точки зрения кода в вопросе? - person Chiel ten Brinke; 08.09.2013
comment
@ Chiel92: Возможно, я что-то пропустил; stdscr.can_change_color() возвращает True? - person Martijn Pieters; 08.09.2013
comment
@ Chiel92: обычно это происходит только в терминале Linux, доступном в большинстве дистрибутивов через CTRL+ALT+<1...6> (7 — ваша среда рабочего стола). В таких терминалах вы можете назначить значение RGB для каждого цвета. - person MestreLion; 28.01.2015

В настоящее время я помещаю эти строки перед своим сценарием.

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

Я не знаю, лучшее ли это решение, но, по крайней мере, оно дает несколько цветовых пар, соответствующих цветовой палитре терминала.

person Chiel ten Brinke    schedule 08.09.2013
comment
Это хорошее решение, так как оно присваивает первым 8 парам соответствующий цвет переднего плана, используя фон по умолчанию (возможно, прозрачный). Просто имейте в виду, что у вас может быть гораздо больше, чем 8 пар: здесь curses.COLOR_PAIRS возвращает 64. - person MestreLion; 28.01.2015
comment
отличный вариант для быстрой инициализации .. просто интересно, есть ли способ связать номер цвета с реальным названием цвета (скажем, «красный») здесь .. или просто нужно делать пробы и ошибки .. есть ли какой-либо порядок по умолчанию, в котором появляются эти цвета? - person kollery; 05.12.2016
comment
Лучшее, что вы можете сделать, это посмотреть на цвета с помощью скрипта из stackoverflow.com/a/22166613/1546844 и попробовать чтобы найти шаблоны, которые позволяют вам делать то, что вы хотите. Для каждого терминала может быть по-разному, какие цвета соответствуют каким числам, я не уверен. - person Chiel ten Brinke; 05.12.2016
comment
OSX 10.13.2 -- Я поместил ваш код в верхнюю часть функции main() для оболочки, и цвета заработали для меня. Спасибо. - person 7stud; 01.02.2018

У меня нет репутации, чтобы представить это как комментарий к отличному ответу Чиэля тен Бринке, поэтому я предложу здесь более полезную версию его цветового сценария:

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)
person user1404316    schedule 06.01.2017

Вы можете использовать пакет culour, установив его с помощью:

pip install culour

И тогда вы можете использовать его для печати с цветными проклятиями:

culour.addstr(window, "colored string")
person speller    schedule 27.07.2017

curses.use_default_colors() просто устанавливает для цветов fg или bg по умолчанию значение -1, из справочной страницы init_pair(x,COLOR_RED,-1) инициализирует пару x красным цветом на фоне по умолчанию, а init_pair(x,-1,COLOR_BLUE) инициализирует пару x как передний план по умолчанию синим цветом.

Я всегда предполагал, что curses поддерживает только 8 именованных curses.COLOR_... и обычно этого достаточно, но я хотел добавить изюминку в свои приложения, поэтому недолгий поиск нашел меня здесь. Скорее всего, большинство терминов будут поддерживать 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)

Снимок экрана:

скриншот

person Christopher Ferrin    schedule 20.08.2020