ANSI-коды

Есть много программ, печатающих что-нибудь в терминал. Но их можно сильно разнообразить: можно покрасить текст, двигать курсор или переписать какой-нибудь текст поверх старого. Благодаря этому в Git красивые прогресс-бары, а Vim и Nano позволяют редактировать текст внутри терминала, как будто это происходит в обычном блокноте.

Существуют библиотеки, которые помогают в такой работе с терминалом. Но куда круче научиться делать это самостоятельно. В этой статье мы рассмотрим, что можно сделать простой командой print в Python.

Большинство программ взаимодействуют с терминалом через управляющие коды ANSI. Это специальные инструкции для терминала. В терминалах разных систем поддерживаются разные коды. В Википедии есть список поддерживаемых кодов. В этой статье мы научимся работать с терминалами Unix систем: Ubuntu или OS-X. На Windows такие команды поддерживаются только в Windows Terminal, в обычном cmd они не работают.

Покраска текста

Начнём с простого вывода в консоль. Так выглядит обычный, не покрашенный текст:

sample text

Чтобы покрасить текст, нужно в придачу вывести пару ANSI-кодов. Вот два ваших первых ANSI-кода:

  • Красный текст: \u001b[31m
  • Вернуть как было: \u001b[0m

Вот как их применять:

sample text

Покрасилась не только фраза Hello, World!, но и всё что под ней. На первой строчке терминал получил команду “Теперь выводи всё красным”. Дальше он выводил весь текст красным, пока не получил команду “Верни как было”: \u001b[0m.

Чтобы получилось аккуратно, лучше ставить команду “Верни как было” сразу после фразы:

sample text

С этим разобрались, а как ещё можно красить? У большинства терминалов есть 8 базовых цветов:

Цвет Код
Чёрный \u001b[30m
Красный \u001b[31m
Зелёный \u001b[32m
Жёлтый \u001b[33m
Синий \u001b[34m
Пурпурный \u001b[35m
Голубой \u001b[36m
Белый \u001b[37m
Сброс цвета \u001b[0m

sample text

Чтобы сделать цвет ярче, можно прибавить к коду ;1. Получается ещё 8 цветов:

Цвет Код
Яркий чёрный \u001b[30;1m
Яркий красный \u001b[31;1m
Яркий зелёный \u001b[32;1m
Яркий жёлтый \u001b[33;1m
Яркий синий \u001b[34;1m
Яркий пурпурный \u001b[35;1m
Яркий голубой \u001b[36;1m
Яркий белый \u001b[37;1m
Сброс цвета \u001b[0m

sample text

Все цвета

Всего в ANSI 256 цветов. Они составляются так: \u001b[38;5;КОДm, где вместо КОД — число от 0 до 255:

sample text

Покраска фона

Работает так же, только коды другие:

Цвет Код
Чёрный \u001b[40m
Красный \u001b[41m
Зелёный \u001b[42m
Жёлтый \u001b[43m
Синий \u001b[44m
Пурпурный \u001b[45m
Голубой \u001b[46m
Белый \u001b[47m

Сброс цвета такой же: \u001b[0m.

Либо можно собрать один из 256 доступных по схеме \u001b[48;5;КОДm, где вместо КОД — число от 0 до 255:

sample text

Навигация курсора

Курсор – это вот эта “мигающая палочка”, откуда набирается текст:

Если продолжить набирать текст в примере выше, то он будет набираться между словами “вот” и “эта”.

Вывод в терминале тоже набирает “курсор”. Вот как он выглядит:

Вы можете двигать обычный курсор просто кликнув мышью в нужное место. В терминале так не получится, для этого есть отдельные ANSI-коды::

Направление Код
Вверх \u001b[ШАГA
Вниз \u001b[ШАГB
Вправо \u001b[ШАГC
Влево \u001b[ШАГD

Вместо ШАГ нужно подставить нужное число, например, \u001b[10A — переведёт курсор на 10 строк вверх. Вот так это выглядит:

sample text

Мы вывели код, который поднимает курсор терминала на 4 строчки вверх. Поэтому следующий текст вывелся поверх того, что уже был выведен на третьей строчке.

Заметьте, что сам курсор в итоге оказался на следующей строке. Дело тут в том, что print добавляет перенос строки:

Каждый новый принт выводит строку, потом ставит перенос строки. Если выводить нечего, он выводит только перенос строки.

Это можно отключить:

Теперь первый print() перенос не вывел, курсор не сдвинулся вниз, поэтому вторая строка вывелась прямо следом за первой же.

Очистка экрана

Перемещение курсора по экрану позволяет выводить один текст поверх другого, но это не всегда просто сделать:

print('Первая надпись на экране')
print('Это вторая надпись на экране')
print('\u001b[2A')
print('Третья надпись')

В результате получим абракадабру. Одна надпись наложилась поверх другой, но не затерла её полностью:

Первая надпись на экране
Третья надписьпись на экране

Эту проблему можно решить добавлением пробелов к коротким строкам, чтобы все получились одной ширины:

print('Первая надпись на экране')
print('Это вторая надпись на экране')
print('\u001b[2A')
print('Третья надпись              ')

Это работает, но очень неудобно: сложно менять текст сообщений и легко ошибиться с количеством пробелов. Если вы можете себе позволить очистить экран полностью, то воспользуйтесь спец.символом для очистки экрана '\033[2J':

print('Это первая надпись на экране')
print('\033[2J')  # очистит экран
print('Вторая надпись')

Теперь всё будет работать как надо:

Вторая надпись

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.