Die curses-Bibliothek stellt Funktionen bereit, mit denen man auf einem Textterminal graphikartige Ausgaben erzeugen und Tastatureingaben behandeln kann.

Probiere die folgenden Codebeispiele der Reihe nach aus.

Minimales Programm

import curses

def main(stdscr):
    # Bildschirm löschen; am Anfang nicht unbedingt notwendig.
    stdscr.clear()

    # Aufgrund einer Eigenheit der curses-Bibliothek
    # ist folgendes refresh notwendig, sonst funktionieren
    # einige Befehle nicht wie erwartet.
    stdscr.refresh()

    # Text auf Bildschirm ausgeben
    stdscr.addstr(0, 0, "Taste zum Beenden drücken...")

    # Warte auf beliebigen Tastendruck, bevor die
    # Konsole wieder aufgeräumt und das Programm beendet wird.
    stdscr.getkey()

# main-Funktion mit dem Wrapper starten.
# Der Wrapper sorgt für nötige Initialisierungen
# und Aufräumarbeiten im Fehlerfall.
curses.wrapper(main)

Text ausgeben

import curses

def main(stdscr):
    stdscr.clear()
    stdscr.refresh()

    # Text an bestimmten Koordinaten ausgeben:
    # Erster Parameter: Zeile, also y
    # Zweiter Parameter: Spalte, also x
    stdscr.addstr(0, 0, "Text an den Koordinaten y=0, x=0")
    stdscr.addstr(2, 20, "Text an den Koordinaten y=2, x=20")

    # curses.LINES gibt die Anzahl der Zeilen der aktuellen Konsole zurück.
    # curses.COLS die Anzahl der Spalten.
    # Folgender Befehl schreibt ein "X" ganz unten rechts in die Ecke.
    # (Falls die maximale Anzahl der Zeilen oder Spalten überschritten wird,
    #  kommt es zu einem Fehler: _curses.error: addwstr() returned ERR)
    stdscr.addstr(curses.LINES - 1, curses.COLS - 2, "X")

    # Gibt einen Text in der letzten Zeile aus
    stdscr.addstr(curses.LINES - 1, 0, "Taste zum Beenden drücken...")

    stdscr.getkey()

curses.wrapper(main)

Fenster / Debugging

import curses

# globale Variable
debugstr = ""

def main(stdscr):

    # Globale Variable innerhalb der Funktion bekannt machen
    # (ansonsten ist sie lokal).
    global debugstr

    stdscr.clear()
    stdscr.refresh()

    # Cursor ausblenden, wenn nicht benötigt.
    curses.curs_set(False)

    # Ein Fenster mit einer bestimmten Größe und Position erzeugen.
    begin_x = 5
    begin_y = 3
    height = 10
    width = 35
    win = curses.newwin(height, width, begin_y, begin_x)

    # Attribute des win-Objekts auflisten und dem debugstr zuweisen.
    debugstr = str(dir(win))

    # Das Fenster soll einen Rahmen haben.
    win.border()

    # Das Fenster mit Buchstaben füllen.
    for y in range(1, height - 1):
        for x in range(1, width - 1):
            win.addch(y, x, ord('a') + (x - 1) % 26)

    # Refresh, ansonsten wird der Fensterhinhalt nicht angezeigt.
    win.refresh()

    stdscr.addstr(curses.LINES - 1, 0, "Taste zum Beenden drücken...")
    stdscr.getkey()

curses.wrapper(main)

# Nach Beenden des Programms kann man die Debug-Nachricht auf der Konsole lesen.
print(debugstr)

Textattribute und Farben

import curses

def main(stdscr):

    stdscr.clear()
    stdscr.refresh()
    curses.curs_set(False)

    i = 0

    # Attribute setzen

    stdscr.addstr(i, 0, "Attribut: keins"); i += 1
    stdscr.addstr(i, 0, "Attribut: A_REVERSE", curses.A_REVERSE); i += 1
    stdscr.addstr(i, 0, "Attribut: A_BOLD", curses.A_BOLD); i += 1
    stdscr.addstr(i, 0, "Attribut: A_DIM (oft äquivalent zu keins)", curses.A_DIM); i += 1
    stdscr.addstr(i, 0, "Attribut: A_BLINK", curses.A_BLINK); i += 1
    stdscr.addstr(i, 0, "Attribut: A_STANDOUT (oft äquivalent zu A_BOLD)", curses.A_STANDOUT); i += 1
    stdscr.addstr(i, 0, "Attribut: A_UNDERLINE", curses.A_UNDERLINE); i += 1

    i += 1 # Leere Zeile

    # Farben setzen.
    #
    # Farben kann man immer nur in der Kombination
    # Text- und Hintergrundfarbe setzen.
    # Diese Kombination wird in einer color_pair-Nummer > 0 registriert.

    curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
    stdscr.addstr(i, 0, "Farbe: COLOR_RED", curses.color_pair(1))
    i += 1

    curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
    stdscr.addstr(i, 0, "Farbe: COLOR_BLUE", curses.color_pair(2))
    i += 1

    colors = [ curses.COLOR_CYAN, curses.COLOR_GREEN, curses.COLOR_MAGENTA,
               curses.COLOR_WHITE, curses.COLOR_YELLOW ]

    for ci, col in enumerate(colors):
        col_pair_num = ci + 3
        curses.init_pair(col_pair_num, col, curses.COLOR_BLACK)
        stdscr.addstr(i, 0, "Farbe: {}".format(col), curses.color_pair(col_pair_num))
        i += 1

    i += 1 # Leere Zeile

    # Kombination aus Attributen und Farben.
    # HINWEIS: Nicht alle Kombinationen aus Attributen und Farben funktionieren.

    # color_pair-Nummer 1 von oben
    stdscr.addstr(i, 0, "Farbe und Blinken 1", curses.color_pair(1) + curses.A_BLINK); i += 1

    curses.init_pair(9, curses.COLOR_BLUE, curses.COLOR_YELLOW)
    stdscr.addstr(i, 0, "Farbe und Blinken 2", curses.color_pair(9) + curses.A_BLINK); i += 1

    stdscr.addstr(curses.LINES - 1, 0, "Taste zum Beenden drücken...")
    stdscr.refresh()
    stdscr.getkey()

curses.wrapper(main)

Benutzer-Eingabe

import curses

def main(stdscr):

    stdscr.clear()
    stdscr.refresh()

    prompt = "Bitte Text eingeben: "
    stdscr.addstr(0, 0, prompt)

    # Wenn Tasten gedrückt werden, dann sollen diese auch sichtbar sein.
    curses.echo()

    # Liest einen String mit der maximalen Länge 15 ein.
    # Der zurückgegebe Wert ist ein byte-String, der mit
    # der decode-Funktion in einen normalen String konvertiert
    # wird.
    s = stdscr.getstr(0, len(prompt), 15).decode("utf-8")

    # Ausgabe von gedrückten Tasten wieder abstellen.
    curses.noecho()

    stdscr.addstr(1, 0, "Der Text war '{}'.".format(s))

    stdscr.addstr(curses.LINES - 1, 0, "Taste zum Beenden drücken...")
    stdscr.refresh()
    stdscr.getkey()

curses.wrapper(main)

Referenzen