Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 7 Min.

Attraktives GUI mit Spectre.Console

Mit der Bibliotheksfamilie Spectre.Console steht ein neues Produkt ante portas, das die Realisierung von visuell ansprechenden Kommandozeileninterfaces zu erleichtern sucht.
© https://spectreconsole.net/

Kommandozeilenbasierte Benutzerinterfaces punkten insbesondere in Remote-Situationen – für das Aufrechterhalten einer SSH-Verbindung ist nun mal nur vergleichsweise wenig Bandbreite erforderlich. Mit der Ausgabe von Semigrafik lassen sich dann „attraktiv aussehende“ Interfaces realisieren, die auch dann verwendbar bleiben, wenn die Endanwender technisch nur moderat begabt sind.

Inbetriebnahme und Farbcodierung

Für das Anlegen einer neuen Beispiel-Kommandozeilenapplikation nutzen wir die Vorlage Konsolen-App. In den folgenden Schritten wird der Autor als Name SpectreDemo1 verwenden, als Version des .NET Frameworks soll 8.0 dienen. Im nächsten Schritt folgt der mittlerweile hinreichend bekannte Wechsel in die NuGet-Konsole, wo fürs Erste die Pakete Spectre.Console und Spectre.Console.Cli ins Projekt wandern. Die Verwendung der Preview-Version .0.51.2-preview.0.1 verursacht keine Probleme.

Für einen ersten Versuch wollen wir folgenden Code zur Ausführung bringen, der das in Bild 1 gezeigte Fenster erzeugt:

 

using Spectre.Console;
Console.WriteLine("Hello, World!");
AnsiConsole.Write(new Markup("[bold yellow]FAUCHEN![/] [red]ist lästig![/]"));
Console.WriteLine("Byebye, World!");

 

Technisch orientiert sich Spectre.Console am aus diversen Webforen bekannten BBcode-System. Die nach dem Schema [/] aufgebauten Slots terminieren dabei die einzelnen Formatierungsfelder.

Entsprechend bietet Spectre.Console im Bereich der Formatierungsmöglichkeiten weitgehend dieselben Optionen an, die auch das Webforum BBcode unterstützt. Bild 2 gibt einen Überblick über die Möglichkeiten.

Demo im Dienst der Ruhe (Bild 1)

Demo im Dienst der Ruhe (Bild 1)

© Autor
Bitte wählen (Bild 2)
Bitte wählen (Bild 2) © Spectreconsole.net

Bei sorgfältiger Betrachtung der Dokumentation fällt auf, dass Spectre.Console zwei Ausgabe-Methoden anbietet. Die Markup-Familie der Methoden unterscheidet sich dabei insofern, als sie das Interface IRenderable implementiert und somit für fortgeschrittene Aufgaben besser geeignet erscheint. Für einen kleinen Test bietet es sich an, nach folgendem Schema einen Hyperlink in die Konsole auszugeben:

 

 

AnsiConsole.WriteLine("[link=http://www.google.com]Google[/]");
AnsiConsole.MarkupLine("[link=http://www.google.com]Google[/]");
Console.WriteLine("http://www.google.com");

 

Als primäres Hindernis erweist sich hier der geringe Funktionsumfang der in Visual Studio enthaltenen Konsole – Bild 3 zeigt die wenig beeindruckenden optischen Ergebnisse.

 

Die Visual-Studio-Entwicklerkonsole kann mit Hyperlinks nichts anfangen (Bild 3)

Die Visual-Studio-Entwicklerkonsole kann mit Hyperlinks nichts anfangen (Bild 3)

© Autor

Angemerkt sei außerdem noch, dass Spectre.Console zur grafischen Ausgestaltung von Exceptions befähigt ist. Dies erfolgt nach dem gezeigten Schema – zu beachten ist, dass eine „synthetisch“ geschaffene Exception keinen Callstack aufweist und die Möglichkeiten des Frameworks deshalb nur leidlich demonstriert:

 

AnsiConsole.WriteException(new InvalidDataException("Heute möchte ich nicht fauchen!"));

 

Erzeugung von dynamischen Steuerelementen

Im Linux-Bereich gelten Commandline-GUI-Bibliotheken als absoluter Standard. Das hinter Spectre.Console stehende Entwicklerteam versucht, ähnliche Dienste anzubieten.

Als erstes Beispiel wollen wir ein Balkendiagramm erzeugen, wofür folgender Code erforderlich ist:

 

AnsiConsole.Write(new BarChart()
    .Width(80)
    .Label("[red bold underline]Lästigkeit[/] \n")
    .CenterLabel()
    .AddItem("Annette", 77, Color.Yellow)
    .AddItem("Beatrice", 54, Color.Green)
    .AddItem("Clara", 33, Color.Red));

 

