Table Of Contents

Previous topic

6. Maongo-Charts

Next topic

6.2. Gemeinsame Properties aller Charttypen

This Page

Section author: jo, benjamin

6.1. Getting Started

MaongoMP bietet für das automatische Platzieren von Labels und für die Anbindung von dynamischen Daten und daraus resultierenden automatischen Layouts ein ganzes Subsystem an Optionen an, die es erlauben, so gut wie jeden Gestaltungswunsch umzusetzen.

Um allerdings einfach loslegen zu können, lohnt es sich, zunächst einmal ein Minimalchart ohne diese Features zu bauen.

6.1.1. Minimalistisch...

Ein einfaches BalkenChart könnte so erzeugt werden:

Beispiel 1:

<widget name="HelloWorldBars" type="BarChart" x="10" y="10" shape="R 320 180">
  <property name="Values" type="List[Number]" value="3.5,2.7,3.3,4.4" />
  <property name="ForegroundColor" value="gray" />
</widget>
../_images/6_1_1.png

Das Barchart nutzt die Liste von vier zugewiesenen Werten, um Balken in der (angegebenen oder ererbten) ForegroundColor zu zeichnen. Es ordnet die Balken automatisch innerhalb des durch Shape und Padding angegebenen Bereichs an. Dasselbe funktioniert auch horizontal:

Beispiel 2:

<widget name="HelloWorldBars" type="BarChart" x="10" y="10" shape="R 320 180">
  <property name="Padding" value="20" />
  <property name="Values" type="List[Number]" value="3.5,2.7,3.3,4.4" />
  <property name="ForegroundColor" value="gray" />
  <property name="Orientation" value="left" />
</widget>
../_images/6_1_2.png

Eine minimalistische Umsetzung eines Linecharts wäre:

Beispiel 3:

<widget name="HelloWorldLine" type="LineChart" shape="R 320 180">
  <property name="Values" type="List[Number]" value="[[3.5,2.7,3.3,4.4]]" />
  <property name="ForegroundColor" value="gray" />
</widget>
../_images/6_1_3.png

Das Beispiel skaliert die Werte im verfügbaren Raum und zeichnet eine Linie in der ForegroundColor mit den in Values angegebenen Werten. Da eine Linie aus einer Liste von Werten besteht, und in einem LineChart mehrer Linien gezeichnet werden können, werden die Values als Liste von Listen (für jede Linie eine Liste) notiert. Hier ein LineChart mit drei Linien:

Beispiel 4:

<widget name="HelloWorldLine" type="LineChart" shape="R 320 180">
  <property name="Values" type="List[Number]" value="[[3.5,2.7,3.3,4.4],[1,3,5,9.5],[6,1.7,3.8,12]]" />
  <property name="Colors" type="List[Color]" value="[red, green, blue]" />
</widget>
../_images/6_1_4.png

Die Farben der Linien werden mit einer Liste von Farben in der Property Colors gesetzt. Wenn mehr Linien als Farbeinträge vorhanden sind, wird wieder von vorne aus der Liste ausgelesen.

Beispiel 5 zeichnet eine minimalistische Tortengrafik:

Beispiel 5:

<widget name="HelloWorldPie" type="PieChart" shape="R 320 180">
  <property name="Values" type="List[Number]" value="1,3,5,9.5,4,3,2,6,7" />
      <property name="ForegroundColor" value="gray" />
  <property name="BackgroundColor" value="gray" />
</widget>
../_images/6_1_5.png

Auch hier werden die Standardeinstellungen des Charttyps genutzt, um ein Kreischart zentriert in die Mitte des verfügbaren Raums zu zeichnen.

Mit geringen Modifikationen können wir auch ein Halbkreis- und eine ellipsoides Chart zeichnen:

Beispiel 6:

<widget name="HelloWorldPie2" type="PieChart" x="10" y="10" shape="R 320 180">
  <property name="BorderWidth" value="1" />
  <property name="Values" type="List[Number]" value="1,3,5,9.5" />
  <property name="Colors" type="List[Color]" value="[red, green, blue]" />
  <property name="PieMode" value="seats" />
</widget>

<widget name="HelloWorldPie3" type="PieChart" x="10" y="200" shape="R 320 180">
  <property name="BorderWidth" value="1" />
  <property name="Values" type="List[Number]" value="1,3,5,9.5" />
  <property name="Colors" type="List[Color]" value="[#ffcc00/66, #33ff88/33, #ff0000/33]" />
  <property name="PieMode" value="ellipse" />
</widget>
../_images/6_1_6.png

Auch in diesem Beispiel haben wir die Property Colors genutzt, um die einzelnen Segmente einzufärben. Im ellipsoiden Chart haben wir den Farben zusätzlich Transparenz gegeben.

