Gallery

Poche righe di Python,
e il risultato.

Esempi piccoli e funzionanti, in esecuzione su un'istanza Genropy: il codice a sinistra, a destra la stessa pagina in esecuzione.

01 · Interfaccia

Dal primo div
a una piccola applicazione.

Pagine costruite in Python: dal classico Hello World a un bilancio personale.

Hello World

Ecco il classico Hello World in Genropy.

Si definisce una classe GnrCustomWebPage e in questa il metodo main che riceve la root della pagina.

Alla root aggiungiamo quindi un div con i parametri desiderati.

hello_world.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):
        root.div('Hello World',
                 text_align='center',
                 font_size='40px')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Hello World 2

Qui vediamo come innestare degli elementi.

Alla root aggiungiamo quindi un contenitore, ovvero un div con i parametri desiderati che chiamiamo box.

Nel box poi aggiungiamo due div e un button con relativa action.

hello_world_2.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):
        box=root.div(margin='20px',padding='10px',
                     border='1px solid gray',
                     text_align='center',
                     font_size='32px',rounded=10)
        box.div('Hello World',color='green')
        box.div('I am Genropy',color='blue')
        box.button('I am a button',font_size='18px',
                   action='alert ("Good click!")')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Parametri dinamici

In questo esempio vediamo come chiedere dei parametri che poi usiamo per comporre un testo.

Nella root mettiamo un formlet (un form a griglia, qui su due colonne) al quale aggiungiamo un textBox su tutta la riga per richiedere il testo da mostrare e due elementi di tipo comboBox, affiancati, per scegliere colore e dimensione del testo.

I valori richiesti sono messi nello store locale alle locazioni mytext, mycolor e myfontsize.

Alla root attacchiamo quindi un div il cui contenuto, colore e dimensione dipenderanno dal valore corrente delle variabili.

Da notare che il comboBox ha dei valori prefissati tra cui scegliere ma è possibile inserire anche altri valori.

hello_dynamic.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):
        fl = root.formlet(cols=2)
        fl.textBox('^mytext',lbl='Text to show',
                   default='Hello World',colspan=2)
        fl.comboBox('^mycolor',lbl='Color',
                    values='black,red,green,orange')
        fl.comboBox('^myfontsize',lbl='Size',
                    values='20px,30px,40px,60px',
                    default='30px')

        root.div('^mytext',color='^mycolor',margin_top='20px',
                 text_align='center',
                 font_size='^myfontsize')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Convertitore bytes

In questo esempio utilizziamo un dataController per eseguire un pezzo di codice javascript in grado di convertire un numero in bytes ’leggibili'.

Rimandiamo alle lezioni di genropy per vedere i dettagli.

convertbyte.py
class GnrCustomWebPage(object):

    def main(self,root,**kwargs):
        pane=root.div(datapath='bytesconverter')
        pane.h1('Bytes Converter')
        fl = pane.formlet(cols=2)
        fl.numberTextBox('^.bytes',lbl='Bytes')
        fl.div('^.conv',lbl='Converted',color='#777',font_size='16px')
        fl.dataController("""
             var s = ['Bytes','KB','MB','GB','TB'];
             if (b == 0) return 'n/a';
             var i = parseInt(Math.floor(Math.log(b) / Math.log(1024)));
             SET .conv = (b / Math.pow(1024, i)).toFixed(1) + ' ' + s[[i]];""",
             b='^.bytes')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Bilancio personale

In questo esempio usiamo Genropy per realizzare un piccolo esempio di foglio di calcolo. Si fa un largo uso dell’elemento dataFormula che consente di eseguire dei calcoli sullo store e mettere il risultato nella locazione desiderata.

La facilità di definire dei blocchi (in questo caso la funzione expensesGroup) permette di condensare ed estendere il codice con grande semplicità. In questo esempio sono considerati 3 gruppi di spesa ed è evidente come sia facile aggiungere nuove voci ad un gruppo o aggiungere nuovi gruppi.

Da notare che il colore del balance dipende dal suo valore.

personal_balance.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):
        box=root.div(datapath='balance',color='#666')
        box.div('Personal Balance',text_align='center',font_size='24px')
        fl = box.formlet(cols=3)
        fl.numberTextBox('^.income',lbl='Income',
                         default_value=0, _autoselect=True)
        fl.numberTextBox('^.total_expenses',lbl='Total expenses',
                         background='lightyellow', readOnly=True)
        fl.numberTextBox('^.balance',lbl='Balance', readOnly=True,
                         background='^.balance_color',font_weight='bold',color='white')
        fl.dataFormula('.total_expenses','home+work+personal',home='^.home.total',
                       work='^.work.total', personal='^.personal.total')
        fl.dataFormula('.balance','income-total_expenses',
                       income='^.income', total_expenses='^.total_expenses')
        fl.dataFormula('.balance_color', "(balance>100)?'green':'orange'",
                        balance='^.balance',_if='balance>0', _else="'red'")
        self.expensesGroup(box,datapath='.home',title='Home',
                           items='rent,electricity,internet,cleaning,insurance')
        self.expensesGroup(box,datapath='.work',title='Work',
                           items='brushes,canvas,colors')
        self.expensesGroup(box,datapath='.personal',title='Personal',
                           items='food,newspapers,transport,restaurant,cinema')

    def expensesGroup(self,pane,datapath=None,title=None, items=None):
        box=pane.div(datapath=datapath,border_top='1px solid silver',margin_top='6px')
        box.div('%s expenses' % title,font_weight='bold',padding='4px 7px 0')
        fl=box.formlet(cols=3,datapath='.detail')
        for item in items.split(','):
            fl.numberTextBox(value='^.%s' % item,default_value=0,_autoselect=True,
                             lbl=item.capitalize())
        fl.numberTextBox('^.#parent.total', background='lightyellow',
                         lbl_font_weight='bold', readOnly=True,
                         lbl='Total %s' % title)
        box.dataFormula('.total','detail.sum()', detail='^.detail')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

02 · Chiamate remote

Il server risponde,
la pagina si aggiorna.

Metodi Python chiamati dal browser, anche a intervalli regolari.

Tempi CPU

In questo esempio utilizziamo la libreria python psutil per leggere delle informazioni sul server e presentarle in una griglia. Nella root mettiamo una quickGrid con altezza e larghezza automatiche il cui contenuto è collegato alla locazione mydata dello store.

Aggiungiamo poi un dataRpc che è un elemento attivo il cui scopo è quello di eseguire una chiamata remota. Nel nostro caso viene chiamato self.getCpuTimes e la chiamata è eseguita, grazie al parametro _timing=1, ogni secondo. Il risultato della chiamata sarà messo nella locazione mydata.

Definiamo poi un metodo getCpuTimes che, grazie al decoratore @public_method sarà richiamabile dal client. Questo metodo utilizza cpu_times di psutil per ottenere dal sistema le informazioni per ogni core e popola una Bag (ovvero un contenitore gerarchico) con il risultato e provvede a rendere la Bag così preparata.

cpu_times.py
from psutil import cpu_times
from gnr.core.gnrdecorator import public_method
from gnr.core.gnrbag import Bag

class GnrCustomWebPage(object):
    
    def main(self,root,**kwargs):
        root.quickGrid(value='^mydata',height='auto',
                       width='auto',font_size='1.2em')
        
        root.dataRpc('mydata', self.getCpuTimes,
                     _timing=1)
        
    @public_method
    def getCpuTimes(self):
        result=Bag()
        for j, core in enumerate(cpu_times(True)):
            row = Bag()
            row['core']=j+1
            for k in ('user','nice','system','idle'):
                row.setItem(k, getattr(core,k))
            result.setItem('r_%i'%j, row)
        return result
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Processi

Utilizziamo nuovamente psutil per leggere delle informazioni sul server e presentarle in una griglia. Per prima cosa ci definiamo le grandezze che possiamo leggere per i processi in esecuzione (properties). Nella root mettiamo poi un formlet con dentro un checkBoxText i cui valori possibili sono le properties sopra definite.

Aggiungiamo poi un dataRpc per chiamare self.getProcessesBag ogni secondo (_timing=1) e passiamo le colonne volute. Il risultato della chiamata sarà messo nella locazione data.

Mettiamo quindi una quickGrid con larghezza automatica il cui contenuto è collegato alla locazione data dello store.

Definiamo poi un metodo getProcessesBag che, grazie al decoratore @public_method sarà richiamabile dal client. Questo metodo utilizza psutil.process_iter, chiedendo solo le colonne richieste, per ottenere dal sistema le informazioni per ogni processo e popola una Bag (ovvero un contenitore gerarchico) limitatamente alle colonne richieste.

processes.py
import psutil
import datetime
from gnr.core.gnrdecorator import public_method
from gnr.core.gnrbag import Bag

PROPERTIES = ['pid','ppid','name','username','status','create_time',
              'cpu_percent','memory_percent','nice','cpu_times','memory_info']

class GnrCustomWebPage(object):

    def main(self,root,**kwargs):
        fl=root.formlet(cols=1)
        fl.checkBoxText('^columns',values=','.join(PROPERTIES),
                        cols=3,
                        default_value='pid,name,cpu_percent',
                        lbl='Columns',popup=True)

        root.dataRpc('data', self.getProcessesBag, columns='^columns',
                             _timing=1)

        root.quickGrid(value='^data',height='200px',width='auto',
                                      sortedBy='cpu_percent:d',
                                      border='1px solid silver')

    @public_method
    def getProcessesBag(self,columns=None):
        # only the published properties: the client must not read anything else
        columns=[c for c in (columns or '').split(',') if c in PROPERTIES] or ['pid','name']
        result = Bag()
        for p in psutil.process_iter(columns):
            d=p.info
            if d.get('create_time'):
                d['create_time'] = datetime.datetime.fromtimestamp(d['create_time'])
            if 'cpu_percent' in d:
                d['cpu_percent'] = d['cpu_percent'] or 0.
            if 'memory_percent' in d:
                d['memory_percent'] = d['memory_percent'] or 0.
            row = Bag([(k,d[k]) for k in columns if k in d])
            result.setItem('p_%s'%p.pid,row)
        return result
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

03 · Database

Dati dal database
senza scrivere query.

Select, griglie e alberi collegati alle tabelle.

dbSelect

In questo esempio vediamo come si possono ricercare voci di database. Nella root mettiamo un formlet a due colonne e in quest’ultimo una dbSelect sulle regioni ed una sui comuni italiani, affiancate.

Nella dbSelect mentre digitiamo ci vengono proposti gli elementi che soddisfano. Nella prima, dato che ci aspettiamo pochi risultati, il parametro hasDownArrow ci consente di vedere tutti i risultati.

Nel caso dei comuni il bottone di downArrow invece sarebbe del tutto inutile e possiamo digitare una parte del nome. Ad esempio possiamo digitare ‘aban’ e troveremo solo Abano Terme mentre digitando ‘san’ troveremmo troppi valori. Per questo la dbselect ricerca anche per parti interne e quindi ‘giov cro’ ci porta direttamente a ‘San Giovanni in croce’.

dbselect.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):

        fl=root.formlet(cols=2)
        fl.dbselect(value='^regione',
                    dbtable='glbl.regione',
                    lbl='Regione',
                    hasDownArrow=True)

        fl.dbselect(value='^comune',
                    dbtable='glbl.comune',
                    lbl='Comune')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Griglia db

In questo esempio vediamo come presentare i risultati di ricerche sul database.

Come nell’esempio precedente nella root mettiamo un formlet e in quest’ultimo una dbSelect sulle regioni. La regione selezionata sarà messa alla locazione regione_selezionata.

Sempre in root mettiamo un dataSelection, ovvero un elemento attivo in grado di richiedere al server dei record di una tabella. Il primo parametro è la locazione dove mettere il risultato (’listaprov’), poi viene messa la tabella interessata (glbl.provincia), quindi la where ($regione=:curr_reg) e infine l’ordinamento voluto. Il parametro curr_reg (referenziato nella where) viene associato alla locazione regione_selezionata e ogni volta che questa cambierà la chiamata al server verrà ripetuta e il risultato della query finirà in listaprov. Il parametro recordResolver=False indica che siamo interessati solo ai record di provincia.

L’elemento quickGrid a sua volta collegato a listaprov provvederà poi a visualizzare gli elementi caricati ogni volta che si seleziona una diversa regione.

dbgrid.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):

        fl=root.formlet(cols=1,width='20em')
        fl.dbselect(value='^regione_selezionata',
                    dbtable='glbl.regione',
                    lbl='Regione',
                    hasDownArrow=True)

        root.dataSelection('listaprov','glbl.provincia',
                          where='$regione=:curr_reg',
                          curr_reg='^regione_selezionata',
                          order_by='$sigla',
                          recordResolver=False)

        g = root.quickGrid('^listaprov',height='auto',
                      width='auto',margin='7px')
        g.column('sigla',name='Sigla',width='4em')
        g.column('nome',name='Nome',width='20em')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Albero db

In questo esempio vediamo come presentare i risultati di ricerche sul database.

Come nell’esempio precedente nella root mettiamo un formlet e in quest’ultimo una dbSelect sulle regioni. La regione selezionata sarà messa alla locazione regione_selezionata.

Sempre in root mettiamo un dataSelection, ovvero un elemento attivo in grado di richiedere al server dei record di una tabella. Il primo parametro è la locazione dove mettere il risultato (’listaprov’), poi viene messa la tabella interessata (glbl.provincia), quindi la where ($regione=:curr_reg) e infine l’ordinamento voluto. Il parametro curr_reg (referenziato nella where) viene associato alla locazione regione_selezionata e ogni volta che questa cambierà la chiamata al server verrà ripetuta e il risultato della query finirà in listaprov.

Questa volta il parametro recordResolver=False è omesso perché desideriamo avere tutte le informazioni ottenibili dalla provincia.

Invece della quickGrid questa volta mettiamo un tree che si approvvigiona sempre alla stessa locazione listaprov. Selezionando una provincia questa volta abbiamo un albero e aprendo i vari rami possiamo leggere tutti i dati degli elementi collegati. Selezionando ad esempio Lombardia e aprendo nell’albero la provincia Milano possiamo andare al nodo @clienti e quindi entrare in un cliente e da lì risalire alle fatture tramite @fatture.

dbtree.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):

        fl=root.formlet(cols=1,width='20em')
        fl.dbselect(value='^regione_selezionata',
                    dbtable='glbl.regione',
                    lbl='Regione',
                    hasDownArrow=True)

        root.dataSelection('listaprov','glbl.provincia',
                          where='$regione=:curr_reg',
                          curr_reg='^regione_selezionata',
                          order_by='$sigla')

        root.tree(storepath='listaprov',labelAttribute='nome')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

04 · Shared Objects

Uno stesso dato,
condiviso tra più pagine.

Uno shared object tiene allineate le pagine aperte da utenti diversi.

Shared hello

Una o più zone di una pagina di genropy possono essere condivise in tempo reale con altri utenti. A titolo di esempio abbiamo un div di cui possiamo settare contenuto e vari parametri. Tramite la chiamata sharedObject possiamo fornire un id unico.

sharedhello.py
class GnrCustomWebPage(object):

    def main(self,root,**kwargs):
        bc=root.borderContainer(datapath='hello')
        bc.sharedObject('hello',shared_id='shared_helloworld',
                        autoLoad=True)
        fl=bc.contentPane(region='top').formlet(cols=3)

        fl.textBox('^.text',lbl='Text to show',colspan=3)
        fl.textBox('^.color',lbl='Color')
        fl.textBox('^.background',lbl='Background')
        fl.textBox('^.font_size',lbl='Font Size')
        fl.textBox('^.border',lbl='Border')
        fl.textBox('^.rounded',lbl='Rounded')
        fl.textBox('^.text_align',lbl='Text Align')
        fl.textBox('^.margin',lbl='Margin')
        fl.textBox('^.padding',lbl='Padding')
        fl.textBox('^.shadow',lbl='Shadow')

        bc.contentPane(region='center').div('^.text',
                       border='^.border', margin='^.margin',
                       padding='^.padding',color='^.color',
                       background='^.background',
                       text_align='^.text_align',
                       font_size='^.font_size',
                       shadow='^.shadow',rounded='^.rounded')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

05 · Esempi vari

Problemi classici,
risolti in Genropy.

Esercizi noti, scritti con i widget e il datastore di Genropy.

FizzBuzz

Ecco il classico FizzBuzz Test realizzato in genropy.

fizzbuzz.py
class GnrCustomWebPage(object):
    def main(self,root,**kwargs):
        root.style(""".mcell{width:42px;font-size:11px;background:#777;
                             color:white;
                             text-align:center;}
                       .fizz {background:blue;}
                       .buzz {background:red;}
                       .fizz.buzz {background:green;}""")
        t=root.table(margin="5px").tbody()
        r=t.tr()
        for k in range(1,101):
            fizz= 'fizz'* int(not k%3)
            buzz= 'buzz'* int(not k%5)
            v=str(k)* int(not (fizz or buzz))
            r.td().div('%s%s%s'%(fizz,buzz,v),
                       _class='mcell %s %s' % (fizz,buzz))
            if not k%10:
                r=t.tr()
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Crivello di Eratostene

