Grid

Chiamiamo grid della stampa, la griglia che riporta i dati un certo numero di righe.

La grid è suddivisa a sua volta in 3 parti principali

  • grid_header: una riga speciale che contiene le intestazioni di tutte le colonne della griglia.

  • grid_body: il contenuto vero e proprio, cioè le N righe contenenti i dati.

  • grid_footer: una riga speciale ed opzionale che mostra i totali delle colonne numeriche.

L’altezza di grid_header e grid_footer si può specificare, espressa in millimetri attraverso gli attributi di classe

  • grid_header_height

  • grid_footer_height

E se essa dovesse essere calcolata, in funzione dei dati o di opzioni fornite al lancio della stampa, si possono implementare i metodi hook

  • calcGridHeaderHeight

  • calcGridFooterHeight

I quali dovranno ritornare un valore numerico riferito all’altezza in millmetri.

Per quanto riguarda il grid_body, l’unico attributo da assegnare è grid_row_height, il cui valore di default è 5. Anche in questo caso esiste un metodo hook per modificare in modo dinamico l’altezza della riga: il metodo calcRowHeight

Il riempimento della griglia

Per riempire le righe della griglia, ovvero il grid_body, occorre prima di tutto fare il necessario per caricare i dati della griglia .

Dopodiché a meno di particolari necessità il ciclo sui dati delle righe, riempirà la griglia secondo la definizione fornita dal metodo gridStruct .

Override di prepareRow

Se tuttavia, la riga della griglia richiede una gestione totalmente custom è possobile ridefinire il metodo hook prepareRow, il quale viene chiamato ad ogni iterazione del ciclo sulle righe nei dati e riceve l’elemento row da riepire. Tutti i dati della riga saranno in disponibili in self.rowData.

L’implementazione standard di prepareRow non fa altro che definire un elemento cell per ogni colonna definita dal metodo gridStruct e riempirla con i dati letti in self.rowData.

Personalizzare una cella

Secondo il funzionamento standard in ogni cella viene semplicemente inserito il valore del dato che gli corrisponde. Ma può capitare la necessità di dover personalizzare il contenuto di una singola cella.

Supponiamo di dover mostrare in cella corrispondente ad una colonna numerica, il valore numerico se fosse positivo ed una certa immagine se negativo. In questo caso lo sviluppatore dovrebbe avere la possibilità di fare qualcosa del genere:

if self.rowData['importo'] >=0 :
    row.cell(self.rowData['importo'])
else:
    row.cell().img(src='negativo.jpg')

In tal caso posso definire il metodo

renderGridCell_importo, ovvero definire un metodo che concateni nella sua signature il prefisso renderGridCell con il nome della colonna di cui voglio personalizzare la cella. Tutti i metodi di tipo renderGridCell ricevono i seguenti parametri

  • col : il nome della colonna

  • rowData : l’oggetto di tipo dict contenente i dati della riga

  • parentRow : ovvero l’elemento row sul quale dovrò attaccare la cella personalizzata

  • Altri parametri di cell tra cui mm_width, cell_content, align_class, etc.

Ma all’interno di una cella personalizzata posso anche annidare un nuovo layout con al suo interno altre righe.

Ad esempio in una stampa di righe di anagrafica, desidero che nella colonna telefono sia contenuto in un piccolo layout innestato in tutti i numeri di telefono del record, presentandoli uno sotto l’altro. O ancora, se il campo note supera un certo numero di caratteri avrà un font-size più piccolo o sarà addirittura troncato, altrimenti seguirà lo stile di default.

All’interno di un metodo renderGridCell personalizzato dovremo aggiungere l’elemento cell ed eventuali altri layout e row annidate.

def renderGridCell_note(self, col = None, rowData = None, parentRow = None, **cell_kwargs):
      cell_kwargs['width'] = cell_kwargs.pop('mm_width',None)
      cell = parentRow.cell(overflow='hidden', **cell_kwargs)
      noteLayout = cell.layout(name='ivaL', border_width=0, style='text-indent:1mm;')
      if not rowData.get('dati'):
          return
      for dato in rowData['dati'].values():
          r = noteLayout.row()
          note_text = dato.get('note') or ''
          fsize=''
          if len(note_text) > 30:
              fsize ='font-size:6pt;'
              if len(note_text)>40:
                  note_text = '{n}...'.format(n=note_text[:40])
          r.cell(note_text, style=fsize)

Il metodo calcRowHeight

Abbiamo anticipato l’esistenza di un metodo di hook che serve a modificare l’altezza della riga, pre-impostata dall’attributo grid_row_height Il metodo calcRowHeight è essenziale nel caso in cui una riga abbia delle celle possono contenere a loro volta più righe o che devono espandersi in altezza a seconda del loro contenuto.

In questi casi bisogna implementare tale metodo, che non riceve alcun parametro. I dati di riga presenti in self.rowData saranno a disposizione per stabilire quanto aggiungere all’altezza della riga.

import math
from gnr.core.gnrstring import weightedLen

def calcRowHeight(self):
      desc_offset = 50 if self.page_orientation=='V' else 100
      n_rows = math.ceil(weightedLen(self.rowField('descrizione'), upper_coeff=1.3)/desc_offset)
      height = (self.grid_row_height * n_rows)
      return height

Suggerimento

Nell’esempio proposto si fa uso del metodo weightedLen, che si occupa di stimare la lunghezza «pesata» di una stringa, dando un maggior peso alle lettere maiuscole (upper_coeff) ed eventualmente uno inferiore alle minuscole (lower_coeff). Non è obbligatorio fare uso di questa tecnica, tuttavia è consigliata dal momento che non esiste una scienza esatta nella stima dell’ingombro di una stringa partendo dal numero di caratteri.