Die bisher beschriebenen minimalistischen Ansätze reichen häufig nicht aus, um komplexere Charts zu erstellen: wir wollen Balken, Linien und Kreissegmente beschriften, wollen Data (siehe –> Data) nutzen, um Werte des Charts und der Labels zu setzen, und wollen häufig auch die Labels in spezifischer Art und Weise um die Charts gruppieren oder an Werte des Charts koppeln.

Bevor wir solch komplexe Charts erstellen, hier noch eine ziemlich minimalistische Version eines PieCharts mit einfachen Labels:

Beispiel 7:

<!-- Das PieChart -->
<widget name="ThePie" type="PieChart" x="10" y="10" shape="R 320 180">
  <property name="Values" type="List[Number]" value="1,3,5,9.5" />
  <property name="Colors" type="List[Color]" value="[#ffcc00/99, #33ff88/66, #ff0000/66, #8833ff/66]" />
  <property name="PieMode" value="seats" />
</widget>

<!-- Die erste LabelBar -->
<widget name="FirstLabels" type="LabelBar" x="10" y="190" shape="R 320 20">
  <template name="LabelTemplate" type="Text" shape="R 80 35"/>
  <property name="Values" type="List[String]" value="Wert1,Wert2,Wert3,Wert4" />
  <property name="LabelTemplates" value="[@LabelTemplate]"/>
</widget>

<!-- Ein Template außerhalb der Labelbar -->
<template name="EinAnderesLabelTemplate" type="Text" shape="R 80 35">
  <property name="BackgroundColor" value="#dddddd" />
</template>

<!-- Die zweite LabelBar -->
<widget name="SecondLabels" type="LabelBar" x="10" y="210" shape="R 320 20">
  <property name="Values" type="List[Number]" value="1,3,5,9.5" />
  <property name="LabelTemplates" value="[@EinAnderesLabelTemplate]" />
</widget>
../_images/6_1_7.png

Hier haben wir unter dem PieChartWidget zwei LabelBarWidgets angeordnet, die für jedes ihrer Values ein neues Element aus einem Template generieren. Die Templates, die in einem LabelBar benutzt werden sollen, müssen als Liste aus Template-Referenzen in der Property LabelTemplates gesetzt werden. Die zweite LabelBar zeigt, daß das benutzte Template auch an anderer Stelle im Mad stehen kann. Mehr zu Templates und ihrer Benutzung im folgenden Absatz:

6.1.2. Templates nutzen

Alle Charts können für die wiederholten Elemente (Balken, Linien, Kreissegmente, Labels) Templates nutzen. Templates sind ja bekanntlich (–> Templates ) beliebige Widgets, die bei der Ausführung der Presentation initialisiert (und im Fall eines Charts mehrfach automatisch eingesetzt) werden.

Wenn wir das Beispiel 5 von oben so modifizieren wollen, dass jedes Kreissegment eine anderes Template mit unterschiedlichen Farben enthält, so können wir so vorgehen:

Beispiel 8:

      <template name="red" type="Widget">
        <property name="BackgroundColor" value="red" />
      </template>

      <template name="green" type="Widget">
        <property name="BackgroundColor" value="green" />
      </template>

      <template name="yellow" type="Widget">
        <property name="BackgroundColor" value="yellow" />
      </template>

      <template name="gray" type="Widget">
        <property name="BackgroundColor" value="gray" />
      </template>

      <widget name="ColorPie" type="PieChart" x="20" y="20" shape="R 300 300">
        <property name="Values" type="List[Number]" value="1,3,5,9.5" />
        <property name="ForegroundColor" value="gray" />
        <property name="PieTemplates" type="List[String]" value="[@red,@green,@yellow,@gray]" />
      </widget>
../_images/6_1_8.png

Die Property PieTemplates erhält hier eine Liste von Templatenamen zugewiesen, die für das PieChartWidget per Lookup erreichbar sein müssen. Es nutzt diese Templates der Reihe nach, um die Werte damit als Kreissegmente zu zeichnen.

Ebenso könnten wir aber auch entscheiden, dass ein Template mehrfach genutzt werden soll. Auch ein Button kann hier als Template verwendet werden:

Beispiel 9:

<template name="mybutton" type="Button">
  <property name="BorderWidth" value="2" />
  <property name="BorderColor" value="orange" />
  <action trigger="button-clicked">
    this.BackgroundColor = "red";
  </action>
</template>

<widget name="ClickablePie" type="PieChart" x="20" y="20" shape="R 300 300">
  <property name="Values" type="List[Number]" value="1,3,5,9.5" />
  <property name="PieTemplates" type="List[String]" value="[@mybutton]" />
  <property name="PieMode" value="seats" />
</widget>
../_images/6_1_9.png

Hier wird das Template mybutton für die Darstellung aller Segmente benutzt. Ein Klick auf ein Kreissegment färbt es rot.

6.1.3. Chart und Templates an Data binden

Bereits Beispiel 8 und 9 wecken weitere Wünsche: Es wäre sehr praktisch, unser Chart und die zum Zeichnen verwendeten Templates an die Werte eines auf das Widget gerouteten Data binden zu können.

Angenommen, wir haben ein Data, das auf einem Button definiert ist und so aussieht:

Beispiel 10:

<widget name="Databutton" type="Button" x="200" y="140" shape="O 100 50">
  <property name="Text" value="Route Data!" />
  <route datatype="*" target="BC_CTRL"/>
  <property name="DataMode" value="store-forward"/>
  <property name="Data">
    <data type="SimpleData" key="mydata">
      <table name="Table">
        <row>
          <cell>Hans</cell>
          <cell type="Color">#cc0000</cell>
          <cell type="Decimal">1.0</cell>
          <cell type="Decimal">-1.0</cell>
          <cell>Gruppe 1</cell>
          <cell type="Image">../images/hans.png</cell>
        </row>
        <row>
          <cell>Peter</cell>
          <cell type="Color">#00cc00</cell>
          <cell type="Decimal">3.0</cell>
          <cell type="Decimal">-1.5</cell>
          <cell>Gruppe 1</cell>
          <cell type="Image">../images/peter.png</cell>
        </row>
        <row>
          <cell>Dieter</cell>
          <cell type="Color">#0000cc</cell>
          <cell type="Decimal">5.0</cell>
          <cell type="Decimal">0.5</cell>
          <cell>Gruppe 2</cell>
          <cell type="Image">../images/dieter.png</cell>
        </row>
        <row>
          <cell>Georg</cell>
          <cell type="Color">#cc00cc</cell>
          <cell type="Decimal">9.5</cell>
          <cell type="Decimal">3.0</cell>
          <cell>Gruppe 2</cell>
          <cell type="Image">../images/georg.png</cell>
        </row>
      </table>
    </data>
  </property>
</widget>

Ein Klick auf den Button leitet das Data zum Widget TheChart weiter:

Beispiel 10 cont'd:

<engine type="ChartController" name="BC_CTRL">
  <property name="ColumnNames" value="names,colors,values,diffs,groups,images"/>
  <property name="DataTableView" value="Table" />
</engine>

<widget name="TheChart" type="BarChart" x="10" y="10" shape="R 320 120">
  <property name="Controller" value="@BC_CTRL"/>
  <bind property="Values" to="Controller.Columns" key="values" />
  <bind property="Groups" to="Controller.Columns" key="groups" />
  <property name="GroupMetrics" value="spacing: 10" />
      <property name="BarTemplates" type="List[String]" value="[@BarTempl]" />
  <template name="BarTempl" type="Widget">
    <bind property="Texture" to="ControllerProxy" key="images" />
    <property name="TextureMode" value="top-center" />
    <bind property="BackgroundColor" to="ControllerProxy" key="colors" />
  </template>
</widget>

Wir sehen, dass es Properties gibt, denen Wertelisten zugewiesen werden, da sie für das ganze Chart gelten: Values und Groups (letztere erlaubt uns, mit GroupMetrics die Balken des BarCharts zu gruppieren).

Daneben gibt es Properties im Template, die wir an einen bestimmten Wert einer Liste binden wollen. Dazu dient das Objekt “Controller.current”: beim Erzeugen des Charts wird für jeden Wert eine Kopie des Templates erzeugt. Diese Kopie wird mit den Binding-Angaben (hier für Texture und BorderColor an den gerade aktuellen Index (= current) des Controller-Objekts gebunden und erhält den unter key verfügbaren Wert, hier also eine Farbe und ein Bild.

Die LabelBars können sich an den Controller des PieCharts binden und von dort Ihre Values füllen.

Nun wollen wir auch endlich die LabelBar für die Zahlenwerte mit einem Textwidget formatieren. In diesem Fall binden wir nicht nur die Werteliste, sondern im Template auch die einzelnen Werte mit current:

Beispiel 10 cont'd:

<widget name="TheLabels2" type="LabelBar" x="10" y="230" shape="R 320 20">
  <property name="Gap" value="2" />
  <property name="Controller" value="@BC_CTRL"/>
  <bind property="Values" to="Controller.Columns" key="values" />
  <property name="LabelTemplates" type="List[String]" value="[@LabelBarTempl]" />

  <template name="LabelBarTempl" type="Text">
    <property name="TextAlign" value="center" />
    <bind property="Text" to="ControllerProxy" key="values" />
  </template>
</widget>
../_images/6_1_10.png

6.1.4. Raster und Layout zur Platzierung der Labels nutzen

In manchen Situationen ist es nötig, die Positionierung der Labels nicht unabhängig vom (Balken-, Linien-)Chart vorzunehmen, sondern an die Positionen im gezeichneten Chart zu koppeln. Die LabelBars, die wir in den bisherigen Beispielen mit den Charts verwendet haben, bieten diese Möglichkeit nicht: sie sind unabhängige Widgets, die ihre eigene Layout-Funktionalität nutzen.

In einem gruppierten Säulenchart werden die Säulen auseinander gerückt: Die Labels sollten dem folgen. Dies erreichen wir, indem wir im BarChart ein einfaches Raster für die Labels definieren und mit LayoutConstraints die Labelpositionierung festlegen.

Beispiel 11:
Databutton wie oben

<engine type="ChartController" name="BC_CTRL">
  <property name="ColumnNames" value="names,colors,values,diffs,groups,images" />
  <property name="DataTableView" value="Table" />
</engine>

<widget name="TheChart" type="BarChart" x="10" y="10" shape="R 320 220">
  <!-- Die Referenz auf den ChartController -->
  <property name="Controller" value="@BC_CTRL" />

  <!-- Binden der Werte an den Controller -->
  <bind property="Values" to="Controller.Columns" key="values" />
  <bind property="Groups" to="Controller.Columns" key="groups" />

  <!-- Abstände zwischen Balken und Gruppen -->
  <property name="BarMetrics" value="spacing: 3"/>
  <property name="GroupMetrics" value="spacing: 20" />

  <!-- Definition des horizontalen und vertikalen Rasters -->
  <property name="HorizontalRaster" type="List[KeyValue]" value="[left: 9, chart: max, right: 9]" />
  <property name="VerticalRaster" type="List[KeyValue]" value="[top: 6, textlabel: 20, valuelabel: 20, space: 6, chart: max]" />

  <!-- Definition der zu nutzenden Templates -->
  <property name="BarTemplates" type="List[String]" value="[@BarTempl]" />
  <property name="BarLabels" type="List[String]" value="[@Wertlabel, @Textlabel]" />
</widget>

Wir haben hier ein BarChart soweit vorbereitet wie in Beispiel 10 das PieChart.

Neu hinzugekommen ist die Definition eines horizontalen und eines vertikalen Rasters. Was noch fehlt, ist die in ElementLabels genannten Templates zu definieren und in dieser Definition die Verortung im Raster zu benennen.

Die Balken werden per Konvention in der Rasterposition chart-chart (also HorizontalRasterPosition=chart und VerticelRasterPosition=chart) gezeichnet.

Mit der Property GroupMetrics haben wir außerdem festgelegt, dass zwischen den beiden Gruppen von Balken ein Abstand von 20px gezeichnet werden soll. BarMetrics sorgt für den Abstand zwischen den Balken.

Definition der Templates für die Labels und die Balken:

Beispiel 11 cont'd:

<template name="Textlabel" type="Text">
  <bind property="Text" to="ControllerProxy" key="names" />
  <property name="TextAlign" value="center" />
  <property name="LayoutConstraints">
    <map type="whatever">
      <property name="VerticalRasterPosition" value="textlabel" />
      <property name="HorizontalRasterPosition" value="chart" />
      <property name="XSource" value="anchor" />
      <property name="YSource" value="raster" />
      <property name="WidthSource" value="raster" />
      <property name="HeightSource" value="raster" />
      <property name="Align" value="top-center" />
    </map>
  </property>
</template>
<template name="Wertlabel" type="Text">
  <bind property="Text" to="ControllerProxy" key="values" />
  <property name="TextAlign" value="center" />
  <property name="BackgroundColor" value="#efefef" />
  <property name="LayoutConstraints">
    <map type="whatever">
      <property name="VerticalRasterPosition" value="valuelabel" />
      <property name="HorizontalRasterPosition" value="chart" />
      <property name="XSource" value="anchor" />
      <property name="YSource" value="raster" />
      <property name="WidthSource" value="raster" />
      <property name="HeightSource" value="raster" />
      <property name="Align" value="top-center" />
    </map>
  </property>
</template>
<template name="BarTempl">
  <bind property="BackgroundColor" to="ControllerProxy" key="colors" />
  <property name="LayoutConstraints">
    <map type="whatever">
      <property name="XSource" value="raster" />
      <property name="YSource" value="raster" />
      <property name="WidthSource" value="raster" />
      <property name="HeightSource" value="raster" />
    </map>
  </property>
</template>
../_images/6_1_11.png

Wir sehen, dass die Labels wie vorgegeben an unterschiedlichen vertikalen Positionen gezeichnet werden. Die Gruppierung wirkt sich in erster Linie auf die Säulen aus; da die Labels über das Raster aber an die Säulenpositionen gebunden sind, sehen wir bei ihnen denselben Gruppenabstand.

Nähere Informationen zum LayoutRaster und den möglichen LayoutConstraints siehe im Kapitel Raster und LayoutConstraints.

Test: See this example script.