Die Methode Label übernimmt eines der weiter oben erwähnten Renderable-Objekte. Aus diesem Grund ist es dem Autor erlaubt, den Formatstring mit einzuschreiben – er sorgt dafür, dass das Diagramm wie in Bild 4 gezeigt einen gewissen Respektabstand zum Label einhält.

 

Auch in Kommandozeilenapplikationen notwendig: Typografie (Bild 4)

Auch in Kommandozeilenapplikationen notwendig: Typografie (Bild 4)

© Autor

In der Praxis gibt es immer wieder Situationen, in denen Kommandozeilenapplikationen länger ablaufende Prozesse überwachen – ein klassisches Beispiel hierfür wäre das Bring-up eines Clusters oder das Herunterladen von verschiedenen Paketen. Spectre.Console bietet mit dem Live-Widget eine Sondervariante an, die die dynamische Aktualisierung von bereits am Bildschirm befindlichen Elementen ermöglicht.

Hierzu ist eine Modifikation des Programms erforderlich. Anstatt unser BarChart wie bisher direkt in die Konsole zu verfrachten, verpacken wir es im ersten Schritt in eine lokale Variable:

 

var barChart = new BarChart()
    ...
    .AddItem("Clara", 33, Color.Red);

 

Die eigentliche Anzeige des Diagramms erfolgt dann über die Methode Live. Über die Start-Methode wird dann ein Handler angeliefert, der sich um das Einschreiben beziehungsweise Durchführen der Aktualisierungen kümmert:

 

AnsiConsole.Live(barChart)
    .Start(ctx =>
    {
        barChart.AddItem("Faucherin 1", value: 33, Color.Red);
        ctx.Refresh();
        Thread.Sleep(1000);

        barChart.AddItem("Faucherin 2", value: 33, Color.Red);
        ctx.Refresh();
        Thread.Sleep(1000);
    });

 

Die Ausführung der vorliegenden Version des Programms sorgt dann dafür, dass das Diagramm permanent neue Quellen der Lästigkeit aufnimmt. Zu beachten ist lediglich, dass das Markieren eines einzigen Zeichens im Kommandozeilenfenster ausreicht, um das Fenster als Ganzes einzufrieren.

Erzeugung eines Kalenders

Insbesondere beim Planen von Downtimes kann es vernünftig sein, wenn die Kommandozeilenapplikation die Nutzer:innen auch visuell über die anstehenden Zeiten informiert. Am einfachsten lässt sich dies in Spectre.Console durch Nutzung des Kalender-Widgets erreichen – hierzu reicht folgender Code aus, der das in Bild 5 gezeigte Ergebnis generiert:

 

var calendar = new Calendar(2020, 10);
AnsiConsole.Write(calendar);
Kalender herbei! (Bild 5)

Kalender herbei! (Bild 5)

© Autor

Besonders relevante Ereignisse beziehungsweise Daten kann der Entwickler durch Nutzung der Methode HighlightStyle hervorheben. Auch hierfür sei ein kleines Beispiel realisiert:

 

var calendar = new Calendar(2025, 9);
calendar.AddCalendarEvent(2025, 9, 10);
calendar.HighlightStyle(Style.Parse("yellow bold"));
AnsiConsole.Write(calendar);

 

Wer sich hier für den Highlight-Style yellow bold entscheidet, bekommt dann das in Bild 6 gezeigte Ergebnis.

 

Der Geburtstag des Autors ist farblich hervorgehoben (Bild 6)

Der Geburtstag des Autors ist farblich hervorgehoben (Bild 6)

© Autor

Realisierung von komplexeren Designs

In der Bibliothek finden sich auch an Layout-Manager erinnernde Steuerelemente, die die Anzeige von mehreren Widgets oder mehreren Informationen nebeneinander ermöglichen. Zur Vorführung dieser Funktion wollen wir im ersten Schritt ein schlüsselfertiges Fenster rendern, um danach seine Bestandteile anzusehen.

Der soeben erzeugte Kalender dient dabei als eines der Kinder-Widgets. Entfernen Sie im ersten Schritt die Zeile Live, um die Ausgabe der fertig generierten Instanz im Terminal zu beenden. Danach platzieren sie folgenden Code:

 

var root = new Tree("Root");

var foo = root.AddNode("[yellow]Foo[/]");
var table = foo.AddNode(new Table()
    .RoundedBorder()
    .AddColumn("First")
    .AddColumn("Second")
    .AddRow("1", "2")
    .AddRow("3", "4")
    .AddRow("5", "6"));

table.AddNode("[blue]Blauer Baum[/]");
foo.AddNode("Kind des Baumes");

var bar = root.AddNode("[yellow]Kalender-Ast[/]");
bar.AddNode(calendar);

AnsiConsole.Write(root);

 

Ergebnis des Renderings ist das in Bild 7 gezeigte Verhalten.

Spectre.Console kann sehr komplexe Screens generieren (Bild 7)

Spectre.Console kann sehr komplexe Screens generieren (Bild 7)

© Autor

Der erste Block ist insofern interessant, als der String ("[yellow]Foo[/]"); nicht Kind der Haupt-Wurzel, sondern Kind des Tabellen-Objekts ist. Dies ist dadurch erkennbar, dass alle in einer Tabelle hinzugefügten Notes eine AddNode-Methode aufweisen – analog zur klassischen Datenstruktur erlauben auch sie das Einschreiben von Kind-Widgets, die visuell nachrangig behandelt werden.

Das Einpflegen des Kalenders selbst erfolgt dann wieder durch Nutzung der Methode AddNode, die nun aber gegen das Wurzel-Objekt des Baumes zur Anwendung gelangt.

Exkurs: Einsammeln von Benutzerentscheidungen und Ausgabe von Ergebnissen

Schon aus Platzgründen ist es nicht möglich, die Bibliothek in einem einzigen Artikel komplett zu besprechen. Trotzdem möchte der Autor noch zwei interessante Aspekte vorstellen. Erstens ist es möglich, durch nach folgendem Schema aufgebauten Code eine Auswahl-Liste zu präsentieren. Der Benutzer kann in dieser dann analog zum Linux-Bootloader mit den Cursortasten und der Eingabetaste das Objekt auswählen, das er für die weitere Verarbeitung nutzen möchte:

 

var fruit = AnsiConsole.Prompt(
    new SelectionPrompt<string>()
        .Title("What's your [green]favorite fruit[/]?")
        .PageSize(10)
        .MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]")
        .AddChoices(new[] {
            "Apple", "Apricot", "Avocado",
            "Banana", "Blackcurrant", "Blueberry",
            "Cherry", "Cloudberry", "Cocunut",
        }));
AnsiConsole.WriteLine($"I agree. {fruit} is tasty!");

 

Das Hinzufügen des Pakets Spectre.Console.ImageSharp ermöglicht der Konsolen-Applikation dann sogar die Generierung von Pixel Art. Nach folgendem Schema aufgebauter Code lädt und „pixeliert“ das Bild automatisch:

 

var image = new CanvasImage("cake.png");
image.MaxWidth(16);
AnsiConsole.Write(image);

 

Fazit

Wer Kommandozeilenapplikationen mit geringem Aufwand zu einem attraktiven Aussehen verhelfen möchte, sollte auf die Dienste des Pakets Spectre.Console zurückgreifen. Das manuelle Nachimplementieren der hier angelegten Funktionen ist mit Sicherheit wesentlich aufwendiger als das Sich-Einlesen in die – nach Ansicht des Autors exzellent gelungene – Dokumentation der Bibliothek.

Neueste Beiträge

Das Tempo bleibt ordentlich - Neuerungen in Blazor 10.0, TEIL 2
Auch beim Monitoring, dem QuickGrid-Steuerelement und der C#-JavaScript-Interoperabilität bietet Blazor 10.0 einige Verbesserungen.
20 Minuten
1 plus 1 macht 2 - Mendix Artificial Intelligence Assistant
Low Code verspricht die Vereinfachung der Softwareproduktion, künstliche Intelligenz die Steigerung der Developer Productivity. Was passiert, wenn man beide kombiniert?
16 Minuten
22. Okt 2025

Das könnte Dich auch interessieren

Benchmark im Unit-Test-Stil - Best of NuGet, Teil 4
NuGet-Pakete können das Leben des .NET-Entwicklers sogar um neue Werkzeuge erweitern, die sich auf den Entwicklungsworkflow (und nicht den bearbeiteten Code) auswirken. Mit BenchmarkDotNet steht ein Musterbeispiel dieses Konzepts zur Verfügung, das die Überprüfung der Performance von Code aller Arten ermöglicht.
7 Minuten
16. Okt 2025
Loggingdaten-Einlaufstelle mit Komfortfunktionen - Best of NuGet, Teil 5
Die in Android implementierte Logging-Funktion ist ein leistungsfähiges Beispiel für moderne Logging-Systeme. Mit Serilog steht ein ähnliches System für .NET-Applikationen zur Verfügung, das allerdings einige weit über das große Vorbild hinausgehende Funktionen offeriert.
6 Minuten
22. Okt 2025
Erstellung von ZUGFeRD 2.3 mit .NET C# - Rechnungserstellung
ZUGFeRD 2.3 konforme Rechnungen mit TX Text Control .NET Server für ASP.NET erstellen.
3 Minuten
9. Jan 2025
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige