AA_artdeco-haus_sxc1285335_AlePaiva.jpg

© Ale Paiva, sxc.hu

Schlicht, aber schick

Entwicklung textbasierter Oberflächen, Teil 3

21.10.2010
Schnell und komfortabel erstellen Sie mit den Python-Bibliotheken für Dialog und Newt Oberflächen für Konsolenprogramme. So erleichtern Sie Eingaben und geben Informationen barrierefrei aus.

Zum Erstellen von textbasierten Interfaces eignen sich neben den klassischen kompilierten Sprachen auch Skriptsprachen, sofern es entsprechende Module gibt. Für Python finden sich eine ganze Reihe Helferlein, von denen zwei schon im ersten Teil des Workshops [1] zur Sprache kamen: das Modul Curses und Urwid. Beide bieten beim Programmieren viel Flexibilität, erfordern aber auch etwas mehr Arbeit.

Pythondialog

Noch etwas komfortabler gerät die Arbeit mit Pythondialog: Es bietet eine ganze Reihe fertiger Dialogboxen an. Als erstes, einfaches Beispiel dient eine Infobox, die jeweils für drei Sekunden im Terminal erscheint und dann wieder verschwindet (Listing 1). In Zeile 6 wird zunächst ein Objekt aus der Klasse Dialog() initialisiert. Über den Parameter dialog="dialog" legen Sie fest, dass Sie alle Aufrufe an die Bibliothek Dialog senden möchten. Hier sind alternative Werte gestattet, zum Beispiel für die grafischen Variante X-dialog [2].

Listing 1

#! /usr/bin/env python
import dialog, time, sys
# Dialogbox initialisieren
d = dialog.Dialog(dialog="dialog")
# Infobox aufrufen
d.infobox("Hello World!")
# 3 sec Pause
time.sleep(3)
# Programm ohne Fehler beenden
sys.exit(0)

Zeile 9 erzeugt eine Infobox, die als Textinformation den String Hello World! erhält. Der Aufruf in Zeile 12 schickt das Programm für drei Sekunden schlafen, so dass Benutzer die Möglichkeit haben, die Dialogbox wahrzunehmen. In Zeile 15 beendet sich das Skript mit dem Rückgabewert 0 – dass heißt, alles lief fehlerfrei.

Um aus der Infobox eine Dialogbox mit einem OK-Knopf zum Bestätigen zu machen, genügt es, in Zeile 9 infobox() durch msgbox() zu ersetzen. Dabei bestimmt das Modul die Größe des Dialogfensters automatisch. Die Breite stellen Sie über den Parameter width ein, maximal die Breite des Terminalfensters.

Für Ja-Nein-Fragen nutzen Sie die Yesno-Box (Listing 2). Betätigt der Anwender den mit Ja bezeichneten Knopf, liefert der Funktionsaufruf den Wert DIALOG_OK zurück. Daneben gibt es die Rückgabewerte DIALOG_CANCEL (Abbrechen), DIALOG_ESC (Escape-Taste zum Abbrechen des Dialogs), DIALOG_ERROR (Fehler), DIALOG_EXTRA und DIALOG_HELP (für mögliche Hilfeknöpfe).

Listing 2

# -*- coding: utf-8 -*-
# ! /usr/bin/env python
import dialog, time, sys
# Dialogbox initialisieren
d = dialog.Dialog(dialog="dialog")
# Nachrichtenbox aufrufen
antwort = d.yesno("Neues USB-Gerät gefunden: A380. Einbinden?")
# Antwort auswerten
if antwort == d.DIALOG_OK:
  d.msgbox("Yippieh, los geht's :-)")
else:
  d.msgbox("Feigling :-p")
# Programm ohne Fehler beenden
sys.exit(0)

TIPP

In Listing 2 steht in der ersten Zeile ein Hinweis auf das Encoding des Python-Codes. UTF-8 klingt erst einmal unnütz, da Python es ohnehin als Standardwert für das Encoding nutzt. Es ist aber wichtig zur Ansteuern von dialog, damit dieses korrekt mit den Umlauten und Sonderzeichen aus dem Programmquelltext umgeht.

Als Möglichkeiten für Auswahlfelder stehen Menüs, Check- und Radio-Boxen bereit. In den Zeilen 13 bis 18 (Listing 3) erfolgt die Definition des Auswahlmenüs. Zeile 14 legt die Überschrift fest, die über dem Auswahlmenü erscheint. Die Variable width in Zeile 15 bestimmt, wie breit das Fenster der Auswahl erscheint – längere Einträge schneidet die Software ab. Die einzelnen Auswahlpunkte des Menüs geben die Zeilen 16 bis 18 an. Dabei besteht jeder Eintrag aus einem Index und einer Beschreibung.