In questo esempio vediamo come creare un Crivello di Eratostene animato.

Per il funzionamento basta scrivere il limite superiore e il crivello mostrerà i numeri primi trovati.

Per i dettagli sul funzionamento rimandiamo alle lezioni sui dataController nel corso di Genropy.

eratostene.py
# -*- coding: UTF-8 -*-

class GnrCustomWebPage(object):

    def main(self,root,**kwargs):
        root.css('.cellnumber',"""margin:1px; text-align:center;
                                  height:18px;line-height:18px;
                                  font-size:14px; font-family:courier;
                                  width:38px;display:inline-block;""")
        root.css('.notvisited',"color:#666; background:#eaeaea;")
        root.css('.prime',"color:yellow; background:#888;")
        root.css('.noprime',"color:red; background:#ccc;")

        bc=root.borderContainer(datapath='eratosthenes',margin='10px',
                                                border='1px solid silver',rounded=12)
        top=bc.contentPane(region='top',border_bottom='1px solid silver')
        fl = top.formlet(cols=1,width='12em')
        fl.numberTextBox('^.nmax',lbl='Primes limit',
                         validate_max=1000,
                         validate_max_error='Max value: 1000',
                         validate_min='1',
                         validate_min_error='Min value: 1')
        center=bc.contentPane(region='center')
        sieve=center.div(width='400px',margin='auto',margin_top='10px',margin_bottom='10px')
        center.data('.build_timer',0)
        center.data('.calculate_timer',0)
        center.dataController("""var content=sieve.getValue();
                                 if (!_timing){
                                     sieve.replaceContent(genro.src.newRoot());
                                      sieve._('div',{innerHTML:' ',_class:'cellnumber notvisited'})
                                     SET .build_timer=0.01
                                 }else{
                                     n= content.len()+1
                                     if(n<=nmax){
                                         sieve._('div',{innerHTML:n,_class:'cellnumber notvisited'})
                                     }else{
                                         SET .build_timer=0
                                         SET .curr_n=2
                                         SET .calculate_timer=0.1
                                     }
                                  }""",nmax='^.nmax',sieve=sieve,_timing='^.build_timer')

        center.dataController("""
                   if (curr_n<=nmax){
                       var nodes=sieve.getValue().getNodes();
                       var node=nodes[curr_n-1]
                       if (!node._factors) {
                          node.setAttribute('_class','cellnumber prime')
                          for(n2 = curr_n+curr_n; n2 <= nmax; n2=n2+curr_n) {
                             var n_mult=nodes[n2 - 1]
                             if(!n_mult._factors){
                                n_mult.setAttribute('_class','cellnumber noprime')
                                n_mult._factors=[]
                             }
                             n_mult._factors.push(curr_n)
                             n_mult.setAttribute('tip',n_mult._factors.join(','))
                          }
                       }
                       do { curr_n++}
                       while(curr_n<=nmax && nodes[curr_n-1]._factors)
                       SET .curr_n=curr_n
                   }
                   else{
                       SET .calculate_timer=0
                   }
                   """,sieve=sieve,_timing='^.calculate_timer',nmax='=.nmax',curr_n='=.curr_n')
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Color maker

Questo esempio mostra come con circa 40 righe di codice python sia possibile creare un’interfaccia complessa per settare tramite sliders i colori di background, foreground e shadow di un div.

Il codice non è banale ma potrebbe essere una sfida per visitatori curiosi :)

colormaker.py
from random import randint as rn

class GnrCustomWebPage(object):

    def main(self,root,**kwargs):
        box=root.div(color='#666',datapath='colormaker')
        box.div('Color Maker',text_align='center',font_size='24px')
        fl = box.formlet(cols=4,gap='7px 12px',width='28em',margin='auto',
                         item_lbl_text_align='center')
        self.colorRgb(fl.div(datapath='.bkg',lbl='Background'))
        self.colorRgb(fl.div(datapath='.color',lbl='Color'))
        self.colorRgb(fl.div(datapath='.shadow',lbl='Shadow'))
        fl.div('Test', padding='6px',font_size='30px',margin='10px',
                       width='100px',height='100px',rounded=8,
                       background_color='^.bkg.rgb',
                       color='^.color.rgb',
                       shadow_color='^.shadow.rgb',
                       shadow='4px 4px 8px')

    def colorRgb(self,pane):
        fl = pane.formlet(cols=3,gap='8px',padding='0')

        self.colorSlider(fl, value='^.red', component='red')
        self.colorSlider(fl, value='^.green', component='green')
        self.colorSlider(fl, value='^.blue', component='blue')

        fl.dataFormula('.rgb',"'rgb('+red+','+green+','+blue+')'",
                       red='^.red',blue='^.blue',green='^.green',
                       _onStart=True)

    def colorSlider(self,fl,value=None,component=None):
        fl.verticalSlider(value,lbl=component[0].upper(),height='100px',
                          minimum=0,maximum=255,default_value=rn(0,255),
                          discreteValues=256,
                          lbl_background=component,
                          lbl_color='white',
                          lbl_text_align='center',
                          intermediateChanges=True)
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Gioco della vita

Il Gioco della vita (Game of Life o Life in inglese) è un automa cellulare sviluppato dal matematico inglese John Conway sul finire degli anni sessanta.

Ne vogliamo dare la versione Genropy come esempio di animazione di una certa complessità che mostra alcune tecniche di programmazione nel framework.

Per iniziare bisogna creare una configurazione iniziale cliccando su alcune celle fino a creare una configurazione di partenza e poi avviare il gioco con il checkbox ‘Run’. Non è permesso mettere celle vive sui bordi.

Quando si arriva ad una configurazione stabile il programma si arresta. Mentre il gioco è in funzione è comunque possibile aggiungere o togliere celle vive cliccandoci sopra.

game_of_life.py
COLS = 60
ROWS = 36

class GnrCustomWebPage(object):
    def main(self,root,**kwargs):
        box=root.div(margin='5px',datapath='game',
                     border='1px solid silver',rounded=4)
        self.makeButtons(box.div(padding='3px',border_bottom='1px solid silver'))
        self.makeGrid(box.div())

    def makeButtons(self,pane):
        pane.button('Clear all',action='game.clearall()')
        pane.checkBox(value='^.running',label='Run',margin_left='20px')
        pane.dataController("if(running){game.nextgen()}",_timing=0.1,running='^.running')
        pane.script("""var game={cols:%(cols)s, rows:%(rows)s,
                cells : new Array(%(cols)s*%(rows)s).fill(),
                cref : function(c,r){return r*game.cols+c;},
                get : function(c,r){return game.cells[game.cref(c,r)];},
                set : function(c,r,v){game.cells[game.cref(c,r)]= v;genro.dom.setClass(c+'_'+r,'alive',v);},
                toggle :function(c,r){if(c>0 && c<game.cols && r>0 &&r<game.rows){game.set(c,r, !game.get(c,r));}},
                allcell:function(cb){for (var y=1; y < game.rows; y++) {for (var x=1; x< game.cols; x++){cb(x,y);}}},
                clearall:function(){game.allcell(function(c,r){game.set(c,r,false);});},
                nearby:function(c,r){var t=0; for (var y=r-1; y <= r+1; y++) {for (var x=c-1; x<=c+1 ;x++){
                                  if(game.get(x,y) && (x!=c || y!=r)){ t++;}}} return t;},
                nextgen:function(){ game.changes=[];game.allcell(function(c,r){
                            var cn= game.get(c,r);var bn = game.nearby(c,r);
                            if(cn){if (bn<2 || bn>3) {game.changes.push({r:r,c:c,v:false});} }
                            else if (bn==3){game.changes.push({r:r,c:c,v:true});}});
                            if (game.changes.length==0){genro.setData('game.running',false)}
                            game.changes.forEach(function(kw){game.set(kw.c,kw.r,kw.v);});}
                };""" % dict(cols=COLS, rows=ROWS))

    def makeGrid(self,pane):
        pane.style("""td.lcell{width:6px;height:6px;border:1px solid silver;}
                     td.alive{background-color:black;}""")
        t=pane.table(border_spacing='0px',border_collapse='collapse',margin='3px auto')
        t=t.tbody(connect_onclick="function(e){var n=e.target.sourceNode.attr;game.toggle(n.col,n.row)}")
        for row in range (ROWS):
            r=t.tr()
            for col in range (COLS):
                r.td(_class='lcell',row=row,col=col,id='%s_%s'%(col,row))
PythonUTF-8
ANTEPRIMA INTERATTIVA

Caricamento dell’esempio da un’istanza Genropy vera…

Esempio live · i dati si azzerano a ogni riavvio.

Vuoi provarlo sul tuo computer?