curses.md
1> **Источник:** https://python-all.ru/3.0/howto/curses.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Программирование с curses на Python89| Автор: | A.M. Kuchling, Eric S. Raymond |10| --- | --- |11| Версия: | 2.03 |1213Аннотация1415Этот документ описывает, как создавать текстовые программы на Python 2.x, используя расширение [`curses`](https://python-all.ru/3.0/library/curses.html#module-curses) для управления дисплеем.1617## Что такое curses?1819Библиотека curses предоставляет терминально-независимые средства рисования на экране и обработки клавиатуры для текстовых терминалов; к таким терминалам относятся VT100, консоль Linux и симулированные терминалы, предоставляемые программами X11, такими как xterm и rxvt. Дисплейные терминалы поддерживают различные управляющие коды для выполнения типовых операций, таких как перемещение курсора, прокрутка экрана и очистка областей. Разные терминалы используют сильно отличающиеся коды и часто имеют свои мелкие особенности.2021В мире X-дисплеев можно спросить: «зачем всё это?» Действительно, символьные терминалы – устаревшая технология, но есть области, где умение делать с ними нечто необычное всё ещё ценно. Одна из таких областей – маломощные или встраиваемые Unix-системы, не имеющие X-сервера. Другая – инструменты вроде установщиков ОС и конфигураторов ядра, которые могут запускаться до того, как станет доступен X.2223Библиотека curses скрывает все детали разных терминалов и предоставляет программисту абстракцию дисплея, содержащую несколько неперекрывающихся окон. Содержимое окна можно менять различными способами – добавлять текст, стирать его, менять внешний вид – а curses автоматически определит, какие управляющие коды нужно отправить на терминал для получения правильного вывода.2425Библиотека curses изначально была написана для BSD Unix; более поздние версии System V от AT&T добавили множество улучшений и новых функций. BSD curses больше не поддерживается, его заменил ncurses – реализация интерфейса AT&T с открытым исходным кодом. Если вы используете Unix с открытым исходным кодом, такой как Linux или FreeBSD, ваша система почти наверняка использует ncurses. Поскольку большинство современных коммерческих версий Unix основаны на коде System V, все описанные здесь функции, вероятно, будут доступны. Однако более старые версии curses, входящие в некоторые проприетарные Unix-системы, могут поддерживать не всё.2627Никто не портировал модуль curses на Windows. На платформе Windows попробуйте модуль Console, написанный Фредриком Лундом. Модуль Console обеспечивает вывод текста с адресацией по курсору, а также полную поддержку ввода с мыши и клавиатуры, и доступен по адресу [http://effbot.org/zone/console-index.htm](https://python-all.ru/3.0/howto/curses.html).2829### Модуль curses в Python3031Модуль Python – довольно простая обёртка над C-функциями, предоставляемыми curses; если вы уже знакомы с программированием curses на C, перенести эти знания на Python очень легко. Самое большое отличие в том, что интерфейс Python упрощает дело, объединяя разные C-функции, такие как `addstr()`, `mvaddstr()`, `mvwaddstr()`, в единый метод `addstr()`. Подробнее об этом будет рассказано позже.3233Данный HOWTO – просто введение в написание текстовых программ с использованием curses и Python. Он не претендует на роль полного руководства по API curses; для этого смотрите раздел о ncurses в руководстве по библиотеке Python и справочные страницы C для ncurses. Однако он даст вам основные понятия.3435## Запуск и завершение приложения curses3637Прежде чем что-либо делать, curses необходимо инициализировать. Это делается вызовом функции `initscr()`, которая определит тип терминала, отправит на терминал необходимые коды настройки и создаст различные внутренние структуры данных. В случае успеха `initscr()` возвращает объект окна, представляющий весь экран; обычно он называется `stdscr`, по имени соответствующей переменной C.3839```python40import curses41stdscr = curses.initscr()42```4344Обычно curses-приложения отключают автоматический эхо-вывод нажатий на экран, чтобы можно было читать клавиши и отображать их только при определённых условиях. Для этого требуется вызвать функцию `noecho()`.4546```python47curses.noecho()48```4950Приложениям также часто требуется мгновенно реагировать на нажатия клавиш, не дожидаясь нажатия Enter; это называется режимом cbreak, в отличие от обычного буферизованного ввода.5152```python53curses.cbreak()54```5556Терминалы обычно возвращают специальные клавиши, такие как клавиши курсора или навигационные клавиши (Page Up, Home), в виде многобайтовых escape-последовательностей. Хотя можно написать приложение, которое будет ожидать такие последовательности и обрабатывать их соответствующим образом, curses может сделать это за вас, возвращая специальное значение, например `curses.KEY_LEFT`. Чтобы curses выполнял эту работу, необходимо включить режим клавиатуры.5758```python59stdscr.keypad(1)60```6162Завершить curses-приложение намного проще, чем запустить. Потребуется вызвать6364```python65curses.nocbreak(); stdscr.keypad(0); curses.echo()66```6768чтобы отменить настройки терминала, сделанные curses. Затем вызовите функцию `endwin()`, чтобы восстановить исходный режим работы терминала.6970```python71curses.endwin()72```7374Типичная проблема при отладке curses-приложения – испорченный терминал, когда приложение завершается, не восстановив терминал в предыдущее состояние. В Python это часто случается, когда код содержит ошибки и вызывает необработанное исключение. Например, нажатия клавиш больше не отображаются на экране, что затрудняет работу в оболочке.7576В Python можно избежать этих сложностей и значительно упростить отладку, импортировав модуль [`curses.wrapper`](https://python-all.ru/3.0/library/curses.html#module-curses.wrapper). Он предоставляет функцию `wrapper()`, которая принимает вызываемый объект. Она выполняет описанную выше инициализацию, а также инициализирует цвета, если поддержка цвета присутствует. Затем она запускает переданный вызываемый объект и в конце корректно деинициализирует всё. Вызываемый объект выполняется внутри блока try-catch, который перехватывает исключения, выполняет деинициализацию curses и затем пробрасывает исключение дальше. Таким образом, ваш терминал не останется в странном состоянии при возникновении исключения.7778## Окна и Pad’ы7980Окна – это основная абстракция в curses. Объект окна представляет прямоугольную область экрана и поддерживает различные методы для отображения текста, его стирания, ввода строк пользователем и так далее.8182Объект `stdscr`, возвращаемый функцией `initscr()`, представляет собой окно, покрывающее весь экран. Многим программам может потребоваться только это одно окно, но, возможно, вы захотите разделить экран на более мелкие окна, чтобы перерисовывать или очищать их по отдельности. Функция `newwin()` создаёт новое окно заданного размера, возвращая новый объект окна.8384```python85begin_x = 20 ; begin_y = 786height = 5 ; width = 4087win = curses.newwin(height, width, begin_y, begin_x)88```8990Несколько слов о системе координат, используемой в curses: координаты всегда передаются в порядке *y, x*, и левый верхний угол окна имеет координаты (0,0). Это нарушает общепринятое соглашение, согласно которому координата *x* обычно идёт первой. Это досадное отличие от большинства других компьютерных приложений, но так было в curses с самого начала, и теперь уже поздно что-то менять.9192Когда вы вызываете метод для отображения или стирания текста, эффект не проявляется на экране немедленно. Это связано с тем, что curses изначально разрабатывался для медленных терминальных соединений со скоростью 300 бод; для таких терминалов минимизация времени перерисовки экрана очень важна. Это позволяет curses накапливать изменения экрана и отображать их наиболее эффективным способом. Например, если ваша программа выводит несколько символов в окне, а затем очищает окно, нет необходимости отправлять исходные символы, так как они никогда не будут видны.9394Соответственно, curses требует явного указания на перерисовку окон с помощью метода `refresh()` объектов окна. На практике это не сильно усложняет программирование с curses. Большинство программ выполняют серию действий, а затем приостанавливаются в ожидании нажатия клавиши или другого действия пользователя. Всё, что нужно сделать – убедиться, что экран перерисован перед паузой в ожидании ввода, просто вызвав `stdscr.refresh()` или метод `refresh()` какого-либо другого соответствующего окна.9596Pad – это особый случай окна; он может быть больше фактического экрана, и в каждый момент отображается только его часть. Для создания pad требуются только его высота и ширина, а для обновления pad нужно указать координаты области на экране, где будет отображаться его подраздел.9798```python99pad = curses.newpad(100, 100)100# Эти циклы заполняют pad буквами; это101# в следующем разделе102for y in range(0, 100):103 for x in range(0, 100):104 try: pad.addch(y,x, ord('a') + (x*x+y*y) % 26 )105 except curses.error: pass106107# Отображает часть pad в середине экрана108pad.refresh( 0,0, 5,5, 20,75)109```110111Вызов `refresh()` отображает участок pad'а в прямоугольнике, простирающемся от координаты (5,5) до координаты (20,75) на экране; левый верхний угол отображаемого участка – это координата (0,0) на pad'е. За исключением этого отличия, pad'ы полностью аналогичны обычным окнам и поддерживают те же методы.112113Если на экране несколько окон и pad'ов, есть более эффективный способ, позволяющий избежать раздражающего мерцания при обновлении. Используйте метод `noutrefresh()` каждого окна для обновления структуры данных, представляющей желаемое состояние экрана; затем измените физический экран в соответствии с желаемым состоянием одним вызовом функции `doupdate()`. Обычный метод `refresh()` последним действием вызывает `doupdate()`.114115## Отображение текста116117С точки зрения C-программиста, curses иногда может выглядеть как запутанный лабиринт функций, тонко отличающихся друг от друга. Например, `addstr()` выводит строку в текущей позиции курсора в окне `stdscr`, в то время как `mvaddstr()` сначала перемещается к заданным координатам y,x, а затем выводит строку. `waddstr()` аналогична `addstr()`, но позволяет указать окно для использования вместо `stdscr` по умолчанию. `mvwaddstr()` работает аналогично.118119К счастью, интерфейс Python скрывает все эти детали; `stdscr` – это такой же объект окна, как и любой другой, а методы, подобные `addstr()`, принимают несколько форм аргументов. Обычно существует четыре различные формы.120121| Форма | Описание |122| --- | --- |123| *str* или *ch* | Отобразить строку *str* или символ *ch* в текущей позиции |124| *str* или *ch*, *attr* | Отобразить строку *str* или символ *ch*, используя атрибут *attr* в текущей позиции |125| *y*, *x*, *str* или *ch* | Переместиться в позицию *y,x* внутри окна и отобразить *str* или *ch* |126| *y*, *x*, *str* или *ch*, *attr* | Переместиться в позицию *y,x* внутри окна и отобразить *str* или *ch*, используя атрибут *attr* |127128Атрибуты позволяют отображать текст в выделенных формах, например, жирным шрифтом, подчёркиванием, инверсным выводом или цветом. Они будут подробнее объяснены в следующем подразделе.129130Функция `addstr()` принимает строку Python в качестве отображаемого значения, тогда как функции `addch()` принимают символ, который может быть как строкой Python длиной 1, так и целым числом. Если это строка, то можно отображать только символы с кодами от 0 до 255. SVr4 curses предоставляет константы для расширенных символов; эти константы – целые числа больше 255. Например, `ACS_PLMINUS` – это символ ±, а `ACS_ULCORNER` – верхний левый угол рамки (удобно для рисования границ).131132Окна запоминают, где был оставлен курсор после последней операции, поэтому если опустить координаты *y,x*, строка или символ будут отображаться там, где закончилась последняя операция. Курсор также можно перемещать с помощью метода `move(y,x)`. Поскольку некоторые терминалы всегда отображают мигающий курсор, стоит убедиться, что курсор находится в таком месте, где он не будет отвлекать; может сбивать с толку, если курсор мигает в каком-то, казалось бы, случайном месте.133134Если вашему приложению вообще не нужен мигающий курсор, можно вызвать `curs_set(0)`, чтобы сделать его невидимым. Эквивалентно, и для совместимости со старыми версиями curses, существует функция `leaveok(bool)`. Когда *bool* равно true, библиотека curses попытается подавить мигающий курсор, и вам не придётся беспокоиться о том, что он остаётся в неположенных местах.135136### Атрибуты и цвет137138Символы могут отображаться разными способами. Строки состояния в текстовом приложении обычно показываются инверсным видео; средство просмотра текста может выделять определённые слова. curses поддерживает это, позволяя задавать атрибут для каждой ячейки экрана.139140Атрибут – это целое число, каждый бит которого представляет отдельный атрибут. Можно попробовать отобразить текст с несколькими установленными битами атрибутов, но curses не гарантирует, что все возможные комбинации доступны или что они визуально различимы. Это зависит от возможностей используемого терминала, поэтому безопаснее всего ограничиться наиболее распространёнными атрибутами, перечисленными ниже.141142| Атрибут | Описание |143| --- | --- |144| `A_BLINK` | Мигающий текст |145| `A_BOLD` | Яркий или полужирный текст |146| `A_DIM` | Текст пониженной яркости |147| `A_REVERSE` | Инверсный текст |148| `A_STANDOUT` | Лучший из доступных режимов выделения |149| `A_UNDERLINE` | Подчёркнутый текст |150151Итак, чтобы вывести инверсную строку состояния на верхней строке экрана, можно написать код:152153```python154stdscr.addstr(0, 0, "Current mode: Typing mode",155 curses.A_REVERSE)156stdscr.refresh()157```158159Библиотека curses также поддерживает цвет на тех терминалах, которые его предоставляют. Самый распространённый такой терминал – скорее всего, консоль Linux, за ней следуют цветные xterm-терминалы.160161Чтобы использовать цвет, необходимо вызвать функцию `start_color()` сразу после вызова `initscr()`, чтобы инициализировать набор цветов по умолчанию (функция [`curses.wrapper.wrapper()`](https://python-all.ru/3.0/library/curses.html#curses.wrapper.wrapper) делает это автоматически). После этого функция `has_colors()` возвращает TRUE, если используемый терминал может отображать цвет. (Примечание: curses использует американское написание 'color' вместо британского 'colour'. Если вы привыкли к британскому написанию, придётся смириться с неправильным написанием ради этих функций.)162163Библиотека curses поддерживает конечное количество цветовых пар, каждая из которых содержит цвет переднего плана (или текста) и цвет фона. Получить значение атрибута, соответствующее цветовой паре, можно с помощью функции `color_pair()`; это значение можно побитово объединить OR с другими атрибутами, такими как `A_REVERSE`, но, опять же, такие комбинации не гарантированно работают на всех терминалах.164165Пример, который выводит строку текста с использованием цветовой пары 1:166167```python168stdscr.addstr( "Pretty text", curses.color_pair(1) )169stdscr.refresh()170```171172Как уже говорилось, цветовая пара состоит из цвета переднего плана и фона. `start_color()` инициализирует 8 основных цветов при активации цветового режима. Это: 0:чёрный, 1:красный, 2:зелёный, 3:жёлтый, 4:синий, 5:пурпурный, 6:голубой и 7:белый. Модуль curses определяет именованные константы для каждого из этих цветов: `curses.COLOR_BLACK`, `curses.COLOR_RED` и так далее.173174Функция `init_pair(n, f, b)` изменяет определение цветовой пары *n*, устанавливая цвет переднего плана f и цвет фона b. Цветовая пара 0 жёстко задана как белый на чёрном и не может быть изменена.175176Давайте соберём всё вместе. Чтобы изменить цвет 1 на красный текст на белом фоне, нужно вызвать:177178```python179curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)180```181182При изменении цветовой пары любой уже отображаемый текст с этой парой изменится на новые цвета. Новый текст в этой цветовой паре можно вывести так:183184```python185stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1) )186```187188Очень продвинутые терминалы могут изменять определения реальных цветов на заданное значение RGB. Это позволяет изменить цвет 1, обычно красный, на пурпурный, синий или любой другой по вашему желанию. К сожалению, консоль Linux не поддерживает эту возможность, поэтому я не смог это опробовать и не могу привести примеры. Вы можете проверить, поддерживает ли это ваш терминал, вызвав `can_change_color()`, которая возвращает TRUE, если такая возможность есть. Если вам повезло иметь такой талантливый терминал, обратитесь к man-страницам вашей системы за дополнительной информацией.189190## Пользовательский ввод191192Сама библиотека curses предоставляет лишь очень простые механизмы ввода. Поддержка Python добавляет виджет текстового ввода, который частично компенсирует этот недостаток.193194Самый распространённый способ получения ввода от окна – использование его метода `getch()`. `getch()` приостанавливает работу и ждёт, пока пользователь нажмёт клавишу, отображая её, если ранее была вызвана `echo()`. Можно также указать координаты, на которые следует переместить курсор перед паузой.195196Это поведение можно изменить с помощью метода `nodelay()`. После вызова `nodelay(1)` метод `getch()` для окна становится неблокирующим и возвращает `curses.ERR` (значение -1), если нет готового ввода. Также существует функция `halfdelay()`, которая (по сути) устанавливает таймер на каждый вызов `getch()`; если в течение заданной задержки (измеряется в десятых долях секунды) ввод не поступает, curses вызывает исключение.197198Метод `getch()` возвращает целое число; если оно находится в диапазоне от 0 до 255, оно представляет ASCII-код нажатой клавиши. Значения больше 255 – это специальные клавиши, такие как Page Up, Home или клавиши курсора. Возвращённое значение можно сравнивать с константами, например `curses.KEY_PPAGE`, `curses.KEY_HOME` или `curses.KEY_LEFT`. Обычно главный цикл программы выглядит примерно так:199200```python201while True:202 c = stdscr.getch()203 if c == ord('p'): PrintDocument()204 elif c == ord('q'): break # Выход из while()205 elif c == curses.KEY_HOME: x = y = 0206```207208Модуль [`curses.ascii`](https://python-all.ru/3.0/library/curses.ascii.html#module-curses.ascii) предоставляет функции определения принадлежности к классу ASCII, которые принимают аргументы как целочисленного типа, так и строки длиной 1 символ; они могут быть полезны для написания более читаемых тестов для ваших интерпретаторов команд. Также предоставляются функции преобразования, принимающие аргументы целочисленного или строкового типа (длиной 1 символ) и возвращающие значение того же типа. Например, [`curses.ascii.ctrl()`](https://python-all.ru/3.0/library/curses.ascii.html#curses.ascii.ctrl) возвращает управляющий символ, соответствующий переданному аргументу.209210Также есть метод для получения целой строки – `getstr()`. Он используется нечасто, так как его функциональность весьма ограничена; из доступных клавиш редактирования – только клавиша backspace и клавиша Enter, которая завершает ввод строки. При желании можно ограничить длину строки фиксированным числом символов.211212```python213curses.echo() # Включить эхо-вывод символов214215# Получить строку из 15 символов, курсор на верхней строке216s = stdscr.getstr(0,0, 15)217```218219Модуль Python [`curses.textpad`](https://python-all.ru/3.0/library/curses.html#module-curses.textpad) предоставляет нечто лучшее. С его помощью можно превратить окно в текстовое поле, которое поддерживает набор сочетаний клавиш в стиле Emacs. Различные методы класса `Textbox` поддерживают редактирование с проверкой ввода и сбор результатов редактирования как с завершающими пробелами, так и без них. Подробнее см. в документации библиотеки к [`curses.textpad`](https://python-all.ru/3.0/library/curses.html#module-curses.textpad).220221## Для получения дополнительной информации222223Это руководство не охватило некоторые продвинутые темы, такие как скрапинг экрана или захват событий мыши из экземпляра xterm. Но страница библиотеки Python для модулей curses теперь достаточно полная. Рекомендуется изучить её далее.224225Если есть сомнения в детальном поведении любой из точек входа ncurses, обратитесь к man-страницам вашей реализации curses, будь то ncurses или проприетарная версия от поставщика Unix. В man-страницах будут описаны все особенности, а также приведены полные списки всех доступных функций, атрибутов и символов `ACS_*`.226227Поскольку API curses очень велик, некоторые функции не поддерживаются в интерфейсе Python не потому, что их сложно реализовать, а потому, что они пока никому не понадобились. Вы можете добавить их и затем отправить патч. Кроме того, у нас пока нет поддержки библиотек меню или панелей, связанных с ncurses; вы можете добавить их.228229Если вы напишете интересную небольшую программу, не стесняйтесь внести её в качестве очередной демонстрации. Мы всегда можем использовать больше таких!230231Часто задаваемые вопросы по ncurses: [http://invisible-island.net/ncurses/ncurses.faq.html](https://python-all.ru/3.0/howto/curses.html)232