Grids und Graphen

Beispielmodul: hellogrid.

Ein Graph ist kein eigenständiger Mechanismus: er ist eine Visualisierung eines bestehenden Grids. Alles beginnt also beim Grid.

Der Grundmechanismus: ein wiederholter Snapshot

Ein Grid ist nichts anderes als ein wiederholter Snapshot der Basiselemente seiner eigenen Property — es gibt keine API, um direkt “eine Zeile mit diesen Werten hinzuzufügen”:

getEltInt("mesures", "t")->setValue(t, false);
getEltFloat("mesures", "valeur")->setValue(v, false);
getProperty("mesures")->push();   // copie l'état courant des éléments en nouvelle ligne
  • clearGrid() setzt das Grid zurück.
  • gridLimit (JSON-Schlüssel) begrenzt die Anzahl der Zeilen: darüber hinaus wird die älteste stillschweigend entfernt (FIFO).
"mesures": {
  "hasGrid": true,
  "showGrid": true,
  "showElts": false,
  "gridLimit": 30,
  "e": {
    "t": { "type": "int" },
    "valeur": { "type": "float" }
  }
}
  • showGrid: zeigt das Grid selbst an.
  • showElts: zeigt das Formular der Basiselemente (der “Entwurf” vor push()). Normalerweise ausgeblendet (false), wenn es das Modul ist, das die Zeilen pusht.

Generisches CRUD, ohne jeglichen Code

Mit permission:2 (lesen/schreiben) auf einer Property mit Grid erhält der Client kostenlos Steuerelemente zum Erstellen/Ändern/Löschen/Neuordnen von Zeilen — vollständig vom Framework verwaltet, ohne dass auf Modulseite Code nötig wäre:

EreignisAufgerufene PropertyMulti-MethodeEffekt
GCnewLine(valeurs)Fügt eine Zeile hinzu
GUupdateLine(index, valeurs)Ändert eine bestehende Zeile
GDdeleteLine(index)Löscht eine Zeile
GFfetchLine(index)Kopiert die Werte der Zeile in die Basiselemente zurück — “zum Bearbeiten laden”
GHswapLines(index, index-1)Rückt eine Zeile einen Rang nach oben
GBswapLines(index, index+1)Rückt eine Zeile einen Rang nach unten
GRclearGrid()Leert das Grid

Der typische Client (die ostcore-Oberfläche) zeigt für jede nicht auf nur-lesbar gesperrte Property mit Grid bereits an: einen Bearbeiten-Button (löst GF aus), Hoch/Runter (GH/GB), Löschen (GD), und ein Hinzufügen-/Bearbeiten-Formular (GC/GU) — bedingungslos, sobald showGrid und die Permission es erlauben. Mit zusätzlich showElts:true löst das Klicken einer Zeile (außerhalb der Aktions-Buttons) ebenfalls GF aus und lädt ihre Werte in das Formular der Basiselemente. Genau das demonstriert bereits gridvisible im Beispielmodul — kein zusätzlicher Code nötig, um davon zu profitieren.

Zwei klar unterschiedliche Anwendungsfälle

Ein Grid kann vom Modul gepusht werden (nur lesbar auf Client-Seite, der Server pusht die Zeilen fortlaufend — zum Beispiel ein Messverlauf) oder vom Client verwaltet werden (lesen/schreiben, das generische CRUD oben — eine Aufgabenliste, Zielliste, Filterliste…). Beide Muster leben in derselben Mechanik nebeneinander, nur die permission ändert sich.

autoSelect / autoUpDown: reserviert, noch nicht angebunden

PropertyMulti stellt auch autoSelect()/setAutoSelect() und autoUpDown()/setAutoUpDown() bereit (JSON-Schlüssel "autoselect"/"autoupdown"). Ihr vorgesehener Zweck: dem Modul die Kontrolle darüber zu geben, was passieren soll, wenn der Benutzer eine Zeile auswählt oder neu ordnet — keine Darstellungsfrage auf Client-Seite, der Client zeigt seine Grid-Steuerelemente in jedem Fall gleich an.

Noch nicht implementiert

Diese beiden Einstellungen werden gespeichert und wieder eingelesen, aber nichts im Framework fragt sie derzeit tatsächlich ab — es gibt heute keinen Einstiegspunkt (virtuelle Methode, Ereignis), über den ein Modul auf eine Zeilenauswahl oder -umordnung reagieren könnte. Verfolgt in ost/ostserver#270.

Ein Graph ist ein visualisiertes Grid

graphType/graphParams erfordern hasGrid:true — ohne Grid kein Graph.

XY: eine Kurve oder eine Punktwolke

"courbe": {
  "hasGrid": true, "showGrid": false, "showElts": false,
  "gridLimit": 30,
  "graphType": "XY",
  "graphParams": { "X": "t", "Y": "valeur" }
}

X/Y benennen die als Achsen zu verwendenden Elemente des Grids. Die Punkte werden standardmäßig durch eine Linie verbunden — für eine Punktwolke ohne Ordnungsbeziehung (zum Beispiel nicht sequenzielle Messungen) deaktiviert das Hinzufügen von "scatter": true in graphParams das Zeichnen der Linie, die Punkte selbst werden weiterhin angezeigt.

SDY: mehrere Serien, auf mehreren Achsen

"graphType": "SDY",
"graphParams": {
  "S": "serie", "D": "date", "Y": "valeur",
  "graphColors": {
    "temp": { "R": 255, "G": 0, "B": 0 },
    "hum":  { "R": 0, "G": 0, "B": 255 }
  },
  "axis": {
    "gauche": { "pos": "left", "text": "Température" },
    "droite": { "pos": "right", "text": "Humidité" }
  },
  "mapping": { "temp": "gauche", "hum": "droite" }
}

Ein einziges Grid, mehrere verschachtelte benannte Serien — jede Zeile hat ihren eigenen Serienwert (S), einen Zeitstempel (D) und einen Wert (Y). Um zwei Serien bei jedem Sample zu befüllen, wird pro Serie eine Zeile gepusht:

getEltString("historique", "serie")->setValue("temp");
getEltDateTime("historique", "date")->setValue(maintenant, false);
getEltFloat("historique", "valeur")->setValue(temperature, false);
getProperty("historique")->push();

getEltString("historique", "serie")->setValue("hum");
getEltDateTime("historique", "date")->setValue(maintenant, false);
getEltFloat("historique", "valeur")->setValue(humidite, false);
getProperty("historique")->push();

graphColors/axis/mapping erlauben es zwei Serien mit sehr unterschiedlichen Skalen, sich visuell nicht gegenseitig zu überdecken, jede auf ihrer eigenen Achse.