Module zur Kursorsteuerung im Textmodus für Win oder Linux

Zurück zur Übersicht

 

Als alter TPascal - Progammierer ist einem noch die schön einfache Kursorsteuerung mit der Unit Crt vertraut. Mit dem Kommando gotoxy(x, y) konnte der Kursor im Textfeld gesetzt werden. Das geht auch in der supermodernen Programmiersprache Python, wozu man aber die geeigneten Module finden muss, denn solche Funktionen sind nicht eingebaut. Also auf die Suche! (Weiterlesen zur Kursorsteuerung unter Linux)


1. Windows

Irgendwo lief mir mal ein kleines Pythonmodul (ansi.py) über den Weg, das die alten ANSI - Befehle unterstützt. Es setzt aber voraus, dass der ansi.sys - Treiber installiert ist und es geht zwar noch mit Win9x aber nicht mehr unter WinNT/2000/XP. Dafür nutzte es die Möglichkeiten des ANSI - Treibers richtig aus. So konnte man den Bildschirm leeren, den Kursor nach links oben oder auf jede andere gewünschte Text-Position auf dem Screen setzen und allerlei Spielchen mit Vorder- und Hintergrundfarbe der Texte machen. Leider gab es kein inkey() bzw. getch(), mit dem man einen Tastendruck auch ohne die Verbindung mit der Entertaste auswerten könnte. Allerdings konnte man hierfür wiederum das Modul msvcrt einsetzen, das genau diese Tastaturabfrage im Textmodus ermöglicht

Das alles hat sich nun inzwischen geändert. Durch einen heißen Tip in Form einer Antwort auf meine diesbezügliche Anftrage auf http://www.python.de erhielt ich den hier zu findenen Link, der mir zum Modul WConio.py verhalf. Es wurde dort wie folgt beschrieben:

Old Turbo-C CONIO.H compatibility library for LCC-Win32
and GCC/EGCS Mingw32 compilers.
Version 1.0 (September 1999).
Created by Daniel Guerrero Miralles (daguer@geocities.com).
This source code is public domain.

Also stammt es aus den klassischen Borland-Produkten. Nun, hier ist wirklich alles im Stück vertreten, was ich oben aufgeführt habe (hier mal kurz rausgeschnitten, welche Fuktionsvielfalt da ist). Beschreibung siehe Link.

WConio.error
WConio.cgets(length)
WConio.clreol()
WConio.clrscr()
WConio.cputs(string)
WConio.delline()
WConio.getch()
WConio.getche()
WConio.getkey()
WConio.gettext(left, top, right, bottom)
WConio.gettextinfo()
WConio.gotoxy(x, y)
WConio.highvideo()
WConio.insline()
WConio.kbhit()
WConio.lowvideo()
WConio.movetext(left, top, right, bottom, x, y)
WConio.normvideo()
WConio.putch(ch)
WConio.puttext(left, top, right, bottom, saved)
WConio.setcursortype(n)
WConio.settitle(s)
WConio.textattr(attr)
WConio.textbackground(color)
WConio.textcolor(color)
WConio.textmode()
WConio.ungetch(ch)
WConio.wherex()
WConio.wherey()

Ich habe schon einiges probiert, es geht prima. Mit der Funktion .settitle(s) lässt sich sogar einstellen,welcher Text im Titel des Kommandozeilenfensters stehen soll. Na da kann man doch schon was mit anfangen! Getestet habe ich unterWin95/98 sowie unter WinNT. Test unter Win2000 steht noch aus, ich hoffe, dass das auch klappt. Sofern hier jemand Erkenntnisse beisteuern kann, bitte ich um Hinweise. Inzwischen hat mir Andreas Penzel geschrieben, dass er einen erfolgreichen Versuch in der Kombination WinXP mit Python 2.2.3 gemacht hat.


 

2. Linux

Das moderne Linux hält dagegen etwas vor, das garnicht so modern ist, denn das gehört mit zum UNIX - Urgestein:

curses

Wie funktioniert das aber? Es handelt sich um ein Modul, das durch Import dazugeladen werden muss. Hat man das getan, stehen plötzlich allerlei schöne neue Möglichkeiten zur Verfügung. Nun kann man den Bildschirm leeren, danach den Kursor links oben in die Ecke setzen, oder auf eine beliebige Position des sichtbaren Screens schreiben und nicht zuletzt, um eine Menüauswahl zu unterstützen, einen Tastendruck abwarten, der nicht auf dem Bildschirm ausgegeben werden muss. Letzteres geht auch unter Windows, unter Zuhilfenahme des msvcrt - Moduls (siehe Dokumentation von Python). Allerdings lief das dann nicht unter Tkinter, sondern wirklich nur im Kommandofenster. Curses läuft allerdings auch nur im Terminalfenster.

Wenn man diese Funktionen wieder verwenden kann. da lacht das alte TPascal - Herz, genau das haben wir gebraucht! Allerdings wird das alles ein wenig anders organisiert, als wir es bisher gewohnt waren. Dazu hier (weiter unten) ein kleines kommentiertes Beispielprogramm, zum schnellen Hineinfinden. Weitere Feinheiten bleiben dem eigenen Experimentieren vorbehalten, wie immer bei Python.

Wie funktioniert das Ganze eigentlich? Nun, es wird programmintern ein reservierter Speicherbereich vorgehalten, der den Bildschirminhalt repräsentiert. Dorthinein werden alle Veränderungen mit den eigens dafür geschaffenen Funktinen reingeschrieben. Intern merkt sich das Programm noch,wo der Kursor zuletzt stand. Auch gibt es einige Funktionen mit denen man das Terminalverhalten einstellen kann. Sollen dann die neuen Veränderungen sichtbar gemacht werden, wird der komplette Inhalt mit der Funktion refresh() auf den echten Screen gebracht.

Allerdings stecken wieder einige Haken im Detail. So habe ich feststellen müssen, dass sich das Modul curses anders verhält, als es in der Dokumentation beschrieben ist. Zumindest ist mir das bei der Behandlung von Sondertasten aufgefallen. In der Dokumentation wird es so dargestellt, dass die Sondertasten (also z.B. die Kursortasten) mit der Funktion getch() einen Rückgabewert zurückliefern, der dann eben > 255 ist. Dann könnte man ihn vergleichen mit vordefinierten Konstanten, die im Modul enthalten sind. So hat z.B. die Konstante für die Kursortaste links, KEY_LEFT den Wert 260. In der Praxis ging es bei mir so garnicht. Erstens brachte getch(), wie der name schon sagt, natürlich keinen 2-Byte-Integer zurück, sondern einzelne Zeichen. Um das zu zeigen, starte man folgendes kleines Beispielprogramm, mit dem man den Code für die Tasten selbst herausexperimentieren kann:
 

#!/usr/bin/python
import curses
# Die Instanz, die wir jetzt benutzen werden
screen = curses.initscr()
# nun noch einige Standardeinstellungen

curses.nonl()
curses.noecho()
curses.cbreak()

screen.clear()
screen.move(1, 1)
screen.addstr("Druecke einmal die zu untersuchende Sondertaste und dann <q>")
screen.refresh()

x = []

while 1:
    c = screen.getch()
    if c == ord('q'): break # Exit the while()
    x.append(c)

curses.noecho()
screen.move(3, 1)
screen.addstr('folgende Bytes wurden empfangen: ' + str(x))
screen.move(5, 1)
screen.addstr('beenden mit <Enter>')
screen.refresh()

# abwarten

a = screen.getch()

# Werte zurücksetzen

curses.echo()
curses.nocbreak()
curses.endwin()


Mit Hilfe eines derartigen Programms habe ich dann noch herausgefunden, dass die verschiedenen Python - Versionen offenbar unterschiedliche Tastencodes auswerfen. So gab es z.B. Unterschiede zwischen Python 1.5.2. und Python 2.1.

Abhilfe könnte man dadurch schaffen, dass man entweder in einer Lernschleife vor der Ausführung des endgültigen Programms die Codes abfragt und dann abspeichert, oder die Versionsnumer von Python abfragt. Nachfolgend mal ein kleines Beispiel, wie es unter Python 2.1 geht:

#!/usr/bin/python
# obee Zeile nicht vergessen!

import curses

# Die Instanz, die wir jetzt benutzen werden

screen = curses.initscr()

curses.nonl()
curses.noecho()
curses.cbreak()
screen.clear()

x = 6
y = 1
max = 12

for i in range(1, max + 1):
    screen.move(i, x)
    screen.addstr(str(I))

screen.refresh()
x = 1
screen.move(y, x)
screen.addstr("-->")
screen.refresh()

while 1:
    c = screen.getch()
    if c == 13:
        break # Exit the while()
        # ein if muss wenigstens dabei sein
    elif c == 27:
        c = screen.getch()
        if c == 79:
            screen.move(y, x)
            screen.addstr(' ')
            screen.refresh()
            c = screen.getch()
            if c == 66:
                if y < max: y = y + 1
            elif c == 65:
                if y > 1: y = y - 1
            screen.move(y, x)
            screen.addstr('-->')

screen.move(y, 8)
screen.addstr('Das ist die markierte Position ' + str(y))
screen.refresh()

while 1:
    c = screen.getch()
    if c == 27: break # Exit the while()

curses.echo()
curses.nocbreak()
curses.endwin()


Dieses Programm zeigt eine Zahlenliste mit einem Markierungspfeil aus den Zeichen "-->", der mit den Kursortasten in einem begrenzten Bereich auf und ab bewegt werden kann. Drückt man Enter, bleibt die letzte Zeile markiert. Mit Esc geht es wieder raus aus dem Programm. Das sieht dann so aus:

curses