PYTHON-KIVY: Bottoni e interazioni con il codice

Vediamo come far interagire la parte grafica dell’UI con la logica dello script Python. Nell’esempio creiamo un interfaccia grafica con un campo di testo ed un bottone il quale quando verra’ premuto e rilasciato, modificherà il testo.

Prima della pressione sul bottone
Dopo la pressione

Vediamo il file main.py

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty

class finestraPrincipale(BoxLayout):
    bottone = ObjectProperty(None)
    testo = ObjectProperty(None)
    def saluta(self):
        self.testo.text = "Ciao Daniel!"
        
class MainApp(App):
    def build(self):
        #Classe prinmcipale che si occupa di restituire e aggiornare la UI 
        return finestraPrincipale()
    # end def
    

if __name__ == "__main__":
    MainApp().run()

ed il file main.kv

<finestraPrincipale>
    bottone: bottone
    testo: testo

    BoxLayout:
        orientation: "vertical"
        Label:
            id: testo
            text: "CIAO MONDO"
        Button:
            id: bottone
            text: "SALUTA"
            on_release: 
                root.saluta()

Capiamo il funzionamento partendo dal file main.kv. Possiamo subito notare che ogni elemento con il quale vogliamo interagire e’ provvisto di un id:

Questo id deve essere dichiarato all’inizio della sessione <‘classe’> nel file .kv in modo da fare da ‘ponte’ tra il file.kv e il file .py, questo e’ una sorta di riferimento per la comunicazione tra i due file.

Sotto la dichiarazione del Button, vediamo l’evento on_release, che richiamerà il metodo saluta() della classe finestraPrincipale che eseguirà l’azione specificata nel codice del file .py

Nel file main.py ritroveremo i riferimenti testo e bottone, che saranno due variabili a cui assoceremo l’oggetto ObjectProperty della libreria kivy.properties importato in precedenza.

Queste due variabili/oggetti, ci serviranno per interagire con i due widget della UI, infatti alla pressione e rilascio del Button, la scritta “CIAO MONDO” verrà modificata, dal metodo saluta() della classe finestraPrincipale() , in “Ciao Daniel!”

PYTHON-KIVY: file .kv per la costruzione della UI

il file che descrive la struttura dell’interfaccia utente.

Il file .kv e’ un file di testo dove risiedono tutte le istruzioni per la costruzione dell’ interfaccia grafica. Si preferisce usare un file esterno su applicazioni mediamente complesse in quanto questo ci dà la possibilità di separare la parte logica del programma da quella grafica, rendendo tutto molto più leggibile.

Per usare questo metodo ci sono alcune regole da seguire:

  • Il nome del file .kv deve essere tutto in minuscolo
  • il nome deve coincidere con lo stesso nome della classe principale del programma
  • se la classe principale contiene la scritta “App” ad esempio MainApp() il file kv dovrà escludere App, quindi dovrà essere chiamato “main.kv”
  • il file .kv dovra’ essere creato nella stessa directory del file .py

vediamo un semplicissimo esempio “Hello World ! “

file PYTHON main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class finestraPrincipale(BoxLayout):
    pass

class MainApp(App):
    def build(self):
        #Classe prinmcipale che si occupa di restituire e aggiornare la UI 
        return finestraPrincipale()
    # end def
    

if __name__ == "__main__":
    MainApp().run()

file KV main.kv

<finestraPrincipale>
    BoxLayout:
        orientation: "vertical"
        Label:
            text: "CIAO MONDO"

Risultato

Cosi some e’ strutturato il programma ora l’applicazione python girerà in un loop infinito in quanto, il metodo .run() della classe MainApp() ereditato da App (dichiarato nella sua costruzione MainApp(App)) genera appunto il loop infinito. Questo loop infinito e’ necessario per il continuo refresh della GUI del programma scritto nel file main.py

PYTHON: Ambiente Virtuale

A cosa servono e come crearli.

