.. _le_stampe/stampe_genropy/stampe_risorsa/tipi_stampa/multipagina_con_griglia/grid: 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 :ref:`dati della griglia` . Dopodiché a meno di particolari necessità il ciclo sui dati delle righe, riempirà la griglia secondo la definizione fornita dal :ref:`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 :ref:`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 :ref:`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 :ref:`cell` ed eventuali altri :ref:`layout` e :ref:`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 .. hint:: 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.