Curses-Programmierung mit Python
********************************


Zusammenfassung
^^^^^^^^^^^^^^^

Dieses Dokument beschreibt, wie du mit dem Erweiterungsmodul "curses"
Anzeigen im Textmodus steuerst.


Was ist curses?
===============

Die curses-Bibliothek bietet für textbasierte Terminals eine
terminalunabhängige Einrichtung zum Zeichnen des Bildschirms und zum
Verarbeiten der Tastatur. Zu solchen Terminals gehören VT100-Geräte,
die Linux-Konsole und das nachgebildete Terminal, das verschiedene
Programme bereitstellen. Anzeigeterminals unterstützen verschiedene
Steuercodes für verbreitete Vorgänge wie das Bewegen des Cursors, das
Rollen des Bildschirms und das Löschen von Bereichen. Unterschiedliche
Terminals verwenden stark voneinander abweichende Codes und haben oft
ihre eigenen kleinen Eigenheiten.

In einer Welt grafischer Anzeigen könnte man fragen: „Warum die Mühe?“
Es ist wahr, dass Terminals mit Zeichenzellen eine überholte Technik
sind, aber es gibt Nischen, in denen es noch wertvoll ist, ausgefeilte
Dinge mit ihnen tun zu können. Eine Nische sind kleine oder
eingebettete Unix-Systeme, auf denen kein X-Server läuft. Eine andere
sind Werkzeuge wie Betriebssysteminstallationsprogramme und Kernel-
Konfiguratoren, die möglicherweise laufen müssen, bevor überhaupt
grafische Unterstützung verfügbar ist.

Die curses-Bibliothek bietet recht grundlegende Funktionalität: Sie
gibt dem Programmierer die Abstraktion einer Anzeige, die mehrere sich
nicht überlappende Textfenster enthält. Der Inhalt eines Fensters
lässt sich auf verschiedene Weisen ändern – Text hinzufügen, löschen,
sein Erscheinungsbild ändern – und die curses-Bibliothek findet
heraus, welche Steuercodes an das Terminal gesendet werden müssen, um
die richtige Ausgabe zu erzeugen. curses bietet nicht viele Konzepte
für Benutzeroberflächen wie Schaltflächen, Kontrollkästchen oder
Dialoge, wenn du solche Funktionen brauchst, erwäge eine Bibliothek
für Benutzeroberflächen wie Urwid.

Die curses-Bibliothek wurde ursprünglich für BSD Unix geschrieben. Die
späteren System-V-Versionen von Unix von AT&T ergänzten viele
Verbesserungen und neue Funktionen. BSD-curses wird nicht mehr
gepflegt und wurde durch ncurses ersetzt, eine Open-Source-Umsetzung
der AT&T-Schnittstelle. Wenn du ein Open-Source-Unix wie Linux oder
FreeBSD verwendest, nutzt dein System so gut wie sicher ncurses. Da
die meisten heutigen kommerziellen Unix-Versionen auf System-V-Code
beruhen, werden alle hier beschriebenen Funktionen wahrscheinlich
verfügbar sein. Die älteren curses-Versionen, die manche proprietären
Unix-Systeme mitbringen, unterstützen allerdings möglicherweise nicht
alles.

Die Windows-Fassung von Python enthält das Modul "curses" nicht. Das
Paket windows-curses von Dritten bietet dieselbe Schnittstelle unter
Windows.


Das Python-Modul curses
-----------------------

Das Python-Modul ist eine recht einfache Hülle um die C-Funktionen,
die curses bereitstellt, wenn du mit curses-Programmierung in C schon
vertraut bist, lässt sich dieses Wissen wirklich leicht auf Python
übertragen. Der größte Unterschied ist, dass die Python-Schnittstelle
die Dinge vereinfacht, indem sie verschiedene C-Funktionen wie
"addstr()", "mvaddstr()" und "mvwaddstr()" zu einer einzigen Methode
"addstr()" zusammenfasst. Das wird später ausführlicher behandelt.

Dieses HOWTO ist eine Einführung in das Schreiben von Textmodus-
Programmen mit curses und Python. Es versucht nicht, eine vollständige
Anleitung zur curses-API zu sein. Dafür lies den Abschnitt zu ncurses
in der Anleitung zur Python-Bibliothek und die C-Handbuchseiten zu
ncurses. Es gibt dir aber die grundlegenden Ideen mit.


Eine curses-Anwendung starten und beenden
=========================================

Bevor irgendetwas geschieht, muss curses initialisiert werden. Das
geschieht durch den Aufruf der Funktion "initscr()", die den
Terminaltyp bestimmt, alle nötigen Einrichtungscodes an das Terminal
sendet und verschiedene interne Datenstrukturen anlegt. Bei Erfolg
gibt "initscr()" ein Fensterobjekt zurück, das den gesamten Bildschirm
darstellt. Es wird üblicherweise "stdscr" genannt, nach dem Namen der
entsprechenden C-Variablen.

   import curses
   stdscr = curses.initscr()

Üblicherweise schalten curses-Anwendungen das automatische Anzeigen
der Tastenanschläge auf dem Bildschirm ab, um Tasten lesen und sie nur
unter bestimmten Umständen anzeigen zu können. Dazu ist ein Aufruf der
Funktion "noecho()" nötig.

   curses.noecho()

Häufig müssen Anwendungen außerdem unmittelbar auf Tasten reagieren,
ohne dass die Eingabetaste gedrückt werden muss. Das heißt cbreak-
Modus, im Gegensatz zum üblichen gepufferten Eingabemodus.

   curses.cbreak()

Terminals geben besondere Tasten – etwa die Cursortasten oder
Navigationstasten wie Bild auf und Pos1 – üblicherweise als Mehrbyte-
Escape-Sequenz zurück. Du könntest deine Anwendung so schreiben, dass
sie solche Sequenzen erwartet und entsprechend verarbeitet, aber
curses kann das für dich tun und einen besonderen Wert wie
"curses.KEY_LEFT" zurückgeben. Damit curses diese Arbeit übernimmt,
musst du den keypad-Modus einschalten.

   stdscr.keypad(True)

Eine curses-Anwendung zu beenden ist viel leichter, als sie zu
starten. Du musst Folgendes aufrufen:

   curses.nocbreak()
   stdscr.keypad(False)
   curses.echo()

um die curses-freundlichen Terminaleinstellungen rückgängig zu machen.
Rufe dann die Funktion "endwin()" auf, um das Terminal in seine
ursprüngliche Betriebsart zurückzuversetzen.

   curses.endwin()

Ein verbreitetes Problem beim Debuggen einer curses-Anwendung ist,
dass dein Terminal durcheinandergerät, wenn die Anwendung stirbt, ohne
das Terminal in seinen früheren Zustand zurückzuversetzen. In Python
passiert das häufig, wenn dein Code fehlerhaft ist und eine nicht
gefangene Ausnahme auslöst. Tastenanschläge werden dann zum Beispiel
nicht mehr auf dem Bildschirm angezeigt, was die Arbeit mit der Shell
erschwert.

In Python kannst du diese Komplikationen vermeiden und das Debuggen
deutlich erleichtern, indem du die Funktion "curses.wrapper()"
importierst und so verwendest:

   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)

Die Funktion "wrapper()" nimmt ein aufrufbares Objekt und führt die
oben beschriebenen Initialisierungen durch. Sie initialisiert auch die
Farben, wenn Farbunterstützung vorhanden ist. "wrapper()" führt dann
das von dir übergebene aufrufbare Objekt aus. Sobald es zurückkehrt,
versetzt "wrapper()" das Terminal in seinen ursprünglichen Zustand
zurück. Das aufrufbare Objekt wird innerhalb eines "try"…"except"
aufgerufen, das Ausnahmen fängt, den Zustand des Terminals
wiederherstellt und die Ausnahme dann erneut auslöst. Dein Terminal
bleibt bei einer Ausnahme also nicht in einem komischen Zustand
zurück, und du kannst die Meldung der Ausnahme und den Traceback
lesen.


Fenster und Pads
================

Fenster sind die grundlegende Abstraktion in curses. Ein Fensterobjekt
stellt einen rechteckigen Bereich des Bildschirms dar und unterstützt
Methoden, um Text anzuzeigen, ihn zu löschen, den Benutzer
Zeichenketten eingeben zu lassen und so weiter.

Das Objekt "stdscr", das die Funktion "initscr()" zurückgibt, ist ein
Fensterobjekt, das den gesamten Bildschirm abdeckt. Viele Programme
brauchen nur dieses einzige Fenster, aber du möchtest den Bildschirm
vielleicht in kleinere Fenster teilen, um sie getrennt neu zu zeichnen
oder zu leeren. Die Funktion "newwin()" erzeugt ein neues Fenster
gegebener Größe und gibt das neue Fensterobjekt zurück.

   begin_x = 20; begin_y = 7
   height = 5; width = 40
   win = curses.newwin(height, width, begin_y, begin_x)

Beachte, dass das in curses verwendete Koordinatensystem ungewöhnlich
ist. Koordinaten werden immer in der Reihenfolge *y,x* übergeben, und
die linke obere Ecke eines Fensters ist die Koordinate (0,0). Das
bricht die übliche Konvention für Koordinaten, bei der die
*x*-Koordinate zuerst kommt. Das ist ein unglücklicher Unterschied zu
den meisten anderen Computeranwendungen, aber es gehört zu curses,
seit es zum ersten Mal geschrieben wurde, und es ist zu spät, daran
noch etwas zu ändern.

Deine Anwendung kann die Größe des Bildschirms über die Variablen
"curses.LINES" und "curses.COLS" bestimmen, um die *y*- und *x*-Größe
zu bekommen. Zulässige Koordinaten reichen dann von "(0,0)" bis
"(curses.LINES - 1, curses.COLS - 1)".

Wenn du eine Methode aufrufst, um Text anzuzeigen oder zu löschen,
erscheint die Wirkung nicht sofort auf der Anzeige. Du musst
stattdessen die Methode "refresh()" der Fensterobjekte aufrufen, um
den Bildschirm zu aktualisieren.

Der Grund: curses wurde ursprünglich mit Blick auf langsame
Terminalverbindungen mit 300 Baud geschrieben. Bei diesen Terminals
war es sehr wichtig, die Zeit zum Neuzeichnen des Bildschirms gering
zu halten. curses sammelt die Änderungen am Bildschirm stattdessen und
zeigt sie auf die effizienteste Weise an, wenn du "refresh()"
aufrufst. Wenn dein Programm zum Beispiel etwas Text in einem Fenster
anzeigt und das Fenster dann leert, muss der ursprüngliche Text nicht
gesendet werden, weil er nie sichtbar ist.

In der Praxis verkompliziert es die Programmierung mit curses nicht
wirklich sehr, curses ausdrücklich zum Neuzeichnen eines Fensters
aufzufordern. Die meisten Programme stürzen sich in eine Flut von
Tätigkeiten und halten dann an, um auf einen Tastenanschlag oder eine
andere Handlung des Benutzers zu warten. Du musst nur sicherstellen,
dass der Bildschirm neu gezeichnet wurde, bevor du zum Warten auf
Benutzereingaben anhältst – indem du zuvor "stdscr.refresh()" oder die
Methode "refresh()" eines anderen betroffenen Fensters aufrufst.

Ein Pad ist ein Sonderfall eines Fensters. Es kann größer sein als der
tatsächliche Anzeigebildschirm, und es wird jeweils nur ein Teil des
Pads angezeigt. Zum Erzeugen eines Pads sind Höhe und Breite des Pads
nötig, während beim Aktualisieren eines Pads die Koordinaten des
Bildschirmbereichs angegeben werden müssen, in dem ein Teilbereich des
Pads angezeigt wird.

   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)

Der Aufruf von "refresh()" zeigt einen Teilbereich des Pads in dem
Rechteck an, das sich auf dem Bildschirm von Koordinate (5,5) bis
Koordinate (20,75) erstreckt. Die linke obere Ecke des angezeigten
Teilbereichs ist die Koordinate (0,0) auf dem Pad. Abgesehen von
diesem Unterschied sind Pads genau wie gewöhnliche Fenster und
unterstützen dieselben Methoden.

Wenn du mehrere Fenster und Pads auf dem Bildschirm hast, gibt es
einen effizienteren Weg, den Bildschirm zu aktualisieren und das
störende Flimmern zu verhindern, das beim Aktualisieren jedes
Bildschirmteils entsteht. "refresh()" tut nämlich zwei Dinge:

1. Es ruft die Methode "noutrefresh()" jedes Fensters auf, um eine
   darunterliegende Datenstruktur zu aktualisieren, die den
   gewünschten Zustand des Bildschirms darstellt.

2. Es ruft die Funktion "doupdate()" auf, um den tatsächlichen
   Bildschirm an den gewünschten Zustand anzupassen, der in der
   Datenstruktur festgehalten ist.

Du kannst stattdessen "noutrefresh()" für eine Reihe von Fenstern
aufrufen, um die Datenstruktur zu aktualisieren, und dann
"doupdate()", um den Bildschirm zu aktualisieren.


Text anzeigen
=============

Aus der Sicht eines C-Programmierers kann curses manchmal wie ein
verwundenes Labyrinth von Funktionen aussehen, die alle feine
Unterschiede haben. So zeigt zum Beispiel "addstr()" eine Zeichenkette
an der aktuellen Cursorposition im Fenster "stdscr" an, während
"mvaddstr()" zuerst zu einer gegebenen y,x-Koordinate springt, bevor
es die Zeichenkette anzeigt. "waddstr()" ist genau wie "addstr()",
erlaubt aber die Angabe eines Fensters anstelle der Vorgabe "stdscr".
"mvwaddstr()" erlaubt die Angabe von Fenster und Koordinate.

Zum Glück verbirgt die Python-Schnittstelle all diese Details.
"stdscr" ist ein Fensterobjekt wie jedes andere, und Methoden wie
"addstr()" nehmen mehrere Argumentformen an. Üblicherweise gibt es
vier verschiedene Formen.

+-----------------------------------+-------------------------------------------------+
| Form                              | Beschreibung                                    |
|===================================|=================================================|
| *str* or *ch*                     | Zeigt die Zeichenkette *str* oder das Zeichen   |
|                                   | *ch* an der aktuellen Position an               |
+-----------------------------------+-------------------------------------------------+
| *str* or *ch*, *attr*             | Zeigt die Zeichenkette *str* oder das Zeichen   |
|                                   | *ch* mit dem Attribut *attr* an der aktuellen   |
|                                   | Position an                                     |
+-----------------------------------+-------------------------------------------------+
| *y*, *x*, *str* or *ch*           | Springt zur Position *y,x* innerhalb des        |
|                                   | Fensters und zeigt *str* oder *ch* an           |
+-----------------------------------+-------------------------------------------------+
| *y*, *x*, *str* or *ch*, *attr*   | Springt zur Position *y,x* innerhalb des        |
|                                   | Fensters und zeigt *str* oder *ch* mit dem      |
|                                   | Attribut *attr* an                              |
+-----------------------------------+-------------------------------------------------+

Attribute erlauben es, Text in hervorgehobenen Formen anzuzeigen, etwa
fett, unterstrichen, invertiert oder in Farbe. Sie werden im nächsten
Unterabschnitt ausführlicher erklärt.

Die Methode "addstr()" nimmt als anzuzeigenden Wert eine Python-
Zeichenkette oder eine Bytefolge. Der Inhalt von Bytefolgen wird
unverändert an das Terminal gesendet. In einem Build ohne
Unterstützung für breite Zeichen werden Zeichenketten mit dem Wert des
Attributs "encoding" des Fensters kodiert. Dieses entspricht
standardmäßig der Standardkodierung des Systems, wie sie
"locale.getencoding()" zurückgibt.

Die Methoden "addch()" nehmen ein Zeichen, das entweder eine
Zeichenkette der Länge 1, eine Bytefolge der Länge 1 oder eine
Ganzzahl sein kann.

Für die Zeichen des alternativen Zeichensatzes des Terminals gibt es
Konstanten. So ist zum Beispiel "ACS_PLMINUS" ein Plus-Minus-Zeichen
und "ACS_ULCORNER" die linke obere Ecke eines Kastens (praktisch zum
Zeichnen von Rahmen). Du kannst auch das passende Unicode-Zeichen
verwenden.

Fenster merken sich, wo der Cursor nach der letzten Operation stehen
geblieben ist. Lässt du die Koordinaten *y,x* weg, wird die
Zeichenkette oder das Zeichen also dort angezeigt, wo die letzte
Operation endete. Du kannst den Cursor auch mit der Methode
"move(y,x)" bewegen. Weil manche Terminals immer einen blinkenden
Cursor anzeigen, solltest du dafür sorgen, dass der Cursor an einer
Stelle steht, an der er nicht ablenkt. Es kann verwirren, wenn der
Cursor an einer scheinbar zufälligen Stelle blinkt.

Wenn deine Anwendung überhaupt keinen blinkenden Cursor braucht,
kannst du "curs_set(False)" aufrufen, um ihn unsichtbar zu machen. Die
Fenstermethode "leaveok()" tut etwas anderes: Ist ihr Argument wahr,
lässt curses den Cursor dort, wo die letzte Aktualisierung ihn
hingesetzt hat, anstatt ihn zurück auf die Cursorposition des Fensters
zu bewegen.


Attribute und Farbe
-------------------

Zeichen lassen sich auf verschiedene Weisen anzeigen. Statuszeilen in
einer textbasierten Anwendung werden häufig invertiert dargestellt,
oder ein Textbetrachter muss bestimmte Wörter hervorheben. curses
unterstützt das, indem du für jede Zelle des Bildschirms ein Attribut
angeben kannst.

Ein Attribut ist eine Ganzzahl, wobei jedes Bit für ein anderes
Attribut steht. Du kannst versuchen, Text mit mehreren gesetzten
Attributbits anzuzeigen, aber curses sichert nicht zu, dass alle
möglichen Kombinationen verfügbar oder visuell voneinander
unterscheidbar sind. Das hängt von den Fähigkeiten des verwendeten
Terminals ab. Am sichersten bleibst du daher bei den am häufigsten
verfügbaren Attributen, die hier aufgeführt sind.

+------------------------+----------------------------------------+
| Attribut               | Beschreibung                           |
|========================|========================================|
| "A_BLINK"              | Blinkender Text                        |
+------------------------+----------------------------------------+
| "A_BOLD"               | Besonders heller oder fetter Text      |
+------------------------+----------------------------------------+
| "A_DIM"                | Halbheller Text                        |
+------------------------+----------------------------------------+
| "A_REVERSE"            | Invertierter Text                      |
+------------------------+----------------------------------------+
| "A_STANDOUT"           | Die beste verfügbare Hervorhebungsart  |
+------------------------+----------------------------------------+
| "A_UNDERLINE"          | Unterstrichener Text                   |
+------------------------+----------------------------------------+

Um also eine invertierte Statuszeile in der obersten Zeile des
Bildschirms anzuzeigen, könntest du Folgendes schreiben:

   stdscr.addstr(0, 0, "Current mode: Typing mode",
                 curses.A_REVERSE)
   stdscr.refresh()

Die curses-Bibliothek unterstützt auf den Terminals, die das anbieten,
außerdem Farbe. Das verbreitetste solche Terminal ist wahrscheinlich
die Linux-Konsole, gefolgt von farbfähigen xterms.

Um Farbe zu verwenden, musst du kurz nach dem Aufruf von "initscr()"
die Funktion "start_color()" aufrufen, um den Standardfarbsatz zu
initialisieren (die Funktion "curses.wrapper()" tut das automatisch).
Ist das erledigt, gibt die Funktion "has_colors()" TRUE zurück, wenn
das verwendete Terminal tatsächlich Farbe anzeigen kann. (Beachte:
curses verwendet die amerikanische Schreibweise „color“ anstelle der
kanadisch-britischen Schreibweise „colour“. Wenn du an die britische
Schreibweise gewöhnt bist, musst du dich für diese Funktionen damit
abfinden, sie falsch zu schreiben.)

Die curses-Bibliothek führt eine begrenzte Anzahl von Farbpaaren, die
jeweils eine Vordergrundfarbe (oder Textfarbe) und eine
Hintergrundfarbe enthalten. Den Attributwert, der zu einem Farbpaar
gehört, bekommst du mit der Funktion "color_pair()". Dieser lässt sich
bitweise mit anderen Attributen wie "A_REVERSE" ODER-verknüpfen, aber
auch hier ist nicht zugesichert, dass solche Kombinationen auf allen
Terminals funktionieren.

Ein Beispiel, das eine Textzeile mit dem Farbpaar 1 anzeigt:

   stdscr.addstr("Pretty text", curses.color_pair(1))
   stdscr.refresh()

Wie gesagt besteht ein Farbpaar aus einer Vordergrund- und einer
Hintergrundfarbe. Die Funktion "init_pair(n, f, b)" ändert die
Definition des Farbpaars *n* auf die Vordergrundfarbe f und die
Hintergrundfarbe b. Das Farbpaar 0 ist fest auf Weiß auf Schwarz
verdrahtet und lässt sich nicht ändern.

Farben sind durchnummeriert, und "start_color()" initialisiert beim
Einschalten des Farbmodus 8 Grundfarben. Es sind: 0: Schwarz, 1: Rot,
2: Grün, 3: Gelb, 4: Blau, 5: Magenta, 6: Cyan und 7: Weiß. Das Modul
"curses" definiert für jede dieser Farben eine benannte Konstante:
"curses.COLOR_BLACK", "curses.COLOR_RED" und so weiter.

Fügen wir das alles zusammen. Um die Farbe 1 auf roten Text auf weißem
Hintergrund zu ändern, würdest du Folgendes aufrufen:

   curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)

Wenn du ein Farbpaar änderst, wechselt aller bereits mit diesem
Farbpaar angezeigte Text auf die neuen Farben. Du kannst neuen Text in
dieser Farbe auch so anzeigen:

   stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1))

Sehr ausgefeilte Terminals können die Definitionen der tatsächlichen
Farben auf einen gegebenen RGB-Wert ändern. Damit kannst du die Farbe
1, die üblicherweise Rot ist, auf Violett, Blau oder jede andere Farbe
ändern, die du magst. Leider unterstützt die Linux-Konsole das nicht,
ich kann es daher nicht ausprobieren und keine Beispiele geben. Ob
dein Terminal das kann, prüfst du mit einem Aufruf von
"can_change_color()", das "True" zurückgibt, wenn die Fähigkeit
vorhanden ist. Wenn du das Glück hast, ein so begabtes Terminal zu
besitzen, findest du weitere Informationen in den Handbuchseiten
deines Systems.


Benutzereingaben
================

Die C-Bibliothek curses bietet nur sehr einfache Eingabemechanismen.
Das Python-Modul "curses" ergänzt ein grundlegendes Bedienelement zur
Texteingabe. (Andere Bibliotheken wie Urwid haben umfangreichere
Sammlungen von Bedienelementen.)

Es gibt drei Methoden, um Eingaben aus einem Fenster zu bekommen:

* "get_wch()" aktualisiert den Bildschirm und wartet dann darauf, dass
  der Benutzer eine Taste drückt. Die Taste wird angezeigt, wenn
  "echo()" vorher aufgerufen wurde. Du kannst wahlweise eine
  Koordinate angeben, zu der der Cursor vor dem Anhalten bewegt werden
  soll.

* "getch()" tut dasselbe, gibt aber anstelle eines Zeichens den Code
  der Taste zurück. Bei ncurses ist das ein einzelnes Byte der
  Kodierung der Taste im aktuellen Gebietsschema. Ein Zeichen, das mit
  mehreren Bytes kodiert ist, braucht daher mehrere Aufrufe, ein Byte
  je Aufruf.

* "getkey()" tut dasselbe wie "getch()", gibt aber eine Zeichenkette
  zurück: eine gewöhnliche Taste als Zeichenkette mit einem Zeichen
  und eine besondere Taste als ihren Namen, etwa "KEY_UP".

Mit der Fenstermethode "nodelay()" ist es möglich, nicht auf den
Benutzer zu warten. Nach "nodelay(True)" blockieren die Lesevorgänge
für das Fenster nicht mehr. Um anzuzeigen, dass keine Eingabe
bereitsteht, löst "get_wch()" und "getkey()" eine Ausnahme aus, und
"getch()" gibt "-1" zurück. Es gibt außerdem eine Funktion
"halfdelay()", mit der sich (im Ergebnis) für jeden Lesevorgang ein
Zeitgeber setzen lässt. Steht innerhalb einer angegebenen Verzögerung
(in Zehntelsekunden) keine Eingabe bereit, scheitert der Lesevorgang
auf dieselbe Weise.