Gli ambienti virtuali in python sono ambienti creati per poter installare le librerie necessarie per sviluppare un progetto, senza che vadano ad influire sugli altri progetti, o peggio ancora a danneggiare il sistema operativo principale se questo si basa su python.

Praticamente si crea un ambiente “isolato” dal sistema operativo principale dove possiamo scaricare tutte le librerie che ci servono e sopratutto in diverse versioni, senza intaccare gli altri progetti.

Gli ambienti virtuali si creano in modo diverso se ci si trova in ambiente linux o in ambiente vindows, vediamo come crearli nei due diversi S.O.

LINUX:

A apriamo una shell nella directori del progetto che in questo caso chiameremo MYPROJ

mio_pc: mkdir MYPROJ
mio_pc: cd MYPROJ

mio_pc:~/MYPROY$ python3 -m venv [nomeambiente] (es: myproj)
mio_pc:~/MYPROY$ python3 -m venv myproj
 

Con questo comando si creeranno l’ambiente virtuale generando una nuova serie di directory la principale che avra’ lo stesso nome di myproj e altre sottodirectory e files saranno:

bin, include, lib e due files : lib64 e pyvenv.cfg

A questo punto l’ambiente virtuale e’ pronto ma dovremmo attivarlo prima di poterlo utilizzare, l’attivazione si esegue dando il seguente comando nella directory dell’ambiente:

mio_pc:~/MYPROY/myproj$ source bin/activate

L’ambiente sara’ attivo e la conferma l’avremo in quanto nel prompt comarira’ tra parentesi il nome dell’ambiente virtuale:

(myproj) mio_pc:~/MYPROY/myproj$

Ora possiamo installare tutte le librerie che vogliamo senza andare ad intaccare quelle di sistema.

Se volessimo uscire dall’ambiente virtuale sara’ sufficente dare il comando deactivate in qualsiasi punto della consolle.

WINDOWS:

Per creare ambienti viruali python in windows la procedura e’ leggermente diversa ma il concetto e’ il medesimo. I comandi da dare, sempre dal promt, sono :

c:\>python -m venev [nomeambiente]

cd [nomeambiente]

c:\[nomeambiente]\Scripts\activate

Come si puo vedere cambia solamente il comando per attivare l’ambiente virtuale mente la disattivazione rimane uguale con deactivate

PYTHON: creare, leggere, scrivere files in python

In questo articolo vediamo come gestire la creazione e le altre funzioni di apertura, lettura e scrittura di file di testo nel linguaggio python. Non serve importare librerie in quanto la gestione dei file e’ nativa in python .

Creare o aprire un file.

file=open("nomefile.ext", "w")

Con questo comando cerco di aprire il file nomefile.ext se non esiste lo crea vuoto. Il parametro “w” definisce il modo di apertura del file nel seguente elenco vediamo le diverse opzioni

"a" – apre il file e aggiunge il contenuto alla fine lo crea se non esiste

"w" – apre il file in scrittura sovrascrivendo evenutuali modifiche, se non esiste lo crea

"x" – Crea il file se non esiste, restituisce errore se esiste già

in aggiunta e’ possibile specificare il tipo di file e come esso verra’ gestito:

"t" – File di testo

"b" -file binario , dati , (ad esempio i file immagini)

le opzioni si aggiungono consecutivamente ad esempio :

file = open("fileimmagine.jpg", "rb")

Questo comando aprira il fileimmagine.jpg in sola lettura e lo gestira’ come file binario

Lettura file

Per la lettura di un file di testo si possono usare diverse funzioni . read(), readline() e readlines(). Queste funzioni ovviamente restituiscono dati diversi.

file = open("filedaleggere.txt", "r")
dati = file.read()
print(dati)

read()

Questo codice apre il file in sola lettura e legge tutto il contenuto, compresa la formattazione, mettendolo nella variabile dati che poi sara’ stampato a schermo

readline()

file = open("filedaleggere.txt", "r")
dati = file.readline()
print(dati)

questo modo leggera’ la prima riga e l’assegna alla variabile dati che poi sara’ stampata, per leggere le successive righe del file si dovra’ invocare nuovamente la funzione file.readline().

readlines()

Con questa funzione il contenuto del file letto verra ritornata sottoforma di lista, e i suoi elementi saranno le righe

file = open("filedaleggere.txt", "r")
dati = file.readline()
print(dati)

il codice sopra restituira’ [‘riga1\n’,’riga2\n’, … ‘rigan\n’]

Scrittura

Per la scrittura su file esiste solo una funzione, write(), la scrittura dei dati, sara’ eseguita in base alla modalita’ con la quale e’ stato aperto il file “a” aggiungera’ i dati alla fine del file, “w” cancellera’ il contenuto.

riga = "Riga da scrivere nel file"

file = open("filedascrivere.txt", "w")
dati = file.write(riga)
file.close()

Questo codice apre il file filedascrivere.txt , cancellandolo, se non presente lo crea, poi ci scrive i dati contenuti nella variabile ‘riga’.

PYTHON: i file di configurazione config-parser

Ci sarà utile , nei programmi da quelli piu’ semplici a quelli piu’ complessi, sfuttare un file di testo editabile dove inserire i dati di configurazione di un programma, ad esempio dove definiamo il nome della porta seriale o i dati di accesso ad un database etc.etc.

Questi file chiamati file di configurazione sono file di testo scritti con una particolare sintassi che vengono caricati dal programma e letti, ricavando cosi’ dei valori da assegnare a delle variabili che poi utilizzeremo nel programma principale. facciamo un esempio.

Creiamo un file di testo che si chiama config.conf:

[PORTASERIALE]
device = /dev/ttyUSB0
speed = 9600

ora creeremo un file che apra una connession alla porta seriale definita nel file config.conf:

import configparser #importo il modulo per la gestione dei file di config
import pyserial #importo il modulo per la seriale

config = configparser.ConfigParser() #creo l'oggetto config
config.read("config.conf") #leggo il file

