.. _tutor/pagina/containers: Elementi di Layout ================== Per creare pagine articolate in diverse sezioni e parti sono disponibili dei widget di tipo layout. Identifichiamo due diverse categorie di widget di tipo layout: i **pane**, delle "*tiles*" da riempire con un contenuto, ad esempio :ref:`Formbuilder` o altri :ref:`Widgets` , testi o `components `_, e i **container**, veri e propri "slot", che hanno lo scopo di ospitare uno o più *pane*. .. raw:: html
La strutturazione di una pagina complessa in Genropy implicherà dunque innanzitutto di **dividere o organizzare lo spazio con dei container**, e successivamente di **riempire questo spazio con dei pane**. .. list-table:: :widths: 30 70 :align: left :header-rows: 1 * - Elemento Layout - Può contenere * - Containers - Altri containers, contentPane * - contentPane - Altri containers, Widget, Testi, Components Il *pane* principale è il `contentPane`_, mentre i *container* si suddividono in `tabContainer`_, `borderContainer`_ e `stackContainer`_. contentPane ----------- Il contentPane è l'unico widget di layout che **può contenere element HTML o widget**. Può anche contenere un altro container ma in tal caso deve essere figlio unico. .. raw:: html .. raw:: html Nell'esempio viene mostrato che gli attributi di un elemento possono essere anche settati dopo la creazione dell'elemento stesso: in particolare il background color è assegnato successivamente:: cp=root.contentPane(margin='5px',position='relative', border='1px solid silver', rounded=6) cp.attributes['background_color']='lightyellow' Creiamo poi usando un posizionamento assoluto una serie di div:: for k in range (10): x=5+k*30 cp.div(position='absolute',top='%spx'%x,left='%spx'%x, height='26px',width='58px', background='#ddd',rounded=5) cp.div(position='absolute',top='%spx'%x,left='%spx'%(280-x), height='26px',width='58px', background='#ddd',rounded=5) Qui di seguito un ulteriore esempio in cui vediamo l'utilizzo tipico con all'interno un :ref:`Formbuilder` : .. raw:: html tabContainer ------------ Il tabContainer serve a **mettere nello stesso spazio più contenuti organizzati in pagine** attivate da "tab". Al tabContainer è possibile aggiungere dei `contentPane`_ oppure degli altri container (`borderContainer`_ o altri *tabContainer*). Quando si aggiunge un figlio deve essere specificato un ``title``. .. raw:: html Nell'esempio vediamo la creazione di un tabContainer più esterno e dei suoi figli. Notiamo che possiamo mettere come figli sia dei `contentPane`_ che degli altri *container*. In questo caso usiamo solo tabContainer per non anticipare *container* che vedremo successivamente. L'attributo ``tabPosition`` consente di settare la posizione dei tab. Nel primo contentPane mettiamo solo un div:: helloworld.div('Helloworld',font_size='30px',margin='50px') Nel secondo contentPane mettiamo un tabContainer che riempiamo con i giorni della settimana:: wd=pane.tabContainer(margin='4px',tabPosition="left-h") for t in ('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'): wd.contentPane(title=t).div(t,font_size='30px',margin='50px') Il terzo figlio del tabContainer primario è già un tabContainer e quindi può essere popolato direttamente:: for t in ('green','red','pink','blue'): tc.contentPane(title=t,color=t).div(t,font_size='30px',margin='50px') Lo stesso per il quarto:: for t in ('courier','times','helvetica','cursive'): tc.contentPane(title=t,font_family=t).div(t,font_size='30px',margin='50px') borderContainer --------------- Il borderContainer permette di **suddividere lo spazio interno in 5 regioni**: - top - bottom - left - right - center .. image:: /_static/images/schema-containers.png :width: 100% :align: center Il *center* prende tutto lo spazio libero, mentre per le regioni *left* e *right* è possibile dare una ``width``. Per *bottom* e *top* è possibile specificare una ``height``. A tutte le regioni, tranne *center*, è possibile assegnare il parametro ``splitter=True`` per richiedere uno splitter. Ogni regione può essere o un `contentPane`_ oppure un altro *container*. .. raw:: html Per prima cosa creiamo un borderContainer:: def main(self,root,**kwargs): bc=root.borderContainer(margin='10px',text_align='center') Procediamo poi a creare il pannello di sinistra specificando la larghezza:: bc.contentPane(region='left',width='100px', background_color='lightyellow').div('Left') E di seguito gli altri:: bc.contentPane(region='right',width='100px', background_color='lightgreen').div('Right') bc.contentPane(region='top',height='30px', background_color='darkred',color='white').div('Top') bc.contentPane(region='bottom',height='30px', background_color='darkblue',color='white').div('Bottom') ed infine il *center*:: bc.contentPane(region='center').div('Center') .. raw:: html L'esempio che andiamo ora ad esaminare, oltre a mostrare alcune caratteristiche del borderContainer, darà modo di vedere qualche dettaglio in più della creazione della GUI con Genropy. Dal momento che l'esempio si propone di mostrare più borderContainers creiamo per prima cosa un `tabContainer`_:: def main(self,root,**kwargs): tc=root.tabContainer(margin='5px') Procediamo quindi a creare il primo `borderContainer`_ e, dal momento che siamo in un *tabContainer*, passiamo come primo parametro il titolo da mostrare nel tab:: bc=tc.borderContainer(title='Headline') Chiamiamo quindi il metodo ``makeRegions`` (che esamineremo poi) passando come parametro il colore che vogliamo per il testo contenuto:: self.makeRegions(bc, color='darkred') Infine prepariamo il centro del nostro *borderContainer*:: bc.contentPane(region='center').div('Center
Headline') Il second tab è analogo ma specifica che il design del *borderContainer* è ``sidebar``, ovvero le regioni *left* e *right* sono a tutta altezza:: bc=tc.borderContainer(title='Sidebar') self.makeRegions(bc, design='sidebar',color='darkblue') bc.contentPane(region='center').div('Center
Sidebar') L'ultimo tab si propone di mostrare un borderContainer innestato in un altro:: bc=tc.borderContainer(title='Nested') self.makeRegions(bc,color='darkgreen') Come region *center* mettiamo ora, invece di un contentPane, un altro borderContainer:: bc=bc.borderContainer(region='center') Procediamo quindi a creare le regioni anche per questo borderContainer:: self.makeRegions(bc,font_size='12px',color='red', background_color='lightyellow' margin='0px',rounded=0,border=0) E infine creiamo il *center* del borderContainer innestato:: bc.contentPane(region='center').div('Center
Nested') Passiamo ora ad esaminare il codice del metodo makeRegions:: def makeRegions(self,bc, **kwargs): Oltre al borderContainer ci arrivano dei parametri che vogliamo mettere al borderContainer stesso. Abbiamo però già costruito questo elemento e quindi dovremo agire su ``bc.attributes`` e modificarli opportunamente. Per prima cosa mettiamo dei parametri di default:: bc.attributes.update(border='1px solid silver',font_size='16px', margin='5px', rounded=6,text_align='center') Poi procediamo ad aggionare nuovamente con i kwargs ricevuti:: bc.attributes.update(**kwargs) Andiamo quindi a definire la region *left* specificando la ``width``, richiedendo la creazione dello *splitter*, mettendo il bordo di destra e aggiungendo un div con il nome della region:: bc.contentPane(region='left',width='50px',splitter=True, border_right='1px solid silver').div('Left') Procediamo in analogia per le altre regions:: bc.contentPane(region='right',width='50px',splitter=True, border_left='1px solid silver').div('Right') bc.contentPane(region='top',height='30px',splitter=True, border_bottom='1px solid silver').div('Top') bc.contentPane(region='bottom',height='30px',splitter=True, border_top='1px solid silver').div('Bottom') stackContainer -------------- Lo stackContainer è simile al `tabContainer`_ ma **non mostra delle etichette per selezionare la pagina voluta**. Presenta invece un attributo ``selected`` che assume il valore della pagina selezionata, e cambiando questo valore viene cambiata la pagina corrente. Uno degli utilizzi tipici è un wizard che guidi l'utente in una serie di passaggi. .. raw:: html Come prima cosa definiamo un `borderContainer`_:: bc=root.borderContainer(border='1px solid silver', margin='5px',datapath='mystack') Definiamo poi lo *stackContainer* e indichiamo la locazione nel :ref:`datastore` dove mettere la pagina correntemente selezionata:: sc=bc.stackContainer(region='center', selected='^.selected',font_size='50px') Inizializziamo la pagina selezinata a 0 con la chiamata ``data``:: sc.data('.selected',0) Procediamo poi a creare 10 pagine con un ciclo for:: for p in range(10): sc.contentPane(padding='20px').div("Page : %s"%p) Mettiamo ora nel *bottom* un bottone per passare alla pagina precedente e uno per passare a quella seguente e tra i due un :ref:`numberTextBox` nel quale potremmo digitare il numero di pagina:: fb=bc.contentPane(region='bottom', border_top='1px solid silver').formbuilder(cols=3) fb.button(' < ',action="SET .selected= p-1", p='^.selected', visible='== (p > 0)') fb.numberTextBox('^.selected',font_size='18px',width='30px') fb.button(' > ',action="SET .selected= p+1", p='^.selected', visible='== (p <9)') In questa ultima parte di codice possiamo notare che la ``action`` del bottone che porta alla pagina precedente riceve in 'p' il numero di pagina e lo mette nuovamente nello store decrementato di uno. Possiamo anche vedere che esiste un attributo ``visible`` il cui valore viene calcolato a True solo se il numero di pagina è >0. Allo stesso modo funziona il bottone di pagina seguente, con la differenza che questa volta il bottone è visibile solo se il numero di pagina è minore di 9. Si noti che non è obbligatorio gestire le pagine con dei valori numerici facendo uso dell'attributo ``selected``. È possibile anche assegnare identificativi differenti di tipo testuale. In quel caso allo *stackContainer* verrà passato l'attributo ``selectedPage`` al posto di *selected*, mentre ai vari *contentPane* verrà assegnato un ``pageName`` come vediamo nel prossimo esempio: .. raw:: html