Listing 3

# -*- coding: utf-8 -*-
#! /usr/bin/env python
import dialog, time, sys
# Dialogbox initialisieren
d = dialog.Dialog(dialog="dialog")
# Menubox erzeugen
while 1:
  (code, tag) = d.menu(
    "Wohin geht die Reise?",
    width=60,
    choices=[("Amsterdam", "Stadt der Kanäle und Grachten"),
             ("Berlin", "In die beste Stadt der Welt"),
             ("Cannes", "Filme und Meer")])
  if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
    break
  if code == d.DIALOG_OK:
    d.msgbox("Ihre Auswahl: " + tag)
    break
# Programm ohne Fehler beenden
sys.exit(0)

Als Rückgabe erhalten Sie aus dem Aufruf des Menüs (Abbildung 1) ein Tupel aus zwei Werten zurück – dem Tastencode (code) und dem ausgewählten Menüpunkt (tag). Mit [Esc] oder der Auswahl der Schaltfläche Abbrechen brechen Sie die Auswahl und damit das Programm ab (Zeilen 20 bis 21). Mit [Eingabe] wählen Sie einen Menüpunkt aus; die Auswahl erscheint nachfolgend in einer Messagebox (Zeilen 23 bis 25).

Abbildung 1: Die Menüauswahl mit Pythondialog liefert als Rückgabewert den gewählte Punkt.

Checkbox und Radiobox ähneln der Menüauswahl – der Aufruf erfolgt fast identisch. Der Unterschied liegt darin, wie Sie die einzelnen Menüpunkte festlegen. Es gilt einen zusätzlichen Parameter anzugeben, der entweder den Wert 0 oder 1 hat. 0 signalisiert, dass ein Punkt nicht ausgewählt ist, 1 bedeutet hingegen ausgewählt. Bei der Checkbox (Abbildung 2) darf der Benutzer mehrere Menüpunkte auswählen, bei der Radiobox nur einen einzelnen. Das Ergebnis der Auswahl landet in der Variable tag, die bei der Checkbox aus einer Liste besteht, bei der Radiobox wiederum nur aus einem Einzelwert.

Abbildung 2: Über eine Checkbox mit Pythondialog fragen Sie auf einfache Weise Werte ab.

Newt

Newt spielt in der gleichen Liga wie Pythondialog – es bringt bereits viele fertige Elemente mit. Dazu zählen Fenster, Knöpfe und Auswahlfelder. Im Vergleich zu Pythondialog wirkt Newt einfacher und schlanker, was sich aber in der Praxis nicht als Nachteil erweist.

Newt setzt jedoch ein wenig mehr Arbeit voraus als Pythondialog. Nach dem Importieren des Moduls snack gilt es, zunächst einen Rahmen zum Ansprechen, zum Sichern und Wiederherstellen des aktuellen Bildschirminhalts bereitzustellen (Listing 4).

Listing 4

from snack import *
class gb:
  scrn = None
def initScreen():
  gb.scrn = SnackScreen()
  return
def restoreScreen():
  gb.scrn.finish()
  return

Die lokal definierte Klasse gb fasst alle Variablen zusammen, die das Programm global benötigt. Das ist kein Muss, erleichtert aber im Beispiel den Zugriff. Die Variable gb.scrn dient dazu, Operationen auf dem Bildschirm auszuführen – zum Beispiel Fenster zu erzeugen, darzustellen und wieder zu entfernen.

Die Funktion initScreen() sichert den aktuellen Bildschirminhalt, restoreScreen() stellt ihn später wieder. Beide Funktionen braucht das Programm vor und nach einem Aufruf für ein Dialogfenster. Eine Auswahlbox (ListboxChoiceWindow) für eine Fahrtzielauswahl (Abbildung 3) erstellen Sie damit ganz leicht (Listing 5).

Listing 5

destination = ('Helgoland', 'Trier', 'Bautzen', 'Friedrichshafen')
listboxSelection = 0
initScreen()
lbcw = ListboxChoiceWindow(
  gb.scrn,
  'Auswahlmenu',
  'Ein Fahrtziel festlegen:',
  destination,
  default = listboxSelection,
  help = 'Hilfe?',
  buttons = ('OK', 'Beenden')
)
restoreScreen()
listboxButton = lbcw[0]
listboxSelection = lbcw[1]
if listboxButton == 'ok':
  print "Fahrtziel: %s" % destination[listboxSelection]
