Viele Anwender zeichnen ihre Grafen und Diagramme langwierig per Hand und Malprogramm. Dass es auch schneller geht, beweist der Werkzeugkasten Graphviz. Er erzeugt aus einer kurzen textuellen Beschreibung in Sekundenschnelle eine ansehnliche und aufgeräumte Zeichnung.
In nur 15 Minuten will der Chef ein hübsches Diagramm der niegelnagelneuen Netzwerkarchitektur für eine Präsentation. Das riecht nach einem schnellen Griff zu Open Office Draw, in dem man noch schnell ein paar Kreise zusammenklickt. Das Ergebnis wirkt auch nach mehreren Korrekturversuchen recht unsymmetrisch und die Pfeile scheinen ein paar Zentimeter über dem Ziel zu schweben. Zwei Türen weiter windet sich die Entwicklungsabteilung gerade im Würgegriff eines größeren UML-Diagramms. Egal wie der Teamleiter die Klassen auch positioniert, das Ergebnis bleibt unübersichtlich.
In beiden Fällen hilft Graphviz. Die kleine Programmsammlung springt immer dann ein, wenn automatisch Grafen (siehe Kasten “Terminologie”) zu zeichnen sind. Über die stolpern Computernutzer immer wieder in den unterschiedlichsten Lebenslagen. Angefangen bei Entity-Relationship-Diagrammen, die ein Datenbankschema visualisieren, bis zu hierarchischen Baumstrukturen, die Unternehmensbeteiligungen oder eine Mitarbeiterhierarchie darstellen.
Normalerweise steckt dabei jede Information in einem rechteckigen Kasten, den man anschließend möglichst geschickt und ohne Überlappungen mit anderen Kästen über Pfeile verbindet. Genau diese Aufgabe übernimmt Graphviz: Der Anwender legt nur fest, welche Elemente welche Beziehungen pflegen, und schon spuckt das Programm eine ansehnliche Zeichnung aus. Diese Festlegung findet jedoch nicht in einem hübschen grafischen Editor statt. Graphviz liest vielmehr die textuelle Beschreibung des Grafen in einer speziellen Auszeichnungssprache. Ein analoges Konzept verwendet das bekannte Latex, auch wenn die beiden Systeme sonst keine Gemeinsamkeiten aufweisen.
Graphviz liegt schon seit vielen Jahren allen gängigen Linux-Distributionen bei. Die Installation beschränkt sich somit auf ein paar Klicks im Paketmanager. Wahlweise liegt auf der Graphiz-Homepage [1] die stets letzte Version im Quelltext, den der bekannte Dreisatz »configure; make; make install« übersetzt und einspielt. In jedem Fall finden anschließend auf der Festplatte mehrere kleine Programme eine neue Heimat.
Schreib mal wieder
Bevor die Graphviz-Tools jedoch in Aktion treten, brauchen sie noch eine Beschreibung des Grafen. Hierzu erstellt der Nutzer eine Textdatei in der leicht zu lernenden Sprache DOT. Listing 1 realisiert einen einfachen gerichteten Grafen. Die Beschreibung definiert zunächst einen gerichteten Grafen mit dem Namen »G«, der drei Knoten enthält: »vater«, »sohn« und »tochter«. Im Gegensatz zu vielen anderen Programmiersprachen müssen die Knoten nicht explizit deklariert oder eingeführt sein. Es genügt, sie einfach zu verwenden.
|
Listing 1: |
|---|
01 digraph G {
02 /* Der Vater hat einen Sohn und eine Tochter: */
03 vater -> sohn;
04 vater -> tochter;
05 }
|
Die Pfeile legen fest, zwischen welchen Knoten eine Kante verläuft. Auch Schleifen sind erlaubt: »vater -> vater« zeigt auf sich selbst. Ein optionales Semikolon schließt jede Zeile ab. Wie an der Klammerung zu sehen ist, lehnt sich die Syntax an die Programmiersprache C an. Von dort stammt auch die Notation für Kommentare, sie stehen zwischen »/*« und »*/«.
Auf den Punkt gebracht
Die textuelle Beschreibung wandelt das mitgelieferte Werkzeug »dot« in eine Zeichnung um. Verwirrenderweise trägt es den gleichen Namen wie die Beschreibungssprache. Der folgende Befehl erzeugt beispielsweise eine PNG-Datei (Abbildung 1):
dot -Tpng beispiel1.dot -o beispiel1.png
Darüber hinaus kennt »dot« noch Postscript (Parameter »-Tps«), Jpg, Gif, Fig, SVG und einige weitere mehr oder weniger exotische Formate. Eine direkte PDF-Ausgabe ist derzeit allerdings nicht möglich. Hier hilft nur der Umweg über das Ghostscript-Werkzeug »ps2pdf«. Auf den Parameter »-o« samt Dateinamen sollte man übrigens besser nicht verzichten. Wenn diese Angabe fehlt, fließt die Ausgabe des Programms direkt in die Konsole. Analoges gilt für den Parameter »-T«, ohne den »dot« einfach nur wieder eine textuelle Beschreibung der Zeichnung auswirft.
Wenn in der Beschreibung andere Angaben fehlen, erzeugt »dot« ovale Knoten, die mit ihren Namen beschriftet sind. Im Beispiel soll das Familienoberhaupt jedoch mit seinem richtigen Vor- und Zunamen in einem rot ausgefüllten Rechteck erscheinen. Für derartige Änderungswünsche stehen in der DOT-Sprache einige Attribute zur Verfügung. Um eine bestimmte Eigenschaft wie die Farbe oder die Beschriftung zu ändern, erhält das zugehörige Attribut einfach einen neuen Wert:
color = red label ="Hans Schmitt"
Diese Attribute schreibt der Anwender durch Kommata getrennt und von eckigen Klammern umschlossen hinter den Knotennamen. Im Fall des Vaters sieht dies folgendermaßen aus:
vater [shape = box, style = filled, color = red, label ="Hans Schmitt"];
Diese Zeile platziert Listing 2 nun irgendwo zwischen den geschweiften Klammern des Grafen. Empfehlenswert ist es dabei, immer das Aussehen der Knoten zu Beginn festzulegen und erst dann die Kanten zu definieren.
|
Terminologie |
|---|
|
Ein Graf verbindet immer mehrere Kästchen über Linien. Die Kästchen bezeichnet man als Knoten, die Linien als Kanten. Ein gutes Beispiel für einen Grafen wäre eine Autobahnkarte: Die Städte bilden die Knoten, während die Straßen die Kanten repräsentieren. Wenn die Verbindungen als Pfeile dargestellt werden, spricht man von einem gerichteten Grafen. Fehlen die Pfeile, handelt es sich entsprechend um einen ungerichteten Grafen. Gerichtete Kanten entsprechen Einbahnstraßen, die der Fahrer nur in einer Richtung passieren darf. Das Ganze lässt sich noch mathematisch korrekt und somit etwas kryptischer ausdrücken: Danach besteht ein (gerichteter) Graf G = (V,E) aus einer Menge von Knoten V und einer Menge von Kanten E, wobei Letztere die Paare (geordneter) Knoten in der Form e = (x,y) enthält. |
Farbige Linien
Was mit den Knoten klappt, funktioniert selbstverständlich auch mit Kanten. Genau wie die Knoten darf der Anwender sie färben, ihren Linienstil verändern oder beschriften. Auch hier genügt es, die entsprechenden Wertepaare in eckige Klammern hinter die entsprechende Kante zu setzen, beispielsweise:
vater -> tochter [style = bold, label = "Erstgeborene", color = blue];
Die komplette Familienbeziehung zeigt Listing 2, das Ergebnis von »dot« ist in Abbildung 2 zu sehen.
|
Listing 2: |
|---|
01 digraph G {
02 vater [shape = box, style = filled, color = red, label ="Hans Schmitt"];
03 tochter [label ="Petra"];
04 sohn [label = "Patrick"];
05
06 vater -> sohn;
07 vater -> tochter [style = bold, label = "Erstgeborene", color = blue];
08 }
|
Sollen in einem recht großen Grafen alle Knoten eine andere Form erhalten, artet dies schnell zu einer etwas größeren Arbeit aus. Für diesen Fall hält die DOT-Beschreibungssprache mit »node« und »edge« zwei reservierte Wörter bereit. Eine Änderung ihrer Attribute wirkt sich somit auf alle anderen Elemente aus. Die Angabe
digraph G {
node [shape = box];
...
}
weist allen Knoten einen rechteckigen Rahmen zu – Voraussetzung ist allerdings, dass der einzelne Knoten selbst keine anders lautenden Attributzuweisungen enthält. Die Anweisung »edge« funktioniert analog für Kanten.
Sehr nützlich sind die so genannten Subgrafen. In ihnen lassen sich einzelne, zusammengehörige Knoten zusammenfassen. Listing 3 zeigt dies an einem Beispiel. Es führt einen Subgrafen ein, der die Tochter und den Sohn umfasst. Damit »dot« ihn im Ausgabebild gesondert zeichnet, muss sein Name mit dem Präfix »cluster« beginnen. Die weiteren Attribute gehören dem Subgrafen und sorgen in diesem Beispielfall für seine Färbung und Beschriftung. Abbildung 3 zeigt das Ergebnis.
|
Listing 3: |
|---|
01 digraph G {
02 subgraph cluster_kinder {
03 style = filled;
04 color = lightgrey;
05 label = "Kinder";
06
07 tochter [label ="Petra"];
08 sohn [label = "Patrick"];
09 }
10
11 vater [label = "Schmitt"];
12
13 vater -> tochter;
14 vater -> sohn;
15 }
|

Abbildung 3: Die Beschreibung aus Listing 3 sorgt für eine grau unterlegte Hervorhebung des Subgrafen mit den Kindern.
Teilbar
Bislang enthielt jeder Knoten in einem Diagramm nur eine einfache Beschriftung. Die eingangs erwähnten UML-Klassendiagramme erfordern jedoch spezielle Knoten, die mehrfach unterteilt sind. In der DOT-Sprache muss dazu zunächst der Rahmen auf die spezielle Form »record« umgeschaltet werden:
node [shape = record]
Ab dieser Stelle im Code unterzieht »dot« die Beschriftung des Knotens einer speziellen Kur. Striche etwa unterteilen den Knoten in Abschnitte:
mitarbeiter [label = "{Mitarbeiter|+ gehalt : intl+ name : stringl | + arbeite() : voidl}" ]
Die Zeichenfolgen »l« sorgen für eine linksbündige Ausrichtung und die geschweiften Klammern für waagerechte Striche. Damit ist eine UML-Klasse beinahe perfekt, es fehlen nur noch die Beschriftungen an den Kanten. In UML dürfen die Kardinalitäten an beiden Enden einer Kante stehen. Das ist durch die Attribute »headlabel« und »taillabel« zu erreichen:
edge [headlabel = "1", taillabel = "1..*" ]
Ein Beispiel für ein kleines UML-Diagramm zeigt Listing 4, das entsprechende Resultat Abbildung 4.
|
Listing 4: |
|---|
01 digraph G {
02 node [shape = record] /* Senkrechte Linien im label wird als
Linie gezeichnet */
03 edge [arrowhead = "none", headlabel = "1", taillabel = "1..*" ]
04
05 /* Geschweifte Klammern bedeuten: Zeichne die Linien waagerecht
06 und nicht senkrecht (drehe den Kasten um 90 Grad) */
07 mitarbeiter [label = "{Mitarbeiter|+ gehalt : intl+ name : stringl | +
arbeite() : voidl}" ]
08 unternehmen [label = "{Unternehmen| | + zahleGehalt() : voidl}" ]
09 mitarbeiter -> unternehmen
10 }
|
Pfeile zeigen auf Slots
Ein »record« kann aber noch mehr. So darf der Anwender die Abtrennungen innerhalb der Knoten verwenden, um die Verbindungen zu mehreren anderen Nachfolgeknoten zu steuern. Wie dies funktioniert, zeigen Listing 5 und Abbildung 5. Die spitzen Klammern markieren spezielle Punkte, die so genannten Slots, von denen die Pfeile im Diagramm ausgehen beziehungsweise bei denen sie enden dürfen.
|
Listing 5: Komplexe |
|---|
01 digraph G
02 {
03 node [shape = record];
04 /* In eckigen Klammern stehen die so genannten "Ports" */
05 vater [ label ="<links> | <mitte> Vater | <rechts>"];
06 sohn [ label ="<links> | <mitte> Sohn | <rechts>"];
07 tochter [ label ="<links> | <mitte> Tochter | <rechts>"];
08
09 "vater":mitte -> "sohn":links;
10 "vater":mitte -> "tochter":rechts;
11 }
|

