© Tithi Luadthong/Shutterstock.com
Wie der Einsatz von Lanterna gelingt

Ich geh mit meiner Laterne


Lanterna ist eine Library, mit der sich Kommandozeilentools in Java erstellen lassen. Es ähnelt der bekannten C-Library Curses [1], kommt jedoch mit mehr Funktionen daher. Lanterna kann reine Terminals emulieren, sodass damit Installationsassistenten möglich sind. Andererseits lassen sich damit textbasierte GUIs mit Buttons, Drop-down-Menüs und dergleichen erstellen, was zum Beispiel für Systemtools wie YaST [2] von openSUSE geeignet ist.

Um ressourcenschonende Software zu erstellen, muss es nicht immer eine so mächtige Frontend Library wie JavaFX sein. Beliebte Systemtools, die auf Linux laufen, machen es vor: alsamixer, YaST, NVIDIA Installer. Allesamt machen sie von Bibliotheken Gebrauch, die textbasierte GUIs einsetzen. Sie haben den Vorteil, dass sie auf der Konsole laufen und sich remote einfach öffnen lassen.

Ein Nachteil von Lanterna ist sicherlich, dass Entwickler von JavaFX-Programmen erst einmal umdenken, sprich den Aufbau eines Terminals verstehen müssen, ehe sie ein funktionsfähiges textbasiertes GUI erstellen können.

Drei-Schichten-Modell

Lanterna [3] besteht aus drei Schichten (Abb. 1), nämlich Terminal, Screen sowie TextGUI. Auf der Terminal-Ebene lassen sich Zeichen ausgeben, der Cursor bewegen, Tastatureingaben erkennen sowie die Ausgaben formatieren. Im Prinzip ähnelt die Terminalausgabe einer Session auf der Linux-Konsole (Abb. 2).

minosi_lanterna_1.tif_fmt1.jpgAbb. 1: Lanterna kann Text auf drei Ebenen ausgeben
minosi_lanterna_2.tif_fmt1.jpgAbb. 2: Ein Programm, das auf der Terminalebene läuft

Die Screen-Ebene baut auf der Terminal-Ebene auf, sodass zuerst ein Terminal erstellt werden muss, ehe die Screen-Ebene funktioniert. Ein Screen gleicht einem doppelt gepufferten Videospeicher (Abb. 3). Beide Puffer – vorne sowie hinten – lassen sich direkt ansprechen und modifizieren. Der Inhalt wird in den Puffern gespeichert, wobei der Inhalt aus dem hinteren Puffer nach vorne gebracht werden kann. Anders als bei GUIs bestehen Terminal, Screen und TextGUI nicht aus Pixeln, sondern aus Textzeichenoberflächen.

minosi_lanterna_3.tif_fmt1.jpgAbb. 3: Die Screenebene besteht aus einem vorderen und einem hinteren Puffer

Die oberste Ebene bildet das TextGUI, auf der sich dieselben Elemente implementieren lassen wie bei einem pixelbasierten GUI: Tabellen, Buttons, Dialogfenster usw.

Start einer Session

Um überhaupt Text auf der Konsole auszugeben, ist es zuerst erforderlich, eine Terminalsitzung zu erzeugen:

import com.googlecode.lanterna.terminal.DefaultTerminalFactory; public void setupTerminal() { DefaultTerminalFactory terminalFactory = new DefaultTerminalFactory(); }

Es sind nur ein paar Zeilen Code erforderlich, um ein Programm auf der Screen-Ebene zu betreiben. Jedoch wird beim Start der Screen mit dem Terminal verknüpft (Listing 1).

Listing 1

import com.googlecode.lanterna.screen.Screen; import java.io.IOException; import com.googlecode.lanterna.terminal.DefaultTerminalFactory; public void setupScreen() { try { Screen screen = terminalFactory.createScreen(); screen.startScreen(); } catch(IOException e) { throw new RuntimeException(e); } }

Anschließend wird der Screen dem TextGUI übergeben:

import com.googlecode.lanterna.gui2.*; public void setupGui() { WindowBasedTextGUI textGUI = new MultiWindowTextGUI(screen); }

Die nächsten Abschnitte beschäftigen sich mit einem Beispielprogramm namens ChartCreator. Zunächst wählt der User ein Diagramm aus, das er erstellen möchte, und passt das Diagramm an seine Wünsche an. So kann er die Größe des Diagramms, die Farben der Linien und den Speicherort bestimmen. Das Programm liest außerdem die zu zeichnenden Datenreihen ein. Für die Erstellung des Diagramms greift der ChartCreator auf eine Vorlage zurück, die die Software Pyxplot [4] zum Zeichnen benutzt.

Menü

