|
Module zur Kursorsteuerung im Textmodus für Win oder Linux |
---|
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.
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()