Abbildung 3: Eine einfach Auswahlbox haben Sie mit Python/Newt in wenigen Minuten programmiert.

Zeile 1 definiert die einzelnen Fahrtziele (destination) als Liste, Zeile 2 setzt die Vorauswahl auf den ersten Eintrag in dieser Liste. Da das Zählen der Indizes in Python bei Null beginnt, reicht im vorliegenden Fall der der Wertebereich von 0 bis 3 (vier Einträge). Der Aufruf der Funktion initScreen() in Zeile 4 bereitet den Bildschirm entsprechend vor.

Die Zeilen 6 bis 14 erzeugen die Auswahlbox. Newt stellt dazu die Funktion ListboxChoiceWindow() bereit, die die folgenden Parameter benötigt:

  • Zeile 7: das Fensterobjekt zum Darstellen aller Elemente
  • Zeile 8: der Fenstertitel
  • Zeile 9: die Beschreibung zur Auswahl
  • Zeile 10: die Auswahlmöglichkeiten (hier: die Fahrtziele)
  • Zeile 11: die Vorauswahl in der Auswahlliste
  • Zeile 12: der angezeigte Hilfetext
  • Zeile 13: die Knöpfe, über die das Fenster verfügt. Hier sind es zwei – OK und Beenden. Für weitere Schaltflächen ergänzen Sie die Liste nach Bedarf.

Zeile 14 enthält die schließende Klammer zum Funktionsaufruf in Zeile 6. Zeile 16 dient als Gegenstück zu Zeile 4, wobei diese den Bildschirminhalt in den Zustand vor dem Aufruf der Auswahlbox versetzt. Die nachfolgenden Zeilen werten das Ergebnis der Auswahlbox aus. Dieses liegt als Liste mit zwei Elementen vor: Der erste Eintrag enthält den betätigten Knopf, der zweite Eintrag den Index in der Auswahlliste.

Zeile 20 prüft, ob der Anwender den Knopf OK gedrückt hat. Falls ja, gibt das Skript das gewünschte Fahrtziel aus (Zeile 21). Es spielt beim Auswerten keine Rolle, ob Sie den Knopf mit Groß- oder Kleinbuchstaben beschriften – die Funktion ListboxChoiceWindow() gibt in jedem Fall den Text in Kleinbuchstaben zurück.

Als weiteres Beispiel (Listing 6) dient der Eingabedialog für ein kleines Adressbuch, das aus dem Projekt Nanowawi [3] stammt (Abbildung 4). Die Zeilen 2 bis 11 definieren die Eingabefelder und deren Bezeichner für das Adressbuch. Zeile 13 legt die Breite des Fensters auf 70 Zeichen fest; Zeile 14 bestimmt für die Eingabefelder eine Breite von 50 Zeichen. Das Fenster benötigt zwei Knöpfe – OK und Cancel – diese definiert Zeile 15. Die Zeilen 16 und 17 bestimmen den Titel und die Textinformation des Fensters.

Abbildung 4: Nur wenige Zeilen Code reichen aus, um eine Eingabemaske zu erstellen.

Listing 6

# list of entry fields
entryFields = [
  ('Title', ''),
  ('First Name', ''),
  ('Last Name', ''),
  ('Street', ''),
  ('City', ''),
  ('ZIP Code', ''),
  ('Region', ''),
  ('Country', '')
]
windowWidth = 70
entryFieldLength = 50
buttons = ['OK', 'Cancel']
windowTitle = 'Address Fields for customer 2'
windowMessage = 'Currently, the following information is stored:'
# init newt screen
initScreen()
returnValue = EntryWindow(
  gb.scrn,
  windowTitle,
  windowMessage,
  entryFields,
  1,
  windowWidth,
  entryFieldLength,
  buttons,
  None)
# restore old screen
restoreScreen()

Das Initialisieren des Fensters geschieht in Zeile 19, der eigentliche Aufruf des Fensters in den Zeilen 22 bis 31. Zeile 34 stellt den Fensterzustand vor dem Aufruf wieder her. Um zu erfahren, wie der Benutzer das Eingabefenster verließ, wertet das Skript zum Abschluss die Variable returnValue aus. Sie besteht aus einem Feld mit zwei Einträgen, der erste für den gedrückten Knopf und der zweite mit einer Liste der Eingabewerte (Listing 7).

Listing 7

returnButton = returnValue[0]
returnFields = returnValue[1]
if returnButton == 'ok':
  description = ["Titel", "Vorname", "Name", "Strasse", "Ort", "PLZ", "Region", "Land"]
  index = 0
  for item in description:
    print "%s: %s" % (item, returnFields[index])
    index = index + 1

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Ähnliche Artikel

  • Entwicklung textbasierter Oberflächen, Teil 2
    Textbasierte Benutzeroberflächen zu erstellen, ist nicht besonders kompliziert. Die vorgestellten Frameworks erfordern zwar zunächst etwas Einarbeitungszeit, ermöglichen aber sehr viel Freiheit beim Design.
  • Textbasierte User-Interfaces (Teil 1)
    Die Kommandozeile halten viele Anwender für trist und schlecht bedienbar. Das muss aber nicht so sein: Mithilfe von Dialog und Whiptail werten Sie Ihre eigenen Skripts unkompliziert mit Fenstern, Menüs und Dialogen auf.
  • Mehr Komfort
    Von einfachen Abfragen bis hin zu komplexen Menüs: Mit dem Toolkit Dialog bauen Sie eine grafische Oberfläche für Shell-Skripte, die oft nicht mehr als eine zusätzliche Zeile brauchen.
  • KDE Themes Teil 3
    Dass KDE die Fähigkeit besitzt, recht schnell mittels sogenannter Themes sein Äußeres zu ändern, dürfte inzwischen hinreichend bekannt sein. Diese Serie beschreibt, wie man eigene Themes erstellt.
  • KDE mit dem Kiosk-Modus absichern
    Ob im Unternehmen mit Hunderten von Arbeitsplätzen oder einfach nur zu Hause als Kindersicherung – Nutzerrechte auf dem Desktop einzuschränken ist oft ein wichtiges Anliegen. Mit KDE geht das ganz einfach.
Kommentare

Infos zur Publikation

LU 11/2014: VIDEOS BEARBEITEN

Digitale Ausgabe: Preis € 4,95
(inkl. 19% MwSt.)

Mit der Zeitschrift LinuxUser sind Sie als Power-User, Shell-Guru oder Administrator im kleinen Unternehmen monatlich auf dem aktuelle Stand in Sachen Linux und Open Source.

Sie sind sich nicht sicher, ob die Themen Ihnen liegen? Im Probeabo erhalten Sie drei Ausgaben zum reduzierten Preis. Einzelhefte, Abonnements sowie digitale Ausgaben erwerben Sie ganz einfach in unserem Online-Shop.

NEU: DIGITALE AUSGABEN FÜR TABLET & SMARTPHONE

HINWEIS ZU PAYPAL: Die Zahlung ist auch ohne eigenes Paypal-Konto ganz einfach per Kreditkarte oder Lastschrift möglich!       

Tipp der Woche

Schnell Multi-Boot-Medien mit MultiCD erstellen
Schnell Multi-Boot-Medien mit MultiCD erstellen
Tim Schürmann, 24.06.2014 12:40, 0 Kommentare

Wer mehrere nützliche Live-Systeme auf eine DVD brennen möchte, kommt mit den Startmedienerstellern der Distributionen nicht besonders weit: Diese ...

Aktuelle Fragen

WLAN-Signalqualität vom Treiber abhängig
GoaSkin , 29.10.2014 14:16, 0 Antworten
Hallo, für einen WLAN-Stick mit Ralink 2870 Chipsatz gibt es einen Treiber von Ralink sowie (m...
Artikelsuche
Erwin Ruitenberg, 09.10.2014 07:51, 1 Antworten
Ich habe seit einige Jahre ein Dugisub LinuxUser. Dann weiß ich das irgendwann ein bestimmtes Art...
Windows 8 startet nur mit externer Festplatte
Anne La, 10.09.2014 17:25, 6 Antworten
Hallo Leute, also, ich bin auf folgendes Problem gestoßen: Ich habe Ubuntu 14.04 auf meiner...
Videoüberwachung mit Zoneminder
Heinz Becker, 10.08.2014 17:57, 0 Antworten
Hallo, ich habe den ZONEMINDER erfolgreich installiert. Das Bild erscheint jedoch nicht,...
internes Wlan und USB-Wlan-Srick
Gerhard Blobner, 04.08.2014 15:20, 2 Antworten
Hallo Linux-Forum: ich bin ein neuer Linux-User (ca. 25 Jahre Windows) und bin von WIN 8 auf Mint...