486 lines
21 KiB
TeX
486 lines
21 KiB
TeX
\documentclass{howto}
|
|
|
|
\title{Curses Programming with Python}
|
|
|
|
\release{2.01}
|
|
|
|
\author{A.M. Kuchling, Eric S. Raymond}
|
|
\authoraddress{\email{amk@amk.ca}, \email{esr@thyrsus.com}}
|
|
|
|
\begin{document}
|
|
\maketitle
|
|
|
|
\begin{abstract}
|
|
\noindent
|
|
This document describes how to write text-mode programs with Python 2.x,
|
|
using the \module{curses} extension module to control the display.
|
|
|
|
This document is available from the Python HOWTO page at
|
|
\url{http://www.python.org/doc/howto}.
|
|
\end{abstract}
|
|
|
|
\tableofcontents
|
|
|
|
\section{What is curses?}
|
|
|
|
The curses library supplies a terminal-independent screen-painting and
|
|
keyboard-handling facility for text-based terminals; such terminals
|
|
include VT100s, the Linux console, and the simulated terminal provided
|
|
by X11 programs such as xterm and rxvt. Display terminals support
|
|
various control codes to perform common operations such as moving the
|
|
cursor, scrolling the screen, and erasing areas. Different terminals
|
|
use widely differing codes, and often have their own minor quirks.
|
|
|
|
In a world of X displays, one might ask ``why bother''? It's true
|
|
that character-cell display terminals are an obsolete technology, but
|
|
there are niches in which being able to do fancy things with them are
|
|
still valuable. One is on small-footprint or embedded Unixes that
|
|
don't carry an X server. Another is for tools like OS installers
|
|
and kernel configurators that may have to run before X is available.
|
|
|
|
The curses library hides all the details of different terminals, and
|
|
provides the programmer with an abstraction of a display, containing
|
|
multiple non-overlapping windows. The contents of a window can be
|
|
changed in various ways--adding text, erasing it, changing its
|
|
appearance--and the curses library will automagically figure out what
|
|
control codes need to be sent to the terminal to produce the right
|
|
output.
|
|
|
|
The curses library was originally written for BSD Unix; the later System V
|
|
versions of Unix from AT\&T added many enhancements and new functions.
|
|
BSD curses is no longer maintained, having been replaced by ncurses,
|
|
which is an open-source implementation of the AT\&T interface. If you're
|
|
using an open-source Unix such as Linux or FreeBSD, your system almost
|
|
certainly uses ncurses. Since most current commercial Unix versions
|
|
are based on System V code, all the functions described here will
|
|
probably be available. The older versions of curses carried by some
|
|
proprietary Unixes may not support everything, though.
|
|
|
|
No one has made a Windows port of the curses module. On a Windows
|
|
platform, try the Console module written by Fredrik Lundh. The
|
|
Console module provides cursor-addressable text output, plus full
|
|
support for mouse and keyboard input, and is available from
|
|
\url{http://effbot.org/efflib/console}.
|
|
|
|
\subsection{The Python curses module}
|
|
|
|
Thy 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 \function{addstr},
|
|
\function{mvaddstr}, \function{mvwaddstr}, into a single
|
|
\method{addstr()} method. You'll see this covered in more detail
|
|
later.
|
|
|
|
This HOWTO is simply an introduction to writing text-mode programs
|
|
with curses and Python. It doesn't attempt to be a complete guide to
|
|
the curses API; for that, see the Python library guide's serction on
|
|
ncurses, and the C manual pages for ncurses. It will, however, give
|
|
you the basic ideas.
|
|
|
|
\section{Starting and ending a curses application}
|
|
|
|
Before doing anything, curses must be initialized. This is done by
|
|
calling the \function{initscr()} function, which will determine the
|
|
terminal type, send any required setup codes to the terminal, and
|
|
create various internal data structures. If successful,
|
|
\function{initscr()} returns a window object representing the entire
|
|
screen; this is usually called \code{stdscr}, after the name of the
|
|
corresponding C
|
|
variable.
|
|
|
|
\begin{verbatim}
|
|
import curses
|
|
stdscr = curses.initscr()
|
|
\end{verbatim}
|
|
|
|
Usually curses applications turn off automatic echoing of keys to the
|
|
screen, in order to be able to read keys and only display them under
|
|
certain circumstances. This requires calling the \function{noecho()}
|
|
function.
|
|
|
|
\begin{verbatim}
|
|
curses.noecho()
|
|
\end{verbatim}
|
|
|
|
Applications will also commonly need to react to keys instantly,
|
|
without requiring the Enter key to be pressed; this is called cbreak
|
|
mode, as opposed to the usual buffered input mode.
|
|
|
|
\begin{verbatim}
|
|
curses.cbreak()
|
|
\end{verbatim}
|
|
|
|
Terminals usually return special keys, such as the cursor keys or
|
|
navigation keys such as Page Up and Home, as a multibyte escape
|
|
sequence. While you could write your application to expect such
|
|
sequences and process them accordingly, curses can do it for you,
|
|
returning a special value such as \constant{curses.KEY_LEFT}. To get
|
|
curses to do the job, you'll have to enable keypad mode.
|
|
|
|
\begin{verbatim}
|
|
stdscr.keypad(1)
|
|
\end{verbatim}
|
|
|
|
Terminating a curses application is much easier than starting one.
|
|
You'll need to call
|
|
|
|
\begin{verbatim}
|
|
curses.nocbreak(); stdscr.keypad(0); curses.echo()
|
|
\end{verbatim}
|
|
|
|
to reverse the curses-friendly terminal settings. Then call the
|
|
\function{endwin()} function to restore the terminal to its original
|
|
operating mode.
|
|
|
|
\begin{verbatim}
|
|
curses.endwin()
|
|
\end{verbatim}
|
|
|
|
A common problem when debugging a curses application is to get your
|
|
terminal messed up when the application dies without restoring the
|
|
terminal to its previous state. In Python this commonly happens when
|
|
your code is buggy and raises an uncaught exception. Keys are no
|
|
longer be echoed to the screen when you type them, for example, which
|
|
makes using the shell difficult.
|
|
|
|
In Python you can avoid these complications and make debugging much
|
|
easier by importing the module \module{curses.wrapper}. It supplies a
|
|
function \function{wrapper} that takes a hook argument. It does the
|
|
initializations described above, and also initializes colors if color
|
|
support is present. It then runs your hook, and then finally
|
|
deinitializes appropriately. The hook is called inside a try-catch
|
|
clause which catches exceptions, performs curses deinitialization, and
|
|
then passes the exception upwards. Thus, your terminal won't be left
|
|
in a funny state on exception.
|
|
|
|
\section{Windows and Pads}
|
|
|
|
Windows are the basic abstraction in curses. A window object
|
|
represents a rectangular area of the screen, and supports various
|
|
methods to display text, erase it, allow the user to input strings,
|
|
and so forth.
|
|
|
|
The \code{stdscr} object returned by the \function{initscr()} function
|
|
is a window object that covers the entire screen. Many programs may
|
|
need only this single window, but you might wish to divide the screen
|
|
into smaller windows, in order to redraw or clear them separately.
|
|
The \function{newwin()} function creates a new window of a given size,
|
|
returning the new window object.
|
|
|
|
\begin{verbatim}
|
|
begin_x = 20 ; begin_y = 7
|
|
height = 5 ; width = 40
|
|
win = curses.newwin(height, width, begin_y, begin_x)
|
|
\end{verbatim}
|
|
|
|
A word about the coordinate system used in curses: coordinates are
|
|
always passed in the order \emph{y,x}, and the top-left corner of a
|
|
window is coordinate (0,0). This breaks a common convention for
|
|
handling coordinates, where the \emph{x} coordinate usually comes
|
|
first. This is an unfortunate difference from most other computer
|
|
applications, but it's been part of curses since it was first written,
|
|
and it's too late to change things now.
|
|
|
|
When you call a method to display or erase text, the effect doesn't
|
|
immediately show up on the display. 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 is very important. This lets curses accumulate changes to the
|
|
screen, and display them in the most efficient manner. For example,
|
|
if your program displays some characters in a window, and then clears
|
|
the window, there's no need to send the original characters because
|
|
they'd never be visible.
|
|
|
|
Accordingly, curses requires that you explicitly tell it to redraw
|
|
windows, using the \function{refresh()} method of window objects. In
|
|
practice, this 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 simply calling \code{stdscr.refresh()} or the
|
|
\function{refresh()} method of some other relevant window.
|
|
|
|
A pad is a special case of a window; it can be larger than the actual
|
|
display screen, and only a portion of it displayed at a time.
|
|
Creating a pad simply requires the pad's height and width, while
|
|
refreshing a pad requires giving the coordinates of the on-screen
|
|
area where a subsection of the pad will be displayed.
|
|
|
|
\begin{verbatim}
|
|
pad = curses.newpad(100, 100)
|
|
# These loops fill the pad with letters; this is
|
|
# explained in the next section
|
|
for y in range(0, 100):
|
|
for x in range(0, 100):
|
|
try: pad.addch(y,x, ord('a') + (x*x+y*y) % 26 )
|
|
except curses.error: pass
|
|
|
|
# Displays a section of the pad in the middle of the screen
|
|
pad.refresh( 0,0, 5,5, 20,75)
|
|
\end{verbatim}
|
|
|
|
The \function{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 go, which will prevent annoying screen flicker at
|
|
refresh time. Use the methods \method{noutrefresh()} and/or
|
|
\method{noutrefresh()} of each window to update the data structure
|
|
representing the desired state of the screen; then change the physical
|
|
screen to match the desired state in one go with the function
|
|
\function{doupdate()}. The normal \method{refresh()} method calls
|
|
\function{doupdate()} as its last act.
|
|
|
|
\section{Displaying Text}
|
|
|
|
{}From a C programmer's point of view, curses may sometimes look like
|
|
a twisty maze of functions, all subtly different. For example,
|
|
\function{addstr()} displays a string at the current cursor location
|
|
in the \code{stdscr} window, while \function{mvaddstr()} moves to a
|
|
given y,x coordinate first before displaying the string.
|
|
\function{waddstr()} is just like \function{addstr()}, but allows
|
|
specifying a window to use, instead of using \code{stdscr} by default.
|
|
\function{mvwaddstr()} follows similarly.
|
|
|
|
Fortunately the Python interface hides all these details;
|
|
\code{stdscr} is a window object like any other, and methods like
|
|
\function{addstr()} accept multiple argument forms. Usually there are
|
|
four different forms.
|
|
|
|
\begin{tableii}{|c|l|}{textrm}{Form}{Description}
|
|
\lineii{\var{str} or \var{ch}}{Display the string \var{str} or
|
|
character \var{ch}}
|
|
\lineii{\var{str} or \var{ch}, \var{attr}}{Display the string \var{str} or
|
|
character \var{ch}, using attribute \var{attr}}
|
|
\lineii{\var{y}, \var{x}, \var{str} or \var{ch}}
|
|
{Move to position \var{y,x} within the window, and display \var{str}
|
|
or \var{ch}}
|
|
\lineii{\var{y}, \var{x}, \var{str} or \var{ch}, \var{attr}}
|
|
{Move to position \var{y,x} within the window, and display \var{str}
|
|
or \var{ch}, using attribute \var{attr}}
|
|
\end{tableii}
|
|
|
|
Attributes allow displaying text in highlighted forms, such as in
|
|
boldface, underline, reverse code, or in color. They'll be explained
|
|
in more detail in the next subsection.
|
|
|
|
The \function{addstr()} function takes a Python string as the value to
|
|
be displayed, while the \function{addch()} functions take a character,
|
|
which can be either a Python string of length 1, or an integer. If
|
|
it's a string, you're limited to displaying characters between 0 and
|
|
255. SVr4 curses provides constants for extension characters; these
|
|
constants are integers greater than 255. For example,
|
|
\constant{ACS_PLMINUS} is a +/- symbol, and \constant{ACS_ULCORNER} is
|
|
the upper left corner of a box (handy for drawing borders).
|
|
|
|
Windows remember where the cursor was left after the last operation,
|
|
so if you leave out the \var{y,x} coordinates, the string or character
|
|
will be displayed wherever the last operation left off. You can also
|
|
move the cursor with the \function{move(\var{y,x})} method. Because
|
|
some terminals always display a flashing cursor, you may want to
|
|
ensure that the cursor is positioned in some location where it won't
|
|
be distracting; it can be confusing to have the cursor blinking at
|
|
some apparently random location.
|
|
|
|
If your application doesn't need a blinking cursor at all, you can
|
|
call \function{curs_set(0)} to make it invisible. Equivalently, and
|
|
for compatibility with older curses versions, there's a
|
|
\function{leaveok(\var{bool})} function. When \var{bool} is true, the
|
|
curses library will attempt to suppress the flashing cursor, and you
|
|
won't need to worry about leaving it in odd locations.
|
|
|
|
\subsection{Attributes and Color}
|
|
|
|
Characters can be displayed in different ways. Status lines in a
|
|
text-based application are commonly shown in reverse video; a text
|
|
viewer may need to highlight certain words. curses supports this by
|
|
allowing you to specify an attribute for each cell on the screen.
|
|
|
|
An attribute is a integer, each bit representing a different
|
|
attribute. You can try to display text with multiple attribute bits
|
|
set, but curses doesn't guarantee that all the possible combinations
|
|
are available, or that they're all visually distinct. That depends on
|
|
the ability of the terminal being used, so it's safest to stick to the
|
|
most commonly available attributes, listed here.
|
|
|
|
\begin{tableii}{|c|l|}{constant}{Attribute}{Description}
|
|
\lineii{A_BLINK}{Blinking text}
|
|
\lineii{A_BOLD}{Extra bright or bold text}
|
|
\lineii{A_DIM}{Half bright text}
|
|
\lineii{A_REVERSE}{Reverse-video text}
|
|
\lineii{A_STANDOUT}{The best highlighting mode available}
|
|
\lineii{A_UNDERLINE}{Underlined text}
|
|
\end{tableii}
|
|
|
|
So, to display a reverse-video status line on the top line of the
|
|
screen,
|
|
you could code:
|
|
|
|
\begin{verbatim}
|
|
stdscr.addstr(0, 0, "Current mode: Typing mode",
|
|
curses.A_REVERSE)
|
|
stdscr.refresh()
|
|
\end{verbatim}
|
|
|
|
The curses library also supports color on those terminals that
|
|
provide it, The most common such terminal is probably the Linux
|
|
console, followed by color xterms.
|
|
|
|
To use color, you must call the \function{start_color()} function
|
|
soon after calling \function{initscr()}, to initialize the default
|
|
color set (the \function{curses.wrapper.wrapper()} function does this
|
|
automatically). Once that's done, the \function{has_colors()}
|
|
function returns TRUE if the terminal in use can actually display
|
|
color. (Note from AMK: curses uses the American spelling
|
|
'color', instead of the Canadian/British spelling 'colour'. If you're
|
|
like me, you'll have to resign yourself to misspelling it for the sake
|
|
of these functions.)
|
|
|
|
The curses library maintains a finite number of color pairs,
|
|
containing a foreground (or text) color and a background color. You
|
|
can get the attribute value corresponding to a color pair with the
|
|
\function{color_pair()} function; this can be bitwise-OR'ed with other
|
|
attributes such as \constant{A_REVERSE}, but again, such combinations
|
|
are not guaranteed to work on all terminals.
|
|
|
|
An example, which displays a line of text using color pair 1:
|
|
|
|
\begin{verbatim}
|
|
stdscr.addstr( "Pretty text", curses.color_pair(1) )
|
|
stdscr.refresh()
|
|
\end{verbatim}
|
|
|
|
As I said before, a color pair consists of a foreground and
|
|
background color. \function{start_color()} initializes 8 basic
|
|
colors when it activates color mode. They are: 0:black, 1:red,
|
|
2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The curses
|
|
module defines named constants for each of these colors:
|
|
\constant{curses.COLOR_BLACK}, \constant{curses.COLOR_RED}, and so
|
|
forth.
|
|
|
|
The \function{init_pair(\var{n, f, b})} function changes the
|
|
definition of color pair \var{n}, to foreground color {f} and
|
|
background color {b}. Color pair 0 is hard-wired to white on black,
|
|
and cannot be changed.
|
|
|
|
Let's put all this together. To change color 1 to red
|
|
text on a white background, you would call:
|
|
|
|
\begin{verbatim}
|
|
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
|
|
\end{verbatim}
|
|
|
|
When you change a color pair, any text already displayed using that
|
|
color pair will change to the new colors. You can also display new
|
|
text in this color with:
|
|
|
|
\begin{verbatim}
|
|
stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1) )
|
|
\end{verbatim}
|
|
|
|
Very fancy terminals can change the definitions of the actual colors
|
|
to a given RGB value. This lets you change color 1, which is usually
|
|
red, to purple or blue or any other color you like. Unfortunately,
|
|
the Linux console doesn't support this, so I'm unable to try it out,
|
|
and can't provide any examples. You can check if your terminal can do
|
|
this by calling \function{can_change_color()}, which returns TRUE if
|
|
the capability is there. If you're lucky enough to have such a
|
|
talented terminal, consult your system's man pages for more
|
|
information.
|
|
|
|
\section{User Input}
|
|
|
|
The curses library itself offers only very simple input mechanisms.
|
|
Python's support adds a text-input widget that makes up some of the
|
|
lack.
|
|
|
|
The most common way to get input to a window is to use its
|
|
\method{getch()} method. that pauses, and waits for the user to hit
|
|
a key, displaying it if \function{echo()} has been called earlier.
|
|
You can optionally specify a coordinate to which the cursor should be
|
|
moved before pausing.
|
|
|
|
It's possible to change this behavior with the method
|
|
\method{nodelay()}. After \method{nodelay(1)}, \method{getch()} for
|
|
the window becomes non-blocking and returns ERR (-1) when no input is
|
|
ready. There's also a \function{halfdelay()} function, which can be
|
|
used to (in effect) set a timer on each \method{getch()}; if no input
|
|
becomes available within the number of milliseconds specified as the
|
|
argument to \function{halfdelay()}, curses throws an exception.
|
|
|
|
The \method{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
|
|
\constant{curses.KEY_PPAGE}, \constant{curses.KEY_HOME}, or
|
|
\constant{curses.KEY_LEFT}. Usually the main loop of your program
|
|
will look something like this:
|
|
|
|
\begin{verbatim}
|
|
while 1:
|
|
c = stdscr.getch()
|
|
if c == ord('p'): PrintDocument()
|
|
elif c == ord('q'): break # Exit the while()
|
|
elif c == curses.KEY_HOME: x = y = 0
|
|
\end{verbatim}
|
|
|
|
The \module{curses.ascii} module supplies ASCII class membership
|
|
functions that take either integer or 1-character-string
|
|
arguments; these may be useful in writing more readable tests for
|
|
your command interpreters. It also supplies conversion functions
|
|
that take either integer or 1-character-string arguments and return
|
|
the same type. For example, \function{curses.ascii.ctrl()} returns
|
|
the control character corresponding to its argument.
|
|
|
|
There's also a method to retrieve an entire string,
|
|
\constant{getstr()}. It isn't used very often, because its
|
|
functionality is quite limited; the only editing keys available are
|
|
the backspace key and the Enter key, which terminates the string. It
|
|
can optionally be limited to a fixed number of characters.
|
|
|
|
\begin{verbatim}
|
|
curses.echo() # Enable echoing of characters
|
|
|
|
# Get a 15-character string, with the cursor on the top line
|
|
s = stdscr.getstr(0,0, 15)
|
|
\end{verbatim}
|
|
|
|
The Python \module{curses.textpad} module supplies something better.
|
|
With it, you can turn a window into a text box that supports an
|
|
Emacs-like set of keybindings. Various methods of \class{Textbox}
|
|
class support editing with input validation and gathering the edit
|
|
results either with or without trailing spaces. See the library
|
|
documentation on \module{curses.textpad} for the details.
|
|
|
|
\section{For More Information}
|
|
|
|
This HOWTO didn't cover some advanced topics, such as screen-scraping
|
|
or capturing mouse events from an xterm instance. But the Python
|
|
library page for the curses modules is now pretty complete. You
|
|
should browse it next.
|
|
|
|
If you're in doubt about the detailed behavior of any of the ncurses
|
|
entry points, 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 \constant{ACS_*} characters available to
|
|
you.
|
|
|
|
Because the curses API is so large, some functions aren't supported in
|
|
the Python interface, not because they're difficult to implement, but
|
|
because no one has needed them yet. Feel free to add them and then
|
|
submit a patch. Also, we don't yet have support for the menus or
|
|
panels libraries associated with ncurses; feel free to add that.
|
|
|
|
If you write an interesting little program, feel free to contribute it
|
|
as another demo. We can always use more of them!
|
|
|
|
The ncurses FAQ: \url{http://dickey.his.com/ncurses/ncurses.faq.html}
|
|
|
|
\end{document}
|