Textbasierte Programme lassen sich mit einer Aktionsliste starten, bei der nach Auswahl einer Aktion durch die ENTER-Taste zum nächsten Fenster gesprungen wird (Abb. 4). Unter Lanterna heißt diese Art von Menüs ActionListDialogBuilder. Ihr könnt die Methoden zum Starten eines TextGUI, die weiter oben erscheinen, in einer eigenen Klasse namens MyScreen deklarieren (Listing 2) und sie mit den Klassen verbinden, die das Menü ausgibt (Listing 3). Denn die einzelnen Views brauchen Zugang zum TextGUI oder Screen.

minosi_lanterna_4.tif_fmt1.jpgAbb. 4: Das Hauptmenü erscheint direkt nach dem Starten des Programms

Listing 2

import java.io.IOException; import java.io.*; import com.googlecode.lanterna.screen.Screen; import com.googlecode.lanterna.terminal.DefaultTerminalFactory; import com.googlecode.lanterna.gui2.*; public class MyScreen{ private DefaultTerminalFactory terminalFactory; private Screen screen; private WindowBasedTextGUI textGUI; public MyScreen() { setupTerminal(); setupScreen(); setupGui(); } public void refreshScreen() { try { this.screen.clear(); this.screen.refresh(); } catch(IOException e) { throw new RuntimeException(e); } } public WindowBasedTextGUI getTextGui() { return this.textGUI; } public MyScreen getScreen() { return this.screen; } }

Auffallend an Listing 3 ist, dass die Strings in der Klasse MainView zwar global definiert sind, jedoch in der setListeners-Methode sowie in anderen Methoden vom Typ Runnable zusätzlich lokal definiert werden. Ansonsten meckert der Compiler, dass er die Variablen nicht kennt.

Listing 3

public class MainView { private String dialogTitle; private String selectItm; private String item1; private String item2; private MyScreen myscreen; private ActionListDialogBuilder menu; public MainView (MyScreen s) { this.myscreen = s; setDialog(); setValues(); setupDialog(this.dialogTitle, this.selectItm); setListeners(this.item1, this.item2,this.screen); showDialog(this.myscreen.getTextGui()); } public void setDialog() { this.menu = new ActionListDialogBuilder(); } public void setupDialog(String title, String descr) { this.menu.setTitle(title) .setDescription(descr); } public void setListeners(String item1, String item2, MyScreen s) { this.menu.addAction(item1, new Runnable() { public void run() { new FileView(s); } }) .addAction(item2, new Runnable() { public void run() { // show history } }); } public void setValues() { this.dialogTitle = "ChartCreator"; this.selectItm = "Waehle einen Eintrag aus!"; this.item1 = "Kuchendiagramm erstellen"; this.item2 = "Historie"; } public void showDialog(WindowBasedTextGUI tGUI) { this.menu.build() .showDialog(tGUI); } }

Ihr könnt euch beim Erstellen des Menüs an den folgenden Punkten orientieren:

  • eine Instanz des ActionListDialogBuilders erstellen

  • Strings für den Titel sowie die Menüeinträge festlegen

  • den Dialogtitel sowie eine kurze Beschreibung an den ActionListDialogBuilder übergeben

  • die Aktionen definieren, die nach dem Drücken der ENTER-Taste auf den jeweiligen Menüeintrag ausgeführt werden

  • zum Schluss noch den ActionListDialogBuilder mit dem TextGUI verknüpfen, damit das Menü dargestellt wird

Fensterdeko

Im Vergleich mit Aktionslisten brauchen Buttons, Labels und Eingabefelder ein Window, um diese Elemente zu zeichnen. Um den Wechsel von der Aktionsliste zu einem Window zu implementieren, solltet ihr in der Methode addAction (s. setListeners in Listing 3) eine neue Instanz der nächsten View erstellen. Ihr könnt das in Listing 4 dargestellte Beispiel als Vorlage benutzen, um weitere Elemente zum Window hinzuzufügen.

Listing 4

import com.googlecode.lanterna.gui2.*; import com.googlecode.lanterna.gui2.Window.Hint; import java.util.Arrays; public class FileView { private MyScreen screen; private Window window; private Panel contentPanel; public FileView (MyScreen s) { this.screen = s; setupWindow("Title"); setPanel(); showWindow(); } public void setupWindow(String title) { this.window = new BasicWindow(title); this.window.setHints(Arrays.asList(Hint.MODAL,Hint.FULL_SCREEN,Hint.NO_DECORATIONS)); } public void showWindow() { this.window.setComponent(this.contentPanel); this.screen.wait(this.window); } public void setPanel() { this.contentPanel = new Panel(); } }

Wie ihr Listing 4 entnehmen könnt, ist das Window gleich erstellt (Abb. 5). Ihr müsst dazu lediglich in der View eine Instanz der Klasse BasicWindow erzeugen, die in Listing 4 durch die Methode setupWindow realisiert wird. Darüber hinaus lässt sich das Window mit Hilfe der setHints-Methode (Tabelle 1) dekor...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang