21 October 2022, Ralf D. Mueller

Deine Diagramme sind Legende?

Deine PlantUML-Diagramme sind Legende?

…​dann verpasse ihnen eine Legende! Ein Diagramm soll nicht nur für Insider lesbar sein. Mit einer Legende erklärst du die verwendeten Symbole und Farben. In diesem Artikel zeige ich dir, wie es geht.

PlantUML verfügt über ein wenig dokumentiertes Element namens "Legend". Damit lässt sich eine Box im Diagramm z. B. in der rechten unteren Ecke platzieren. Wie aber der Inhalt dargestellt werden soll ist unklar.

@startuml
skinparam actorStyle awesome

database Datenbank
:User: -> [Komponente]
[Komponente] -> Datenbank #green

legend right
   <b>Legende</b>
   ???
endlegend
@enduml
Diagram

Google findet als Idee, dass die Legende als Tabelle in Creole-Syntax erstellt werden kann. Farben kann man damit gut erklären, aber für Symbole können nur Emojis oder spezielle Zeichen verwendet werden.

@startuml
skinparam actorStyle awesome

database Datenbank
:User: -> [Komponente]
[Komponente] -> Datenbank #green

legend right
   <b>Legende</b>
   | <#red>    | Benutzer-Zugriff |
   | <#green>  | Datenbank-Verbindung |
   | <:smiley:> | Benutzer :-) |
endlegend
@enduml
Diagram

In einem Forum habe ich am Rande den Hinweis gefunden, dass man mit dem Map-Statement des Objektdiagramms auch eine Tabelle aufbauen kann. Nur geht das nicht direkt innerhalb der Legende. Es gibt aber den Trick, dass man mit der `{{ …​ }} Syntax ein neues Diagramm innerhalb des Diagramms erstellen kann. Damit lässt sich dann auch eine Map innerhalb der Legende aufbauen.

@startuml
skinparam actorStyle awesome

database Datenbank
:User: -> [Komponente]
[Komponente] -> Datenbank #green

legend right
{{
   map "<b>Legende</b>" as legend #white {
   <#red>    => Benutzer-Zugriff
   <#green>  => Datenbank-Verbindung
   <:smiley:> => Benutzer :-)
   }
}}
endlegend
@enduml
Diagram

Und wenn wir jetzt schon dabei sind Diagramme innerhalb von Diagrammen zu nutzen, dann können wir das auch noch eine Ebene tiefer machen. Dadurch schaffen wir es in der Legende die Diagramm-Elemente zu zeichnen, die wir beschreiben wollen.

Dazu bauen wir uns in einer Prozedur ein universelles Mini-Diagramm:

scale $scale
skinparam backgroundcolor transparent
label " " as A
label " " as B
$type

Der scale-Befehl erlaubt es die zu beschreibende Komponente kleiner darzustellen und somit die Legende kompakt zu halten.

Die beiden unsichtbaren Labels sorgen dafür, dass wir einen Connector von A nach B darstellen können.

Das ganze sieht dann kompakt wie folgt aus:

@startuml
skinparam actorStyle awesome

database Datenbank
:User: -> [Komponente]
[Komponente] -> Datenbank #green

legend right
{{

   !procedure $entry($type, $label, $scale=1)
      {{\nscale $scale \nskinparam backgroundcolor transparent\nlabel " " as A\nlabel " " as B\n $type \n}} => $label
   !endprocedure
   map "<b>Legende</b>" as legend #white {
      $entry(":Actor:"," Benutzer", 0.5)
      $entry("[component]"," Benutzer", 0.7)
      $entry("database db","Datenbank", 0.7)
      $entry("A -> B","Benutzer-Zugriff")
      $entry("A -> B #green","Datenbank-Verbindung")
   }
}}
endlegend
@enduml
Diagram

Im letzten Schritt möchte ich die Legende mit ein paar Styles noch aufhübschen. Der doppelte Rahmen soll weg und etwas kleiner wäre auch nicht schlecht.

@startuml
skinparam actorStyle awesome
skinparam legendBackgroundColor transparent
skinparam legendBorderColor transparent

database Datenbank
:User: -> [Komponente]
[Komponente] -> Datenbank #green


legend right
{{
   scale 0.8
   skinparam defaultFontSize 14
   skinparam BackGroundColor transparent
   skinparam defaultBackgroundColor white

   !procedure $entry($type, $label, $scale=1)
      {{\nscale $scale \nskinparam backgroundcolor transparent\nlabel " " as A\nlabel " " as B\n $type \n}} => $label
   !endprocedure
   map "<b>Legende</b>" as legend #white {
      $entry(":Actor: #green","\nBenutzer", 0.5)
      $entry("[component]","\nBenutzer", 0.7)
      $entry("database db","\nDatenbank", 0.7)
      $entry("A -> B","Benutzer-Zugriff")
      $entry("A -> B","Datenbank-Verbindung")
   }
}}

endlegend
@enduml
Diagram

Bei der Nutzung fällt schnell auf, dass die Legende zu viel Platz einnimmt. Sie duldet keine anderen Diagramm-Elemente neben sich. Also haben wir weiter geforscht. Mit dem Diagramm in der Legende besteht eigentlich kein Grund mehr wirklich das Element Legend zu verwenden. Was passiert, wenn wir es durch eine rectangle ersetzen und diese entsprechend Stylen?

Dazu müssen wir dem Element einen Stereotype verpassen, da wir sonst alle rectangle-Elemente stylen würden. Und siehe da, es funktioniert.

Durch diesen Trick haben wir nun mehr Einfluss auf die Platzierung, denn wir können dieses rectangle-Element durch versteckte Verbindungen beeinflussen.

@startuml
skinparam actorStyle awesome

database Datenbank
:User: -> [Komponente]
[Komponente] -down-> Datenbank #green

rectangle a <<test>>
Datenbank -left-> a

skinparam rectangle<<legend>> {
    backgroundColor transparent
    borderColor transparent
    shadowing false
}
hide <<legend>> stereotype

rectangle legende <<legend>> [
{{
   scale 0.8
   skinparam defaultFontSize 14
   skinparam BackGroundColor transparent
   skinparam defaultBackgroundColor white

   !procedure $entry($type, $label, $scale=1)
      {{\nscale $scale \nskinparam backgroundcolor transparent\nlabel " " as A\nlabel " " as B\n $type \n}} => $label
   !endprocedure
   map "<b>Legende</b>" as legend #white {
      $entry(":Actor:","\nBenutzer", 0.5)
      $entry("[component]","\nBenutzer", 0.7)
      $entry("database db","\nDatenbank", 0.7)
      $entry("A -> B","Benutzer-Zugriff")
      $entry("A -> B #green","Datenbank-Verbindung")
   }
}}
]

User -[hidden]-> legende
legende -[hidden]down-> a

@enduml
Diagram

Übrigens: PlantUML möchte Elemente und ihre Verbindungen immer optimiert platzieren. Es kann also sein, dass die neue Legende deshalb noch mal kräftig durchmischt. Es gibt aber nicht nur die Pfeildefinition -[hidden]→, um eine Verbindung nicht anzuzeigen. Der Pfeil -[norank]→ ist eine Verbindung, welche bei besagter Optimierung ignoriert wird. Beide Features kann man kombinieren: Mit einem -[norank,hidden]→ ist die Legende unsichtbar mit einem anderen Element verbunden, ohne dass dies das Diagramm umstrukturiert.

diagrams plantuml docs-as-code