Abbildung 5: Die Slots aus Listing 5 erlauben Knoten, wie sie beispielsweise zur Visualisierung von Hashfunktionen oder Arrays benötigt werden.
Das Tool »dot« ist auf gerichtete Grafen spezialisiert. Wer mit ungerichteten hantiert, weicht besser auf seinen Kollegen »neato« aus. Er möchte ebenfalls mit DOT-Beschreibungen gefüttert werden, wobei in diesem Fall jedoch anstelle des Schlüsselworts »digraph« nur »graph« zum Einsatz kommt. Die gerichteten Kanten werden durch »–« zu ungerichteten. Das Listing 6 zeigt ein kleines Beispiel, die Abbildungen 6a und 6b stellen die entstandenen Diagramme von Dot und Neato gegenüber.
|
Listing 6: Ungerichteter |
|---|
01 graph G {
02 server [label = "Hauptserver Leo"];
03
04 server -- client_schmitt
05 server -- client_mueller;
06 server -- client_turgau;
07 server -- client_meier;
08 }
|
Ungerichtete Dinge
Die beiden Tools »dot« und »neato« verwenden unterschiedliche Zeichenalgorithmen. Während »dot« die Knoten hierarchisch anordnet, ersetzt »neato« die Kanten durch virtuelle Sprungfedern und ermittelt über simulierte Anziehungskräfte den korrekten Abstand zwischen den Knoten. Hierdurch entsteht ein symmetrisches Layout. Ebenfalls interessant sind die Werkzeuge »twopi« und »circo«. Sie ordnen die Knoten kreisförmig an. Detaillierte Informationen über die verwendeten Algorithmen liefert [5].
Das Kommandozeilenwerkzeug »dot« lässt sich mit Hilfe des Pipes-and-Filters-Konzepts spielend leicht in andere Skripte oder Unix-Programme integrieren. Dazu bereitet der Benutzer zunächst die grafische Beschreibung in einem Textpuffer vor, schiebt diesen dann an »dot« weiter und fängt schließlich das Ergebnis wieder auf. In einem Shellskript könnte dies beispielsweise folgendermaßen geschehen:
echo "digraph G {vater->sohn; vater->tochter;}" | dot -Tpng >beispiel7.png
Diese Zeile lenkt die »dot«-Ausgabe einfach in eine Datei um, sie könnte selbstverständlich aber auch von einem anderen (Shell-)Skript weiterverarbeitet werden. Auf diese Weise funktioniert übrigens der mitgelieferte grafische Editor »dotty« (Abbildung 7). Er ist zwar nicht sehr komfortabel, erlaubt aber ein Zusammenklicken der Grafen.
Wem diese Möglichkeiten noch nicht ausreichen, der kann auch die mitgelieferten Graphviz-Bibliotheken direkt in C nutzen. Sie enthalten bequeme Funktionen, um Textdateien im DOT-Format einzulesen, in eine extra dafür bereit gestellte Datenstruktur zu gießen, die Grafen im Speicher zu manipulieren und schließlich wieder auszugeben.
Das Programm aus Listing 7 zeigt ein kurzes Beispielsprogramm. Es liest eine Textdatei mit »dot«-Befehlen ein, zeichnet den Grafen mit Hilfe des »dot«-Algorithmus und schiebt das Ergebnis wieder raus. Auf ganz analoge Weise ist übrigens auch »dot« selbst realisiert. Weitere kleine Beispielprogramme liegen im Unterverzeichnis »dot.demo« des Quelltext-Pakets, eine umfassende API-Referenz findet sich auf der Graphviz-Homepage. Bei der Einbindung in eigene Projekte ist jedoch darauf zu achten, dass die Bibliotheken der Common Public License unterstehen.
|
Listing 7: |
|---|
01 #include <gvc.h>
02
03 int main()
04 {
05 GVC_t *context;
06 graph_t *graph;
07 FILE *fp;
08
09 context = gvContext();
10 fp = fopen("beispiel.dot", "r");
11 graph = agread(fp); /* Lese Graphen ein */
12 gvLayout(context, graph, "dot"); /*Erstelle Layout mit dot-Algorithmus */
13 gvRender(context, graph, "png", stdout); /* Gib Graph im PNG-Format aus */
14 /*Aufräumen */
15 gvFreeLayout(context, graph);
16 agclose(graph);
17 gvFreeContext(context);
18 }
|
Gut für Skripte
Wer einmal mit der Beschreibungssprache DOT warm geworden ist, möchte die Werkzeuge des Graphviz-Pakets bald nicht mehr missen. Schneller und professioneller kann man Grafen kaum erstellen. Dank Pipes and Filter lassen sich »dot« & Co. einfach in andere Abläufe und Shellskripte integrieren. Wer mit dem Paket gar nicht zurechtkommt, findet im Kasten “Alternativen” einige verwandte Programme. (ofr)
|
Alternativen |
|---|
|
Die Graphviz-Programme verlangen nach einer kurzen Beschreibung des Grafen, den sie dann selbstständig in eine Zeichnung umwandeln. Die Einflussmöglichkeiten beschränken sich somit auf einige wenige Parameter. Wer die Vorzüge einer Beschreibungssprache nutzen, aber dennoch detaillierten Einfluss auf die Position und Form eines jeden Elements nehmen muss oder möchte, weicht besser auf ein Konkurrenzprodukt aus. Besonders erwähnenswert in diesem Bereich sind Asymptote [2] und GLE [3]. Beide lassen sich sogar bequem aus Latex heraus nutzen. In eine ähnliche Richtung gehen XML-basierte Sprachen wie beispielsweise das bekannte SVG [4], für das jedoch passende Software derzeit rar gesät ist. |
|
Infos |
|---|
|
[1] Graphviz-Homepage: [http://www.graphviz.org] [2] Asymptote-Homepage: [http://asymptote.sourceforge.net] [3] GLE-Homepage: [http://www.gle-graphics.org] [4] Spezifikation des SVG-Formats: [http://www.w3.org/Graphics/SVG/] [5] Informationen zu den in Graphviz verwendeten Algorithmen: [http://www.graphviz.org/Documentation.php] |
Copyright © 2002 Linux New Media AG











