Програмування Curses на Python¶
- Автор:
A.M. Kuchling, Eric S. Raymond
- Реліз:
2.04
Що таке прокляття?¶
Бібліотека curses надає незалежне від терміналу засіб малювання екрану та керування клавіатурою для текстових терміналів; такі термінали включають VT100s, консоль Linux і імітований термінал, що надається різними програмами. Дисплейні термінали підтримують різні керуючі коди для виконання типових операцій, таких як переміщення курсору, прокручування екрана та стирання областей. Різні термінали використовують дуже різні коди та часто мають власні незначні особливості.
У світі графічних дисплеїв хтось може запитати «навіщо турбуватися»? Це правда, що дисплеї з символьними комірками є застарілою технологією, але є ніші, в яких можливість робити з ними фантастичні речі все ще цінна. Одна ніша займає малогабаритні або вбудовані системи Unix, на яких не працює X-сервер. Інші інструменти, такі як інсталятори ОС і конфігуратори ядра, які, можливо, доведеться запустити, перш ніж буде доступна будь-яка графічна підтримка.
The curses library provides fairly basic functionality, providing the programmer with an abstraction of a display containing multiple non-overlapping windows of text. The contents of a window can be changed in various ways—adding text, erasing it, changing its appearance—and the curses library will figure out what control codes need to be sent to the terminal to produce the right output. curses doesn’t provide many user-interface concepts such as buttons, checkboxes, or dialogs; if you need such features, consider a user interface library such as Urwid.
Бібліотека curses спочатку була написана для BSD Unix; пізніші версії System V Unix від AT&T додали багато вдосконалень і нових функцій. BSD curses більше не підтримується, їх замінив ncurses, який є реалізацією інтерфейсу AT&T з відкритим кодом. Якщо ви використовуєте Unix з відкритим кодом, наприклад Linux або FreeBSD, ваша система майже напевно використовує ncurses. Оскільки більшість сучасних комерційних версій Unix базується на коді System V, усі описані тут функції, ймовірно, будуть доступні. Однак старіші версії curses, які містяться в деяких пропрієтарних системах Unix, можуть не підтримувати все.
The Windows version of Python doesn’t include the curses
module. A ported version called UniCurses is available.
Модуль проклинань Python¶
The Python module is a fairly simple wrapper over the C functions provided by
curses; if you’re already familiar with curses programming in C, it’s really
easy to transfer that knowledge to Python. The biggest difference is that the
Python interface makes things simpler by merging different C functions such as
addstr()
, mvaddstr()
, and mvwaddstr()
into a single
addstr()
method. You’ll see this covered in more
detail later.
Цей HOWTO є вступом до написання програм у текстовому режимі за допомогою curses і Python. Він не намагається бути повним посібником з curses API; для цього дивіться розділ посібника з бібліотеки Python про ncurses і сторінки посібника C для ncurses. Однак це дасть вам основні ідеї.
Запуск і завершення програми curses¶
Before doing anything, curses must be initialized. This is done by
calling the initscr()
function, which will determine the
terminal type, send any required setup codes to the terminal, and
create various internal data structures. If successful,
initscr()
returns a window object representing the entire
screen; this is usually called stdscr
after the name of the
corresponding C variable.
import curses
stdscr = curses.initscr()
Зазвичай програми curses вимикають автоматичне відлуння клавіш на екрані, щоб мати можливість читати клавіші та відображати їх лише за певних обставин. Для цього потрібно викликати функцію noecho()
.
curses.noecho()
Додаткам також зазвичай потрібно миттєво реагувати на натискання клавіш, не вимагаючи натискання клавіші Enter; це називається режимом cbreak, на відміну від звичайного режиму буферизованого введення.
curses.cbreak()
Термінали зазвичай повертають спеціальні клавіші, такі як клавіші керування курсором або навігаційні клавіші, такі як Page Up і Home, як багатобайтову escape-послідовність. Хоча ви можете написати свою програму так, щоб очікувати такі послідовності та обробляти їх відповідно, curses може зробити це за вас, повертаючи спеціальне значення, наприклад curses.KEY_LEFT
. Щоб змусити прокляття виконувати роботу, вам потрібно буде ввімкнути режим клавіатури.
stdscr.keypad(True)
Завершити роботу програми curses набагато легше, ніж розпочати її. Вам потрібно зателефонувати:
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
щоб змінити налаштування терміналу, зручні для curses. Потім викличте функцію endwin()
, щоб відновити термінал до вихідного режиму роботи.
curses.endwin()
Поширеною проблемою під час налагодження програми curses є псування терміналу, коли програма вимикається без відновлення попереднього стану терміналу. У Python це зазвичай трапляється, коли ваш код має помилки та викликає неперехоплений виняток. Наприклад, клавіші більше не відтворюються на екрані, коли ви їх вводите, що ускладнює використання оболонки.
У Python ви можете уникнути цих ускладнень і значно полегшити налагодження, імпортувавши функцію curses.wrapper()
і використовуючи її таким чином:
from curses import wrapper
def main(stdscr):
# Clear screen
stdscr.clear()
# This raises ZeroDivisionError when i == 10.
for i in range(0, 11):
v = i-10
stdscr.addstr(i, 0, '10 divided by {} is {}'.format(v, 10/v))
stdscr.refresh()
stdscr.getkey()
wrapper(main)
The wrapper()
function takes a callable object and does the
initializations described above, also initializing colors if color
support is present. wrapper()
then runs your provided callable.
Once the callable returns, wrapper()
will restore the original
state of the terminal. The callable is called inside a
try
…except
that catches exceptions, restores
the state of the terminal, and then re-raises the exception. Therefore
your terminal won’t be left in a funny state on exception and you’ll be
able to read the exception’s message and traceback.
Вікна та колодки¶
Вікна є основною абстракцією в curses. Об’єкт вікна представляє прямокутну область екрана та підтримує методи відображення тексту, його стирання, дозволу користувачеві вводити рядки тощо.
Об’єкт stdscr
, що повертається функцією initscr()
, є об’єктом вікна, що покриває весь екран. Багатьом програмам може знадобитися лише це одне вікно, але ви можете розділити екран на менші вікна, щоб перемалювати або очистити їх окремо. Функція newwin()
створює нове вікно заданого розміру, повертаючи новий об’єкт вікна.
begin_x = 20; begin_y = 7
height = 5; width = 40
win = curses.newwin(height, width, begin_y, begin_x)
Зверніть увагу, що система координат, яка використовується в curses, є незвичною. Координати завжди передаються в порядку y,x, а верхній лівий кут вікна є координатою (0,0). Це порушує звичайну угоду щодо обробки координат, де координата x стоїть першою. Це прикра відмінність від більшості інших комп’ютерних програм, але вона була частиною прокляття відтоді, як була написана, і зараз надто пізно щось змінювати.
Ваша програма може визначити розмір екрана за допомогою змінних curses.LINES
і curses.COLS
для отримання розмірів y і x. Тоді юридичні координати розширюватимуться від (0,0)
до (curses.LINES - 1, curses.COLS - 1)
.
Коли ви викликаєте метод для відображення або стирання тексту, ефект не відразу відображається на дисплеї. Натомість ви повинні викликати метод refresh()
об’єктів вікна, щоб оновити екран.
This is because curses was originally written with slow 300-baud
terminal connections in mind; with these terminals, minimizing the
time required to redraw the screen was very important. Instead curses
accumulates changes to the screen and displays them in the most
efficient manner when you call refresh()
. For example, if your
program displays some text in a window and then clears the window,
there’s no need to send the original text because they’re never
visible.
In practice, explicitly telling curses to redraw a window doesn’t
really complicate programming with curses much. Most programs go into a flurry
of activity, and then pause waiting for a keypress or some other action on the
part of the user. All you have to do is to be sure that the screen has been
redrawn before pausing to wait for user input, by first calling
stdscr.refresh()
or the refresh()
method of some other relevant
window.
Колодка — окремий випадок вікна; він може бути більшим, ніж фактичний екран дисплея, і одночасно відображатися лише частина панелі. Щоб створити блокнот, потрібно вказати його висоту та ширину, тоді як для оновлення блокнота потрібно вказати координати області на екрані, де відображатиметься його підрозділ.
pad = curses.newpad(100, 100)
# These loops fill the pad with letters; addch() is
# explained in the next section
for y in range(0, 99):
for x in range(0, 99):
pad.addch(y,x, ord('a') + (x*x+y*y) % 26)
# Displays a section of the pad in the middle of the screen.
# (0,0) : coordinate of upper-left corner of pad area to display.
# (5,5) : coordinate of upper-left corner of window area to be filled
# with pad content.
# (20, 75) : coordinate of lower-right corner of window area to be
# : filled with pad content.
pad.refresh( 0,0, 5,5, 20,75)
The refresh()
call displays a section of the pad in the rectangle
extending from coordinate (5,5) to coordinate (20,75) on the screen; the upper
left corner of the displayed section is coordinate (0,0) on the pad. Beyond
that difference, pads are exactly like ordinary windows and support the same
methods.
If you have multiple windows and pads on screen there is a more
efficient way to update the screen and prevent annoying screen flicker
as each part of the screen gets updated. refresh()
actually
does two things:
Викликає метод
noutrefresh()
кожного вікна, щоб оновити базову структуру даних, що представляє потрібний стан екрана.Викликає функцію
doupdate()
, щоб змінити фізичний екран відповідно до потрібного стану, записаного в структурі даних.
Instead you can call noutrefresh()
on a number of windows to
update the data structure, and then call doupdate()
to update
the screen.
Відображення тексту¶
From a C programmer’s point of view, curses may sometimes look like a
twisty maze of functions, all subtly different. For example,
addstr()
displays a string at the current cursor location in
the stdscr
window, while mvaddstr()
moves to a given y,x
coordinate first before displaying the string. waddstr()
is just
like addstr()
, but allows specifying a window to use instead of
using stdscr
by default. mvwaddstr()
allows specifying both
a window and a coordinate.
На щастя, інтерфейс Python приховує всі ці деталі. stdscr
є віконним об’єктом, як і будь-який інший, і такі методи, як addstr()
приймають кілька форм аргументів. Зазвичай є чотири різні форми.
Форма |
опис |
---|---|
str або ch |
Відобразити рядок str або символ ch у поточній позиції |
str або ch, attr |
Відобразити рядок str або символ ch, використовуючи атрибут attr у поточній позиції |
y, x, str або ch |
Перейдіть до позиції y,x у вікні та відобразіть str або ch |
y, x, str або ch, attr |
Перейдіть до позиції y,x у вікні та відобразіть str або ch, використовуючи атрибут attr |
Атрибути дозволяють відображати текст у виділених формах, наприклад жирним шрифтом, підкресленням, зворотним кодом або кольором. Вони будуть пояснені більш детально в наступному підрозділі.
The addstr()
method takes a Python string or
bytestring as the value to be displayed. The contents of bytestrings
are sent to the terminal as-is. Strings are encoded to bytes using
the value of the window’s encoding
attribute; this defaults to
the default system encoding as returned by locale.getencoding()
.
Методи addch()
приймають символ, який може бути рядком довжини 1, байтовим рядком довжини 1 або цілим числом.
Константи надаються для символів розширення; ці константи є цілими числами, більшими за 255. Наприклад, ACS_PLMINUS
— це символ +/-, а ACS_ULCORNER
— верхній лівий кут прямокутника (зручно для малювання меж). Ви також можете використовувати відповідний символ Unicode.
Windows запам’ятовує, де був залишений курсор після останньої операції, тому, якщо ви пропустите координати y,x, рядок або символ відображатиметься там, де зупинилася остання операція. Ви також можете перемістити курсор за допомогою методу move(y,x)
. Оскільки деякі термінали завжди відображають блимаючий курсор, ви можете переконатися, що курсор розташований у певному місці, де він не буде відволікати; блимання курсора в певному, начебто випадковому, місці може бути збентеженим.
Якщо вашій програмі зовсім не потрібен мерехтливий курсор, ви можете викликати curs_set(False)
, щоб зробити його невидимим. Для сумісності зі старішими версіями curses існує функція leaveok(bool)
, яка є синонімом curs_set()
. Якщо bool має значення true, бібліотека curses намагатиметься придушити миготливий курсор, і вам не потрібно буде турбуватися про те, щоб залишити його в незвичних місцях.
Атрибути та колір¶
Символи можуть відображатися різними способами. Рядки стану в текстовій програмі зазвичай відображаються у зворотному відео, або програмі перегляду тексту може знадобитися виділити певні слова. curses підтримує це, дозволяючи вам вказати атрибут для кожної клітинки на екрані.
Атрибут — це ціле число, кожен біт якого представляє окремий атрибут. Ви можете спробувати відобразити текст з кількома встановленими бітами атрибутів, але curses не гарантує, що всі можливі комбінації доступні, або що всі вони візуально відрізняються. Це залежить від можливостей використовуваного терміналу, тому найбезпечніше дотримуватися найпоширеніших атрибутів, перелічених тут.
Атрибут |
опис |
---|---|
Миготливий текст |
|
Дуже яскравий або жирний текст |
|
Наполовину яскравий текст |
|
Зворотний відеотекст |
|
Найкращий доступний режим підсвічування |
|
Підкреслений текст |
Отже, щоб відобразити рядок стану реверсивного відео у верхньому рядку екрана, ви можете закодувати:
stdscr.addstr(0, 0, "Current mode: Typing mode",
curses.A_REVERSE)
stdscr.refresh()
Бібліотека curses також підтримує колір на тих терміналах, які його надають. Найпоширенішим таким терміналом, ймовірно, є консоль Linux, за якою слідують кольорові xterms.
Щоб використовувати колір, ви повинні викликати функцію start_color()
відразу після виклику initscr()
, щоб ініціалізувати набір кольорів за замовчуванням (це робить функція curses.wrapper()
автоматично). Після цього функція has_colors()
повертає TRUE, якщо термінал, що використовується, справді може відображати колір. (Примітка: curses використовує американське написання «колір» замість канадсько-британського написання «колір». Якщо ви звикли до британського написання, вам доведеться змиритися з орфографічною помилкою заради цих функцій. )
Бібліотека curses підтримує кінцеву кількість пар кольорів, що містять колір переднього плану (або тексту) і колір фону. Ви можете отримати значення атрибута, що відповідає парі кольорів, за допомогою функції color_pair()
; це можна об’єднати порозрядним АБО з іншими атрибутами, такими як A_REVERSE
, але знову ж таки, такі комбінації не гарантовано працюють на всіх терміналах.
Приклад, який відображає рядок тексту за допомогою пари кольорів 1:
stdscr.addstr("Pretty text", curses.color_pair(1))
stdscr.refresh()
Як я вже говорив раніше, пара кольорів складається з кольору переднього плану та кольору фону. Функція init_pair(n, f, b)
змінює визначення пари кольорів n на колір переднього плану f і колір фону b. Пара кольорів 0 жорстко з’єднана з білим на чорному, і її неможливо змінити.
Кольори пронумеровані, і start_color()
ініціалізує 8 основних кольорів, коли він активує колірний режим. Це: 0:чорний, 1:червоний, 2:зелений, 3:жовтий, 4:синій, 5:пурпурний, 6:блакитний і 7:білий. Модуль curses
визначає іменовані константи для кожного з цих кольорів: curses.COLOR_BLACK
, curses.COLOR_RED
і так далі.
Давайте все це разом. Щоб змінити колір 1 на червоний текст на білому фоні, ви повинні викликати:
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
Коли ви змінюєте пару кольорів, будь-який текст, який уже відображається за допомогою цієї пари кольорів, зміниться на нові кольори. Ви також можете відобразити новий текст у цьому кольорі за допомогою:
stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1))
Дуже модні термінали можуть змінювати визначення фактичних кольорів на задане значення RGB. Це дозволяє змінити колір 1, який зазвичай червоний, на фіолетовий, синій або будь-який інший колір, який вам подобається. На жаль, консоль Linux не підтримує це, тому я не можу випробувати це й не можу надати жодних прикладів. Ви можете перевірити, чи ваш термінал може зробити це, викликавши can_change_color()
, який повертає True
, якщо така можливість є. Якщо вам пощастило мати такий талановитий термінал, зверніться до сторінок керівництва вашої системи для отримання додаткової інформації.
Введення користувача¶
The C curses library offers only very simple input mechanisms. Python’s
curses
module adds a basic text-input widget. (Other libraries
such as Urwid have more extensive
collections of widgets.)
Існує два способи отримання даних із вікна:
getch()
оновлює екран, а потім чекає, поки користувач натисне клавішу, відображаючи клавішу, якщоecho()
було викликано раніше. Додатково можна вказати координату, до якої слід перемістити курсор перед паузою.getkey()
робить те саме, але перетворює ціле число на рядок. Окремі символи повертаються як рядки з 1 символу, а спеціальні клавіші, такі як функціональні клавіші, повертають довші рядки, що містять ім’я ключа, наприкладKEY_UP
або^G
.
It’s possible to not wait for the user using the
nodelay()
window method. After nodelay(True)
,
getch()
and getkey()
for the window become
non-blocking. To signal that no input is ready, getch()
returns
curses.ERR
(a value of -1) and getkey()
raises an exception.
There’s also a halfdelay()
function, which can be used to (in
effect) set a timer on each getch()
; if no input becomes
available within a specified delay (measured in tenths of a second),
curses raises an exception.
The getch()
method returns an integer; if it’s between 0 and 255, it
represents the ASCII code of the key pressed. Values greater than 255 are
special keys such as Page Up, Home, or the cursor keys. You can compare the
value returned to constants such as curses.KEY_PPAGE
,
curses.KEY_HOME
, or curses.KEY_LEFT
. The main loop of
your program may look something like this:
while True:
c = stdscr.getch()
if c == ord('p'):
PrintDocument()
elif c == ord('q'):
break # Exit the while loop
elif c == curses.KEY_HOME:
x = y = 0
Модуль curses.ascii
надає функції приналежності до класу ASCII, які приймають цілі або односимвольні рядкові аргументи; вони можуть бути корисними для написання більш читабельних тестів для таких циклів. Він також надає функції перетворення, які приймають цілі або односимвольні аргументи та повертають той самий тип. Наприклад, curses.ascii.ctrl()
повертає керуючий символ, що відповідає його аргументу.
Існує також метод для отримання цілого рядка, getstr()
. Використовується не дуже часто, оскільки його функціональність досить обмежена; єдиними доступними клавішами редагування є клавіша повернення та клавіша Enter, яка завершує рядок. За бажанням його можна обмежити фіксованою кількістю символів.
curses.echo() # Enable echoing of characters
# Get a 15-character string, with the cursor on the top line
s = stdscr.getstr(0,0, 15)
Модуль curses.textpad
надає текстове поле, яке підтримує Emacs-подібний набір сполучень клавіш. Різні методи класу Textbox
підтримують редагування з перевіркою введених даних і збиранням результатів редагування з пробілами в кінці або без них. Ось приклад:
import curses
from curses.textpad import Textbox, rectangle
def main(stdscr):
stdscr.addstr(0, 0, "Enter IM message: (hit Ctrl-G to send)")
editwin = curses.newwin(5,30, 2,1)
rectangle(stdscr, 1,0, 1+5+1, 1+30+1)
stdscr.refresh()
box = Textbox(editwin)
# Let the user edit until Ctrl-G is struck.
box.edit()
# Get resulting contents
message = box.gather()
Перегляньте документацію бібліотеки на curses.textpad
для отримання додаткової інформації.
Для отримання додаткової інформації¶
Цей HOWTO не охоплює деякі складні теми, такі як читання вмісту екрана чи захоплення подій миші з екземпляра xterm, але сторінка бібліотеки Python для модуля curses
тепер досить повна. Ви повинні переглянути його далі.
If you’re in doubt about the detailed behavior of the curses functions, consult the manual pages for your curses implementation, whether it’s ncurses or a proprietary Unix vendor’s. The manual pages will document any quirks, and provide complete lists of all the functions, attributes, and ACS_* characters available to you.
Оскільки API curses дуже великий, деякі функції не підтримуються в інтерфейсі Python. Часто це відбувається не тому, що їх важко реалізувати, а тому, що вони ще нікому не потрібні. Крім того, Python ще не підтримує бібліотеку меню, пов’язану з ncurses. Латки, що додають підтримку для них, будуть вітатися; див. Посібник розробника Python, щоб дізнатися більше про надсилання виправлень у Python.
Writing Programs with NCURSES: a lengthy tutorial for C programmers.
«Використовуйте прокляття… не лайтеся»: відео розмови на PyCon 2013 про керування терміналами за допомогою проклять або Urwid.
«Console Applications with Urwid»: video of a PyCon CA 2012 talk demonstrating some applications written using Urwid.