print(config.sections()) # questa riga stampera tutte le sezioni
print(config['PORTASERIALE']['device'] #questa invece stampera solo il device

dev = config['PORTASERIALE']['device']
baud = config['PORTASERIALE']['speed']

serial = serial.Serial(port = dev,baudrate = baud) #apertura della porta 

...
continuazione del codice per la ricezione dei dati 
...

Il codice con i commenti e’ abbastanza esplicativo, ho ommesso la parte di gestione della seriale in quanto non e’ fondamentale per capire la gestione del parser dei file di configurazione.

PYTHON: if __name__ == “__main__”

Vediamo cosa significa questa sintassi che troviamo spesso negli script python

Prendiamo come esempio due script python uno principale chiamato radice.py e un secondo chiamato secondo.py. Il primo script sara il file che conterra il codice principale della nostra applicazione mentre nel secondo ci saranno delle funzioni secondarie che potranno essere chiamate da radice.py importando secondo.py come modulo.

Vediamo il codice di radice.py:

Vediamo ora come si presenta lo script secondo.py:

Ora vediamo cosa succede se mandiamo in esecuzione lo script radice.py:

Verdiamo che come prima cosa, lo script, stampera’ Secondo Script in quanto alla chiamata import secondo as secondo per l’importazione del secondo script come modulo, lo stesso viene anche eseguito, pertanto se ci sono delle istruzioni fuori dalla definizione di funzioni, queste vengono processate mentre non viene processato quello che e’ contenuto nell’ if __name__ == “__main__”:

Se invece mandiamo in esecuzione direttamente lo script secondo.py:

Come si vede, verranno eseguite sia le istruzioni che non sono contenute dentro le funzioni sia le istruzioni che sono contenute nell’ if __name__ == “__main__”:

Questa e’ la funzione dell’ istruzione if __name__ == “__main__”: , quella di far eseguire determinate istruzioni solo se lo script viene invocato direttamente come programma a se stante e non quando viene chiamato come modulo da un altro script

PYTHON: connessione a un server MySQL

Usiamo la libreria pymysql, per i dettagli vai al sito https://pypi.org/project/pymysql/ per la connessione ad un database che puo essere sulla macchina locale oppure su un server remoto.

Il codice precedente non fa altro che instaurare un collegamento al database istanziando l’oggetto conn, con questo oggetto e’ possibile interagire sul database attraverso i suoi metodi.

Attraverso la print(conn.host_info) stamperemo l’ip e la porta di connessione al server sempre che la connessione sia andata a buon fine

Infine con l’istruzione conn.close() chiudiamo la connessione al server.

Ineragire con mysql

Vediamo un esempio con cui andiamo a leggere i record di una tabella.

idfornitore
1Mario Rossi
2Giuseppe Verdi
3Valerio Bianchi
tabFornitori

Questo codice stampera una tupla con all’interno il primo record dell atabella sopra descritta

Sostituendo l’istruzione result = cursore.fetchone() con l’istruzione result = cursore.fetchall() l’output sara’ una tupla di tuple contenente tutti i record della tabella.

PYTHON: Gestire i file CSV con la libreria CVS

In python esiste la libreria CSV completa per gestire la lettura e scrittura dei file CSV, creando una lista per ogni riga del file letto ed accetando in ingresso una lista/tupla per ogni riga da scrivere.

Vediamo con un esempio la lettura di alcuni dati meteo e la conversione della temperatura da gradi celsius a gradi Fahrenheit.

Il file CSV in ingresso :

I commenti al codice spiegano tutto, in sostanza viene caricato il file datimeteo.csv visualizzato nell’immagine precedente, con il caricamento si ottiene un oggetto iterabile che tramite il for viene iterato riga per riga. Ad ogni iterazione il valore della temperatura che risiede nell’elemento 2 della lista cvs_reader, viene passato come argomento alla funzione convFareneit() la quale ritorna il valore di temperatura convertito.

A questo punto sempre durante l’iterazione della riga vengono copiati gli altri valori in una lista di tuple che poi useremo come fonte dati per la generazione del nuovo file CSV.

L’uso del comando try:/except ,consente allo script di non bloccarsi durante la lettura della prima riga del file dove risiedono i nomi dei campi, infatti la funzione convFareneit() tenterebbe di convertire il valore letto ‘temp’ in un numero float, generando un errore e bloccando lo script.

Alla fine dell’iterazione di tutte le righe viene creato un nuovo file CSV ed iterando la lista di touple creata con il nome di newdataset scriviamo riga per riga i nuovi valori. Il risultato nell’immagine sotto:

Il codice non e’ ottimizzato, infatti la conversione si potrebbe fare anche tramite delle funzioni comprension, ma lo scopo principale di questo articolo e’ quello di mostrare la lettura e scrittura di file CSV.

PYTHON: list-set-dictionary comprehension

Le comprehension (comprensione) e’ una forma di scrittura del codice, che serve ad ottimizare la generazione di liste, set e dizionari la sua sintassi e’ la seguente

per le liste:

lista = [ <espressione sulla variabile> for <variabile> in <input_> <filtro opzionale> ]

per le set:

lista = { <espressione sulla variabile> for <variabile> in <input_> <filtro opzionale> }

per i dictionary:

dic = { <espressione(variabile Key): espressione(variabile Value)> for variabile Key,variabileValue in <input_key, input_value> <filtro opzionale> }

Dettagli dei parametri:

  • <espressione sulla variabile> e’ un esperssione matematica o una funzione che agisce sul valore della variabile ricavato dal <input_> il quale puo essere una qualsiasi struttura dati o funzione
  • <input_> struttura o funzione (es. range()) che fa da base dati per la generazione della nuova struttura dati
  • <filtro opzionale> e’ una condizione if che se rispettata scrvie il dato nella nuova struttura, altrimenta la scarta.

Vediamo alcuni esempi :

#genero una set di divisi per due
list1 = [12,13,14,15,16,17,18]
set2 = {x/2 for x in list1}
print(set2)

#genero una lista di divisi per due sono se interi 
list2 = [12,13,14,15,16,17,18]
set3 = [x/2 for x in list2 if x%2 == 0]
print(set3)

#genero un dizionario delle temperature massime per mese
mesi = [('GENNAIO',-1),('FEBBRAIO',-2),('MARZO', 4),('APRILE',9)]
temp_mesi = { m: t for m,t in mesi  }
print(temp_mesi)

#genero un dizionario dei mei con temperature massime sotto 0
temp_mesi_freddi = { m: t for m,t in mesi if t < 0}
print(temp_mesi_freddi)

######risultati 
{6.0, 7.0, 8.0, 7.5, 6.5, 8.5, 9.0}
[6.0, 7.0, 8.0, 9.0]
{'GENNAIO': -1, 'FEBBRAIO': -2, 'MARZO': 4, 'APRILE': 9}
{'GENNAIO': -1, 'FEBBRAIO': -2}

PYTHON: Iteratori Customizzati

Sono Iteratori creati ad-hoc dal programmatore, praticamente sono delle CLASSI che implementano almeno i due metodi __iter__() e __next__()

nel metodo __iter__() Avviene l’inizializzazione dell’iteratore e il suo return (di solito viene ritornato l’oggetto stesso)

nel metodo __next__() ritorna il prossimo elemento nella sequenza e solleva l’eccezione StopIteration alla fine degli elementi

Facciamo un esempio di un iteratore che ad ogni chiamata restituise il valore precedente aumentato di 1.44 partendo da 0

class numeri:
    #inizializzo l'iteratore partendo da 0
    def __iter__(self):
        self.x = 0
        return self

    #calcolo valore successivo restituito ad ogni chimata next(<oggetto>)
    def __next__(self):
        a = self.x
        self.x += 1.44
        return round(a,2)

oggetto = numeri()

#converto in iteratore
test = iter(oggetto)
#scorro iteratore
for i in range(0,100):
    print(next(test))

I commenti al codice dicono tutto, l’unica cosa e’ che questo iteratore non finira’ mai di generare in quanto nella dichiarazione del metodo __next__() non c’e’ limite, pertanto lanciando un for i in range(oggetto) il ciclo durerebbe all’infinito. Se ad esempio volessimo fermare la generazione al numero superiore a 144.00 dovremmo modificare il codice nel modo seguente:

class numeri:
    def __iter__(self):
        self.x=0
        return self
    def __next__(self):
        a = self.x
        self.x +=1.44
        if(a <= 144):
            return round(a,2)
        else:
            StopIteration
    
oggetto = numeri()

test = iter(oggetto)

for i in range (0,120):
    print(next(test))

In fine vediamo un esempio di iteratore customizzato con la possibilita’ di passergli dei parametri (esempio range(0,100):

class numeriDispari(object):
    def __init__(self, max):
        self.max = max #imposto il valore massimo
        self.n = -1 #imposto la partenza che sara sempre 1 
    
    def __next__(self):
        self.n += 2 #incremeto di 2 il valore precedente 
                    ( all inizio self.n = -1, quindi self.n + 2 = 1)
        if self.n > self.max:
            raise StopIteration #se supero il valore max fermo l'iterazione
        else:
            return self.n #altrimenti rimando il valore calcolato
    def __iter__(self):
        return self
    
#stampo i primi 10 numeri dipsari
numeri = numeriDispari(10)
for n in numeri:
    print(n)
#creo una lista dei primi 80 numeri dipari
numeri_d = [ x for x in numeriDispari(100) if x < 80]
print(numeri_d)

I commentio nel codice spiegano il funzionamento