Besondere Tasten wie Bild auf, Pos1 oder die Cursortasten werden von
allen drei Methoden als eine der KEY_*-Konstanten zurückgegeben, die
alle größer als 255 sind. Du kannst den zurückgegebenen Wert mit
Konstanten wie "curses.KEY_PPAGE", "curses.KEY_HOME" oder
"curses.KEY_LEFT" vergleichen. Die Hauptschleife deines Programms kann
etwa so aussehen:

   while True:
       c = stdscr.get_wch()
       if c == 'p':
           PrintDocument()
       elif c == 'q':
           break  # Exit the while loop
       elif c == curses.KEY_HOME:
           x = y = 0

Das Modul "curses.ascii" stellt Funktionen bereit, die die
Zugehörigkeit zu ASCII-Klassen prüfen und entweder ganzzahlige
Argumente oder Zeichenketten mit einem Zeichen annehmen. Die können
nützlich sein, um lesbarere Prüfungen für solche Schleifen zu
schreiben. Es stellt außerdem Umwandlungsfunktionen bereit, die
entweder ganzzahlige Argumente oder Zeichenketten mit einem Zeichen
annehmen und denselben Typ zurückgeben. So gibt zum Beispiel
"curses.ascii.ctrl()" das Steuerzeichen zurück, das zu seinem Argument
gehört.

Es gibt außerdem eine Methode, um eine ganze Zeile abzurufen:
"getstr()". Sie wird nicht sehr oft verwendet, weil ihre
Funktionalität recht begrenzt ist. Die einzigen verfügbaren
Bearbeitungstasten sind das Lösch- und das Kill-Zeichen sowie die
Eingabetaste, die die Zeile beendet. Sie gibt ein bytes-Objekt zurück
und lässt sich wahlweise auf eine feste Anzahl von Bytes begrenzen.

   curses.echo()            # Enable echoing of characters

   # Get a line of at most 15 bytes, with the cursor on the top line
   s = stdscr.getstr(0,0, 15)

Das Modul "curses.textpad" stellt ein Textfeld bereit, das einen
Emacs-artigen Satz von Tastenbelegungen unterstützt. Verschiedene
Methoden der Klasse "Textbox" unterstützen das Bearbeiten mit
Eingabeprüfung und das Einsammeln der Bearbeitungsergebnisse – mit
oder ohne angehängte Leerzeichen. Hier ein Beispiel:

   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()

Näheres steht in der Bibliotheksdokumentation zu "curses.textpad".


Weitere Informationen
=====================

Dieses HOWTO behandelt einige fortgeschrittene Themen nicht, etwa das
Lesen des Bildschirminhalts oder das Erfassen von Mausereignissen aus
einer xterm-Instanz, aber die Bibliotheksseite zum Modul "curses" ist
inzwischen recht vollständig. Die solltest du als Nächstes durchsehen.

Wenn du über das genaue Verhalten der curses-Funktionen im Zweifel
bist, lies die Handbuchseiten deiner curses-Umsetzung, ob ncurses oder
die eines proprietären Unix-Anbieters. Die Handbuchseiten
dokumentieren alle Eigenheiten und geben vollständige Listen aller
Funktionen, Attribute und ACS_*-Zeichen, die dir zur Verfügung stehen.

Weil die curses-API so groß ist, werden manche Funktionen in der
Python-Schnittstelle nicht unterstützt. Oft liegt das nicht daran,
dass sie schwer umzusetzen wären, sondern daran, dass sie bisher
niemand gebraucht hat. Außerdem unterstützt Python die
Menü-Bibliothek, die zu ncurses gehört, noch nicht. Patches, die
Unterstützung dafür ergänzen, sind willkommen. Wie du Patches bei
Python einreichst, erfährst du im Python Developer's Guide.

* Writing Programs with NCURSES: ein ausführliches Tutorial für
  C-Programmierer.

* Die ncurses-Handbuchseite

* Die ncurses-FAQ

* „Use curses... don't swear“: Video eines Vortrags von der PyCon 2013
  über die Steuerung von Terminals mit curses oder Urwid.

* „Console Applications with Urwid“: Video eines Vortrags von der
  PyCon CA 2012, der einige mit Urwid geschriebene Anwendungen
  vorführt.
