Griglie e grafici

Modulo di esempio: hellogrid.

Un grafico non è un meccanismo separato: è una visualizzazione di una griglia esistente. Tutto parte quindi dalla griglia.

Il meccanismo di base: un’istantanea ripetuta

Una griglia è solo un’istantanea ripetuta degli elementi di base della propria proprietà — non esiste alcuna API per “aggiungere una riga con questi valori” direttamente:

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() reinizializza la griglia.
  • gridLimit (chiave JSON) imposta un tetto al numero di righe: oltre, la più vecchia viene rimossa silenziosamente (FIFO).
"mesures": {
  "hasGrid": true,
  "showGrid": true,
  "showElts": false,
  "gridLimit": 30,
  "e": {
    "t": { "type": "int" },
    "valeur": { "type": "float" }
  }
}
  • showGrid: mostra la griglia stessa.
  • showElts: mostra il form degli elementi di base (la “bozza” prima di push()). Generalmente nascosto (false) quando è il modulo a spingere le righe.

CRUD generico, senza alcun codice

Con permission:2 (lettura/scrittura) su una proprietà con griglia, il client guadagna gratuitamente controlli di creazione/modifica/eliminazione/riordinamento delle righe — interamente gestiti dal framework, senza che sia necessario alcun codice lato modulo:

EventoMetodo PropertyMulti chiamatoEffetto
GCnewLine(valeurs)Aggiunge una riga
GUupdateLine(index, valeurs)Modifica una riga esistente
GDdeleteLine(index)Elimina una riga
GFfetchLine(index)Ricopia i valori della riga negli elementi di base — “carica per modificare”
GHswapLines(index, index-1)Fa risalire una riga di un rango
GBswapLines(index, index+1)Fa scendere una riga di un rango
GRclearGrid()Svuota la griglia

Il client tipico (interfaccia ostcore) mostra già, per qualsiasi proprietà con griglia non bloccata in sola lettura: un pulsante modifica (attiva GF), sali/scendi (GH/GB), elimina (GD), e un form di aggiunta/modifica (GC/GU) — incondizionatamente, non appena showGrid e il permesso lo consentono. Con showElts:true in più, cliccare una riga (fuori dai pulsanti di azione) attiva anche GF e carica i suoi valori nel form degli elementi di base. È esattamente ciò che dimostra già gridvisible nel modulo di esempio — nessun codice supplementare da scrivere per beneficiarne.

Due usi ben distinti

Una griglia può essere spinta dal modulo (sola lettura lato client, il server spinge le righe col passare del tempo — una cronologia di misure, per esempio) oppure gestita dal client (lettura/scrittura, il CRUD generico sopra — una lista di task, di target, di filtri…). I due pattern convivono nella stessa meccanica, solo la permission cambia.

autoSelect / autoUpDown: riservati, non ancora cablati

PropertyMulti espone anche autoSelect()/setAutoSelect() e autoUpDown()/setAutoUpDown() (chiavi JSON "autoselect"/"autoupdown"). Il loro ruolo previsto: lasciare che il modulo prenda il controllo di ciò che deve accadere quando l’utente seleziona una riga o la riordina — non una questione di presentazione lato client, il client mostra i suoi controlli di griglia nello stesso modo in tutti i casi.

Non ancora implementato

Queste due impostazioni si memorizzano e si rileggono, ma nulla nel framework le consulta ancora per davvero — non esiste oggi alcun punto di ingresso (metodo virtuale, evento) che permetta a un modulo di reagire a una selezione o a un riordinamento di riga. Monitorato in ost/ostserver#270.

Un grafico è una griglia, visualizzata

graphType/graphParams richiedono hasGrid:true — senza griglia, nessun grafico.

XY: una curva, o una nuvola di punti

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

X/Y nominano gli elementi della griglia da usare come assi. I punti sono per default collegati da una linea — per una nuvola di punti senza relazione d’ordine (misure non sequenziali, per esempio), aggiungere "scatter": true in graphParams disabilita il tracciamento della linea, i punti stessi restano visualizzati.

SDY: più serie, su più assi

"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" }
}

Una sola griglia, più serie con nome intrecciate — ogni riga ha il proprio valore di serie (S), un timestamp (D) e un valore (Y). Per alimentare due serie a ogni campione, spingere una riga per serie:

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 permettono a due serie di scale molto diverse di non schiacciarsi visivamente l’una l’altra, ciascuna sul proprio asse.