Welcome to pygraph’s documentation!

Contents:

Introduzione

Dove si cerca di giustificare il tempo perso per questo lavoro e dove viene data qualche idea su come installare l’interprete Python e la libreria grafica.

Scopo di Pygraph e di questo manuale

Mi sono imbattuto in Python mentre cercavo un linguaggio di alto livello, multi-piattaforma, con una sintassi semplice. Oltre ad uso personale volevo utilizzarlo anche a scuola per insegnare qualche elemento di informatica ai miei alunni. Ritenendo che la programmazione di elementi grafici possa coinvolgere di più dei semplici comandi di testo, ho cercato le librerie grafiche messe a disposizione da questo linguaggio. Con sorpresa e grande gioia ho scoperto che una delle librerie ufficiali implementa la grafica della tartaruga. Dopo averla utilizzata per un po’ ho sentito la necessità di modificarla e darle una connotazione più Object-Orienteed. Ho incominciato a pasticciarla, modificarla, adattarla alle mie esigenze (grazie software libero!). L’ho suddivisa in due parti: l’implementazione di un piano cartesiano e della grafica della tartaruga. Nel frattempo, sempre per esigenze didattiche ho aggiunto il modulo per tracciare funzioni matematiche integrandolo con il piano cartesiano. Altro strumento didattico interessante è costituito dai programmi di geometria interattiva (Cabri Géomètre, Kig, Dr. Geo, GeoGebra, CaR, ...). Un forte limite di questi programmi è l’assenza, o il difficile uso, di un linguaggio al loro interno, ho pensato quindi di aggiungere a pygraph una libreria per realizzare figure geometriche interattive. Il risultato finale è abbastanza diverso dalla libreria turtle.py da cui sono partito.

Dopo i primi anni di uso in diverse situazioni - uso personale, con alunni di scuola media e di scuola superiore - queste librerie mi sono sembrate abbastanza robuste da poter essere proposte ad altre persone interessate a Python e alla grafica. Così ho riunito un po’ di esempi di uso e ho scritto questo manualetto per documentarle. Ora, dopo altre esperienze, ho rivisto un po’ tutto il materiale per rilasciare una nuova versione.

I primi capitoli di questo testo sono pensati per guidare passo passo alla scoperta della programmazione in Python. Non vogliono essere una guida completa, per questo ci sono altri lavori ben più importanti del mio, ma vorrebbero essere uno stimolo e un’indicazione per iniziare l’esplorazione del mondo dei linguaggi di programmazione.

Installazione

Per installare un qualunque software bisogna essere un po’ pratici di computer. Conviene eventualmente farsi aiutare. L’autore non può essere ritenuto responsabile della perdita di informazioni conseguenti ad errori di installazione.

Installare Python

  1. Linux: il programma deve essere installato dall’amministratore del sistema, lui sa come fare. Se si utilizza una distribuzione la cosa più semplice è caricare Python al momento dell’installazione del sistema. O comunque installarlo a partire dai repository della distribuzione stessa. Sotto Linux a volte Python è distribuito separatamente da IDLE. In questo caso, per utilizzare l’ambiente di sviluppo IDLE (vedi capitolo 1), bisogna installarlo separatamente.
  2. Windows: doppio clic sul pacchetto da installare.
  3. La versione più aggiornata di Python può essere scaricata da http://www.python.org.
  4. Python è un software libero ed è distribuito sotto una licenza compatibile con la licenza GPL quindi è possibile usarlo, copiarlo e distribuirlo senza restrizioni.

Installare pygraph

  1. Procurarsi il file “pygraph_x.xx” (dove al posto dei vari “x” ci saranno delle cifre). Lo si può scaricare a partire dal sito http://www.fugamatematica.blogspot.com.
  2. Scompattare il file all’interno di una directory di lavoro.
  3. La directory pygraph contiene le tre directory seguenti:
    • doc: documentazione varia,
    • examples: esempi d’uso
    • pygraph: le librerie del progetto:
      • pycart.py: un piano cartesiano
      • pyturtle.py: la grafica della tartaruga
      • pyplot.py: grafico di funzioni nel piano, cartesiane e polari
      • pyig: geometria interattiva
  4. Spostare le directory doc e examples in una propria cartella facilmente raggiungibile (ad esempio: .../mieidocumenti/python/pygraph)
  5. Spostare la directory pygraph e il file pigraph.pth all’interno di: .../pythonx.x/Lib/site-packages/ o .../pythonx.x/dist-packages/

Note

#: per fare questo in Linux bisogna avere i privilegi di amministratore; #: a seconda della versione di Python installata, pythonx.x potrebbe essere python2.6, python2.7 o python3.1...; #: nella mia versione, Python2.6 sotto Debian, la directory in cui spostare la cartella pygraph e il file pygraph.pth si chiama: dist-packages.

Trovare documentazione su Python

  1. http://www.python.org
  2. http://www.python.it
  3. http://www.python-it.org
  4. Un’ottima introduzione all’informatica usando questo linguaggio di programmazione è il testo: “Pensare da informatico: Imparare con Python” di Downey, Allen tradotto magnificamente in italiano (si trova su internet partendo dai link precedenti).
  5. Le dispense di un laboratorio di matematica con Python e pygraph si possono trovare a partire dal sito http://www.fugamatematica.blogspot.com.

Riassumendo

Per eseguire i programmi scritti in Python bisogna installare Python (ma dai!). Per utilizzare la libreria grafica pyturtle (e le altre) bisogna aver copiato i la cartella che le contiene (e anche il file pygraph.pth) in una directory visibile da Python (site-packages).

Feedback

Spero che qualcuno trovi interessante questo lavoro e lo usi per imparare o per insegnare l’informatica e la geometria con Python. Utilizzandolo, senz’altro verranno alla luce molti errori, difetti o carenze sia nel software sia nella documentazione; chiedo a chi troverà qualche motivo di interesse in questo lavoro a inviarmi:

  1. Commenti,
  2. Critiche,
  3. Suggerimenti.

Ringrazio chiunque si prenderà la briga di mandarmi qualche riscontro.

Daniele Zambelli email: daniele.zambelli@inwind.it

I primi comandi

Dove vengono presentati: il comando print, gli oggetti numero e stringa e le variabili. E dove si vede anche come si fa a ripetere più volte un comando.

Comandi ed errori

Quando avviamo IDLE ci si apre una finestra dei comandi, la shell di IDLE, è caratterizzata dalla presenza di 3 simboli di maggiore all’inizio della riga:

>>>

Questi simboli sono il prompt di Python e stanno per la domanda:

Cosa devo fare?

Se scriviamo qualcosa e premiamo il tasto <invio> Python interpreta quello che abbiamo scritto come un comando e cerca di eseguirlo. Non sempre ci riesce! In questo caso ci avvisa con un messaggio (chissà perché, scritto in inglese). Ad esempio:

>>> salta
Traceback (most recent call last):
File "<pyshell#47>", line 1, in ?
    salta
NameError: name 'salta' is not defined

In pratica ci comunica che non sa cosa voglia dire “salta”. Nel messaggio di errore, le prime righe indicano dove è avvenuta l’incomprensione, possono essere utili quando si lavora ad un programma lungo, la riga significativa per noi è l’ultima.

Note

a seconda della versione di Python utilizzata ci sono delle differenze; se sono trasurabili le trascurerò, altrimenti riporterò sia il comportamento di Python 2.x sia quello di Python 3.x.

Si può provare qualche altro esempio:

>>> dammi 10 euri
SyntaxError: invalid syntax

Questa riga non riesce proprio ad interpretarla. Peccato! Scrivendo programmi ci si imbatte spesso in messaggi di questo genere. Anche se all’inizio è un po’ pesante, bisogna abituarsi a leggerli per capire come mai l’interprete non fa quello che noi volevamo. Con il tempo si impara a individuare rapidamente all’interno dei messaggi le informazioni interessanti. Per riuscire ad ottenere qualcosa di significativo dovremmo operare qualche cambiamento al nostro modo di dare i comandi, Per prima cosa teniamo conto che, come i messaggi di errore, anche i comandi di Python sono tutti in inglese.

print

Un primo comando che è utile conoscere è: print <qualcosa>. Esempio:

#versione 2.x
>>> print 45
45

#versione 3.x
>>> print(45)
45

<qualcosa> può essere un numero. Se al posto di un numero però scrivo un nome, ottengo un errore:

Note

in Python 2.x print è un comando che deve essere seguito dall’oggetto che deve essere visualizzato sullo schermo, in Python 3.x, print è una funzione che vuole come argomento, tra parentesi, l’oggetto da visualizzare.

#versione 2.x
>>> print Mario
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in ?
    print Mario
NameError: name 'Mario' is not defined

#versione 3.x
>>> print(Mario)
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    print(Mario)
NameError: name 'Mario' is not defined

In pratica l’interprete ha cercato il nome “Mario” nel suo vocabolario e non l’ha trovato. Per stampare un nome o una frase qualsiasi devo utilizzare le virgolette:

#versione 2.x
>>> print "Mario"
Mario

#versione 3.x
>>> print("Mario")
Mario

Così il comando funziona! Una sequenza di caratteri racchiusa tra virgolette (semplici o doppie) si chiama “stringa”.

Note

IDLE ci mette a disposizione un semplice meccanismo per riscrivere ed eventualmente modificare dei comandi precedenti: basta portare il cursore sul comando e premere il tasto <Invio>. Quindi, se volessimo modificare il comando precedente, possiamo portare il cursore (con il mouse o con i tasti freccia) sul comando da modificare, premere <Invio> e modificare il comando:

#versione 2.x
>>> print "Mario ha 45 anni"
Mario ha 45 anni

#versione 3.x
>>> print("Mario ha 45 anni")
Mario ha 45 anni

Il comando print è il più semplice modo che un programma ha per comunicarci qualcosa.

Variabili

Un elemento fondamentale della programmazione è costituito dalle variabili. Una variabile è una parola (in termini tecnici: identificatore) a cui è collegato un oggetto. Questo “oggetto” può, ad esempio, essere un numero o una stringa. Per associare un oggetto ad un identificatore si utilizza l’operatore di assegnazione: “=”. Per tirar fuori qualcosa basta scrivere il nome della variabile. Esempio:

>>> nome="Mario"

Ora all’identificatore “nome” è associata la stringa “Mario”.

#versione 2.x >>> print nome Mario

#versione 3.x >>> print(nome) Mario

Il comando print può accettare più oggetti separati da virgole, li stamperà uno di seguito all’altro:

>>> eta=45

Ora all’identificatore “eta” è associata la stringa “Mario”.

#versione 2.x >>> print nome, “ha”, eta, “anni.” Mario ha 45 anni.

#versione 3.x >>> print(nome, “ha”, eta, “anni.”) Mario ha 45 anni.

Il comando print, quando viene utilizzato con più oggetti, mette automaticamente uno spazio tra un oggetto e l’altro. Non sempre questo comportamento ci va bene. A volte desideriamo stampare due stringhe una di seguito all’altra senza spazi in mezzo. Ad esempio:

>>> primo_pezzo="mario"
>>> secondo_pezzo="netta"

#versione 2.x
>>> print primo_pezzo, secondo_pezzo
mario netta

#versione 3.x
>>> print(primo_pezzo, secondo_pezzo)
mario netta

Se voglio che le stringhe vengano stampate senza spazi in mezzo, dovrò concatenarle. L’operatore di concatenazione di stringhe è il simbolo: “+”. Esempio:

#versione 2.x
>>> print primo_pezzo + secondo_pezzo
marionetta

#versione 3.x
>>> print(primo_pezzo + secondo_pezzo)
marionetta

Iterazione

Spesso i programmi devono ripetere più volte le stesse istruzioni. Ad esempio se dovessi far stampare tre volte “Ciao” potrei dare il comando:

#versione 2.x
>>> print "Ciao"; print "Ciao"; print "Ciao"
Ciao
Ciao
Ciao

#versione 3.x
>>> print("Ciao"); print("Ciao"); print("Ciao")
Ciao
Ciao
Ciao

Notare che diverse istruzioni devono essere separate o da un <Invio> o da un ”;”. Questo metodo però non risulta affatto comodo se il numero di ripetizioni di un comando è elevato. Tutti i linguaggi di programmazione mettono a disposizione diversi comandi per ripetere un gruppo di istruzioni. Un modo per far scrivere 3 volte “Ciao” a Python è:

#versione 2.x
>>> for cont in range(3):
        print "Ciao"

#versione 3.x
>>> for cont in range(3):
        print("Ciao")
..note::
Attenzione ai due punti che terminano la prima riga. Incontrando il simbolo ”:”, l’interprete non esegue il comando ma va a capo spostando il cursore verso destra in modo da permettere di scrivere il blocco di istruzioni da ripetere.

Ora, premendo una prima volta il tasto <Invio> non succede niente, perché venga eseguito il comando, si deve premere una seconda volta <Invio>. Analizziamo il comando for che è piuttosto complesso:

for <variabile> in range(<numero>):
    <comandi da ripetere>
  • for è il nome del comando;
  • cont è il nome di una variabile, al posto di cont potrei scrivere qualunque altro nome. In questa variabile vengono messi, uno alla volta i valori restituiti dalla funzione range. In questo caso cont non viene usata, ma spesso è utile avere una variabile che contiene, ad ogni ciclo, un valore diverso;
  • in fa parte del comando for;
  • range fornisce l’elenco dei numeri interi tra 0 compreso e il numero tra parentesi escluso;
  • : alla fine di questa riga c’è il carattere due punti, indica che di seguito è scritta la porzione di codice che verrà ripetuta;
  • print "Ciao" (o print("Ciao")) è la parte di codice che viene ripetuta, deve essere indentata cioè deve essere scritta più a destra rispetto al comando for. Può essere formata da più righe, in questo caso devono avere tutte lo stesso rientro (trovarsi allo stesso livello di indentazione).

Da questo esempio può non apparire in modo molto chiaro quanto sia comodo il comando for, ma se il numero di ripetizioni fosse molto più elevato, o il codice che deve essere ripetuto più complesso, si vedrebbe immediatamente la differenza. Consideriamo ad esempio il seguente codice:

>>> for cont in range(3000):
        print "Ciao"

nessuna persona normale si metterebbe a scrivere a mano i singoli comandi per ottenere lo stesso risultato delle due righe precedenti.

Per vedere il contenuto della variabile i durante l’esecuzione del ciclo possiamo dare il comando:

#versione 2.x
>>> for cont in xrange(8):
        print cont

#versione 3.x
>>> for cont in xrange(8):
        print(cont)

0
1
2
3
4
5
6
7
>>>

Se volessi un elenco dei primi otto interi e dei loro quadrati e dei loro cubi:

#versione 2.x
>>> for n in range(8):
        print n, "\t", n*n, "\t", n*n*n

#versione 3.x
>>> for n in range(8):
        print(n, "\t", n*n, "\t", n*n*n)

0     0       0
1     1       1
2     4       8
3     9       27
4     16      64
5     25      125
6     36      216
7     49      343
>>>

Riassunto

  • In Python ci sono due oggetti di base: numeri e stringhe.

  • Le stringhe sono formate da una sequenza di caratteri racchiusa tra apici doppi o singoli.

  • Nell’ambiente IDLE si dà un comando a Python scrivendolo dopo il prompt e terminandolo con il tasto <Invio>

  • Se Python non è in grado di eseguire un nostro comando ci avvisa con un messaggio.

  • Una variabile, in Python è l’associazione tra un identificatore, un nome, e un oggetto.

  • Si può assegnare un oggetto ad un identificatore utilizzando l’operatore di assegnazione: “=”.

  • Si ottiene l’oggetto associato ad un identificatore scrivendo semplicemente l’identificatore.

  • Il comando print scrive uno o più oggetti sullo schermo.

  • Si possono concatenare delle stringhe utilizzando l’operatore di concatenazione: “+”.

  • Posso portare il cursore sulla riga che contiene un comando già scritto, premendo una volta <Invio> il comando viene ricopiato in fondo, posso quindi modificarlo e rieseguirlo.

  • Per ripetere più volte le stesse istruzioni posso utilizzare il comando:

    for <identificatore> in range(<numero>):
        <istruzioni>
    

La grafica della tartaruga

Dove si parla di come generare tartarughe virtuali e mandarle in giro per lo schermo a disegnare.

Creare una tartaruga

Per giocare con la grafica della tartaruga bisogna procurarci... una tartaruga. Ci sono molti modi per farlo, verranno illustrati più avanti, qui ne vediamo uno, il più comune.

Per utilizzare la libreria della grafica della Tartaruga dobbiamo dire a Python di caricarla in memoria. In realtà, di tutta la libreria ci interessa, per ora, solo l’ambiente dove vivono le tartarughe. Per caricare questo ambiente dobbiamo scrivere:

>>> from pyturtle import TurtlePlane

Questa istruzione legge dalla libreria pyturtle.py la classe che permette di creare un territorio dove vivono tartarughe. Per crearne uno dobbiamo inventarci un nome, ad esempio tp e dare il comando:

>>> tp = TurtlePlane()

Ora che abbiamo una superficie adatta: dobbiamo creare una tartaruga. Possiamo chiederlo alla superficie appena creata di fornircene una nuova. Come prima dobbiamo intanto pensare un nome, ad esempio tina:

>>> tina = tp.newTurtle()

Riassumendo:

  • tp è un piano dove vivono tartarughe;
  • tina è una tartaruga generata dal piano tp.

La tartaruga “tina” è ora a nostra disposizione per gli esperimenti. Possiamo provare a darle qualche comando:

>>> tina.forward(100)

tina si sposta avanti di 100 passi lasciando una traccia.

>>> tina.right(90)

Non si sposta verso destra, ma ruota verso destra di 90 gradi. E per ruotare verso sinistra?

>>> tina.left(90)

Esatto! Ripetiamo ora uno spostamento e una rotazione per un po’ di volte:

>>> for i in range(4):
        tina.forward(100)
        tina.right(90)

tina disegna un quadrato.

>>> tina.up()
>>> tina.back(200)
>>> tina.down()

Solleva la penna si sposta indietro di 200 passi e riappoggia la penna, in questo modo si può far muovere Tartaruga senza disegnare.

>>> tina.setcolor("blue")

Cambia il colore della penna.

>>> for i in range(180):
        tina.forward(80)
        tina.back(78)
        tina.left(2)

Altro disegno, questa volt blu... Spostiamo ancora tina:

>>> tina.up()
>>> tina.back(100)
>>> tina.down()

Poi cambiamo il colore della penna:

>>> tina.setcolor((1, 0, 0))

Si può assegnare il colore della penna o usando un nome di colore in inglese o fornendo tre numeri compresi tra 0 e 1 ragruppati in una parentesi tonda. Il primo indica la componente di rosso, il secondo di verde, il terzo di blu (o in un terzo modo che è spiegato più avanti). E ora proviamo a fare una stella con 100 raggi:

>>> for i in range(100):
        tina.forward(80)
        tina.back(80)
        tina.left(360/100)

I raggi sono proprio 100 (li ho contati!), ma la stella non appare affatto regolare, ne manca un pezzo!

..note:
se stai lavorando con Python 3 non hai questo problema, perché in Python 3 la divisione tra due interi dà come risultato un numero decimale. (Se stai lavorando con Python 2 i prossimi 3 paragrafi fanno per te).

Questo è dovuto allo strano modo di fare matematica di Python 2. Se in una divisione tutti gli operandi sono interi il risultato sarà troncato all’intero:

>>> print 360/100
3

Se voglio un risultato più preciso almeno uno degli operandi deve essere un numero decimale:

>>> print 360.0/100
3.6

O anche semplicemente:

>>> print 360./100
3.6

Ora spediamo tina in un altro punto dello schermo e riproviamo:

>>> tina.up()
>>> tina.forward(300)
>>> tina.down()
>>> for i in range(100):
        tina.forward(80)
        tina.back(80)
        tina.left(360.0/100)

Ora disegna una stella regolare! Dopo un po’ che si fanno dei tentativi con i comandi grafici può succedere di avere la finestra grafica così piena di scarabocchi da non capirci più niente. Un altro comando fondamentale è reset. Questo è un metodo del piano della tartaruga, ripulisce la finestra grafica e rimette a posto Tartaruga:

>>> tp.reset()

Riassumendo

  • Per lavorare con la grafica della tartaruga posso scrivere:

    >>> from pyturtle import TurtlePlane
    >>> tp = TurtlePlane()
    >>> tina = tp.newTurtle()
    

    ottengo così due oggetti: un piano delle tartarughe (tp) e una tartaruga (tina).

  • I comandi fondamentali per far muovere la tartaruga creata e disegnare qualcosa sono:

    <tartaruga>.forward(<numero>)
    <tartaruga>.back(<numero>)
    <tartaruga>.left(<numero>)
    <tartaruga>.right(<numero>)
    <tartaruga>.up()
    <tartaruga>.down()
    <piano>.reset()
    

Un po’ di vocabolario

Dove ci si allontana un po’ dalla tastiera per riflettere e capire meglio.

Computer

Ora vediamo alcune parole che possono essere utili per capire meglio questo linguaggio di programmazione.

Possiamo pensare il computer formato da:

  • Il microprocessore: un circuito in grado di leggere e modificare il contenuto della memoria RAM e di eseguire alcune operazioni logiche o matematiche.
  • La memoria RAM (Random Acces Memory, Memoria ad Accesso Casuale): contiene le istruzioni che devono essere eseguite dal microprocessore e i dati necessari al programma. La memoria RAM è realizzata con circuiti elettronici, ha una velocità paragonabile a quella del microprocessore, è volatile cioè quando si spegne il computer, perde irrimediabilmente il suo contenuto.
  • La memoria di massa: generalmente costituita da dischi magnetici (hard disk), ottici (CDRom) o memorie flash (USB Disk), mantiene le informazioni memorizzate anche quando viene spento il computer, ma sono piuttosto lenti.
  • Le periferiche: dispositivi che permettono al computer di comunicare con l’esterno: tastiera, monitor, mouse, stampante, modem, ...

Linguaggi di programmazione

Le istruzioni che il microprocessore deve eseguire sono scritte nella memoria RAM.

Quando un computer funziona, il microprocessore legge una ad una le istruzioni scritte nella memoria RAM e le esegue. Scrivere un programma vuol dire mettere nella memoria RAM le istruzioni giuste per far fare al microprocessore quello che vogliamo noi. Purtroppo il microprocessore non capisce comandi sensati, ma solo il linguaggio macchina che è composto da numeri binari cioè scritti usando solo le due cifre: zero e uno.

Perciò non potremmo scrivere nella RAM:

Somma 3 a 5

ma dovremmo scrivere qualcosa di questo genere:

1000110011101010
0000000000000011
0000000000000101

Ovviamente scrivere un programma, magari fatto da migliaia o milioni di istruzioni, utilizzando numeri binari è un’impresa da super eroi! I programmatori hanno ben presto scritto dei programmi che leggono comandi sensati e li traducono in numeri binari. In questo modo si possono scrivere programmi utilizzando linguaggi più vicini al linguaggio dell’uomo che a quello della macchina: linguaggi di alto livello.

Ci sono due modi per trasformare un programma scritto con linguaggio di alto livello in un programma scritto in linguaggio macchina: tradurlo tutto e poi eseguirlo oppure tradurre ogni singolo comando ed eseguirlo immediatamente. Nel primo caso si parla di compilatori che traducono tutto un programma scritto con linguaggio di alto livello trasformandolo in un programma scritto in linguaggio macchina e perciò eseguibile da un dato computer senza più la necessità della presenza del compilatore stesso. Nel secondo caso si parla di interpreti: per eseguire un programma interpretato, nel computer deve essere presente l’interprete del linguaggio che legge, traduce e fa eseguire istruzione per istruzione.

Python

Python è un linguaggio interpretato (in realtà le cose sono un po’ più complicate di così): non si può eseguire un programma scritto in Python se sul computer non è presente l’interprete. Il nucleo del linguaggio Python è piuttosto limitato: può capire e far eseguire pochi (si fa per dire!) comandi. Ma è dotato di un meccanismo per cui può essere ampliato all’infinito: si possono scrivere librerie che contengono nuove funzioni e che allargano le possibilità del linguaggio. In effetti Python viene fornito con un gran numero di librerie già scritte e notevolmente sicure perché già utilizzate e messe alla prova da programmatori di tutto il mondo. Molte di queste librerie sono scritte in Python stesso e perciò si possono facilmente studiare e, magari, modificare. Altre, per ragioni di efficienza e di velocità, sono scritte usando linguaggi compilati.

Le librerie sono dunque dei programmi che ampliano le possibilità del linguaggio mettendo a disposizione del programmatore delle nuove funzioni non presenti nel nucleo del linguaggio.

Per utilizzare il contenuto di una libreria si deve dire all’interprete che la si vuole usare, il comando per includere nell’interprete le funzioni di una libreria è:

>>> from <nome della libreria> import <elenco degli oggetti>

Ad esempio, il comando:

>>> from pyturtle import TurtlePlane

significa: dalla libreria di nome pyturtle prendi l’oggetto che si chiama TurtlePlane.

A questo punto possiamo creare quanti oggetti vogliamo appartenenti alla classe TurtlePlane. Ce ne basta uno, per crearlo dobbiamo dargli un nome, ad esempio “tp”. Quindi il comando per creare un nuovo oggetto di nome “tp” della classe TurtlePlane è:

>>> tp = TurtlePlane()

Bisogna prestare attenzione alle maiuscole e alle parentesi. Se tutto ila liscio questo punto appare una nuova finestra sullo schermo è l’oggetto piano della tartaruga collegato all’identificatore tp.

Con lo stesso meccanismo proviamo a creare un oggetto della classe Turtle:

>>> from pyturtle import TurtlePlane, Turtle
>>> tp = TurtlePlane()
>>> tina = Turtle()

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    tina=Turtle()
TypeError: __init__() takes at least 2 arguments (1 given)

Non funziona e l’errore non è molto chiaro... Per poter creare una tartaruga dobbiamo specificare in quale mondo deve nascere, quindi, al creatore di tartarughe dobbiamo dare un’informazione:

>>> tina = Turtle(tp)

Si può creare una tartaruga anche in un altro modo, del tutto equivalente. La classe TurtlePlane ha un metodo che crea e dà come risultato una nuova tartaruga. Un metodo di un oggetto viene invocato scrivendo il nome dell’oggetto seguito da un punto, dal nome del metodo e da una coppia di parentesi:

>>> tina = tp.newTurtle()

L’istruzione precedente lega l’oggetto prodotto da tp.newTurtle() al nome “tina”.

Gli oggetti possiedono degli attributi e dei metodi. Gli attributi sono delle caratteristiche che possono variare da un oggetto ad un altro come il colore, la posizione, lo spessore della penna, ... i metodi sono i comandi che l’oggetto è in grado di eseguire. I comandi fondamentali della grafica della tartaruga, i metodi che ogni oggetto della classe Turtle è in grado di eseguire sono, come abbiamo visto precedentemente:

<tartaruga>.forward(<numero>)
<tartaruga>.back(<numero>)
<tartaruga>.left(<numero>)
<tartaruga>.right(<numero>)
<tartaruga>.up()
<tartaruga>.down()
<piano>.reset()

Riassumendo

  • Python è un linguaggio (compilato e) interpretato.

  • È arricchito da numerose librerie. Una libreria che contiene la grafica della tartaruga è pyturtle.

  • Si può caricare una libreria con il comando:

    from <nome libreria> import <elenco di oggetti>.
    

    Ad esempio:

    from pyturtle import TurtlePlane
    
  • Nella libreria pyturtle è descritta la classe TurtlePlane.

  • È possibile creare un oggetto scrivendo un nome, il simbolo di uguale e il nome della classe seguito da una coppia di parentesi tonde. Ad esempio:

    mondoditina = TurtlePlane()
    tina = Turtle(mondoditina)
    
  • È possibile fare eseguire ad un oggetto un suo metodo scrivendo il nome dell’oggetto seguito dal punto e dal nome del metodo completo di parentesi contenenti, se necessario, uno o più valori (argomenti). Ad esempio:

    tina.forward(100)
    

Le funzioni

Dove si impara come insegnare a ``Python`` a eseguire nuovi comandi.

Definire una funzione

Riprendiamo il lavoro con la grafica della Tartaruga. Se abbiamo appena avviato IDLE dobbiamo procurarci una tartaruga con le istruzioni:

>>> from eturtle import *
>>> tina=t

Oppure:

>>> from pyturtle import TurtlePlane
>>> tp=Turtleplane()
>>> tina=tp.newTurtle()

Riscriviamo il comando per disegnare un quadrato

>>> for i in range(4):
        tina.forward(30)
        tina.left(90)

Ora, in un programma può darsi che ci sia bisogno di disegnare molti quadrati, è scomodo e poco chiaro ripetere le tre righe scritte sopra ogni volta che serve un quadrato. Sarebbe molto più semplice avere un comando, magari di nome quadrato, che disegni un quadrato. In pratica sarebbe bello poter scrivere:

>>> quadrato()

e ottenere il disegno del quadrato al posto del messaggio:

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in ?
    quadrato()
NameError: name 'quadrato' is not defined

Gli informatici hanno pensato ad un modo per esaudire questo desiderio che, evidentemente, è di tutti quelli che si mettono a programmare. Hanno realizzato un meccanismo per cui si può ampliare un linguaggio insegnandogli a eseguire comandi nuovi di nostra invenzione. Questi nuovi comandi si chiamano procedure o funzioni.

Per insegnare a Python una nuova funzione bisogna scrivere la parola riservata def seguita dal nome che vogliamo dare alla funzione, da una coppia di parentesi e dal simbolo “:” (due punti). Dopo questa riga, con un opportuno rientro (indentazione), dobbiamo scrivere tutte le istruzioni che vogliamo vengano eseguite quando si chiamerà la funzione.

Per insegnare a Python a disegnare quadrati con la tartaruga dovremo scrivere:

>>> def quadrato():
        for i in xrange(4):
            tina.forward(30)
            tina.left(90)

Dobbiamo terminare la scrittura della funzione premendo due volte il tasto <Invio>. Ma non succede assolutamente niente! Infatti abbiamo insegnato a disegnare un quadrato, ma non abbiamo detto a Python di disegnarlo!

Eseguire una funzione

Proviamo a dare il comando:

>>> quadrato
<function quadrato at 0x836596c>

Strano messaggio... Non è un messaggio di errore, semplicemente Python ci avvisa che quadrato è una funzione. Per dire a Python che deve eseguire la funzione quadrato dobbiamo far seguire al nome le parentesi:

>>> quadrato()

Ora va! tina ha disegnato per noi il quadrato! In modo analogo possiamo insegnare altre funzioni ad esempio per disegnare un triangolo equilatero ...

>>> def triangolo():
        for i in xrange(3):
            tina.forward(30)
            tina.left(...)

Ovviamente al posto dei puntini devo mettere un numero: il numero di gradi dell’angolo del triangolo (quale angolo?). E poi provarlo:

>>> triangolo()

Una volta insegnata una nuova funzione possiamo utilizzarla esattamente come le funzioni primitive del linguaggio, in particolare possiamo richiamarla dall’interno di un’altra funzione:

>>> def bandierina():
        tina.forward(100)
        quadrato()
        tina.back(100)

Ripuliamo lo schermo e disegnamo la bandierina:

>>> tina.reset()
>>> bandierina()

Ovviamente possiamo scrivere una funzione che chiama una funzione che chiama una funzione, che... Possiamo, ad esempio, mettere insieme tante bandierine per costruire una girandola:

>>> def girandola():
    for i in xrange(36):
        bandierina()
        tina.left(10)

E poi provarla:

>>> girandola()

Troppe bandierine: modifichiamo la funzione per diminuirne il numero. Possiamo riscrivere interamente la funzione, oppure portare il cursore sopra la prima riga e premere <Invio>. La funzione viene riscritta e abbiamo la possibilità di modificarla.

>>> def girandola():
        for i in xrange(24):
            bandierina()
            tina.left(360./24)

Qui ho usato un trucchetto, invece di fare le divisioni a mente, le facciamo fare a Python che ha tutta la potenza del computer a disposizione. Dobbiamo ricordagli però di usare l’aritmetica decimale, non quella intera (360.0/24). Ripuliamo lo schermo e proviamola:

>>> tina.reset()
>>> girandola()

Ancora troppe, diminuiamole:

>>> def girandola():
    for i in xrange(20):
        bandierina()
        tina.left(360./20)

E proviamola:

>>> tina.reset()
>>> girandola()

Ohohoh! Proprio come volevo... A dire il vero mi sembra un po’ smorta come girandola. Un po’ di colore non guasterebbe! Modifichiamo la funzione che disegna le bandierine in modo che le disegni con il contorno marron e l’interno verde. Utilizzaremo il metodo fill(flag), quando flag vale 1 la tartaruga incomincia a segnare gli oggetti da riempire di colore, quando flag vale 0 li riempie con il colore attuale della penna.

>>> def bandierina():
        tina.setcolor("brown")
        tina.forward(100)
        tina.fill(1)
        quadrato()
        tina.setcolor("green")
        tina.fill(0)
        tina.setcolor("brown")
        tina.back(100)

Proviamola...

>>> tp.reset()
>>> tina.setwidth(4)
>>> girandola()

Adesso mi piace!

Riassumendo

  • Possiamo insegnare a Python a interpretare ed eseguire comandi nuovi, la sintassi per fare ciò è:

    def <nome della funzione>():
        <istruzioni>
    
  • Una funzione scritta dal programmatore può essere utilizzata esattamente come quelle primitive del linguaggio.

  • Per eseguire una funzione devo scriverne il nome seguito da una coppia di parentesi.

  • Una nuova funzione può essere richiamata da un’altra funzione e così via ricorsivamente.

  • Python 2 ha un modo un po’ suo di fare le divisioni: se dividendo e divisore sono numeri interi dà come risultato il quoziente intero troncando il risultato. Per ottenere il risultato razionale uno degli operandi deve essere un numero con la virgola (il punto in Python).

  • Altri due metodi interessanti degli oggetti della classe Turtle, sono: setwidth e setcolor, la cui sintassi è:

    <tartaruga>.setwidth(<numero>)
    <tartaruga>.setcolor(<colore>)
    

I parametri

Dove si impara a rendere più flessibili le funzioni definite dal programmatore

Scrivere funzioni con parametri

La procedura quadrato ci permette di disegnare quanti quadrati vogliamo scrivendo un solo comando senza dover riscrivere ogni volta il ciclo for. È un risparmio di righe di codice e una bella semplificazione, pensiamo se dovessimo disegnare cento quadrati per disegnare un muro! Ma resta un problema. Se abbiamo bisogno di quadrati più grandi o più piccoli? Per ogni lunghezza del lato dobbiamo scrivere una procedura diversa. Prima di eseguire gli esempi di questo capitolo bisogna creare una tartaruga come visto nel capitolo precedente:

>>> from eturtle import *
>>> tina=t

Oppure:

>>> from pyturtle import TurtlePlane
>>> tp=TurtlePlane()
>>> tina=tp.newTurtle()

Ad esempio se ci serve un quadrato piccolo e uno grande:

>>> def quadratino():
        for i in xrange(4):
            tina.forward(10)
            tina.left(90)

>>> def quadratone():
        for i in xrange(4):
            tina.forward(200)
            tina.left(90)

>>> quadratino()
>>> quadratone()

La soluzione potrebbe anche andare bene se mi bastassero quadrati con due lunghezze diverse di lati, ma se avessi bisogno di molti quadrati con lati diversi? L’interprete mette a disposizione un meccanismo simile a quello usato dal linguaggio Python che permette di tracciare linee di lunghezze diverse semplicemente mettendo tra parentesi la sua lunghezza:

>>>tina.forward(50)
>>>tina.forward(57)
>>>tina.forward(72)
>>>tina.forward(163)
...

Cioè permette di scrivere:

>>>quadrato(50)
>>>quadrato(57)
>>>quadrato(72)
>>>quadrato(163)
...

Per ottenere quadrati di lato: 50, 57, 72, 163, ...

Tutti i linguaggi di programmazione moderni danno questa possibilità. Prima di vedere come fare ciò, riguardiamo le due procedure quadratino e quadratone. Sono praticamente uguali, cambia solo il numero che indica la lunghezza del lato. Al posto di questo numero noi possiamo mettere il nome di una variabile:

...
        tina.forward(lato)
...

Il nome della variabile ovviamente non ha nessun significato per il computer, ma è bene scegliere un nome che sia significativo per noi.

All’interno della funzione, quando Python trova il nome di una variabile lo sostituisce con il suo valore. Quindi se alla variabile di nome lato è collegato il numero 50, verrà disegnato un quadrato con il lato lungo 50, se è collegato il numero 57 la lunghezza del lato sarà di 57 unità, e così via.

Ma qual è il meccanismo per mettere un valore nella variabile lato? Quando viene definita una funzione, Python permette di inserire tra le parentesi che seguono il suo nome il nome di una variabile (o di più variabili):

>>> def quadrato(lato):
...

Le variabili legate alle funzioni in questo modo, si chiamano parametri.

Quando viene chiamata una funzione, il valore che viene scritto tra parentesi, viene, dall’interprete, collegato a quella variabile:

>>> def quadrato(lato):
...
...
>>> quadrato(57)

Nell’esempio precedente lato è il parametro della funzione quadrato e 57 è l’argomento con cui viene chiamata la funzione quadrato. Dopo questo comando, la variabile lato contiene il numero 57. La funzione quadrato diventa dunque:

>>> def quadrato(lato):
        for i in range(4):
            tina.forward(lato)
            tina.left(90)

Proviamola:

>>> quadrato()
Traceback (most recent call last):
  File "<pyshell#65>", line 1, in ?
    quadrato()
TypeError: quadrato() takes exactly 1 argument (0 given)

Accidenti, un errore! Già, ora non basta dire che voglio un quadrato, devo anche specificare la lunghezza del suo lato:

>>> quadrato(47)
>>> quadrato(58)
>>> quadrato(69)

Il parametro, cioè la variabile scritta nella definizione della funzione, è visibile solo all’interno della funzione stessa e non interferisce con altre variabili presenti nel programma.

Usare funzioni con parametri

La nuova funzione quadrato può essere utilizzata all’interno di altre funzioni esattamente come le funzioni primitive del linguaggio o le altre funzioni presenti nelle librerie. Possiamo quindi scrivere una nuova funzione che disegni molti quadrati:

>>> def quadrati():
        for dim in range(0, 200, 20):
            quadrato(dim)

Questa volta range(0, 200, 20) genera tutti i numeri da 0 (compreso) a 200 (escluso) andando di 20 in 20, ognuno di questi numeri viene collegato alla variabile di nome dim e ogni volta viene disegnato un quadrato con quel lato. Proviamo:

>>> tina.reset()
>>> quadrati()

Avrei ottenuto lo stesso risultato definendo quadrati in questo modo:

>>> def quadrati():
        for n in range(10):
            quadrato(n*20)

Quale delle due procedure è più chiara? È una questione di gusti personali... nel seguito teniamo questa seconda versione. Possiamo parametrizzare anche questa funzione: le assegnamo una variabile che controlli il numero di quadrati:

>>> def quadrati(numero):
        for n in range(numero):
            quadrato(n*20)

Può darsi che la distanza di 20 unità, tra un quadrato e l’altro, non sia di nostro gusto: possiamo aggiungere un parametro in modo da controllare l’incremento del lato:

>>> def quadrati(numero, incremento):
        for n in range(numero):
            quadrato(n*incremento)

Ora perché quadrati venga eseguito dobbiamo passargli 2 valori, possiamo fare diverse prove:

>>> tina.reset()
>>> quadrati(12, 5)
>>> tina.reset()
>>> quadrati(70, 2)
>>> tina.reset()
>>> quadrati(5, 50)
...

Ovviamente anche quadrati(numero, incremento) può essere utilizzato all’interno di altre funzioni, proviamo a disegnare una griglia di quadrati:

>>> def griglia():
        for i in range(4):
            quadrati(18, 10)
            tina.left(90)

>>> tina.reset()
>>> griglia()

Ma anche griglia() può essere parametrizzata...

Riassumendo

  • Possiamo insegnare a Python a interpretare ed eseguire comandi nuovi dotati di parametri, la sintassi per fare ciò è:

    def <nome della funzione>(<nome del parametro>):
        <istruzioni>
    
  • Le funzioni con parametri sono molto più flessibili di quelle senza e risolvono intere classi di problemi invece che un solo problema (tutti i possibili quadrati invece che un solo quadrato).

  • Nella definizione della procedura viene deciso il nome dei parametri.

  • Il parametro è una variabile locale che viene creata quando viene chiamata la funzione e viene distrutta quando la funzione termina.

  • Nella chiamata della funzione viene deciso il valore del parametro viene cioè passato alla funzione un argomento.

  • Il valore viene assegnato al parametro ad ogni chiamata di funzione.

Un programma

Dove si scrive il primo programma

Iniziare un programma

La possibilità di scrivere, rivedere, modificare, rieseguire linee di comandi permette di realizzare disegni anche piuttosto complicati utilizzando solo la shell di IDLE. Ma se vogliamo risolvere problemi più complessi dobbiamo realizzare tante funzioni e non è detto che riusciamo a farlo all’interno di un’unica sessione di lavoro. Dobbiamo scrivere un programma Python in una finestra di editor e salvarlo in un file in modo da poterlo riprendere, correggere, completare. Un programma è un testo che contiene una sequenza di funzioni, di comandi e di commenti. Il programma deve essere scritto in un testo piano, senza nessun tipo di formattazione per cui non va usato, per scriverlo, un normale elaboratore di testi, ma un “editor” che permette di scrivere e di salvare esattamente e solo i caratteri immessi dalla tastiera.

Oltre alla shell, che abbiamo utilizzato fin’ora, IDLE metta a disposizione anche un editor di testo e sarà quello che utilizzaremo. Per iniziare un nuovo programma, dal menu File scegliamo la voce New Window e apparirà una nuova finestra completamente vuota con il cursore lampeggiante in alto a sinistra.

Prima ancora di incominciare a riempirla, pensiamo ad un nome da dare al programma, il nome deve essere significativo per noi. Nel mio caso, dato che questo è il primo programma che ho scritto per il manualetto l’ho chiamato: “man1.py”.

Note

L’estensione ”.py” indica che questo file contiene un programma scritto in Python.

Pensato ad un nome adatto, salviamo il file prestando ben attenzione a dove viene salvato, in modo da riuscire a ritrovarlo in seguito: dal menu File scegliamo la voce Salva, spostiamoci nella cartella nella quale abbiamo deciso di mettere il programma, scriviamo il nome del programma e confermiamo con il tasto <Invio>.

I commenti

I comandi e le funzioni sono già stati visti dei capitoli precedenti. I commenti sono molto importanti per poter capire programmi scritti da altre persone, ma anche programmi scritti da noi stessi, magari qualche tempo prima. Ci sono di due tipi di commenti: commenti di linea e commenti su più linee. Il carattere cancelletto, “#”, indica l’inizio di un commento che si estende fino alla fine della riga. Quando l’interprete Python incontra il carattere “#” passa ad interpretare la riga seguente:

# questo è un commento

Se abbiamo bisogno di scrivere un commento che si estenda su più linee possiamo iniziare ogni linea con il carattere “#” oppure iniziare e terminare il commento con tre virgolette “”” di seguito:

"""Questo e' un
commento scritto su
piu' linee"""

Il primo mattone del programma

Rimbocchiamoci le maniche e iniziamo a scrivere il nostro primo programma.

Ogni programma deve essere ben commentato, in particolare deve avere un’intestazione che indichi l’interprete, il nome del file, l’autore, la data il tipo di licenza e, ovviamente, un titolo.

In questo esempio ci poniamo l’obiettivo di far disegnare alla tartaruga un muro di mattoni quadrati. L’intestazione del programma potrebbe essere:

#----------------------Python-pyturtle-----------------man1.py--#
#                                                               #
#                       Muro iterativo                          #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2005--#

Scritta l’intestazione, se vogliamo, possiamo anche eseguire il programma. Possiamo farlo attraverso il menu:

Run - Run module

o premendo semplicemente il tasto:

<F5>.

Per tutta risposta IDLE ci chiede se volgiamo salvare le modifiche apportate al programma. Noi, ovviamente, confermiamo.

Note

dato che ogni volta che vogliamo eseguire il programma, vogliamo anche salvare le modifiche, possiamo andare su menu Option-Configure IDLE e nella scheda General selezionare No Prompt. D’ora in poi, dopo la pressione del tasto F5 non ci verrà più richiesta la conferma del salvataggio del file.

Python lo interpreterà correttamente, non darà alcun messaggio di errore, ma, essendo composto solo da commenti non eseguirà assolutamente niente. In cominciamo allora ad aggiungere istruzioni. Poiché vogliamo utilizzare la grafica della tartaruga, come primo comando dobbiamo caricare la libreria, creare un piano e chiedere al piano di produrre una nuova tartaruga:

from pyturtle import TurtlePlane
tp=TurtlePlane()
tina=tp.newTurtle()

Ora, per costruire un muro di mattoni quadrati, abbiamo bisogno di una funzione che disegni quadrati:

def quadrato(lato):
    """Disegna un quadrato di dato lato."""
    for i in xrange(4)
        tina.forward(lato)
        tina.left(90)

Ormai siamo super esperti in quadrati... Ma questa volta aggiungiamo un commento come prima riga della funzione. Si chiama docstring e ogni funzione, oltre ad un nome significativo, dovrebbe averne una.

Ora dobbiamo eseguire il programma (Run - Run module o più velocemente tasto <F5>). A questo punto i casi sono due:

  1. Non succede assolutamente niente.
  2. Python scrive alcune strane righe nella shell di IDLE.

Nel primo caso, vuol dire che è andato tutto bene, Python non ha eseguito niente perché noi abbiamo definito una funzione, ma non abbiamo dato il comando di eseguirla e giustamente il computer non si prende la responsabilità di farlo di propria iniziativa.

Nel secondo caso, le strane scritte sono un messaggio di errore: Python non è riuscito a interpretare quello che abbiamo scritto e quindi non ha definito la procedura.

Tranello! Se la procedura è stata scritta esattamente come riportato sopra, Python ci risponde con:

File ".../sorgenti/graph/man1.py", line 10
  for i in xrange(4)
                   ^
SyntaxError: invalid syntax

Questo messaggio ci dà le seguenti informazioni:

  • in quale file si trova l’errore: .../sorgenti/graph/man1.py
  • la linea dove ha incontrato un errore: line 10,
  • il punto in cui l’interprete si è bloccato,
  • il tipo di errore: SyntaxError: invalid syntax

Già, ci siamo dimenticati i ”:”, correggiamola:

def quadrato(lato):
    """Disegna un quadrato di dato lato."""
    for i in xrange(4):
        tina.forward(lato)
        tina.left(90)

Ora dobbiamo eseguire di nuovo il programma tasto <F5> o menu: Run - Run module. Se tutto è andato bene, non dovrebbe succedere niente. Spostiamoci nella shell di IDLE e diamo il comando:

>>> quadrato(57)

Se non ci sono altri errori dovremmo ottenere un quadrato. Possiamo provare il funzionamento della funzione quadrato(lato) con diversi valori dell’argomento. È anche possibile aggiungere questo comando in fondo al programma. In questo modo, ogni volta che si esegue il programma verrà creata una tartaruga e disegnato un quadrato. Tutto il nostro programma è ora:

#----------------------Python-pyturtle-----------------man1.py--#
#                                                               #
#                       Muro iterativo                          #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2005--#

from pyturtle import TurtlePlane
tp=TurtlePlane()
tina=tp.newTurtle()

def quadrato(lato):
    """ Disegna un quadrato di dato lato """
    for i in xrange(4):
        tina.forward(lato)
        tina.left(90)

quadrato(57)

Eseguiamolo, se tutto funziona correttamente appare la finestra della grafica della tartaruga con dentro un quadrato. Bene, abbiamo scritto, salvato e provato il nostro primo programma funzionante, possiamo rilassarci un po’ prima di procedere con il lavoro...

Uno strato di mattoni

Disegnare un intero muro di quadrati può sembrare un’impresa piuttosto complicata. L’intero muro può essere pensato come composto da più file e ogni fila da più quadrati:

**muro** ---(fatto da)---> **fila** ---(fatta da)---> **quadrato**

La funzione che disegna un quadrato l’abbiamo realizzata, proviamo allora a realizzare la funziona che realizza una fila di quadrati tutti uguali. Questa funzione avrà bisogno di due informazioni: la dimensione dei quadrati e il numero di quadrati da mettere in una fila. Cioè vorremmo poter scrivere:

>>> fila(20, 15)

per ottenere una fila di 15 quadrati di lato 20. Per il numero di volte che abbiamo deciso, la funzione fila deve disegnare un quadrato e spostare la tartaruga avanti di un tratto uguale al lato del quadrato. La funzione potrebbe essere:

def fila(lato, numero):
    """Disegna una fila di mattoni."""
    for j in xrange(numero):
        quadrato(lato)
        tina.forward(lato)

Aggiungiamo al programma la funzione fila(lato, numero), eseguiamo il programma e proviamo la funzione modificando gli argomenti in modo da realizzare file più o meno lunghe di quadrati più o meno grandi.

La fila di quadrati viene disegnata, ma, la procedura fila, non è “trasparente”: oltre a disegnare una fila di quadretti, sposta la tartaruga senza rimetterla dove l’aveva trovata. È molto importante che ogni funzione faccia solo quello che dice di fare, senza effetti collaterali. Quindi la funzione fila deve preoccuparsi di rimettere a posto Tartaruga. Di quanto l’ha spostata? Per numero volte è andata avanti di lato passi, in totale numero*lato. Per rimettere a posto Tartaruga bisogna, finito il ciclo, farla indietreggiare della stessa quantità:

def fila(lato, numero):
    """Disegna una fila di mattoni."""
    for j in xrange(numero):
        quadrato(lato)
        tina.forward(lato)
    tina.back(numero*lato)

La fila di quadrati che abbiamo ottenuto non richiama per niente dei mattoni, sono troppo appiccicati: un po’ di malta tra uno e l’altro? Per distanziarli un po’ basta allungare lo spostamento dopo ogni quadrato e ovviamente anche al ritorno:

def fila(lato, numero):
    """Disegna una fila di mattoni."""
    for j in xrange(numero):
        quadrato(lato)
        tina.forward(lato+5)
    tina.back(numero*(lato+5))

Funziona? Sì, ma non va bene. Esistono due tipi di errori quelli che bloccano il programma detti anche errori di sintassi e quelli che fanno fare al programma una cosa diversa da quella che volevamo, gli errori di semantica. Nel nostro caso la procedura funziona però non disegna quadrati staccati l’uno dall’altro, ma uniti da una linea non certo bella da vedere. Lo spostamento tra un quadrato e l’altro deve essere fatto senza lasciare segno:

def fila(lato, numero):
    """Disegna una fila di mattoni."""
    for j in xrange(numero):
        quadrato(lato)
        tina.up()
        tina.forward(lato+5)
        tina.down()
    tina.up()
    tina.back(numero*(lato+5))
    tina.down()

Ora va e la distanza tra due quadrati sembra abbastanza adeguata, ma se cambiamo il lato dei quadrati, andrà sempre bene? Possiamo parametrizzare anche quella, ma lo facciamo dandole come valore predefinito 5:

def fila(lato, numero, spazio_colonne=5):
    """Disegna una fila di mattoni."""
    for j in xrange(numero):
        quadrato(lato)
        tina.up()
        tina.forward(lato+spazio_colonne)
        tina.down()
    tina.up()
    tina.back(numero*(lato+spazio_colonne))
    tina.down()

In questo modo la funzione fila può essere chiamata con due o con tre argomenti:

  • Se viene chiamata con due argomenti, il terzo viene automaticamente posto uguale a 5,

  • Se viene chiamata con tre argomenti, il terzo parametro assumerà il terzo valore.

    >>> fila(30, 3)
    

Disegna una fila di 3 quadrati di lato 30 distanziati di 5 passi,

>>> fila(30, 3, 12)

Disegna una fila di 3 quadrati di lato 30 distanziati di 12 passi.

Salviamo, eseguiamo e proviamo la funzione fila provandola con 2 o con 3 parametri dando diversi valori ai parametri. A questo punto il nostro programma è il seguente:

#----------------------Python-pyturtle-----------------man1.py--#
#                                                               #
#                       Muro iterativo                          #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2005--#

from pyturtle import TurtlePlane
tp=TurtlePlane()
tina=tp.newTurtle()

def quadrato(lato):
    """Disegna un quadrato di dato lato."""
    for i in xrange(4):
        tina.forward(lato)
        tina.left(90)

def fila(lato, numero, spazio_colonne=5):
    """Disegna una fila di mattoni."""
    for j in xrange(numero):
        quadrato(lato)
        tina.up()
        tina.forward(lato+spazio_colonne)
        tina.down()
    tina.up()
    tina.back(numero*(lato+spazio_colonne))
    tina.down()

tina.up()
tina.back(200)
tina.down()
fila(20, 15)

L’intero muro

Possiamo essere soddisfatti del risultato ottenuto. Ora passiamo alla costruzione del muro. Cos’è un muro? È una pila di file di mattoni.

def muro():
    """Disegna un muro di quadrati."""
    for i in xrange(15):
        fila(20, 18,)
        tina.up()
        tina.left(90)
        tina.forward(20+5)
        tina.right(90)
        tina.down()
    tina.up()
    tina.left(90)
    tina.forward(15*(20+5))
    tina.right(90)
    tina.down()

Proviamola, va!

Ma c’è una certa differenza tra un programma che funziona e un buon programma. Se vogliamo imparare a programmare non dobbiamo accontentarci di un programma che funziona, dobbiamo affinare una certa sensibilità anche all’esaspetto estetico. Non dobbiamo affezionarci troppo al nostro prodotto, ma cercare di migliorarlo.

Ristrutturazione

Ci sono un paio di cose che stonano nella funzione muro:

  • tre righe si ripetono quasi identiche, e non va bene che in un programma si ripetano blocchi di istruzioni (quasi) identiche;
  • ci sono troppi numeri;

Partiamo dal primo problema: la soluzione è costruire una funzione che le esegua con un solo comando.

Le tre righe che vanno da tina.up() a tina.down() producono uno spostamento di Tartaruga senza disegnare e perpendicolare alla sua direzione. Possiamo generalizzare questo comportamento aggiungendo oltre allo spostamento verticale uno orizzontale. Possiamo costruire una funzione sposta che riceve come argomenti lo spostamento nella direzione della Tartaruga e lo spostamento nella sua direzione perpendicolare. Questa funzione dovrà avere quindi due parametri:

def sposta(avanti=0, sinistra=0):
    """Effettua uno spostamento orizzontale e verticale
       di Tartaruga senza disegnare la traccia."""
    tina.up()
    tina.forward(avanti)
    tina.left(90)
    tina.forward(sinistra)
    tina.right(90)
    tina.down()

Note

in queste funzioni ho utilizzato un terzo metodo per inserire un argomento in un parametro: il passaggio dell’argomento “per nome”.

Scrivendo i parametri in questo modo, possiamo chiamare la funzione sposta in vari modi:

  • sposta(), non fa niente;
  • sposta(47), sposta avanti Tartaruga di 47 unità senza tracciare segni;
  • sposta(47, 61), sposta Tartaruga avanti di 47 e a sinistra di 61 unità senza tracciare segni;
  • sposta(sinistra=61), sposta Tartaruga a sinistra di 61 unità senza tracciare segni;

Anche la funzione fila la può utilizzare:

def fila(lato, numero, spazio_colonne=5):
    """Disegna una fila di mattoni quadrati."""
    for j in xrange(numero):
        quadrato(lato)
        sposta(lato+spazio_colonne)
    sposta(-numero*(lato+spazio_colonne))

E muro diventa:

def muro():
    """Disegna un muro di quadrati."""
    for i in xrange(15):
        fila(20, 18,)
        sposta(sinistra=20+5)
    sposta(sinistra=-15*(20+5))

Confrontando la versione precedente di muro con questa si può notare una bella semplificazione!

Nella funzione fila, l’istruzione:

sposta(lato+spazio_colonne)

significa: chiama la funzione sposta associando al primo parametro il risultato del calcolo: lato+spazio_colonne.

Nella funzione muro, l’istruzione:

sposta(sinistra=20+5)

significa: chiama la funzione sposta associando al parametro di nome sinistra il risultato del calcolo: 20+5.

Ora occupiamoci di eliminare un po’ di numeri parametrizzando anche la procedura muro. I numeri presenti nella funzione riguardano: la lunghezza del lato, il numero di righe, il numero di colonne, lo spazio tra le righe, lo spazio tra le colonne. Questi due ultimi valori possono essere lasciati di default uguali a 5.

Trasformandoli tutti in parametri la funzione muro diventa:

def muro(lato, righe, colonne, spazio_colonne=5, spazio_righe=5):
    """Disegna un muro di mattoni."""
    for i in xrange(righe):
        fila(lato, colonne, spazio_colonne)
        sposta(sinistra=lato+spazio_righe)
    sposta(sinistra=-righe*(lato+spazio_righe))

E tutto il programma:

#----------------------Python-pyturtle-----------------man1.py--#
#                                                               #
#                       Muro iterativo                          #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2005--#

from pyturtle import TurtlePlane
tp=TurtlePlane()
tina=tp.newTurtle()

def sposta(o=0, v=0):
    """Effettua uno spostamento orizzontale e verticale di Tartaruga
       senza disegnare la traccia."""
    tina.up()
    tina.forward(o); tina.left(90); tina.forward(v); tina.right(90)
    tina.down()

def quadrato(lato):
    """Disegna un quadrato di dato lato vuoto o pieno."""
    for i in xrange(4):
        tina.forward(lato)
        tina.left(90)

def fila(lato, colonne, spazio_colonne=5):
    """Disegna una fila di mattoni quadrati."""
    for j in xrange(colonne):
        quadrato(lato)
        sposta(o=lato+spazio_colonne)
    sposta(o=-colonne*(lato+spazio_colonne))

def muro(lato, righe, colonne, spazio_righe=5, spazio_colonne=5):
    """Disegna un muro di mattoni."""
    for i in xrange(righe):
        fila(lato, colonne, spazio_colonne)
        sposta(v=lato+spazio_righe)
    sposta(v=-righe*(lato+spazio_righe))

sposta(-250, -190)
muro(20, 15, 20)

Compattiamo il codice

Funziona tutto a meraviglia, o almeno dovrebbe funzionare se non ho introdotto qualche errore! Potremmo considerarci soddisfatti se l’istinto del programmatore non rodesse dentro... Perdendo un po’ in chiarezza possiamo compattare di più il codice. Vale la pena? Dipende da ciò che si vuole ottenere, comunque, prima di dare un giudizio proviamo un’altra versione.

Prima avevamo staccato delle righe di codice per fare una funzione separata che realizzasse gli spostamenti data la componente orizzontale e verticale. Ora fondiamo due funzioni che hanno degli elementi in comune: la procedura muro e la procedura fila. L’intero muro si può ottenere annidando, uno dentro l’altro due cicli: il ciclo più esterno impila le file e quello più interno allinea quadrati. In pratica al posto della chiamata alla procedura fila(...), trascriviamo tutte le sue istruzioni. Dobbiamo anche aggiustare i nomi e l’indentazione:

def muro(lato, righe, colonne, spazio_colonne=5, spazio_righe=5):
    """Disegna un muro di mattoni."""
    for i in range(righe):
        for j in range(colonne):
            quadrato(lato)
            sposta(avanti=lato+spazio_colonne)
        sposta(avanti=-colonne*(lato+spazio_colonne))
        sposta(sinistra=lato+spazio_righe)
    sposta(sinistra=-righe*(lato+spazio_righe))

I due cicli annidati devono avere due variabili diverse (spesso si usano per queste variabili di ciclo i nomi i e j).

Possiamo ancora eliminare una riga di codice! Come?

Alla fine del ciclo più interno ci sono due chiamate alla funzione sposta. Possono essere fuse in un’unica chiamata. E con questo il programma è terminato...

#----------------------Python-pyturtle-----------------man1.py--#
#                                                               #
#                       Muro iterativo                          #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2005--#

from pyturtle import TurtlePlane
tp=TurtlePlane()
tina=tp.newTurtle()

def sposta(o=0, v=0):
    """Effettua uno spostamento orizzontale e verticale di Tartaruga
       senza disegnare la traccia."""
    tina.up()
    tina.forward(o); tina.left(90); tina.forward(v); tina.right(90)
    tina.down()

def quadrato(lato):
    """Disegna un quadrato di dato lato vuoto o pieno."""
    for i in xrange(4):
        tina.forward(lato)
        tina.left(90)

def muro(lato, righe, colonne, spazio_righe=5, spazio_colonne=5):
    for i in xrange(righe):
        for j in xrange(colonne):
            quadrato(lato)
            sposta(avanti=lato+spazio_colonne)
        sposta(-colonne*(lato+spazio_colonne), lato+spazio_righe)
    sposta(sinistra=-righe*(lato+spazio_righe))

sposta(-250, -190)
muro(20, 15, 20)

O quasi...

Riassumendo

  • Un programma è un documento di testo che contiene istruzioni e funzioni.
  • Scritto un programma bisogna salvarlo: menu: File Save ed eseguirlo: menu: Run - Run module.
  • Le funzioni definite in un programma possono essere eseguite anche dall’ambiente shell IDLE.
  • Quando è possibile è meglio sostituire i numeri e le costanti presenti in una funzione con parametri.
  • Le procedure possono avere anche dei parametri con dei valori predefiniti (di default).
  • Gli argomenti di una funzione possono essere passati anche per nome.
  • Più linee di istruzioni che si ripetono all’interno di un programma possono essere raggruppate in un’unica funzione.
  • All’interno di un ciclo possono essere annidati altri cicli, bisogna fare attenzione al nome delle variabili.

Una nuova classe

Dove definiamo la nostra prima classe

Classi e oggetti

Riassumiamo quello che avviene quando si esegue il programma primo.py:

  1. viene caricata dalla libreria pyturtle.py la classe TurtlePlane;
  2. viene creato un oggetto della classe TurtlePlane();
  3. viene creata una tartaruga;
  4. vengono definite tre funzioni (sposta(), quadrato(), muro());
  5. viene eseguito il programma principale che sposta la tartaruga e chiama la funzione muro che chiama le funzioni quadrato e sposta.

Nel programma precedente abbiamo già creato e utilizzato un oggetto. Ora poniamo l’attenzione sulla creazione di classi di oggetti. Elementi fondamentali di un Programma Orientato agli Oggetti (OOP) sono:

  1. classi
  2. attributi
  3. metodi
  4. oggetti

La libreria PyTurtle definisce la classe Turtle. Ogni oggetto della classe Turtle ha diverse caratteristiche proprie, sono i suoi attributi: il colore, la posizione, la direzione, lo spessore della penna, ...

E, ogni oggetto della classe Turtle è in grado di eseguire alcuni comandi sono i suoi metodi: forward(<numero>), back(<numero>), setpos((<numero>, <numero>)), getpos(), ...

Una classe può essere utilizzata per:

  1. costruire oggetti con le proprietà e i metodi di quella classe;
  2. costruire altre classi che ampliano quella classe.

Nel nostro primo programma abbiamo costruito un oggetto della classe TurtlePlane, di nome “tp” e abbiamo chiesto a “tp” con il metodo newTurtle``di creare un oggetto della classe ``Turtle di nome “tina”:

tp=TurtlePlane()
tina=tp.newTurtle()

Note

Per creare un oggetto basta scrivere il nome dell’oggetto seguito dal simbolo “=” e dal nome della classe con una coppia di parentesi. Bisogna prestare attenzione a due aspetti. Primo, se il nome della classe è “TurtlePlane” non si può sperare che il comando funzioni scrivendo “turtleplane” o “TURTLEPLANE”. Secondo, il nome della classe deve essere seguito da una coppia di parentesi; solo in questo modo tp diventa un oggetto della classe TurtlePlane, senza parentesi diventa un altro nome per la classe TurtlePlane.

Si può creare una nuova tartaruga anche direttamente, senza invocare un metodo di TurtlePlane, in questo caso però bisogna specificare a quale piano deve appartenere la nuova tartaruga:

tp=TurtlePlane()
tina=Turtle(plane=tp)

Note

È necessario specificare, al costruttore delle tartarughe, in quale piano vogliamo che venga creata infatti potremmo avere più piani aperti contemporaneamente.

I metodi sono delle funzioni che possono essere eseguite da tutti gli oggetti di quella classe. Per dire ad un oggetto di una classe di eseguire un suo metodo, devo scrivere il nome dell’oggetto seguito dal punto, dal nome del metodo e da una coppia di parentesi contenenti, eventualmente, gli argomenti necessari. In pratica se tina è un oggetto della classe Turtle l’istruzione:

tina.forward(97)

chiede all’oggetto collegato al nome tina di eseguire il suo metodo forward e 97 è l’argomento passato a questo metodo.

Quindi l’istruzione precedente comanda alla tartaruga tina di avanzare di 97 passi.

tp.reset()

comanda a tp di ripulire tutto il piano e di riportare la situazione allo stato iniziale. Perché ogni metodo deve essere preceduto dal nome dell’oggetto? Non sarebbe più semplice scrivere solo: forward(97) o reset()? Il fatto è che possiamo creare quanti oggetti vogliamo della classe Turtle quindi quando diamo un comando, dobbiamo specificare a quale oggetto quel comando è diretto:

tp=TurtlePlane()
tina=Turtle(plane=tp)
pina=Turtle(plane=tp)
gina=Turtle(plane=tp)
pina.left(120)
gina.left(240)
tina.forward(50)
pina.forward(100)
gina.forward(200)

In questo caso vengono create tre tartarughe, vengono sfasate di 120 gradi l’una dall’altra e infine vengono fatte avanzare di tre lunghezze diverse.

Gli attributi definiscono lo stato di un oggetto. Ad esempio, per cambiare il colore della tartaruga e della sua penna si può usare il comando: setcolor(<colore>) dove <colore> è una stringa che contiene il nome di un colore, o setcolor(<rosso>, <verde>, <blu>) dove <rosso>, <verde>, <blu> sono tre numeri decimali compresi tra 0 e 1:

tina.setcolor("purple")

Viceversa se voglio memorizzare in una variabile l’attuale colore di una tartaruga potrò chiamare il metodo getcolor():

colore_attuale_di_tina=tina.getcolor()

Nuove classi

Una caratteristica importante delle classi è l’ereditarietà. Una classe può venir derivata da un’altra classe essere cioè figlia di un’altra classe; la classe figlia eredita dalla classe genitrice tutte gli attributi e i metodi e può:

  • aggiungere altri attributi,
  • aggiungere altri metodi,
  • modificare i metodi della classe genitrice.

Buona parte della programmazione OOP consiste nel progettare e realizzare classi e gerarchie di classi di oggetti. Realizzare una nuova classe può essere un lavoro molto complicato ma potrebbe essere anche molto semplice quando la classe che realizziamo estende qualche altra classe già funzionante.

Come esercizio proviamo a costruire la classe di una tartaruga che sappia costruire un muro. Come al solito, prima di mettere mano a grandi opere, iniziamo a lavorare ad un problema abbastanza semplice e conosciuto. Voglio avere una tartaruga che oltre a saper fare tutto quello che sanno fare le altre tartarughe sappia anche disegnare mattoni quadrati: la nuova tartaruga deve quindi estendere le capacità di Turtle. La sintassi che Python mette a disposizione per estendere la gerarchia di una classe è:

class <nome di una nuova classe>(<nome di una classe esistente>):

In pratica noi possiamo creare una nuova specie di Turtle in questo modo:

>>> from pyturtle import TurtlePlane, Turtle  # importa la libreria
>>> class Ingegnere(Turtle):                  # crea una nuova classe
         pass                                 # non fa niente

>>> tp=TurtlePlane()
>>> leonardo=Ingegnere(plane=tp)  # crea un oggetto della nuova classe
>>> leonardo.forward(100)         # esegue un metodo della nuova classe

La prima riga importa dalla libreria pyturtle le classi TurtlePlane e Turtle. La seconda e terza definiscono una nuova classe che si chiama Ingegnere e che non ha e non fa niente in più di Turtle. Le tre righe seguenti: creano un piano associato alla parola “tp” e creano un oggetto della classe Ingegnere` legato alla parola “leonardo”.

L’ultima linea comanda a leonardo di eseguire un metodo di Ingegnere. Ma come può farlo, se non abbiamo definito nessun metodo di Ingegnere? Non importa, Ingegnere è un discendente di Turtle e quindi, già alla nascita, sa fare tutto quello che sa fare Turtle.

Ora estendiamo le capacità di Ingegnere in modo che sappia disegnare mattoni quadrati.

Sempre nella shell di IDLE proviamo ad aggiungere alla classe Ingegnere il metodo quadrato() nel modo più intuitivo possibile:

>>> class Ingegnere(Turtle):
        def quadrato(lato):
            for i in range(4):
                forward(lato)
                left(90)

Python non dà errori di sintassi: è già un buon segno! Ora creiamo un oggetto di tipo Ingegnere:

>>> tp=TurtlePlane()
>>> leonardo=Ingegnere(plane=tp)

E anche qui tutto bene!! Ora comandiamo a leonardo di disegnare un quadrato:

>>> leonardo.quadrato(80)
Traceback (most recent call last):
  File "<pyshell#18>", line 1, in ?
    leonardo.quadrato(80)
TypeError: quadrato() takes exactly 1 argument (2 given)

Accidenti, non va!!! E non solo non funziona, ma ci dà un errore decisamente assurdo: Python si lamenta che quadrato vuole un argomento e noi gliene avremmo passati due!? È strabico? Non sa contare?? È stupido??? Boh, mah, forse... Chi ha programmato questo linguaggio ha deciso che l’oggetto che deve eseguire un metodo viene passato come primo parametro del metodo stesso. Noi scriviamo:

leonardo.quadrato(80)

in realtà viene eseguito qualcosa che assomiglia a:

quadrato(leonardo, 80)

Quindi se quadrato è un metodo di una classe deve avere un primo parametro dentro il quale viene messo il riferimento all’oggetto che deve eseguire il metodo stesso. Riscriviamo la classe e il metodo:

>>> class Ingegnere(Turtle):
        def quadrato(self, lato):
            for i in range(4):
                forward(lato)
                left(90)

Ora quadrato ha i due parametri: uno per contenere l’oggetto che deve eseguire il metodo e uno per contenere la lunghezza del lato. Da notare che il primo parametro potrebbe avere qualunque nome, ma è uso comune chiamarlo self (e conviene attenersi a questo uso). Ora creiamo un oggetto di questa nuova classe e proviamo il metodo:

>>> leonardo=Ingegnere(plane=tp)
>>> leonardo.quadrato(80)
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in ?
    leonardo.quadrato(80)
  File "<pyshell#24>", line 4, in quadrato
    forward(lato)
NameError: global name 'forward' is not defined

Ancora qualcosa che non va... Eppure questa volta quadrato ha i due parametri richiesti! Infatti l’errore è cambiato: ci dice che non esiste un nome globale forward. Infatti forward è un metodo della classe Turtle e quindi può essere eseguito solo da un oggetto di questa classe. Ma dove lo trovo un oggetto della classe Turtle mentre sto definendo la mia nuova classe? Se osserviamo bene, proprio la soluzione al precedente errore ce l’ha messo a disposizione, è proprio l’oggetto contenuto in self. Dobbiamo scrivere: self.forward(lato). Terzo tentativo:

>>> class Ingegnere(Turtle):
        def quadrato(self, lato):
            for i in range(4):
                self.forward(lato)
                self.left(90)

Ovviamente quello che facciamo per forward lo dobbiamo fare anche per left. Ora creiamo l’ingegnere e proviamo ancora il nuovo metodo ricorretto:

>>> leonardo=Ingegnere(plane=tp)
>>> leonardo.quadrato(80)

Va!!! Ora abbiamo una classe Ingegnere che oltre a saper fare tutto quello che sanno fare tutte le Tartarughe sa anche disegnare quadrati. Fissiamo il traguardo raggiunto scrivendo la nuova classe nel file “man2.py”:

#----------------------python-pyturtle-----------------man2.py--#
#                                                               #
#                  Muro fatto da Ingegnere                      #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2009--#

from pyturtle import TurtlePlane, Turtle

class Ingegnere(Turtle):

    def quadrato(self, lato):
        """Disegna un quadrato di dato lato."""
        for i in xrange(4):
            self.forward(lato)
            self.left(90)

tp=TurtlePlane()
leonardo=Ingegnere(plane=tp)
leonardo.quadrato(20)

Salviamo, eseguiamo, ... correggiamo gli errori che inevitabilmente sono stati fatti, ..., rieseguiamo...

Ora possiamo prendere le altre funzioni del programma man1.py e trasformarle in metodi aggiungendo il parametro self dove serve:

#----------------------python-pyturtle-----------------man2.py--#
#                                                               #
#                  Muro fatto da Ingegnere                      #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2005--#

from pyturtle import TurtlePlane, Turtle

class Ingegnere(Turtle):

    def sposta(self, o=0, v=0):
        """Effettua uno spostamento orizzontale e verticale di
        Tartaruga senza disegnare la traccia."""
        self.up()
        self.forward(o); self.left(90)
        self.forward(v); self.right(90)
        self.down()

    def quadrato(self, lato):
        """Disegna un quadrato di dato lato."""
        for i in xrange(4):
            self.forward(lato)
            self.left(90)

    def muro(self, lato, righe, colonne,
             spazio_righe=5, spazio_colonne=5):
        """Disegna un muro di mattoni quadrati."""
        for i in xrange(righe):
            for j in xrange(colonne):
                self.quadrato(lato)
                self.sposta(o=lato+spazio_colonne)
            self.sposta(o=-colonne*(lato+spazio_colonne),
                        v=lato+spazio_righe)
        self.sposta(v=-righe*(lato+spazio_righe))

tp=TurtlePlane()
leonardo=Ingegnere(plane=tp)
leonardo.sposta(-250, -190)
leonardo.muro(20, 15, 20)
tp.mainloop()

A parte la complicazione del parametro self, possiamo vedere come Python ci permetta di definire nuove classi in modo estremamente semplice. Utilizzando l’ereditarietà, cioè scrivendo classi derivate da altre già realizzate da altri, possiamo ottenere, con poche righe di programma classi:

  • potenti, perché estendono altre classi,
  • sicure, perché le classi genitrici sono utilizzate da molti altri programmatori,
  • ulteriormente estendibili, ma questo lo vedremo nel prossimo capitolo.

Riassumendo

  • Per definire una classe si utilizza il comando class <nome della classe>()

  • Per definire una classe discendente da un’altra si utilizza il comando:

    class <nome della classe figlia>(<nome della classe genitrice>):
    
  • Quando si scrive una classe discendente da un’altra basta scrivere i metodi che si aggiungono ai metodi della classe genitrice o che li sostituiscono.

  • Quando si definisce un metodo di una classe si deve mettere come primo parametro il nome di una variabile che conterrà l’oggetto stesso, di solito self.

  • All’interno di una classe, il parametro self permette di richiamare i metodi e le proprietà dell’oggetto stesso.

Come modificare il comportamento di una classe

Dove rompiamo un po’ gli schemi e costruiamo un metodo che oscura un metodo del genitore.

Estendere una classe: spostamento casuale

Nel capitolo precedente abbiamo realizzato un muro di quadrati. Tutti in ordine ben allineati, tutti uguali... Un po’ troppo in ordine, un po’ troppo uguali... Proviamo a mettere un po’ di disordine nello schema. Invece che disegnare un quadrato con il primo lato in direzione della tartaruga, possiamo fare in maniera che il quadrato sia spostato casualmente. Sorge subito un problema: Python non ha un comando per ottenere dei valori casuali. Niente paura, c’è una libreria che ci fornisce la funzione adatta. La libreria è random e la funzione che ci interessa è randrange(<numero>) che restituisce un numero intero compreso tra zero incluso e <numero> escluso.

Possiamo ripensare la procedura quadrato in questo modo:

definisco quadrato(lato) così:
  metto nella variabile angolo un numero casuale tra 0 e 30
  metto nella variabile spostamento un numero casuale tra 0 e 30
  ruoto tartaruga di angolo e la sposto di spostamento
  disegno il quadrato
  rimetto a posto tartaruga

Ora se utilizzassimo la programmazione classica dovremmo prendere il programma scritto precedentemente e modificare la procedura quadrato. La programmazione ad oggetti ci permette un meccanismo diverso:

  • si crea una classe discendente della classe Ingegnere,
  • si modifica il metodo quadrato(...),

La nuova classe così costruita possiede tutte le caratteristiche della vecchia classe ma con il metodo quadrato diverso. Proviamola:

#----------------------python-pyturtle-----------------man3.py--#
#                                                               #
#                         Terremoto                             #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2009--#

from man2 import TurtlePlane, Ingegnere
from random import randrange

class Architetto(Ingegnere):
  def quadrato(self, lato):
    """Disegna un mattone spostato rispetto alla posizione
    attuale di Tartaruga."""
    angolo=randrange(30)
    spostamento=randrange(30)
    self.up()
    self.right(angolo); self.forward(spostamento)
    self.down()
    Ingegnere.quadrato(self, lato)
    self.up()
    self.back(spostamento); self.left(angolo)
    self.down()

tp=TurtlePlane()
michelangelo=Architetto(plane=tp)
michelangelo.sposta(-250, -180)
michelangelo.muro(20, 15, 20)
tp.mainloop()

Funziona e non funziona. Funziona perché abbiamo ottenuto i quadrati scombinati, come volevamo. Ma perché in un’altra finestra vengono disegnati anche i quadrati perfettamente schierati? Il fatto è che la prima volta che viene letta la libreria man2.py, questa viene anche eseguita, quindi, in particolare, vengono eseguite le sue ultime tre righe che producono il disegno dei quadrati tutti diritti. Se proviamo ad eseguire un’altra volta man3.py verranno disegnati solo i quadrati scombinati, perché la libreria, già letta in questa sessione di lavoro, non viene riletta. Python mette a disposizione gli strumenti (semplici) per evitare questo meccanismo, ma la loro comprensione va al di là dei nostri scopi. Quindi non preoccupiamoci di questo strano comportamento.

L’effetto ottenuto pare abbastanza naturale, ma cosa avviene nell’interprete? michelangelo è un oggetto della classe Architetto. Il comando michelangelo.sposta(-250, -180) chiama il metodo sposta di Architetto il quale chiama il metodo forward di Turtle... michelangelo.muro(20, 15, 20) chiama il metodo muro di Ingegnere e questo chiama il metodo quadrato di Architetto e il metodo sposta di Ingegnere. Questo comportamento non è semplice per il computer, ma appare naturale per il programmatore. Questo meccanismo permette di estendere a piacere librerie senza doverle modificare. In questo modo si possono realizzare librerie stabili, solide perché condivise e provate da molti utilizzatori, ma adattabili alle proprie esigenze.

Estendere una classe: aggiungiamo i colori

E se mi fossi stancato del bianco e nero e volessi dei mattoni colorati? Probabilmente la cosa più semplice potrebbe essere quella di modificare il metodo quadrato, ma proviamo a utilizzare ancora l’ereditarietà. Chiudiamo questo programma e apriamo una nuova finestra di editor dove definiamo una nuova classe, chiamiamola Artista, discendente da Architetto. Questa classe dovrà ancora modificare il metodo quadrato. Dovrà:

  • scegliere un colore,
  • attivare il comando di riempimento
  • disegnare il quadrato,
  • riempirlo con il colore scelto

Il metodo setcolor(...) di Tartaruga accetta diversi tipi di argomenti: Ci sono vari modi per definire il colore di una tartaruga:

setcolor(<nome di un colore>)

ad esempio:

tina.setcolor('pink')

oppure:

setcolor(<red>, <green>, <blue>)

dove <red>, <green>, e <blue> sono dei numeri razionali compresi tra 0 e 1 che rappresentano l’intensità dei tre colori fondamentali rosso, verde e blu. Ad esempio:

tina.setcolor(0.5, 0, 0.5)

oppure:

tina.setcolor(<stringacolore>)

dove <stringacolore> è una stringa nella forma: “#RRGGBB” dove RR, GG, BB sono tre numeri esadecimali che rappresentano le componenti rossa verde e blu del colore:

tina.setcolor("#f257a0")

Noi utilizziamo la seconda forma in modo da prendere a caso le componenti dei colori in ogni quadrato.

Per riempire di un colore una figura si usa il metodo fill(0|1). Quando viene chiamato Il metodo fill(1), Tartaruga tiene nota delle parti da riempire, quando viene chiamato fill(0), Tartaruga le riempie. Quindi, se si vuole colorare una figura, si deve chiamare il metodo fill con l’argomento uguale a 1 prima di iniziare a disegnarla e fill con l’argomento uguale a 0 alla fine. A questo punto la figura viene riempita con il colore attuale di Tartaruga.:

#----------------------python-pyturtle-----------------man4.py--#
#                                                               #
#                     Terremoto a colori                        #
#                                                               #
#--Daniele Zambelli------License: GPL---------------------2009--#

from man3 import TurtlePlane, Architetto
from random import random
#from random import randrange

class Artista(Architetto):

  def quadrato(self, lato):
    """Disegna un mattone colorato."""
    self.fill(1)
    self.setcolor(random(), random(), random())
    Architetto.quadrato(self, lato)
    self.fill(0)

tp=TurtlePlane()
raffaello=Artista(plane=tp)
raffaello.sposta(-250, -180)
raffaello.muro(20, 15, 20)
tp.mainloop()

Riassumendo

  • Una classe discendente di un’altra, non solo può estendere i metodi di quest’ultima, ma può anche modificare il suo comportamento ridefinendo alcuni suoi metodi.

  • Per ridefinire un metodo basta definirne uno con lo stesso nome.

  • È l’interprete che si incarica di eseguire i metodi corretti tra tutti quelli che hanno lo stesso nome.

  • Una classe può forzare l’interprete ad eseguire un metodo di una sua classe antenata anteponendo al nome del metodo il nome della classe. Ad esempio il metodo quadrato di Artista chiama il metodo quadrato della classe Architetto in questo modo:

    Architetto.quadrato(self, lato)
    
  • Una classe discendente può quindi aggiungere metodi alla classe genitrice oppure sostituirne alcuni o modificarne il comportamento.

La ricorsione

Dove parliamo di procedure che si comportano come gatti che inseguono la loro coda

Definizioni ricorsive

Abbiamo visto che una funzione, una volta definita, può essere utilizzata al pari di ogni altro comando primitivo. È anche possibile che una funzione chiami sé stessa. In questo caso si dirà che la funzione è ricorsiva.

Nella matematica esistono diversi esempi di definizioni ricorsive ad esempio nel caso di potenze ad esponente naturale si può dire che:

a^n=
  \left\{
    \begin{matrix}
      1 & \mbox{se }n=0 \\
      a \cdot a^{n-1}, & \mbox{se }n>0
    \end{matrix}
  \right.

Un altro classico esempio di ricorsione è la ricerca di una parola su un dizionario:

per la ricerca di una parola:
  apro a caso e leggo una parola a caso,
  se è la parola cercata:
    ho finito,
  se la parola che cerco viene prima di quella letta:
    eseguo la ricerca della parola a sinistra di quella letta
  altrimenti:
    eseguo la ricerca della parola a destra di quella letta.

Condizione di terminazione

Vediamo un altro esempio di definizione ricorsiva:

la scalinata di Giacobbe è:
  Un gradino seguito da una scalinata di Giacobbe

Quanti gradini ha? ...

E se volessimo una scalinata un po’ meno impegnativa?:

Una scalinata di enne gradini è:
  uno scalino seguito da una scalinata di enne-1 gradini.

Oppure in una forma direttamente traducibile in un linguaggio di programmazione:

una scalinata di enne gradini è:
  se enne è uguale a 0: (la scalinata è) finita
  (altrimenti è) un gradino seguito da
  una scalinata di enne-1 gradini

Quest’ultima funzione presenta nella prima riga la verifica di una condizione. È la condizione di terminazione ed è importantissima per rendere funzionante la procedura, senza di questa la procedura continua all’infinito, o meglio finché non termina lo spazio disponibile nella memoria RAM e termina quindi con un errore.

Ricorsione non terminale

Negli esempi precedenti la chiamata ricorsiva è l’ultima istruzione eseguita della funzione. Queste sono dette funzioni ricorsive terminali e possono essere tradotte facilmente in un ciclo.

Ci sono anche funzioni che prevedono dei comandi da eseguire dopo la chiamata ricorsiva, in questo caso funzioni molto sintetiche possono presentare un comportamento molto complesso. Vediamo due esempi di funzioni di questo tipo. Per il primo realizziamo un’astrazione a partire da un elemento naturale: un albero.

Un albero è formato da un tronco seguito da alcuni rami. Se seghiamo un ramo e lo raddrizziamo possiamo notare che assomiglia molto ad un albero: ha un primo segmento, una specie di tronco, seguito da alcuni rami, ognuno di questi rami assomiglia a sua volta ad un albero. Potremmo dire che un albero è un tronco con sopra alcuni alberi. Decidiamo una condizione di terminazione: se l’albero è più piccolo di 2 allora è fatto. Decidiamo anche che nel nostro albero da tutte biforcazioni partono due rami uguali e che l’angolo tra questi due rami è di 90 gradi. La funzione in un linguaggio intermedio potrebbe essere:

un albero di una certa lunghezza è:
  se la lunghezza è minore di 2: finito... altrimenti:
  disegno il tronco con quella lunghezza,
  giro a sinistra di 45 gradi
  disegno un albero di metà lunghezza,
  giro a destra di 90 gradi
  disegno un albero di metà lunghezza,
  rimetto a posto tartaruga

Da notare che l’ultima riga è fondamentale: alla fine del disegno di un albero (di ogni sottoalbero) tartaruga deve essere riportata nella posizione iniziale.

La traduzione in Python diventa:

from pyturtle import TurtlePlane

def albero(lung):
    if lung<2: return
    t.forward(lung)
    t.left(45)
    albero(lung/2)
    t.right(90)
    albero(lung/2)
    t.left(45)
    t.back(lung)

tp=TurtlePlane()
t=tp.newTurtle()
t.left(90)
t.up()
t.back(100)
t.down()
albero(100)

Ogni albero ha un caratteristico angolo tra i rami, possiamo aggiungere questo parametro alla nostra funzione:

from pyturtle import TurtlePlane

def alberobin(lung, angolo):
    if lung<2: return
    t.forward(lung)
    t.left(angolo)
    alberobin(lung/2, angolo)
    t.right(2*angolo)
    alberobin(lung/2, angolo)
    t.left(angolo)
    t.back(lung)

tp=TurtlePlane()
t=tp.newTurtle()
t.left(90)
t.up(); t.back(100); t.down()
albero(100, 60)

E possiamo così disegnare alberi di specie diverse. Un albero ha anche la caratteristica di avere lo spessore dei rami che diminuisce man mano che ci si allontana dalle radici. Possiamo utilizzare il comando setwidth(spessore) per rendere più realistici i nostri alberi:

from pyturtle import TurtlePlane

def alberobin(lung, angolo):
    if lung<2: return
    t.setwidth(lung/5)
    t.forward(lung)
    t.left(angolo)
    alberobin(lung/2, angolo)
    t.right(2*angolo)
    alberobin(lung/2, angolo)
    t.left(angolo)
    t.back(lung)

tp=TurtlePlane()
t=tp.newTurtle()
t.left(90)
t.up()
t.back(100)
t.down()
alberobin(100, 60)

Diversi alberi

Nello scrivere la funzione albero sono state fatte molte scelte che rendono piuttosto rigida la funzione stessa, possiamo parametrizzare diversi elementi:

  • il rapporto tra lunghezza e larghezza dei rami,
  • il decremento del sottoalbero di sinistra,
  • che può essere diverso dal decremento del sottoalbero di destra.

Ovviamente non basta aggiungere i parametri nell’intestazione della funzione, ma bisogna anche che tutte le chiamate ricorsive abbiano il giusto numero di parametri:

from pyturtle import TurtlePlane

def alberobin(lung, angolo, larg, decsx, decdx):
    if lung<2: return
    t.setwidth(lung*larg)
    t.forward(lung)
    t.left(angolo)
    alberobin(lung*decsx, angolo, larg, decsx, decdx)
    t.right(2*angolo)
    alberobin(lung*decdx, angolo, larg, decsx, decdx)
    t.left(angolo)
    t.back(lung)

tp=TurtlePlane()
t=tp.newTurtle()
t.left(90)
t.up()
t.back(100)
t.down()
alberobin(100, 60, 0.1, 0.8, 0.6)

Modificando i parametri possiamo disegnare alberi di un gran numero di specie diverse... I disegni ottenuti risultano un po’ innaturali. Nel mondo reale molte cause provocano delle differenze di accrescimento dei vari rami, possiamo aggiungere un elemento di casualità alla crescita dei vari rami, una variazione casuale rispetto alla lunghezza passata come parametro.

from pyturtle import TurtlePlane
from random import randrange

def alberobincas(lung, angolo, larg, decsx, decdx, caos):
    if lung<2: return
    var=int(lung*caos)+1
    l=lung-var+randrange(2*var)
    t.setwidth(lung*larg)
    t.forward(lung)
    t.left(angolo)
    alberobincas(l*decsx, angolo, larg, decsx, decdx, caos)
    t.right(2*angolo)
    alberobincas(l*decdx, angolo, larg, decsx, decdx, caos)
    t.left(angolo)
    t.back(lung)

tp=TurtlePlane()
t=tp.newTurtle()
t.left(90)
t.up()
t.back(100)
t.down()
alberobincas(100, 60, 0.1, 0.8, 0.6, 0.3)

Degli altri alberi possono essere realizzati a partire da alberi ternari:

def alberoter(lung, angolo):
    if lung<2: return
    t.setwidth(lung/5)
    t.forward(lung)
    t.left(angolo)
    alberoter(lung/2, angolo)
    t.right(angolo)
    alberoter(lung/2, angolo)
    t.right(angolo)
    alberoter(lung/2, angolo)
    t.left(angolo)
    t.back(lung)

L’approfondimento è lasciato a chi è interessato.

Il fiocco di neve

Negli alberi la funzione è trasparente, non modifica la posizione di Tartaruga: Tartaruga viene riportata dove era stata presa. C’è un’altra famiglia di linee ricorsive che si comportano come segmenti: prendono Tartaruga in un posto e la spostano in un altro. Un famoso esempio di queste linee è la curva di Koch:

livello 0: ______________

                 /\
livello 1:      /  \
           ____/    \____

               __/\__
livello 2:     \    /
           _/\_/    \_/\_

A livello zero la curva di Koch è un segmento, ad ogni aumento di livello ogni segmento è sostituito da un segmento con la stessa lunghezza e con una protuberanza al centro.

from pyturtle import TurtlePlane

def koch(lung, liv):
    if liv==0: t.forward(lung); return
    koch(lung/3.0, liv-1)
    t.left(60)
    koch(lung/3.0, liv-1)
    t.right(120)
    koch(lung/3.0, liv-1)
    t.left(60)
    koch(lung/3.0, liv-1)

fron pyturtle import *

tp=TurtlePlane()
t=tp.newTurtle()
t.up()
t.back(100)
t.down()
koch(200, 4)

Comportandosi come un segmento, possiamo utilizzare koch per realizzare un poligono, un triangolo con lato frattale:

from pyturtle import TurtlePlane

def fiocco(lato, liv):
    for i in range(3):
        koch(lato, liv)
        t.right(120)

fron pyturtle import *

tp=TurtlePlane()
t=tp.newTurtle()
t.up()
t.back(100)
t.down()
fiocco(200, 4)

Molto regolare, ma se volessi introdurre un elemento di casualità? Ad esempio potremmo produrre la protuberanza a destra o a sinistra rispetto Tartaruga. E con questo realizzare un fiocco casuale.

from pyturtle import TurtlePlane

def kochcas(lung, liv):
    if liv==0: t.forward(lung); return
    verso=randrange(-1, 2, 2)
    kochcas(lung/3.0, liv-1)
    t.left(60*verso)
    koch(lung/3.0, liv-1)
    t.right(120*verso)
    kochcas(lung/3.0, liv-1)
    t.left(60*verso)
    koch(lung/3.0, liv-1)

def fioccocas(lato, liv):
    for i in xrange(3):
        kochcas(lato, liv)
        t.right(120)

fron pyturtle import *
from random import randrange

tp=TurtlePlane()
t=tp.newTurtle()
t.up()
t.back(100)
t.down()
fioccocas(200, 4)

Riassumendo

  • Una funzione si dice ricorsiva quando chiama sé stessa.
  • Una funzione ricorsiva, perché sia utilizzabile all’interno di un programma, deve avere una condizione di terminazione, di solito posta all’inizio.
  • Una funzione ricorsiva si dice terminale se la chiamata ricorsiva non è seguita da altre istruzioni.
  • Le funzioni ricorsive terminali possono essere sostituite facilmente da cicli.
  • Le funzioni ricorsive non terminali hanno un comportamento che può apparire sorprendente.
  • Le funzioni ricorsive avvicinano i linguaggi procedurali a quelli dichiarativi. Infatti la domanda che si pone il programmatore per realizzare una funzione ricorsiva non è: “come si fa a fare...?”, ma: “cosa è ...?”.

Pycart

Dove viene presentata la libreria Pycart

Introduzione

Pycart è una libreria che implementa la classe Piano cartesiano con alcuni metodi che permettono di:

  • Modificare alcuni parametri come la posizione dell’origine e la scala di visualizzazione.
  • Effettuare le trasformazioni di coordinate necessarie.
  • Disegnare gli assi.

Pycart si appoggia su alcune altre librerie:

  • Tkinter per l’output grafico,
  • os per eseguire un programma di conversione del formato grafico nel metodo save(self, filename),
  • colors per la gestione dei colori,
  • pygrapherror per i messaggi di errore

Nella libreria sono presenti:

  • la funzione version, che riporta la versione;
  • la classe Plane, che implementa un piano cartesiano;
  • la classe Pen, che implementa un tracciatore grafico.

Di seguito vengonno descritti questi tre oggetti e per ognuno verrà indicato:

  • lo scopo
  • la sintassi
  • alcune osservazioni
  • un esempio di uso

class Plane

Plane contiene alcuni attributi e metodi che permettono di:

  • cambiare la posizione dell’origine rispetto alla finestra grafica,
  • cambiare la scala di rappresentazione,
  • tracciare gli assi o una griglia di punti,
  • modificare il colore e lo spessore di un puntatore grafico,
  • tracciare alcune semplici figure,
  • salvare il grafico in un file,
  • ...

Introduzione

Pycart è una libreria che permette di eseguire alcune operazioni di base su una finestra che implementa un piano cartesiano.

Pycart si appoggia su alcune altre librerie:

  • Tkinter per l’output grafico,
  • os per eseguire un programma di conversione del formato grafico nel metodo save(self, filename),
  • re per le espressioni regolari,
  • pygrapherror per i messaggi di errore

Nella libreria sono presenti:

  • Una funzione che restituisce il numero di versione corrente.
  • La classe PlotPlane con alcuni metodi che permettono di gestire gli elementi base di un piano cartesiano.
  • La classe Plot con alcuni metodi che permettono di tracciare il grafico di successioni e di funzioni matematiche.

Di seguito vengonno riportati gli attributi e i metodi delle due classi. Per ognuno verrà indicato:

  • lo scopo
  • la sintassi
  • alcune osservazioni
  • un esempio di uso

version

Scopo

È una funzione che restituisce il numero di versione della libreria.

Sintassi

version()

Osservazioni

Non richiede parametri, restituisce la stringa che contiene la versione.

Esempio

Controllare la versione della libreria.

import pycart
if pycart.version()<"02.05.00":
    print "versione un po' vecchiotta"
else:
    print "versione:", pycart.version()

class Plane

Plane contiene alcuni attributi e metodi che permettono di:

  • cambiare la posizione dell’origine rispetto alla finestra grafica,
  • cambiare la scala di rappresentazione,
  • tracciare gli assi o una griglia di punti,

Per poter creare oggetti della classi Plane bisogna importarla dalla libreria. In tutti gli esempi seguenti si suppone che sia già stato eseguito il comando:

from pycart import Plane

Se nell’esegure un esempio ottenete un messaggio che termina con: NameError: name 'Plane' is not defined, forse avete dimenticato di caricare Plane dalla libreria con il comando scritto sopra.

Se nell’eseguire gli esempi osservate dei comportamenti strani della finestra che contiene il piano cartesiano, portate pazienza, o guardate quanto scritto nella descrizione del metodo mainloop di Plane più avanti dove ho cercato di dare delle indicazioni in proposito.

Note

Chi è curioso e va a guardare il sorgente della libreria, scoprirà che ci sono alcuni attributi e metodi che non vengono descritti in questo manuale. Il loro nome inizia con il carattere “_” e sono privati. L’utente non dovrebbe aver bisogno di usarli.

origin

Scopo

Permette di definire la posizione dell’origine degli assi all’interno della finestra grafica.

Sintassi

<piano>.origin = <coppia di numeri>

Osservazioni

Il valore predefinito è al centro della finestra.

Esempio

Crea un piano, sposta l’origine a sinistra, disegna gli assi.

p = Plane('ex_01')
p.origin = (10, 300)
p.axes()

scale

Scopo

Definisce la scala di rappresentazione sui due assi.

Sintassi

<piano>.scale = <coppia di numeri>

Osservazioni

I due valori indicano rispettivamente la scala relativa all’asse x e all’asse y. I due valori sono numeri naturali che indicano il numero di pixel corrispondenti all’unità di misura.

Esempio

Crea un piano, modifica la scala e disegna gli assi e la griglia.

p = Plane('ex_02')
p.scale = (30, 20)
p.axes(color='red')
p.grid(color='red')

__init__

Scopo

Crea il piano cartesiano e inizializza gli attributi di Plane.

Sintassi

<nome_variabile>=Plane(<parametri>)

Osservazioni

Questo metodo non viene chiamato esplicitamente, ma viene eseguito quando si crea un oggetto di questa classe. L’intestazione di questo metodo è:

def __init__(self, name="Cartesian Plane",
             w=600, h=400,
             sx=20, sy=None,
             ox=None, oy=None,
             axes=True, grid=True,
             axescolor='black', gridcolor='black',
             parent=None):

Si può vedere che presenta molti parametri tutti con un valore predefinito. Nel momento in cui si crea un piano cartesiano si possono quindi decidere le sue caratteristiche. Vediamole in dettaglio:

  • titolo della finestra, valore predefinito: “Cartesian Plane”;
  • dimensione, valori predefiniti: larghezza=600, altezza=400;
  • scala di rappresentazione, valori predefiniti: una unità = un pixel;
  • posizione dell’origine, valore predefinito: il centro della finestra;
  • rappresentazione degli assi cartesiani, valore predefinito: False;
  • rappresentazione di una griglia di punti, valore predefinito: False;
  • colore degli assi valore predefinito: ‘black’.
  • colore della griglia valore predefinito: lo stesso degli assi.
  • riferimento alla finestra che contiene il piano cartesiano, valore predefinito: None.

Poiché tutti i parametri hanno un valore predefinito, possiamo creare un oggetto della classe Plane senza specificare alcun argomento: verranno usati tutti i valori predefiniti. Oppure possiamo specificare per nome gli argomenti che vogliamo siano diversi dal comportamento predefinito, si vedano di seguito alcuni esempi.

Esempio

Crea 3 piani cartesiani, il primo: senza assi con tutti i valori predefiniti, il secondo: quadrato, con un titolo e con gli assi, il terzo: con un titolo, con le dimensioni di 600 per 200, con la scala di 20 punti per unità, con assi e griglia colorati:

p0 = Plane('ex_03: primo piano')
p1 = Plane(name="Secondo piano", w=400, h=400, axes=True)
p2 = Plane(name="Terzo piano",
           w=400, h=200,
           sx=10, sy=30,
           ox=40, oy=None,
           axescolor='orange', gridcolor='red')

mainloop

Scopo

Rende attiva la finestra grafica.

Sintassi

<piano>.mainloop()

Osservazioni

Questa istruzione è fondamentale nella geometria interattiva, mentre nei programmi con la geometria cartesiana e della tartaruga, può anche non essere usata.

Note

Se la libreria è usata dall’interno di IDLE, possono sorgere dei problemi a seconda di come è stato avviato IDLE stesso.

  • Se IDLE è stato avviato con il parametro “-n” allora non si deve usare il metodo mainloop() altrimenti, alla chiusura del piano cartesiano IDLE non risponde più ai comandi.
  • Se IDLE è stato avviato senza il parametro “-n” allora non si deve usare il metodo mainloop() altrimenti la finestra con il piano cartesiano non è attiva.

Nel resto degli esempi di questo capitolo non riporto questa istruzione, ma se la finestra creata non risponde al mouse aggiungetela come ultima istruzione.

Esempio

Disegna un piano se IDLE è stato avviato senza sottoprocessi, con il parametro “-n”.

p=Plane('ex_051: un piano', axescolor='pink')

Disegna un piano se IDLE è stato avviato con sottoprocessi, senza il parametro “-n”.

p=Plane('ex_052: altro piano', axescolor='olive drab')
p.mainloop()

after

Scopo

Permette di inserire un ritardo in un programma.

Sintassi

<piano>.after(<numero>)

Osservazioni

Il parametro rappresenta il numero di millisecondi di attesa.

Esempio

Un quadrato in movimento, (Vedi gli esempi di clear, reset, delete).

p = Plane('ex_06: Quadrato in movimento',
          sx=1, sy=1, axes=False, grid=False)
biro = p.newPen(width=4)
for i in range(0, 500, 2):
    color = '#ff%02x00' % (i//2)
    verts = ((-300+i, -30), (-220+i, -50), (-200+i, 30), (-280+i, 50))
    id = biro.drawpoly(verts, color)
    p.after(10)
    p.delete(id)
id = biro.drawpoly(verts, 'green')

axes

Scopo

Disegna una coppia di assi cartesiani ortogonali con l’origine in _o e con le unitò di misura indicate in _s.

Sintassi

<piano>.axes(color=None)

Osservazioni

Se non è specificato il parametro, gli assi vengono disegnati con il colore che ha il cursore grafico in quel momento, altrimenti con il colore specificato.

Esempio

Disegna due sistemi di riferimento con uguale scala ma origini diverse.

p = Plane('ex_07: Sistema di riferimento traslato')
p.origin = (80, 60)
p.axes('red')

grid

Scopo

Disegna una griglia di punti.

Sintassi

<piano>.grid(color=None)

Osservazioni

Se non è specificato il parametro, i punti vengono disegnati con il colore che ha il cursore grafico in quel momento, altrimenti con il colore specificato.

Esempio

Cambia in sequenza gli assi e la griglia del piano cartesiano.:

p=Plane('ex_08: Assi e griglie',  sx=12,  sy=36,
        axescolor='green', gridcolor='red')
biro=p.newPen(x=-10, y=3, color='red', width=2)
biro.drawtext('assi e griglia, attendere... 4')
ritardo=2000
p.after(ritardo)
p.clear()
p.axes('purple')
biro.drawtext('solo assi, attendere... 3')
p.after(ritardo)
p.clear()
p.grid('#22aa55')
biro.drawtext('solo griglia, attendere... 2')
p.after(ritardo)
p.reset()
biro.drawtext('situazione iniziale, attendere... 1')
p.after(ritardo)
p.clear()
biro.drawtext('bianco, finito!')

newPen

Scopo

Per creare un nuovo tracciatore grafico, una nuova penna per disegnare figure geometriche elementari.

Sintassi

<piano>.newPen([x=<numero>, y=<numero>, color=<colore>, width=<numero>])

Osservazioni

Normalmente è utile assegare la penna creata ad un identificatore.

L’intestazione di questo metodo è:

def newPen(self, x=0, y=0, color='black', width=1)

Tutti i parametri hanno un valore predefinito, per creare una penna nell’origine degli assi di colore nero e larghezza 1 non occorre passare argomenti alla funzione:

penna=p.newPen()

Per maggiori informazioni sulle penne vedi la documentazione della classe Pen.

Esempio

Disegna un quadrato blu con il contorno spesso 4 pixel.

p=Plane('ex_11: Quadrato', w=400, h=400)
biro=p.newPen(width=4, color='blue')
biro.drawpoly(((-7, -7), (7, -7), (7, 7), (-7, 7)))

clear

Scopo

Cancella tutti gli elementi disegnati nella finestra grafica, ridisegnando, se presenti, assi e griglia, ma non modifica i puntatori grafici, le penne.

Sintassi

<piano>.clear()

Osservazioni

Non ha parametri.

Esempio

Disegna un quadrato in movimento.

p = Plane('ex_13: Quadrato che scoppia', sx=1, axes=False, grid=False)
biro = p.newPen(width=4, color='blue')
a = 3
b = 1
for i in range (162):
    verts = (((-a*i, -b*i), (b*i, -a*i), (a*i,  b*i), (-b*i, a*i)))
    biro.drawpoly(verts)
    p.after(10)
    p.clear()
    biro.drawpoly(verts)

reset

Scopo

Cancella tutti gli elementi disegnati nella finestra grafica, ridisegnando, se presenti, assi e griglia, riporta nella condizione iniziale i puntatori grafici, le penne.

Sintassi

<plot>.reset()

Osservazioni

Non ha parametri.

Esempio

Disegna gli assi con la griglia, un poligono rosso, attendere un po’, poi cancellare tutto ridisegnando gli assi e ridisegnare il poligono con un altro colore.

p = Plane('ex_14: Poligono', axes=False, grid=False)
p.axes()
vertici = ((-2, -3), (4, -1), (6, 4), (-5, 6))
biro = p.newPen(width=20, color='blue')
biro.drawpoly(vertici)
p.after(500)
p.reset()
biro.color="green"
biro.width=10
biro.drawpoly(vertici)

delete

Scopo

Permette di cancellare un elemento grafico disegnato.

Sintassi

<piano>.delete(<id>)

Osservazioni

Ogni elemento grafico che viene disegnato nel piano ha un suo numero identificativo. Facendo riferimento a questo numero lo si può cancellare.

Esempio

Disegna un segmento che scivola sugli assi.

p = Plane('ex_15: Segmento che scivola sugli assi', sx=1)
biro = p.newPen(width=5, color='navy')
lung = 200
for y in range(200, 0, -1):
    x = (lung*lung-y*y)**.5
    s = biro.drawsegment((0, y), (x, 0))
    p.after(4)
    p.delete(s)
biro.drawsegment((0, 0), (lung, 0))

save

Scopo

Salva in un file l’immagine della finestra grafica.

Sintassi

<piano>.save(<filename>)

Osservazioni

<filename> è una stringa che contiene il nome del file. L’mmagine è salvata nel formato Postscript e a <nomefile> viene aggiunta l’estensione ”.ps” o ”.png” a seconda se trova il programma per la conversione in “png”.

Esempio

Produrre un file che contiene il disegno di un quadrato.

p = Plane('ex_16: Quadrato')
biro = p.newPen(width=6, color='pink')
q = ((-5, -3),(3, -5),(5, 3),(-3, 5))
biro.drawpoly(q)
p.save('quadrato')

getcanvaswidth e getcanvasheight

Scopo

Restituiscono le dimensioni in pixel della finestra grafica.

Sintassi

<piano>.getcanvaswidth()
<piano>.getcanvasheight()

Osservazioni

Restituiscono un numero intero.

Esempio

Disegna un rettangolo che circonda la finestra grafica.

p = Plane('ex_17: Cornice', sx=1, axes=False, grid=False)
biro = p.newPen(width=4, color='green')
bordo = 10
w = p.getcanvaswidth()//2-bordo
h = p.getcanvasheight()//2-bordo
biro.drawpoly(((-w, -h), (w, -h), (w, h), (-w, h)))

getcanvas

Scopo

Restituisce un riferimento alla finestra grafica.

Sintassi

<piano>.getcanvas()

Osservazioni

Il riferimento restituito può essere utilizzato per operare direttamente sulla finestra grafica senza passare attraverso i metodi messi a disposizione da Plane. Per saper cosa farsene bisogna conoscere la classe Canvas della libreria Tkinter.

Esempio

Questo metodo prevede la conoscenza della libreria Tkinter, essendo ciò al di fuori della portata di questo manuale, non viene proposto nessun esempio.

Si può trovare un suo uso nel programma viewfun.py distribuito assieme alla libreria pygraph.

class Pen

La classe Pen contiene alcuni attributi e metodi che permettono di:

  • Modificare le caratteristiche della penna.
  • Modificare la posizione.
  • Disegnare segmenti.
  • Disegnare punti.
  • Disegnare cerchi.
  • Disegnare poligoni.
  • Scrivere del testo.

__init__

Scopo

Crea una penna nel piano cartesiano e inizializza i suoi attributi.

Sintassi

<nome_variabile>=Pen(<parametri>)

Osservazioni

L’intestazione di questo metodo è:

def __init__(self, plane, x=0, y=0, color='black', width=1):

Presenta diversi parametri, il primo è obbligatorio, gli altri hanno dei valori predefiniti. Quando si crea una nuova penna è obbligatorio specificare il piano nel quale inserirla. Si possono anche specificare alcune altre caratteristiche. Vediamole in dettaglio:

  • la posizione del punto di partenza, valori predefiniti: x=0, y=0;
  • il colore, valore predefinito: color=’black’;
  • lo spessore del tratto, valore predefinito: width=1.

Dato che a parte il primo gli altri parametri hanno valori predefiniti, possiamo creare penne passando un diverso numero di argomenti. Gli argomenti non specificati saranno sostituiti con i valori di default.

Questo metodo non viene chiamato esplicitamente, ma viene eseguito quando si crea un oggetto di questa classe.

..note:

vedi il metodo newPen di Plane per un altro modo per creare penne. Le due istruzioni:

p=Plane(sx=100, sy=100, axes=True)
p0=Pen(p, x=1,  y=1,  color='violet', width=9)

e:

p=Plane(sx=100, sy=100, axes=True)
p0=p.newPen(x=1,  y=1,  color='violet', width=9)

sono del tutto equivalenti.

Esempio

Creare 4 penne diverse e le fa convergere nell’origine:

p = Plane('ex_30: __init__', sx=100, sy=100, grid=False)
p0 = Pen(p, x=1,  y=1,  color='violet', width=9)
p1 = Pen(p, x=-1, y=1,  color='magenta', width=7)
p2 = Pen(p, x=-1, y=-1, color='gold', width=5)
p3 = Pen(p, x=1,  y=-1, color='navy', width=3)
for biro in (p0, p1, p2, p3):
    biro.drawto((0, 0))

pos

Scopo

Permette di ottenere o modificare la posizione della penna.

Sintassi

<piano>.pos = (<numero>, <numero>)

Osservazioni

I due numeri rappresentano le coordinate della posizione.

Esempio

Disegna due quadrati in posizioni casuali.

import random
p = Plane('ex_31: pos')
biro = p.newPen()
lato = random.randrange(5)+3
biro.pos = (random.randrange(-14, 10), random.randrange(-9, 5))
x, y = biro.pos
vertici = ((x, y), (x+lato, y), (x+lato, y+lato), (x, y+lato))
biro.drawpoly(vertici, color='gold', width=6)
lato = random.randrange(5)+3
biro.pos = (random.randrange(-14, 10), random.randrange(-9, 5))
x, y = biro.pos
vertici = ((x, y), (x+lato, y), (x+lato, y+lato), (x, y+lato))
biro.drawpoly(vertici, color='pink', width=6)

color

Scopo

Permette di tracciare disegni con linee di differenti colori.

Sintassi

<piano>.pencolor = <colore>

Osservazioni

Il colore può essere indicato in diversi modi:

  • Usando il nome di un colore (in inglese). I colori attualmente disponibili sono:

    'alice blue', 'antique white', 'aquamarine', 'azure', 'beige',
    'bisque', 'black', 'blanched almond', 'blue', 'blue violet', 'brown',
    'burlywood', 'cadet blue', 'chartreuse', 'chocolate', 'coral',
    'cornflowerblue', 'cornsilk', 'cyan', 'dark goldenrod', 'dark green',
    'dark khaki', 'dark orange', 'dark orchid', 'dark salmon',
    'dark turquoise', 'dark violet', 'deep pink', 'dim gray', 'dodger blue',
    'firebrick', 'floral white', 'forest green', 'gainsboro', 'ghost white',
    'gold', 'goldenrod', 'gray', 'green', 'green yellow', 'honeydew',
    'hot pink', 'indian red', 'ivory', 'khaki', 'lavender', 'lavender blush',
    'lawn green', 'lemon chiffon', 'light blue', 'light coral', 'light cyan',
    'light goldenrod', 'light grey', 'light pink', 'light salmon',
    'light yellow', 'lime green', 'linen', 'magenta', 'maroon',
    'medium aquamarine', 'medium blue', 'medium orchid', 'medium purple',
    'medium turquoise', 'midnight blue', 'mint cream',
    'misty rose', 'moccasin', 'navajo white', 'navy', 'old lace',
    'olive drab', 'orange', 'orange red', 'orchid', 'pale goldenrod',
    'pale green', 'pale turquoise', 'papaya whip',
    'peach puff', 'peru', 'pink', 'plum', 'powder blue', 'purple', 'red',
    'rosy brown', 'royal blue', 'saddle brown', 'salmon', 'sandy brown',
    'sea green', 'seashell', 'sienna', 'sky blue', 'slate blue',
    'slate gray', 'snow', 'spring green', 'steel blue', 'tan',
    'thistle', 'tomato', 'turquoise', 'violet', 'violet red', 'wheat',
    'white', 'white smoke', 'yellow', 'yellow green'.
    
  • Usando una stringa nel formato ‘#RRGGBB’ dove RR, GG, BB sono le numeri di due cifre scritti in forma esadecimale che rappresentano le componenti rossa, verde e blu del colore.

  • Una tupla di tre numeri compresi tra 0 e 1 che rappresentano il livello delle componenti Rossa, Verde e Blu del colore.

Esempio

Disegna linee di diverso colore.

p = Plane('ex_32: color')
colors = ['red', 'green', 'blue', 'pink', 'yellow',
          'navy', 'gold', 'magenta', '#a0a0a0', (0.7, 0.5, 0.1)]
biro = p.newPen(width=10)
for i, color in enumerate(colors):
    biro.color = color
    print biro.color
    biro.drawsegment((-5, i-4), (5, i-4))

width

Scopo

Permette di tracciare disegni con linee di differenti spessori.

Sintassi

<piano>.penwidth = <numero>

Osservazioni

Numero indica la larghezza in pixel della traccia lasciata dal cursore grafico.

Esempio

Disegna linee di diverso spessore.

p = Plane('ex_33: width')
biro = p.newPen()
for i in range(10):
    biro.width = i*2
    biro.drawsegment((-5, i-4), (5, i-4))

setpos

Scopo

Sposta il cursore grafico nella posizione contenuta nel parametro, non traccia linee. Il parametro deve essere una coppia di numeri. Vedi pos.

Sintassi

<piano>.setpos(<punto>)

Osservazioni

<punto> è una coppia di numeri. Sposta il puntatore grafico dalla posizione attuale al nuovo <punto> tracciando un segmento.

Equivale a:

<piano>.pos=<punto>

Esempio

Vedi pos.

getpos

Scopo

Restituisce la posizione del cursore grafico.

Sintassi

<piano>.getpos()

Osservazioni

Non ha parametri, restituisce una coppia di numeri.

Equivale a:

<piano>.pos

Esempio

Vedi pos.

drawto

Scopo

Sposta il cursore grafico nella posizione indicata dal parametro tracciando una linea che congiunge la vecchia alla nuova posizione. Il parametro deve essere una coppia di numeri.

Sintassi

<piano>.setpos(<punto>)

Osservazioni

<punto> è una coppia di numeri. Sposta il puntatore grafico dalla posizione attuale al nuovo <punto> tracciando un segmento.

Esempio

Tracciare una linea tratteggiata in diagonale sullo schermo.

p = Plane('ex_36: drawto', sx=1, axes=False, grid=False)
biro = p.newPen(width=2, color='blue')
x = -200
y = -100
biro.pos = (x, y)
for i in range(25):
    x += 10
    y += 5
    biro.drawto((x, y))
    x += 6
    y += 3
    biro.pos = (x, y)

drawsegment

Scopo

Disegna un segmento nel piano cartesiano.

Sintassi

<piano>.drawsegment(p0, p1=None)

Osservazioni

Può essere chiamato:

  • con un argomento, formato da una coppia di numeri, disegna un segmento dalla posizione indicata dal cursore grafico al punto passato come argomento.
  • con due argomenti, due coppie di numeri, traccia il segmento che congiunge i due punti.

Non sposta il cursore grafico. Restituisce un numero che è il riferimento all’oggetto disegnato.

Esempio

Disegna i lati e le diagonali di un pentagono.

p = Plane('ex_37: drawsegment', sx=30)
biro = p.newPen(width=4, color='gold')
vertici = ((-3, -4), (+3, -4), (+5, +1), (0, +5), (-5, +1))
for i, v0 in enumerate(vertici):
    for v1 in vertici[i+1:]:
        biro.drawsegment(v0, v1)

drawpoint

Scopo

Disegna un punto nel piano cartesiano.

Sintassi

<piano>.drawpoint(p=None, color=None, width=None)

Osservazioni

Può essere chiamato:

  • senza argomenti, disegna un punto nell’attuale posizione del cursore grafico;
  • con un argomento, formato da una coppia di numeri, disegna un punto nella posizione indicata dall’argomento.
  • specificando anche il colore o lo spessore.

Non sposta il cursore grafico. Restituisce un numero che è il riferimento all’oggetto disegnato.

Esempio

Disegna 100 punti di colore e spessore casuali.

import random

def randcolor():
    return "#{0:02x}{1:02x}{2:02x}".format(random.randrange(256),
                                           random.randrange(256),
                                           random.randrange(256))

p = Plane('ex_38: drawpoint', sx=1, grid=False)
biro = p.newPen(color='red', width=200)
biro.drawpoint()
for cont in range(100):
    biro.drawpoint((random.randrange(-100, 100),
                    random.randrange(-100, 100)),
                   color=randcolor(), width=random.randrange(30)+1)

`drawcircle

Scopo

Disegna un cerchio nel piano cartesiano.

Sintassi

<piano>.drawcircle(radius, center=None, color=None, width=None, incolor='')

Osservazioni

Può essere chiamato:

  • con un argomento: il raggio del cerchio con centro nell’attuale posizione del cursore grafico;
  • con due argomenti: un numero che rappresenta il raggio e una coppia di numeri, che indicano il centro.
  • specificando anche il colore o lo spessore della circonferenza.
  • specificando anche il colore dell’interno.

Non sposta il cursore grafico. Restituisce un numero che è il riferimento all’oggetto disegnato.

Esempio

Disegna 100 circonferenze.

import random

def randcolor():
    return "#{0:02x}{1:02x}{2:02x}".format(random.randrange(256),
                                           random.randrange(256),
                                           random.randrange(256))

p = Plane('ex_39: drawcircle', sx=1, axes=False, grid=False)
biro = p.newPen(color='red')
biro.drawcircle(200)
for cont in range(100):
    biro.drawcircle(radius=random.randrange(80),
                    center=(random.randrange(-100, 100),
                            random.randrange(-100, 100)),
                    color=randcolor(), width=random.randrange(10),
                    incolor=randcolor())

drawpoly

Scopo

Disegna un poligono dati i vertici

Sintassi

<piano>.drawpoly(<vertici>)

Osservazioni

  • Il primo argomento è una sequenza, (lista o tupla) che contiene coppie di numeri.
  • Si può specificare anche il colore o lo spessore della poligonale.
  • Si può specificare anche il colore dell’interno.
  • Restituisce un numero che è il riferimento all’oggetto disegnato.

Esempio

Disegna un pentagono casuale.

import random

p = Plane('ex_40: drawpoly', sx=10, grid=False)
biro = p.newPen(width=4, color='blue')
q = []
for i in range(5):
    q.append((10-random.randrange(20), 10-random.randrange(20)))
biro.drawpoly(q, incolor='pink')

drawtext

Scopo

Scrive un testo nel piano cartesiano.

Sintassi

<piano>.drawtext(text, p=None, color=None, width=None)

Osservazioni

  • Il primo argomento è il testo da scrivere.
  • Si poò specificare la posizione in cui scrivere.
  • Si può specificare anche il colore o lo spessore dei caratteri.
  • Restituisce un numero che è il riferimento all’oggetto disegnato.

Esempio

Scrive alcune parole poco sensate.

p = Plane('ex_40: drawpoly')
biro = p.newPen(x=-5, y=7, color='blue')
biro.drawtext('testino')
biro.pos = (-5, 3)
biro.drawtext('testo', color='magenta', width=2)
biro.pos = (-5, -2)
biro.drawtext('testone', (-5, -2), color='green', width=4)

Pyplot

Dove viene presentata la libreria Pyplot

Introduzione

pyplot è una libreria che permette di tracciare grafici di funzioni matematiche. Fornisce alcuni metodi che permettono di tracciare grafici di:

  • funzioni date in forma esplicita: y=f(x) e x=f(y);
  • funzioni polari: ro=f(th);
  • funzioni parametriche: x=f(t), y=g(t);
  • e varie successioni.

Nella libreria sono presenti:

  • Una funzione che restituisce il numero di versione corrente.
  • La classe PlotPlane con alcuni metodi che permettono di gestire gli elementi base di un piano cartesiano.
  • La classe Plot con alcuni metodi che permettono di tracciare il grafico di successioni e di funzioni matematiche.

Di seguito vengono descritti questi elementi. Per ognuno verrà indicato:

  • lo scopo
  • la sintassi
  • alcune osservazioni
  • un esempio di uso

version

Scopo

È una funzione che restituisce il numero di versione della libreria.

Sintassi

version()

Osservazioni

Non richiede parametri, restituisce la stringa che contiene la versione.

Esempio

Controllare la versione della libreria.

import pyplot
if pyplot.version()<"02.05.00":
    print "versione un po' vecchiotta"
else:
    print "versione:", pc.version()

class PlotPlane

PlotPlane Estende la classe Plane, quindi è in grado di fare tutto quello che fa Plane. Cambiano i valori predefiniti che vengono utilizzati quando viene creato un oggetto di questa classe e c’è un ulteriore metodo.

In questa sezione presento solo le differenze e aggiunte alla classe Plane. Per tutto ciò che rimane invariato si veda il capitolo relativo a pycart.

Per poter creare oggetti della classi PlotPlane bisogna importarla dalla libreria. In tutti gli esempi seguenti si suppone che sia già stato eseguito il comando:

from pyplot import PlotPlane

Se nell’esegure un esempio ottenete un messaggio che termina con: NameError: name 'PlotPlane' is not defined, forse avete dimenticato di caricare PlotPlane dalla libreria con il comando scritto sopra.

Se nell’eseguire gli esempi osservate dei comportamenti strani della finestra che contiene il piano cartesiano, portate pazienza, o riguardate quanto scritto nella descrizione del metodo mainloop di Plane nel capitolo su pycart dove ho cercato di dare delle indicazioni in proposito.

pyplot si appoggia su altre due librerie:

  • math per avere a disposizione tutte le funzioni matematiche.
  • pycart per le funzioni del piano cartesiano.

__init__

Scopo

Crea il piano cartesiano e inizializza gli attributi di PlotPlane.

Sintassi

<nome_variabile>=PlotPlane(<parametri>)

Osservazioni

Questo metodo non viene chiamato esplicitamente, ma viene eseguito quando si crea un oggetto di questa classe. L’intestazione di questo metodo è:

def __init__(self, name="Function Plotter",
             w=400, h=400,
             sx=20, sy=20,
             ox=None, oy=None,
             axes=True, grid=True,
             axescolor='black', gridcolor='black',
             parent=None):

Si può vedere che presenta molti parametri tutti con un valore predefinito. Nel momento in cui si crea un piano cartesiano si possono quindi decidere le sue caratteristiche. Vediamole in dettaglio:

  • titolo della finestra, valore predefinito: “Functions Plotter”;
  • dimensione, valori predefiniti: larghezza=400, altezza=400;
  • scala di rappresentazione, valori predefiniti: una unità = 20 pixel;
  • posizione dell’origine, valore predefinito: il centro della finestra;
  • rappresentazione degli assi cartesiani, valore predefinito: True;
  • rappresentazione di una griglia di punti, valore predefinito: True;
  • colore degli assi valore predefinito: ‘black’.
  • colore della griglia valore predefinito: ‘black’.
  • riferimento alla finestra che contiene il piano cartesiano, valore predefinito: None.

Poiché tutti i parametri hanno un valore predefinito, possiamo creare un oggetto della classe PlotPlane senza specificare alcun argomento: verranno usati tutti i valori predefiniti. Oppure possiamo specificare per nome gli argomenti che vogliamo siano diversi dal comportamento predefinito, si vedano di seguito alcuni esempi.

Esempio

Si vedano tutti gli esempi seguenti.

newPlot

Scopo

Per creare un nuovo tracciatore di funzioni.

Sintassi

<piano>.newPlot([color=<colore>, width=<numero>])

Osservazioni

Normalmente è utile assegnare il tracciatore di funzioni creato ad un identificatore.

L’intestazione di questo metodo è:

def newPlot(self, color='black', width=1)

I parametri hanno un valore predefinito, per creare un tracciatore di funzioni di colore nero e larghezza 1 non occorre passare argomenti alla funzione:

plot=p.newPlot()

Esempio

Disegna una parabola di equazione: 0.2*x*x-x+1.

pp = PlotPlane("Piano 2")
plot = pp.newPlot(color='red', width=2)
plot.xy(lambda x: 0.2*x*x-x+1)

class Plot

Plot discende da Pen e mette a disposizione, quindi, tutti gli attributi e i metodi di questa classe. Oltre a questi, che sono descritti nel capitolo precedente, contiene alcuni metodi che permettono di tracciare, facilmente, svariati tipi di funzioni.

__init__

Scopo Crea il Tracciatore di funzioni.

Sintassi

<nome_variabile>=Plot(<parametri>)

Osservazioni

L’intestazione di questo metodo è:

def __init__(self, plane, color='black', width=1)

Quando si crea un oggetto Pen si deve specificare in quale piano deve essere inserito. Si possono anche specificare alcune altre caratteristiche:

  • il colore, valore predefinito: color=’black’;
  • lo spessore del tratto, valore predefinito: width=1.

Dato che a parte il primo gli altri parametri hanno valori predefiniti, possiamo creare penne passando un diverso numero di argomenti. Gli argomenti non specificati saranno sostituiti con i valori di default.

Questo metodo non viene chiamato esplicitamente, ma viene eseguito quando si crea un oggetto di questa classe.

..note:

vedi il metodo newPlot di PlotPlane per un altro modo per creare tracciatori di funzioni. Le due istruzioni:

pp=PlotPlane(sx=100, sy=100, axes=True)
p0=Pen(pp, color='violet', width=3)

e:

pp=Plane(sx=100, sy=100, axes=True)
p0=pp.newPlot(color='violet', width=3)

sono del tutto equivalenti.

Esempio

Costruire un oggetto della classe Plot e tracciare una parabola.

primo metodo:

pp = PlotPlane("Piano 1")
plot = Plot(pp, color='blue', width=2)
plot.xy(lambda x: x*x)

secondo metodo:

pp = PlotPlane("Piano 2")
plot = pp.newPlot(color='red', width=2)
plot.xy(lambda x: x*x)
p.xy(lambda x: x*x)

xy

Scopo

Traccia il grafico di una funzione nella forma y=f(x).

Sintassi

<plot>.xy(<funzione>[, color=<colore>][,width=<numero>])

Osservazioni

<funzione> è una funzione Python con un parametro.

<funzione> può anche essere creata con una funzione lambda.

Quando si esegue questo metodo si possono anche specificare il colore e la larghezza deltratto. Questi due parametri hanno dei valori predefiniti e quindi non sono obbligatori.

Queste osservazioni valgono anche per gli altri metodi.

Esempio

Disegnare la funzione: y=sin(1./x).

def fun(x):
    return math.sin(1./x)

pp = PlotPlane('sin(1./x)', w=600, h=250, sx=100, sy=100)
plot = pp.newPlot(color='green', width=2)
plot.xy(fun)

Usando una funzione lambda, lo stesso programma può essere scritto in modo più semplice così:

pp = PlotPlane('sin(1./x)', w=600, h=250, sx=100, sy=100)
plot = pp.newPlot(color='brown', width=2)
plot.xy(lambda x: math.sin(1./x))

yx

Scopo

Traccia il grafico di una funzione nella forma x=f(y).

Sintassi

<plot>.yx(<funzione>[, color=<colore>][,width=<numero>])

Osservazioni

vedi xy

Esempio

Disegnare una parabola con l’asse di simmetria parallelo all’asse y e una con l’asse di simmetria parallelo all’asse x.

def parabola(var):
    return .3*var**2+3*var+4

pp = PlotPlane("ex_03: xy, yx", w=600, h=600)
plot = pp.newPlot(width=2)
plot.xy(parabola, color='green')
plot.yx(parabola, color='magenta')

polar

Scopo

Traccia il grafico di una funzione polare nella forma: ro=f(th).

Sintassi

<plot>.polar(<funzione>[, <ma>][, color=<colore>][,width=<numero>])

Osservazioni

Oltre alle osservazioni fatte per xy, il parametro ma contiene il massimo valore per la variabile th (il minimo è 0).

Esempio

Disegna alcune funzioni polari.

pp = PlotPlane("ex_04: polar", w=600, h=600)
plot = pp.newPlot()
plot.polar(lambda th: 5*math.cos(2*th), color='navy', width=2)
plot.polar(lambda th: 8*math.sin(th/2), color='maroon', width=4)
plot.polar(lambda th: th, 720, color='gold', width=6)

param

Scopo

Traccia il grafico di una funzione parametrica nella forma: x=fx(t) y=fy(t).

Sintassi

<plot>.param(<fx>, <fy>[, mi][, ma][, color=<colore>][,width=<numero>])

Osservazioni

<fx> e <fy> sono funzioni Python con un parametro che possono essere create con una funzione lambda. Il parametro <mi> ha come valore predefinito 0. Il parametro <ma> ha come valore predefinito 100. Si possono specificare il colore e lo spessore del tratto.

Esempio

Disegnare una funzione parametrica.

pp = PlotPlane("ex_05: param", w=500, h=500)
plot = pp.newPlot(color='blue', width=3)
plot.param(lambda t: 7*math.cos(3*t/180.)+3*math.cos(3*t/180.+8*t/180.),
           lambda t: 7*math.sin(3*t/180.)-3*math.sin(3*t/180.+8*t/180.),
           0, math.pi*180*2)

ny

Scopo

Traccia il grafico di una successione nella forma y=f(n) dove n è un numero naturale.

Sintassi

<plot>.ny(<funzione>[, trace=True][, values=True]
          [, color=<colore>][,width=<numero>])

Osservazioni

Per <funzione> valgono le solite considerazioni tenendo presente che la variabile indipendente è un numero naturale.

trace, se posto uguale a True, traccia una linea che collega i punti consecutivi.

value, se posto uguale a True, stampa i valori della successione.

Esempio

Disegnare alcune successioni.

pp = PlotPlane("ex_06: ny", sx=10, sy=10, ox=10)
plot = pp.newPlot(color='blue', width=3)
plot.ny(lambda n: n/2+3)
plot.ny(lambda n: 3*math.sin(3*n), trace=True, values=True, color='gold')
plot.ny(lambda n: (-10*n-6)/(n), trace=True, color='maroon')

succ

Scopo

Traccia il grafico di una successione dato il primo elemento e la definizione dell‘“ennepiùunesimo” dato l‘“ennesimo”.

Sintassi

<plot>.succ(<a0>, <fan> [, trace=True][, values=True]
            [, color=<colore>][,width=<numero>])

Osservazioni

<a0> è il primo valore della successione,

<fan> è una funzione che può avere: * un solo parametro: an * due parametri: an e n * tre parametri: an, n e a0

trace, se posto uguale a True, traccia una linea che collega i punti consecutivi.

value, se posto uguale a True, stampa i valori della successione.

Esempio

Disegnare alcune successioni.

def grandine(an):
    if an % 2:
        return 3*an+1
    else:
        return an//2

pp = PlotPlane("ex_07: succ", sx=10, sy=10, ox=10)
plot = pp.newPlot(color='green', width=3)
plot.succ(7, grandine, trace=True, values=True)
plot.succ(2, lambda an, n: an/10-an, trace=True,
          values=True, color='blue')
plot.succ(-2, lambda an, n: an-(n*2-1))
plot.succ(18, lambda an, n: an**0.5-(-1)**(n+1)*2./3+3,
          trace=True, values=True, color='maroon')
plot.succ(50, lambda an, n, a0: (an+a0/an)/2, values=True, color='pink')

Pyturtle

Dove viene presentata la libreria Pyturtle

Introduzione

Pyturtle è una libreria che implementa la grafica della tartaruga. La classe Turtle ha alcuni metodi che permettono di:

  • Muovere la tartaruga avanti o indietro e farla ruotare su sé stessa.
  • Sollevare la penna o riappoggiarla sul piano di disegno.
  • Cambiare alcuni parametri relativi alla penna e al disegno.
  • pyturtle fornisce una funzione: version e due classi: TurtlePlane e Turtle.

Per poter creare oggetti della classi TurtlePlane bisogna importarla dalla libreria. In tutti gli esempi seguenti si suppone che sia già stato eseguito il comando:

from pyTurtle import TurtlePlane

Se nell’eseguire un esempio ottenete un messaggio che termina con: NameError: name 'TurtlePlane' is not defined, forse avete dimenticato di caricare PlotPlane dalla libreria con il comando scritto sopra.

Nell’eseguire gli esempi può darsi che ci siano dei comportamenti strani della finestra che contiene il piano delle tartarughe:

  • La finestra stessa non risponde ai comandi: si può ignorare il fatto o eseguire il metodo mainloop() del piano.
  • Il programma non termina neppure quando viene chiusa la finestra: bisogna non eseguire il metodo mainloop() del piano.

Per ulteriori informazioni riguardate quanto scritto nella descrizione del metodo mainloop di Plane nel capitolo su pycart dove ho cercato di dare delle indicazioni in proposito.

Negli esempi che seguono non viene riportato il comando mainloop(), se la finestra grafica non risponde aggiungete l’istruzione:

tp.mainloop()

Se vengono creati più piani basta chiamare il mainloop() per un solo piano (si veda il primo esempio della classe TurtlePlane dove si può provare a decommentare (togliere il carattere cancelletto ‘#’) all’istruzione:

#tp0.mainloop()

per vedere la differenza.

pyturtle si appoggia sulle librerie:

  • math per alcune funzioni matematiche,
  • pycart per il piano cartesiano,
  • pygrapherror per i messaggi di errore.

Nella libreria pyturtle sono presenti due classi: TurtlePlane che fornisce lo spazio dove possono vivere le tartarughe e Turtle che mette a disposizione i metodi fondamentali per la grafica della tartaruga.

Di seguito vengonno riportati i metodi delle due classi. Per ognuno verrà indicato:

  • lo Scopo
  • la Sintassi
  • alcune Osservazioni
  • un Esempio di uso

version

Scopo

Restituisce il numero di versione della libreria.

Sintassi

version()

Osservazioni

Non richiede parametri.

Esempio

Controllare la versione della libreria.

from pyturtle import version
print version()

class TurtlePlane

La classe TurtlePlane rappresenta lo spazio dove creare e far muovere le tartarughe e fornisce alcuni metodi che permettono di pulire la superficie, tracciare assi e griglie e gestire le tartarughe.

TurtlePlane è derivata da Plane, ne eredita quindi tutti gli attributi e i metodi, per questi si veda il capitolo relativo a pycart. Di seguito sono riportati i metodi (ri)definiti in TurtlePlane.

__init__

Scopo

Crea un piano in cui far muovere tartarughe.

Sintassi

<piano>=TurtlePlane(<parametri>)

Osservazioni

Il metodo __init__ non viene chiamato esplicitamente ma viene eseguito quando si crea un oggetto di questa classe. Ha esattamente gli stessi parametri di Plane, vedi Plane per una illustrazione di ognuno di essi. I valori predefiniti però sono diversi, l’intestazione di questo metodo è:

def __init__(self, name="Turtle geometry",
             w=600, h=400,
             sx=1, sy=None,
             ox=None, oy=None,
             axes=False, grid=False,
             axescolor='black', gridcolor='black',
             master=None):

Per poter creare oggetti della classe TurtlePlane bisogna aver eseguito il comando:

from pyturtle import TurtlePlane

Esempio

Creare quattro piani con caratteristiche diverse e disegnare in ciascuno di essi un quadrato.

tp0 = TurtlePlane()
tp1 = TurtlePlane("TurtlePlane con gli assi", sx=10, sy=10, axes=True)
tp2 = TurtlePlane("TurtlePlane con gli griglia", sx=20, sy=20, grid=True)
tp3 = TurtlePlane("TurtlePlane con assi e griglia", sx=30, sy=20,
                  axes=True, grid=True)
piani = [tp0, tp1, tp2, tp3]
for p in piani:
    t = p.newTurtle()
    for cont in range(4):
        t.forward(7)
        t.left(90)
# tp0.mainloop()

newTurtle

Scopo

Crea una nuova tartaruga cioè un nuovo oggetto della classe Turtle in questo piano.

Sintassi

<tarta>=<piano>.newTurtle(<parametri>)

Osservazioni

L’intestazione di questo metodo è:

def newTurtle(self, x=0, y=0, d=0, color='black', width=1):

Si può vedere che presenta molti parametri tutti con un valore di default. Nel momento in cui si crea una nuova tartaruga si possono quindi decidere le sue caratteristiche. Vediamole in dettaglio:

  • ascissa e ordinata della sua posizione iniziale: x=0, y=0;
  • direzione iniziale verso cui è rivolta: d=0;
  • colore iniziale: color=’black’.
  • larghezza iniziale: width=1.

Poiché tutti i parametri hanno un valore predefinito, possiamo creare un oggetto della classe Turtle senza specificare alcun argomento: verranno usati tutti i valori predefiniti. Oppure possiamo specificare per nome gli argomenti che vogliamo abbiano valori diversi da quelli predefiniti. Si vedano di seguito alcuni esempi.

Esempio

Creare un piano con tre tartarughe e le fa avanzare di 100 unità.

tp = TurtlePlane()
tina = tp.newTurtle(x=-100, y=-100, d=90, color='red')
gina = tp.newTurtle(y=-100, d=90, color='green')
pina = tp.newTurtle(x=100, y=-100, d=90, color='blue')
tina.forward(100)
pina.forward(100)
gina.forward(100)
#tp.mainloop()

turtles

Scopo

Restituisce una lista che contiene tutte le tartarughe presenti in un piano.

Sintassi

<piano>.turtles()

Osservazioni

Ogni volta che viene creata o viene cancellata una tartaruga viene anche aggiornata questa lista.

Esempio

Creare un piano con tre tartarughe anonime e le fa avanzare di 10 unità.

tp = TurtlePlane("Piano con tre tartarughe",
                 sx=20, sy=20, grid=True, gridcolor="grey")
tp.newTurtle(x=-10, y=-8, d=90, color='red')
tp.newTurtle(y=-8, d=90, color='green')
tp.newTurtle(x=10, y=-8, d=90, color='blue')
for t in tp.turtles():
    t.forward(10)

delturtles

Scopo

Elimina tutte le tartarughe presenti in un piano.

Sintassi

<piano>.delturtles()

Osservazioni

Ogni volta che viene creata o viene cancellata una tartaruga viene anche aggiornata questa lista.

Esempio

Crea 2 tartarughe e fa muovere tutte le tartarughe, dopo un secondo le elimina. Crea altre 2 tartarughe e fa muovere tutte le tartarughe.:

tp = TurtlePlane(name='2+2 Tartarughe', w=400, h=400,
                 sx=20, sy=20, grid=True)
tp.newTurtle(x=-3, y=-3, d=225, color='orange red', width=10)
tp.newTurtle(x=+3, y=-3, d=315, color='orange', width=10)
tp.after(500)
for t in tp.turtles():
    t.forward(5)
tp.after(500)
tp.delturtles()
tp.newTurtle(x=+3, y=+3, d= 45, color='pale green', width=10)
tp.newTurtle(x=-3, y=+3, d=135, color='pale turquoise', width=10)
tp.after(500)
for t in tp.turtles():
    t.forward(5)
libmanex.end(tp)

clear

Scopo

Cancella tutti i segni tracciati dalle tartarughe.

Sintassi

<piano>.clear()

Osservazioni

Non modifica la posizione e lo stato delle tartarughe e neppure eventuali assi e griglia.

Esempio

Disegna un pentagono, si sposta, disegna un altro pentagono, applica clear e ridisegna un pentagono.

from random import random, randrange

def pentagono(lato):
for cont in range(5):
tina.forward(lato) tina.left(72)

tp=TurtlePlane(name=”Clear”, sx=10, grid=True) tina=tp.newTurtle(color=’pink’, width=3) tina.tracer(5) pentagono(5) tina.setcolor((random(), random(), random())) tina.setwidth(randrange(2, 20, 2)) tina.right(randrange(360)) tina.forward(randrange(5)+5) pentagono(5) tp.after(700) tp.clear() pentagono(5)

reset

Scopo

Ripulisce il piano riportando le tartarughe nella situazione iniziale.

Sintassi

<piano>.reset()

Osservazioni

Ogni tartaruga memorizza lo stato che ha quando viene creata, reset riporta tutte le tartarughe al loro stato iniziale.

Esempio

Disegna un pentagono, si sposta, disegna un altro pentagono, applica reset e ridisegna un pentagono.

from random import random, randrange

def pentagono(lato):
for cont in range(5):
tina.forward(lato) tina.left(72)

tp=TurtlePlane(name=”reset”, sx=10, axes=True) tina=tp.newTurtle(color=’pink’, width=3) tina.tracer(5) pentagono(5) tina.setcolor((random(), random(), random())) tina.setwidth(randrange(2, 20, 2)) tina.right(randrange(360)) tina.forward(randrange(5)+5) pentagono(5) tp.after(700) tp.reset() pentagono(5)

class Turtle:

La classe Turtle permette di costruire oggetti tartaruga, possiede diversi metodi che permettono di esplorare la geometria della trartaruga.

..note:
Gli esempi riportati di seguito sono, in generale, semplici, alcuni anche banali. ma verso la fine si possono trovare come esempi dei programmi piuttosto complessi ispirati da: Abelson, Di Sessa, La geometria della tartaruga, Padova 1986. Possono essere utili per approfondire alcuni elementi del linguaggio, ma la primitiva di cui si parla può essere utilizzata anche in programmi più semplici di quelli proposti.

__init__

Scopo

Crea una nuova Tartaruga.

Sintassi

<tarta>=Turtle(self, plane=<piano>):

**Osservazioni**

Il metodo __init__ non viene chiamato direttamente, ma è eseguito quando viene creato un oggetto di questa classe. Ha un parametro obbligatorio, plane, che indica il piano in cui deve essere creata la tartaruga. L’intestazione di questo metodo è:

def __init__(self, plane, x=0, y=0, d=0, color='black', width=1):

Se viene chiamato senza parametri, verranno usati i valori predefiniti come valori iniziali. Vedi anche newTurtle.

Esempio

Creare un piano con tre tartarughe e farle avanzare di 15 unità.

tp = TurtlePlane(name='Tre tartarughe', sx=20, sy=20,
                 axes=True, grid=True)
tina = Turtle(plane=tp, x=-10, y=-8, d=90, width=5, color='red')
gina = Turtle(plane=tp, y=-8, d=90, width=5, color='green')
pina = Turtle(plane=tp, x=10, y=-8, d=90, width=5, color='blue')
tina.forward(15)
pina.forward(15)
gina.forward(15)

forward

Scopo

Muove in avanti il cursore grafico, Tartaruga, di un certo numero di passi.

Sintassi

<tarta>.forward(<passi>)

Osservazioni

Il movimento è relativo alla direzione di Tartaruga. Lo spostamento dipende dalla scala del piano su cui si muove Tartaruga. forward, assieme a back, right e left, costituiscono i comandi base della geometria della tartaruga. Una geometria basata su un riferimento intrinseco al cursore.

forward è una funzione che restituisce il riferimento al segmento tracciato, questo riferimento può essere utile per cancellare un singolo segmento (vedi il programma orologio.py presente nella directory examples/pyturtle).

Esempio

Disegna un quadrato con il lato lungo 40 unità.

def quadrato(lato):
    """Disegna un quadrato con un certo lato"""
    for count in xrange(4):
        tina.forward(lato)
        tina.left(90)

tp = TurtlePlane()
tina = tp.newTurtle()
quadrato(47)

back

Scopo

Muove indietro il cursore grafico, Tartaruga, di un certo numero di passi.

Sintassi

<tarta>.back(<passi>)

Osservazioni

Il movimento è relativo alla direzione di Tartaruga. Lo spostamento dipende dalla scala del piano su cui si muove Tartaruga. Anche back è una funzione che restituisce un riferimento al segmento tracciato.

Esempio

Disegna una stella con colori fumati.

tp = TurtlePlane()
tina = tp.newTurtle()
n = 200
for i in range(n):
    tina.setcolor('#ff%02x00' % (i*256//n))
    tina.forward(150)
    tina.back(150)
    tina.right(360./n)

left

Scopo

Ruota verso sinistra la tartaruga di un certo angolo. All’avvio l’angolo viene misurato in gradi, ma si può anche dire alla tartaruga di misurarlo in radianti (vedi i metodi degree e radians).

Sintassi

<tarta>.left(<angolo>)

Osservazioni

Tartaruga non si sposta, ma ruota semplicemente in senso antiorario. All’inizio Tartaruga viene creata rivolta verso destra, inclinazione di 0 gradi.

Esempio

Disegna una stellina dato il numero e la lunghezza dei raggi.

def stella(num, lung):
  """Disegna una stella con num raggi lunghi lung"""
  for i in range(num):
      tina.forward(lung)
      tina.back(lung)
      tina.right(360./num)

tp = TurtlePlane()
tina = tp.newTurtle()
stella(20, 50)

up

Scopo

Solleva la penna della tartaruga in modo che, muovendosi, non tracci alcun segno.

Sintassi

<tarta>.up()

Osservazioni

Non muove Tartaruga. Se si vuole mandare la tartaruga verso la parte alta dello schermo, bisogna ruotarla verso l’alto e poi mandarla avanti.

Esempio

Trasla la tartaruga delle componenti x e y relative alla propria direzione.

def sposta(x, y):
    """Trasla Tartaruga delle componenti x e y"""
    tina.up()
    tina.forward(x)
    tina.left(90)
    tina.forward(y)
    tina.right(90)
    tina.down()

tp = TurtlePlane()
tina = tp.newTurtle()
tina.left(45)
sposta(100, -50)

down

Scopo

Abbassa la penna di Tartaruga in modo che muovendosi lasci un segno.

Sintassi

<tarta>.down()

Osservazioni

Esegue l’operazione opposta di up e come il metodo up, non muove la tartaruga.

Esempio

Crea una nuova classe di tartarughe che sa tracciare linee tratteggiate e disegna un poligono tratteggiato.

class MyTurtle(Turtle):
    """Una nuova classe di tartarughe
    che traccia anche linee tratteggiate."""
    def tratteggia(self,  lung, pieno=5, vuoto=5):
        """Traccia una linea tratteggiata luga lung. """
        q, r = divmod(lung, pieno+vuoto)
        for cont in xrange(q):
            self.forward(pieno)
            self.up()
            self.forward(vuoto)
            self.down()
        self.forward(r)

tp = TurtlePlane(name="Pentagono tratteggiato")
tina = MyTurtle(tp, color='navy', width=3)
for cont in xrange(5):
    tina.tratteggia(87)
    tina.left(72)

write

Scopo

Scrive un testo sullo schermo a partire dalla posizione di Tartaruga.

Sintassi

<tarta>.write(<messaggio>[, move=False|True])

Osservazioni

Se viene chiamato con un unico parametro, la tartaruga scrive il testo e resta nella posizione attuale. Se oltre al messaggio, viene passato anche un argomento True, la tartaruga viene spostata alla fine della scritta. I metodi up e down non hanno effetto su write.

Esempio

Scrivi alcune parole nel piano della tartaruga.

tp = TurtlePlane(name='Testo nella finestra grafica', w=400, h=200)
tina = tp.newTurtle()
pos = tina.getpos()
tina.up()
tina.setpos((-140, 80))
tina.setcolor('green')
tina.write('Parole')
tina.setcolor('pink')
tina.write('sovrapposte!')
tina.setpos((-140, -80))
tina.setcolor('red')
tina.write('Parole ', move=True)
tina.setcolor('orange')
tina.write('scritte di seguito!')
tina.setpos(pos)
tina.down()

fill

Scopo

Permette di colorare l’interno delle figure realizzate dalla tartaruga.

Sintassi

<tarta>.fill(True|False)

Osservazioni

fill(True) inizia a memorizzare le figure da riempire, fill(False) le riempie con il colore attuale.

Esempio

Una funzione che disegna un quadrato con l’interno colorato.

def quadratopieno(lato, coloreinterno):
    """Disegna un quadrato dati il lato e il colore della sup. interna."""
    tina.fill(True)
    for count in xrange(4):
        tina.forward(lato)
        tina.left(90)
    oldcol = tina.getcolor()
    tina.setcolor(coloreinterno)
    tina.fill(False)
    tina.setcolor(oldcol)

tp = TurtlePlane()
tina = tp.newTurtle(color='blue', width=3)
quadratopieno(100, 'green')
libmanex.end(tp)

tracer

Scopo

Rallenta la velocità di Tartaruga.

Sintassi

<tarta>.tracer(<ritardo>)

Osservazioni

Più è elevato l’argomento, più lentamente si muove Tartaruga nei comandi forward, back e setpos. Se l’argomento è 0, Tartaruga traccia un intero segmento in un solo passo.

Esempio

Disegna una traccia circolare variando la velocita’ e lo spessore.

from random import random

tp = TurtlePlane(name="Giro della morte")
tina = tp.newTurtle(y=-180)
tina.setcolor((random(), random(), random()))
passi = 72
angolo = 180./72
for i in xrange(passi):
  altezza = (tina.getpos()[1]+200)/20
  tina.setwidth(altezza)
  tina.tracer(altezza)
  tina.left(angolo)
  tina.forward(15)
  tina.left(angolo)

setpos

Scopo

Sposta la tartaruga in una posizione relativa al sistema di riferimento assoluto relativo al piano in cui vive.

Sintassi

<tarta>.setpos((<ascissa>, <ordinata>))

Osservazioni

Le coordinate sono costituite da una tupla contenente due numeri, cioè da una coppia di numeri racchiusa tra parentesi tonde.

Esempio

Sposta casualmente la tartaruga.

from random import randrange

def spostaacaso():
    """Sposta tartaruga in una posizione casuale."""
    tina.up()
    tina.setpos((randrange(600)-300, randrange(400)-200))
    tina.down()

tp = TurtlePlane()
tina = tp.newTurtle(width=10, color='green')
for i in range(8):
    spostaacaso()
    tp.after(500)

setdir

Scopo

Ruota Tartaruga verso una certa direzione.

Sintassi

<tarta>.setdir(<angolo>)

Osservazioni

La direzione assoluta ha angolo 0 verso destra e considera come verso positivo di rotazione il verso antiorario.

Esempio

Disegna un cielo stellato.

from random import random, randrange

def spostaacaso(raggio):
    """Sceglie una direzione e sposta la tartaruga di una
    lunghezza casuale minore a raggio."""
    tina.up()
    tina.setpos((0, 0))
    tina.setdir(randrange(360))
    tina.forward(randrange(raggio))
    tina.down()

def stella(lung, n):
    """Disegna una stellina con n raggi lunghi lung."""
    for cont in xrange(n):
        tina.forward(lung)
        tina.back(lung)
        tina.left(360./n)

def cielo():
    """Disegna un cielo stellato"""
    tina.fill(1)
    tina.ccircle(200)
    tina.fill(0)
    for cont in xrange(100):
        spostaacaso(190)
        tina.setcolor((random(), random(), random()))
        stella(10, 5)

tp = TurtlePlane()
tina = tp.newTurtle()
cielo()

setcolor

Scopo

Modifica il colore di Tartaruga e della sua penna.

Sintassi

<tarta>.setcolor(<colore>)

Osservazioni

<colore> può essere definito in 3 modi diversi (vedi Pycart), può essere:

  1. una stringa che contiene il nome di un colore,
  2. una tupla che contiene tre numeri compresi tra 0 e 1 che indicano l’intensità delle componenti rossa, verde, blu,
  3. Una stringa nel formato: ‘#RRGGBB’, dove RR, GG, BB sono numeri esadecimali (da 00 a ff) che indicano i valori delle componenti rossa, verde, blu.

Esempio

Disegna tre tracce colorate su uno sfondo di altro colore.

tp = TurtlePlane()
fina = Turtle(tp)
fina.setcolor('light blue')      # colore passato per nome
fina.fill(True)
fina.ccircle(200)
fina.fill(False)
fina.delete()
tina = Turtle(tp, y=-200, d=100)
tina.setcolor((1, 0, 0))         # tupla che contiene 3 numeri
gina = Turtle(tp, y=-200, d=120)
gina.setcolor((1, 1, 1))         # tupla che contiene 3 numeri
pina = Turtle(tp, y=-200, d=140)
pina.setcolor('#00ff00')         # stringa nel formato '#rrggbb'
for cont in xrange(80):
    for t in tp.turtles():
        t.setwidth(t.getwidth()+1)
        t.forward(4)
        t.right(1)

setwidth

Scopo

Modifica la larghezza della traccia lasciata da Tartaruga.

Sintassi

<tarta>.setwidth(<larghezza>)

Osservazioni

Il valore minimo della larghezza della traccia è 1. Modificando la larghezza della traccia viene modificata anche la dimensione del disegno di Tartaruga.

Esempio

Disegna una fila di 8 tra quadrati e cerchi alternati.

def quadrato(lato):
    """Traccia un quadrato di lato lato."""
    for cont in xrange(4):
        tina.forward(lato)
        tina.left(90)

tp = TurtlePlane()
tina = tp.newTurtle()
tina.up()
tina.back(290)
for i in range(8):
    tina.down()
    if i % 2:
        tina.setwidth(5)
        tina.setcolor('orange')
        tina.circle(20)
    else:
        tina.setwidth(10)
        tina.setcolor('maroon')
        quadrato(40)
    tina.up()
    tina.forward(80)
tina.back(350)
tina.down()

getpos

Scopo

Restituisce l’attuale posizione di Tartaruga.

Sintassi

<tarta>.getpos()

Osservazioni

Il valore restituito è una coppia di numeri, una tupla, che contiene ascissa e ordinata.

Esempio

Disegna un inviluppo di triangoli rettangoli.

def trirett(cateto1, cateto2):
    """Disegna un triangolo rettangolo dati i due cateti"""
    tina.forward(cateto1)
    a = tina.getpos()
    tina.back(cateto1)
    tina.left(90)
    tina.forward(cateto2)
    tina.setpos(a)
    tina.right(90)
    tina.back(cateto1)

def inviluppo(l1, l2, inc=10):
    """Disegna un inviluppo di triangoli rettangoli."""
    if l1 < 0: return
    trirett(l1, l2)
    inviluppo(l1-inc, l2+inc)

tp = TurtlePlane(name="Inviluppo di triangoli rettangoli")
tina = tp.newTurtle(color='turquoise')
for i in range(4):
    inviluppo(200, 0)
    tina.left(90)

getdir

Scopo

Restituisce l’attuale direzione di Tartaruga.

Sintassi

<tarta>.getdir()

Osservazioni

Se stiamo lavorando in gradi, il valore restituito è un numero razionale compreso tra 0 e 360. Se stiamo lavorando in radianti, è un numero razionale compreso tra 0 e 2*pi.

Esempio

Vedi esempio di getwidth.

getcolor

Scopo

Restituisce l’attuale colore di Tartaruga.

Sintassi

<tarta>.getcolor()

Osservazioni

Il valore restituito è una stringa nel formato: ‘#rrggbb’.

Esempio

Vedi esempio di getwidth e di fill.

getwidth

Scopo

Restituisce l’attuale larghezza della penna.

Sintassi

<tarta>.getwidth()

Osservazioni

Il valore restituito è un numero.

Esempio

Modifica a caso gli attributi di Tartaruga e li scrive sullo schermo.

from random import random, randrange
from math import pi

def scrividati(tarta):
    """Scrive, nel piano, gli attributi della tartaruga."""
    tarta.up()
    p = tarta.getpos()
    tarta.write("La posizione di tina e': (%s; %s)" % tarta.getpos())
    tarta.setpos((p[0], p[1]-1))
    tarta.write("La direzione di tina e': %s" % tarta.getdir())
    tarta.setpos((p[0], p[1]-2))
    tarta.write("Il colore della tartaruga e': %s" % str(tarta.getcolor()))
    tarta.setpos((p[0], p[1]-3))
    tarta.write("La larghezza della penna e': %s" % tarta.getwidth())
    tarta.setpos(p)
    tarta.down()

tp = TurtlePlane(name='Stato della tartaruga',
              sx=20, sy=20, axes=True, grid=True)
tina = tp.newTurtle()
tina.left(randrange(180))
tina.setwidth(randrange(10)+1)
tina.setcolor((random(), random(), random()))
tina.forward(randrange(10))
scrividati(tina)
gina = tp.newTurtle()
gina.radians()
gina.left(random()*pi+pi)
gina.setwidth(randrange(10)+1)
gina.setcolor((random(), random(), random()))
gina.forward(randrange(7))
scrividati(gina)

circle

Scopo

Traccia una circonferenza o un arco di circonferenza.

Sintassi

<tarta>.circle(<raggio> [, <estensione>])

Osservazioni

Se è presente un solo argomento viene tracciata una circonferenza di dato raggio.

Se ci sono due argomenti, viene disegnato un arco di circonferenza e il secondo argomento indica l’estensione dell’arco.

Esempio

Disegna 25 archi e 25 segmenti circolari alternati.

from random import random, randrange

tp = TurtlePlane(w=600, h=600)
tina = tp.newTurtle(width=5)
for j in range(50):
    tina.setcolor((random(), random(), random()))
    tina.up()
    tina.setpos((randrange(-250, 250), randrange(-250, 250)))
    tina.down()
    tina.fill(j % 2 == 1)
    tina.circle(randrange(25)+25, randrange(360))
    tina.setcolor((random(), random(), random()))
    tina.fill(0)

ccircle

Scopo

Disegna una circonferenza o un arco centrati nella posizione di Tartaruga.

Sintassi

<tarta>.ccircle(<raggio> [, <estensione>])

Osservazioni

Se ci sono due argomenti, viene disegnato un arco di circonferenza e il secondo argomento indica l’estensione dell’arco. In questo caso la tartaruga ruota dell’ampiezza indicata dal secondo argomento.

Esempio

Disegna 25 archi e 25 settori circolari alternati.

from random import random, randrange

tp = TurtlePlane(w=600, h=600)
tina = tp.newTurtle(width=5)
for j in range(50):
    tina.setcolor((random(), random(), random()))
    tina.up()
    tina.setpos((randrange(-250, 250), randrange(-250, 250)))
    tina.down()
    tina.fill(j % 2 == 1)
    tina.ccircle(randrange(25)+25, randrange(360))
    tina.setcolor((random(), random(), random()))
    tina.fill(0)

radians

Scopo

Dopo aver invocato questo metodo, Tartaruga misura gli angoli in radianti.

Sintassi

<tarta>.radians()

Osservazioni

Ogni tartaruga, appena creata misura gli angoli in gradi sessagesimali,S se si preferisce lavorare con i radianti basta dirglielo.

Esempio

Vedi l’esempio di degree.

degrees

Scopo

Dopo la chiamata a questo metodo, Tartaruga misura gli angoli in gradi.

Sintassi

<tarta>.degrees([<angolo giro>])

Osservazioni

Se usato senza argomenti, l’angolo giro sarà di 360°, l’argomento permette di specificare il numero di gradi di un angolo giro. Io non l’ho mai usato, ma si potrebbe decidere che l’angolo giro sia di 400 gradi o di un qualunque altro valore.

Esempio

Disegna un orologio analogico che riporta l’ora del sistema (mescolare in uno stesso programma gradi e radianti, non mi sembra una grande idea).

from time import time, localtime
from math import pi

def lancette(d, (ore, minuti, secondi)):
    """Disegna le lancette di un orologio che segna ore:minuti:secondi"""
    tina.degrees()
    aore = round(90 - ore*30 - minuti*0.5)
    aminuti = round(90 - minuti*6 - secondi*0.1)
    asecondi = round(90 - secondi*6)
    tina.setdir(aore)
    tina.setwidth(d*0.1)
    tina.forward(d*0.6)
    tina.back(d*0.6)
    tina.setdir(aminuti)
    tina.setwidth(d*0.05)
    tina.forward(d*0.8)
    tina.back(d*0.8)
    tina.setdir(asecondi)
    tina.setwidth(d*0.02)
    tina.forward(d*0.9)
    tina.back(d*0.9)

def quadrante(dimensione):
    """Il quadrante di un orologio"""
    tina.radians()
    tina.ccircle(dimensione)
    d1 = dimensione*0.9
    d2 = dimensione-d1
    for i in range(12):
        tina.up()
        tina.forward(d1)
        tina.down()
        tina.forward(d2)
        tina.up()
        tina.back(dimensione)
        tina.down()
        tina.left(pi/6)
    tina.degrees()

def orologio(dimensione):
    """Disegna un orologio che indica l'ora corrente."""
    quadrante(dimensione)
    lancette(dimensione, localtime(time())[3:6])

tp = TurtlePlane(name="Ora esatta")
tina = tp.newTurtle()
orologio(100)

distance

Scopo

Restituisce la distanza tra Tartaruga e il punto dato come argomento.

Sintassi

<tarta>.distance(<coordinate>)

Osservazioni

L’argomento deve essere una coppia di numeri racchiusa tra parentesi.

Esempio

Dirige Tartaruga verso un bersaglio emulando l’olfatto.

from random import randrange

def saltaacaso():
    """Sposta Tartaruga in una posizione casuale."""
    tina.up()
    tina.setpos((randrange(-280, 280), randrange(-180, 180)))
    tina.down()

def bersaglio():
    """Disegna un bersaglio"""
    saltaacaso()
    for cont in xrange(4):
        tina.forward(5)
        tina.back(5)
        tina.left(90)
    larghezza = tina.getwidth()
    tina.setwidth(2)
    tina.ccircle(20)
    tina.setwidth(larghezza)
    tina.ccircle(10)
    return(tina.getpos())

def odore(p):
    """Riporta un numero inversamente proporzionale
    al quadrato della distanza da p"""
    return 1./(tina.distance(p)**2)

def ricerca_per_odore(bersaglio):
    """Muove Tartaruga in base ad una regola olfattiva"""
    tina.tracer(10)
    ricordo_odore = odore(bersaglio)
    while tina.distance(bersaglio)>10:
        tina.forward(randrange(10))
        nuovo_odore = odore(bersaglio)
        if nuovo_odore < ricordo_odore:
            tina.right(randrange(80))
        ricordo_odore = nuovo_odore

tp = TurtlePlane("Ricerca in base all'odore")
tina = tp.newTurtle()
b = bersaglio()
saltaacaso()
ricerca_per_odore(b)

dirto

Scopo

Restituisce la direzione sotto cui Tartaruga vede un certo punto

Sintassi

<tarta>.dirto(<coordinate>)

Osservazioni

Le coordinate sono una coppia di numeri. Il valore restituito è relativo alla direzione di Tartaruga: se il valore è 0 significa che il punto è a ore 12 cioè esattamente davanti a Tartaruga, se è 180 si trova a ore 6, cioè esattamente dietro, se è 90 si trova a ore 9, se è 270 si trova a ore 3, ...

Esempio

Simula l’inseguimento tra un gatto e un topo.

from random import randrange

class Topo(Turtle):
    """Classe che simula il comportamento di un topo"""
    def __init__(self, **args):
        Turtle.__init__(self, **args)
        piano = self._plane
        self.up()
        self.setpos((piano._s2x(20), piano._s2y(20)))
        self.write("inseguimento in base alla vista")
        self.setpos((randrange(120)-60, randrange(80)-40))
        self.down()

    def scappa(self, da):
        """Fa muovere il topo di un passo cercando di scappare da da"""
        dir_gatto = self.dirto(da.getpos())
        if dir_gatto < 170:
            self.right(randrange(20)-10)
        elif dir_gatto > -170:
            self.left(randrange(20)-10)
        else:
            self.left(randrange(80)-40)
        self.forward(1)

class Gatto(Turtle):
    """Classe che simula il comportamento di un gatto"""
    def __init__(self, **args):
        Turtle.__init__(self, **args)
        self.setcolor("red")
        self.setwidth(3)

    def insegui(self, chi):
        """Fa muovere il gatto di un passo come se inseguisse chi
        ritorna 0 se ha raggiunto chi"""
        if self.distance(chi.getpos()) < 3:
            self.setpos(chi.getpos())
            return 0
        dir_topo = self.dirto(chi.getpos())
        if dir_topo < 180:
            self.left(randrange(5))
        else:
            self.right(randrange(5))
        self.forward(2)
        return 1

tp = TurtlePlane()
topo = Topo(plane=tp)
gatto = Gatto(plane=tp)
while gatto.insegui(topo):
    topo.scappa(gatto)

whereis

Scopo

Restituisce una coppia di numeri che indicano la distanza di un punto e l’angolo sotto cui Tartaruga lo vede.

Sintassi

<tarta>.whereis(<coordinate>)

Osservazioni

Il risultato di questo metodo è una coppia di numeri.

Esempio

Simula la ricerca in base all’udito.

from random import randrange
from math import pi, sin

def saltaacaso():
    """Sposta tartaruga in una posizione casuale."""
    tina.up()
    tina.setpos((randrange(-280, 280), randrange(-180, 180)))
    tina.down()

def bersaglio():
    """Disegna un bersaglio"""
    saltaacaso()
    for cont in xrange(4):
        tina.forward(5)
        tina.back(5)
        tina.left(90)
    larghezza = tina.getwidth()
    tina.setwidth(2)
    tina.ccircle(20)
    tina.setwidth(larghezza)
    tina.ccircle(10)
    return(tina.getpos())

def intensita_destra(distanza, angolo):
    """Restituisce un numero che indica l'intensita' con cui
    l'orecchio destro ode un suono proveniente da un bersaglio
    posto ad una certa distanza e con un certo angolo"""
    senangolo = sin((angolo % 360)*pi/180)
    if senangolo < 0:
        return -10.*senangolo/(distanza**2)
    else:
        return 5.*senangolo/(distanza**2)

def intensita_sinistra(distanza, angolo):
    """Restituisce un numero che indica l'intensita' con cui
    l'orecchio sinistro ode un suono proveniente da un bersaglio
    posto ad una certa distanza e con un certo angolo"""
    senangolo = sin((angolo % 360)*pi/180)
    if senangolo > 0:
        return 10.*senangolo/(distanza**2)
    else:
        return -5.*senangolo/(distanza**2)

def ricerca_per_udito(bersaglio):
    """Simula la ricerca di un bersaglio utilizzando l'udito"""
    tina.tracer(10)
    while tina.distance(bersaglio)>10:
        d, a = tina.whereis(bersaglio)
        tina.forward(randrange(5))
        if intensita_destra(d, a) > intensita_sinistra(d, a):
            tina.right(randrange(30))
        else:
            tina.left(randrange(30))

tp = TurtlePlane("Ricerca in base all'udito")
tina = tp.newTurtle()
b = bersaglio()
saltaacaso()
ricerca_per_udito(b)

lookat

Scopo

Ruota Tartaruga in modo che sia rivolta verso un certo punto.

Sintassi

<tarta>.lookat(<coordinate>)

Osservazioni

A differenza dei tre metodi precedenti, non dà risultato, ma provoca una rotazione di Tartaruga.

Esempio

Disegna una parabola per mezzo dell’inviluppo di rette.

def asse(punto):
    """Disegna l'asse del segmnento che ha per estremi Tartaruga e punto"""
    dir = tina.getdir()
    pos = tina.getpos()
    tina.up()
    tina.lookat(punto)
    tina.forward(tina.distance(punto)/2.)
    tina.left(90)
    tina.back(1000)
    tina.down()
    tina.forward(2000)
    tina.up()
    tina.setpos(pos)
    tina.setdir(dir)
    tina.down()

tp = TurtlePlane(name="Parabola formata da un inviluppo di rette")
tina = tp.newTurtle()
fuoco = (0, 50)
numpassi = 80
lato = 5
tina.up()
tina.back(200)
tina.down()
for i in range(numpassi):
    asse(fuoco)
    tina.forward(lato)

hide

Scopo

Nasconde la tartaruga.

Sintassi

<tarta>.hide()

Osservazioni

Il metodo non richiede argomenti.

Esempio

Vedi il metodo show

show

Scopo

Mostra la tartaruga.

Sintassi

<tarta>.show()

Osservazioni

Il metodo non richiede argomenti.

Esempio

Traccia una linea mostrando e nascondendo Tartaruga.

tp = TurtlePlane("Cucu")
tina = tp.newTurtle()
tina.up()
tina.back(295)
tina.down()
tina.tracer(10)
for i in range(15):
    tina.forward(19)
    tina.hide()
    tina.forward(19)
    tina.show()

clone

Scopo

Crea un clone della tartaruga.

Sintassi

<tarta>.clone()

Osservazioni

Non richiede argomenti.

Esempio

Fa collaborare alcune tartarughe per disegnare un albero.

tp = TurtlePlane("Albero realizzato da una famiglia di tartarughe",
                 w=500, h=500)
tp.newTurtle(y=-200, d=90, color='olive drab')
dim = 100
angl = 25
angr = 35
while dim > 5:
    for t in tp.turtles():
        t.setwidth(dim//5)
        t.forward(dim)
        t1 = t.clone()
        t.left(angl)
        t1.right(angr)
    tp.after(200)
    dim *= 0.7
    print len(tp.turtles())
for t in tp.turtles():
    t.setcolor('lime green')

delete

Scopo

Elimina la tartaruga dal piano.

Sintassi

<tarta>.delete()

Osservazioni

Il metodo non richiede argomenti.

Esempio

Crea 36 tartarughe all’interno dello stesso piano mettendole in una lista, poi le fa muovere.

tp = TurtlePlane("36 tartarughe che eseguono gli stessi comandi",
                 w=500, h=500, sx=20, sy=20, grid=True)
n = 36
l = 4.
for i in xrange(n):
    tp.newTurtle(color='#ff%02x00' % (i*256//n), d=(360/n*i), width=5)
for t in tp.turtles(): t.forward(l)
for t in tp.turtles(): t.left(90)
for t in tp.turtles(): t.forward(l/4)
for t in tp.turtles(): t.right(45)
for t in tp.turtles(): t.forward(l)
for t in tp.turtles(): t.right(120)
for t in tp.turtles(): t.forward(l/2)
for t in tp.turtles(): t.left(60)
for t in tp.turtles(): t.forward(l/2)
tp.after(500)
tp.clear()
for t in tp.turtles(): t.forward(2)
tp.after(500)
tp.reset()
for t in tp.turtles(): t.forward(5)
for t in tp.turtles():
    tp.after(100)
    t.delete()
tp.after(500)
tp.clear()

Elenco Attributi e Metodi di pyturtle

function

version
Restituisce il numero di versione della libreria.

TurtlePlane

__init__
Crea un piano in cui far muovere tartarughe.
clear
Cancella tutti i segni tracciati dalle tartarughe.
delturtles
Elimina tutte le tartarughe presenti in un piano.
newTurtle
Crea una nuova tartaruga cioè un nuovo oggetto della classe Turtle in questo piano.
reset
Ripulisce il piano riportando le tartarughe nella situazione iniziale.
turtles
Restituisce una lista che contiene tutte le tartarughe presenti in un piano.

Turtle

__init__
Crea una nuova Tartaruga.
back
Muove indietro il cursore grafico, Tartaruga, di un certo numero di passi.
ccircle
Disegna una circonferenza o un arco centrati nella posizione della tartaruga.
circle
Traccia una circonferenza o un arco di circonferenza.
clone
Crea un clone della tartaruga.
color
Colore della tartaruga.
degrees
Dopo la chiamata a questo metodo, Tartaruga misura gli angoli in gradi.
delete
Elimina la tartaruga dal piano.
dirto
Restituisce la direzione sotto cui Tartaruga vede un certo punto
distance
Restituisce la distanza tra Tartaruga e il punto dato come argomento.
down
Abbassa la penna di Tartaruga in modo che muovendosi lasci un segno.
fill
Permette di colorare l’interno delle figure realizzate dalla tartaruga.
forward
Muove in avanti il cursore grafico, Tartaruga, di un certo numero di passi.
getcolor
Restituisce l’attuale colore di Tartaruga.
getdir
Restituisce l’attuale direzione di Tartaruga.
getpos
Restituisce l’attuale posizione di Tartaruga.
getwidth
Restituisce l’attuale larghezza della penna.
hide
Nasconde la tartaruga.
left
Ruota verso sinistra la tartaruga di un certo angolo. All’avvio l’angolo viene misurato in gradi, ma si può anche dire alla tartaruga di misurarlo in radianti (vedi i metodi degree e radians).
lookat
Ruota Tartaruga in modo che sia rivolta verso un certo punto.
radians
Dopo aver invocato questo metodo, Tartaruga misura gli angoli in radianti.
right
Ruota verso destra la tartaruga di un certo angolo. All’avvio l’angolo viene misurato in gradi, ma si può anche dire alla tartaruga di misurarlo in radianti (vedi i metodi degree e radians).
setcolor
Modifica il colore di Tartaruga e della sua penna.
setdir
Ruota Tartaruga verso una certa direzione.
setpos
Sposta la tartaruga in una posizione relativa al sistema di riferimento assoluto relativo al piano in cui vive.
setwidth
Modifica la larghezza della traccia lasciata da Tartaruga.
show
Mostra la tartaruga.
tracer
Rallenta la velocità di Tartaruga.
up
Solleva la penna della tartaruga in modo che, muovendosi, non tracci alcun segno.
whereis
Restituisce una coppia di numeri che indicano la distanza di un punto e l’angolo sotto cui Tartaruga lo vede.
write
Scrive un testo sullo schermo a partire dalla posizione di Tartaruga.

Pyig

Dove viene presentata la libreria Pyig

Introduzione

Pyig è una libreria che implementa la geometria interattiva. pyig contiene una classe Interactiveplane che fornisce una finestra grafica dove realizzare le costruzioni geometriche e alcune altre classi che rappresentano gli elementi della geometria interattiva: punti, rette, circonferenze, ...

Gli elementi di base di questa geometria sono i punti liberi che possono venir trascinati con il mouse. Gli altri oggetti dipendono in modo diretto o indiretto dai punti base. Quando viene spostato un punto base, tutti gli oggetti che dipendono da lui vengono ridisegnati. Ad esempio se costruisco una retta passante per due punti, quando sposto uno di questi punti, anche la retta si muove. Il programma minimo che utilizza questa libreria è composto da tre istruzioni:

from pyig import *                 # Carica la libreria
ip = InteractivePlane('Primo')     # Crea un piano
ip.mainloop()                      # Rendi interattivo il piano

Questo programma non fa un gran che: disegna una finestra grafica con un piano cartesiano e la rende attiva. Se volessimo disegnare al suo interno anche una retta per due punti, potremmo scrivere il seguente programma:

from pyig import * # Carica la libreria ip = InteractivePlane(‘Secondo’) # Crea un piano p0 = ip.newPoint(-3, -5) # Disegna due punti p1 = ip.newPoint(2, 3) Line(p0, p1) # Disegna la retta ip.mainloop() # Rendi interattivo il piano

A seconda dell’ambiente in cui viene eseguito il programma, l’ultima istruzione può essere necessaria o, in certi casi, essere di intralcio. È importante osservare il comportamento di questo programma con e senza l’istruzione:

ip.mainloop()

e, in base a come si comporta il nostro sistema decidere se inserirla o no nel proprio programma.

Negli esempi che seguono non riporterò né l’istruzione che carica tutti gli oggetti della libreria:

from pyig import *

né l’struzione che rende attiva la finestra:

ip.mainloop()

La prima è comunque necessaria e l’ultima dipende da come è configurato il vostro sistema.

Nella libreria sono presenti:

  • Una funzione che restituisce il numero di versione corrente.
  • La classe InteractivePlane che è un piano cartesiano con in più alcuni attributi che contengono i valori predefiniti degli oggetti geometrici e alcuni metodi che permettono di creare gli oggetti liberi: punti e testo.
  • Un certo numero di classi che permettono di creare oggetti geometrici.

Di seguito vengonno riportati gli attributi e i metodi delle classi. Per ognuno verrà indicato:

  • lo scopo
  • la sintassi
  • alcune osservazioni
  • un esempio di uso

version

Scopo

È una funzione che restituisce il numero di versione della libreria.

Sintassi

version()

Osservazioni

Non richiede parametri, restituisce la stringa che contiene la versione.

Esempio

Controllare la versione della libreria.

import pyig
if pyig.version()<"02.05.00":
    print "versione un po' vecchiotta"
else:
    print "versione:", pyig.version()

InteractivePlane

InteractivePlane estende la classe Plane, quindi è in grado di fare tutto quello che fa Plane. Cambiano i valori predefiniti che vengono utilizzati quando viene creato un oggetto di questa classe e ci sono alcuni atri attributi e metodi.

In questa sezione presento solo le differenze e aggiunte alla classe Plane. Per tutto ciò che rimane invariato si veda il capitolo relativo a pycart.

Per poter creare oggetti della classi InteractivePlane bisogna importarla dalla libreria. In tutti gli esempi seguenti si suppone che sia già stato eseguito il comando:

from pyig import *

Se nell’esegure un esempio ottenete un messaggio che termina con: NameError: name 'InteractivePlane' is not defined, forse avete dimenticato di caricare la libreria con il comando scritto sopra.

Di seguito sono elencati alcuni attributi della classe InteractivePlane.

__init__

Scopo

Crea il piano cartesiano e inizializza gli attributi di InteractivePlane.

Sintassi

<nome_variabile> = InteractivePlane(<parametri>)

Osservazioni

Questo metodo non viene chiamato esplicitamente, ma viene eseguito quando si crea un oggetto di questa classe. L’intestazione di questo metodo è:

def __init__(self, name="Interactive geometry",
             w=600, h=600,
             sx=20, sy=None,
             ox=None, oy=None,
             axes=True, grid=True,
             axescolor='#080808', gridcolor='#080808',
             parent=None):

Si può vedere che presenta molti parametri tutti con un valore predefinito. Nel momento in cui si crea un piano cartesiano si possono quindi decidere le sue caratteristiche. Vediamole in dettaglio:

  • titolo della finestra, valore predefinito: “Interactive geometry”;
  • dimensione, valori predefiniti: larghezza=600, altezza=600;
  • scala di rappresentazione, valori predefiniti: una unità = 20 pixel;
  • posizione dell’origine, valore predefinito: il centro della finestra;
  • rappresentazione degli assi cartesiani, valore predefinito: True;
  • rappresentazione di una griglia di punti, valore predefinito: True;
  • colore degli assi valore predefinito: ‘#808080’ (grigio).
  • colore della griglia valore predefinito: ‘#808080’.
  • riferimento alla finestra che contiene il piano cartesiano, valore predefinito: None.

Poiché tutti i parametri hanno un valore predefinito, possiamo creare un oggetto della classe InteractivePlane senza specificare alcun argomento: verranno usati tutti i valori predefiniti. Oppure possiamo specificare per nome gli argomenti che vogliamo siano diversi dal comportamento predefinito, si vedano di seguito alcuni esempi.

Esempio

Si vedano tutti gli esempi seguenti.

Attributi

InteractivePlane ha alcuni attributi che possono essere modificati dagli utenti.

Scopo

Questi attributi definiscono alcuni valori predefiniti con i quali saranno creati i nuovi oggetti.

  • defvisible stabilisce se i nuovi oggetti creati saranno visibili o invisibili;
  • defwidth imposta la larghezza predefinita;
  • defwidthtext imposta la larghezza predefinita dei caratteri;
  • defcolor imposta il colore predefinito degli oggetti;
  • defdragcolor imposta il colore predefinito per gli oggetti che si possono trascinare con il mouse.
  • defintcolor imposta il colore predefinito per la superficie di circonferenze e poligoni.

Sintassi

<piano interattivo>.defvisible = v
<piano interattivo>.defwidth = w
<piano interattivo>.defwidthtext = W
<piano interattivo>.defcolor = c
<piano interattivo>.defdragcolor = c
<piano interattivo>.defintern = c

Osservazioni

  • v è un valore booleano, può essere True o False.
  • w è un numero che indica la larghezza in pixel.
  • c può essere:
    • una stringa nel formato: “#rrggbb” dove rr, gg e bb sono numeri esadecimali di due cifre che rappresentano rispettivamente le componenti rossa, verde, e blu del colore;
    • Una stringa contenente il nome di un colore;
    • Una terna di numeri nell’intervallo 0-1 rappresentanti le componenti rossa verde e blu.

Esempio

Disegna l’asse di un segmento usando linee di costruzioni sottili e grige.

ip = InteractivePlane('valori di default')
ip.defwidth = 5
a = ip.newPoint(2, 1, name='A')
b = ip.newPoint(5, 3, name='B')
ip.defcolor = 'turquoise'
Segment(a, b)
ip.defwidth = 1
ip.defcolor = 'gray40'
c0 = Circle(a, b)
c1 = Circle(b, a)
i0 = Intersection(c0, c1, -1)
i1 = Intersection(c0, c1, 1)
asse = Line(i0, i1, width=3, color='royal blue')

newText

Scopo

La geometria interattiva ha alcuni oggetti di base sono oggetti che non dipendono da altri. InteractivePlane ha alcuni metodi che permettono di creare questi oggetti. newText serve per creare un nuovo testo.

Sintassi

<piano>.newText(x, y, testo[, visible][, color][, width][, name])

Osservazioni

L’intestazione di questo metodo è:

def newText(self, x, y, text,
            visible=None, color=None, width=None, name=None):

Molti parametri hanno un valore predefinito, per creare un testo, bisogna specificare la posizione dove metterlo e la stringa che verrà visualizzata:

ip.newText(-3, 12, 'Titolo')

Esempio

Crea un testo nel piano interattivo.

ip = InteractivePlane('02: newText')
ip.newText(0, 13, 'Finestra (quasi) vuota',
           width=20, color='DarkOrchid3')

newPoint

Scopo

La geometria interattiva ha alcuni oggetti di base, oggetti che non dipendono da altri. InteractivePlane ha alcuni metodi che permettono di creare questi oggetti. newPoint serve per creare un nuovo punto.

Sintassi

<piano>.newPoint(x, y[, visible][, color][, width][, name])

Osservazioni

Normalmente è utile assegare il punto creato ad un identificatore.

L’intestazione di questo metodo è:

def newPoint(self, x, y,
             visible=None, color=None, width=None, name=None):

Molti parametri hanno un valore predefinito, per creare un punto, gli unici due argomenti necessari sono i valori delle coordinate:

a = ip.newPoint(3, -7)

Esempio

Crea un punto in una data posizione.

ip = InteractivePlane('03: newPoint')
ip.newText(0, 13, 'Finestra con un punto',
           width=20, color='DarkOrchid3')
p0 = ip.newPoint(-2, 7, width=8, name="P")

newVarText

Scopo

Un testo variabile è costituito da una stringa con alcuni segnaposti e da altrettanti dati. I segnaposti sono indicati dai due caratteri: “’‘%s’‘”. I dati si ricavano dagli oggetti geometrici presenti nel piano.

Sintassi

<piano>.newVarText(x, y, testo, dati[, visible][, color][, width][, name])

Osservazioni

L’intestazione di questo metodo è:

def newVarText(self, x, y, text, variables,
               visible=None, color=None, width=None, name=None):

In questo caso è necessario specificare la posizione dove dovrà essere visualizzata la stringa, il testo, e la (o le) variabile(i):

ip.newText(-3, 12, 'posizione: %s', p.coords())

Esempio

Visualizza alcune informazioni che dipendono da altri oggetti.

ip = InteractivePlane('04: newVarText')
ip.newText(0, 13, 'Finestra con un punto e le sue coordinate',
           width=20, color='DarkOrchid3')
p0 = ip.newPoint(-2, 7, width=8, name="P")
ip.newVarText(-5, -4, 'P = %s', p0.coords())

Point

Scopo

Crea un punto libero date le coordinate della sua posizione iniziale.

Questo oggetto è la base di ogni costruzione; dai punti liberi dipendono, direttamente o indirettamente, gli altri oggetti grafici.

Quando il puntatore del mouse si sovrappone ad un punto libero questo cambia colore. Trascinando un punto libero, con il mouse, tutti gli oggetti che dipendono da lui, verranno modificati.

Point essendo un oggetto che può essere trascinato con il mouse ha un colore predefinito diverso da quello degli altri oggetti.

Sintassi

Point(iplane, x, y[, visible][, color][, width][, name])

Spesso nella pratica è necessario assegare l’oggetto creato ad un identificatore in modo da poter fare riferimento ad un oggetto nella costruzione di altri oggetti:

  <identificatore> = Point(iplane, x, y[, visible][, color][, width][, name])

Si vedano gli esempi seguenti.

Osservazioni

  • iplane è un piano interattivo, gli oggetti liberi, che non dipendono da altri oggetti, devono specificare il piano interattivo in cui devono essere inseriti.
  • x e y sono due numeri interi o razionali relativi x è l’ascissa e y l’ordinata del punto.
  • Per quanto riguarda i parametri non obbligatori si veda quanto scritto nel paragrafo relativo alggli attributi degli oggetti visibili.

Note

Nel resto del manuale riporterò solo gli argomenti obbligatori, è sottinteso che tutti gli oggetti che possono essere visualizzati hanno anche i parametri: visible, color, width, name.

Esempio

Funzione definita in N ad andamento casuale.

import random
ip = InteractivePlane('12: Point')
y = 0
for x in xrange(-14, 14):
    y += random.randrange(-1, 2)
    Point(ip, x, y, color='red')

Operazioni con i punti

Scopo

Sono definite alcune operazioni tra punti: l’addizione, la sottrazione, la moltiplicazione per uno scalare e la moltiplicazione tra due punti.

Sintassi

<identificatore> = <punto> + <punto>
<identificatore> = <punto> - <punto>
<identificatore> = <punto> * <numero>
<identificatore> = <punto> * <punto>

Osservazioni

  • È possibile così scrivere espressioni tra punti.

Esempio

Crea due punti poi calcola la loro somma, differenza e prodotto.

ip = InteractivePlane('Point operations')
a = ip.newPoint(2, 1)
b = ip.newPoint(4, 4)
s = a+b
d = a-b
t = a*3
p = a*b
VarLabel(a, 10, -10, 'A%s', a.coords())
VarLabel(b, 10, -10, 'B%s', b.coords())
VarLabel(s, 10, -10, 'A+B%s', s.coords())
VarLabel(d, 10, -10, 'A-B%s', d.coords())
VarLabel(t, 10, -10, '3A%s', t.coords())
VarLabel(p, 10, -10, 'A*B%s', p.coords())

Attributi degli oggetti geometrici

Scopo

Tutti gli oggetti geometrici hanno degli attributi che possono essere determinati nel momento della creazione dell’oggetto stesso o in seguito. Questi attributi definiscono alcune caratteristiche degli oggetti.

  • visible stabilisce se l’oggetto sarà visibili o invisibile;
  • color imposta il colore dell’oggetto;
  • width imposta la larghezza dell’oggetto.
  • name imposta il nome dell’oggetto.

Sintassi

<oggetto>.visible = v
<oggetto>.color = c
<oggetto>.width = w
<oggetto>.name = s

Osservazioni

  • v è un valore booleano, può essere True o False.
  • w è un numero che indica la larghezza in pixel.
  • c può essere:
    • una stringa nel formato: “#rrggbb” dove rr, gg e bb sono numeri esadecimali di due cifre che rappresentano rispettivamente le componenti rossa, verde, e blu del colore;
    • Una stringa contenente il nome di un colore;
    • Una terna di numeri nell’intervallo 0-1 rappresentanti le componenti rossa verde e blu.
  • s è una stringa

Esempio

Disegna tre punti: uno con i valori di default, uno con colore dimensione e nome definiti quando viene creato, uno con valori cambiati dopo essere stato cerato.

ip = InteractivePlane('0: attributi')
a = ip.newPoint(-5, 3)
b = ip.newPoint(2, 3, color='indian red', width=8, name='B')
c = ip.newPoint(9, 3)
c.color = 'dark orange'
c.width = 8
c.name = 'C'

Metodi degli oggetti geometrici

Scopo

Tutti gli oggetti geometrici hanno dei metodi che danno come risultato alcune informazioni relative all’oggetto stesso.

  • xcoord l’ascissa;
  • ycoord l’ordinata;
  • coords le coordinate.

Sintassi

<oggetto>.xcoord()
<oggetto>.ycoord()
<oggetto>.coords()

Osservazioni

Non richiedono argomenti e restituiscono un particolare oggetto che può essere utilizzato all’interno di un testo variabile.

Esempio

Scrivi ascissa, ordinata e posizione di un punto.

ip = InteractivePlane('coords, xcoord, ycoord')
a = ip.newPoint(-5, 8, name='A')
ip.newVarText(-5, -1, 'ascissa di A: %s', a.xcoord())
ip.newVarText(-5, -2, 'ordinata di A: %s', a.ycoord())
ip.newVarText(-5, -3, 'posizione di A: %s', a.coords())

Segment

Scopo

Crea un segmento dati i due estremi, i due estremi sono punti.

Sintassi

<identificatore> = Segment(point0, point1)

Osservazioni

point0 e point1 sono due punti.

Esempio

Disegna un triangolo con i lati colorati in modo differente.

ip = InteractivePlane('13: Segment')
# creo i 3 vertici
v0 = Point(ip, -4, -3, width=5)
v1 = Point(ip, 5, -1, width=5)
v2 = Point(ip, 2, 6, width=5)
# creo i 3 lati
l0 = Segment(v0, v1, color='steel blue')
l1 = Segment(v1, v2, color='sea green')
l2 = Segment(v2, v0, color='saddle brown')

length

Scopo

È il metodo della classe Segment che restituisce un oggetto data contenete la lunghezza del segmento stesso.

Sintassi

<obj>.length()

Osservazioni

La lunghezza è la distanza tra point0 e point1.

Esempio

Disegna un segmento e scrivi la sua lunghezza.

ip = InteractivePlane('34: length')
p0 = Point(ip, -4, 7, width=5, name='A')
p1 = Point(ip, 8, 10, width=5, name='B')
seg = Segment(p0, p1)
VarText(ip, -5, -5, 'lunghezza di AB = %s', seg.length())

midpoint

Scopo

È il metodo della classe Segment che restituisce il punto medio del segmento.

Sintassi

<obj>.midpoint()

Osservazioni

L’oggetto restituito è un punto.

Esempio

Disegna un segmento e il suo punto medio.

ip = InteractivePlane('35: midpoint')
seg = Segment(Point(ip, -4, 7, width=5, name='A'),
              Point(ip, 8, 10, width=5, name='B'))
seg.midpoint(color='gold', width=10)

Line, Ray e Segment hanno dei metodi che forniscono degli oggetti che contengono alcune informazioni relative alla linea stessa. Vengono elencati di seguito.

equation

Scopo

È il metodo presente in tutte le classi linea che restituisce l’equazione esplicita della retta o della retta che a cui appartiene l’oggetto.

Sintassi

<obj>.equation()

Osservazioni

L’equazione è data sotto forma di stringa.

Esempio

Disegna una retta e scrivi la sua equazione .

ip = InteractivePlane('31: equation')
p0 = Point(ip, -4, 7, width=5)
p1 = Point(ip, 8, 10, width=5)
retta = Line(p0, p1, name='r')
VarText(ip, -5, -5, 'equazione di r: %s', retta.equation())

slope

Scopo

È il metodo presente in tutte le classi linea che restituisce la pendenza della retta o della retta che a cui appartiene l’oggetto.

Sintassi

<obj>.slope()

Osservazioni

Se la retta è verticale la pendenza restituita è None.

Esempio

Disegna una semiretta e scrivi la sua pendenza .

ip = InteractivePlane('32: slope')
p0 = Point(ip, -4, 7, width=5)
p1 = Point(ip, 8, 10, width=5)
semiretta = Ray(p0, p1, name='r')
VarText(ip, -5, -5, 'pendenza di r: %s', semiretta.slope())

point0 e point1

Scopo

Sono i metodi presenti in tutte le classi linea che restituiscono rispettivamente il punto0 e il punto1 dell’oggetto.

  • Il punto0 è il primo punto della retta passante per due punti, il primo estremo del segmento, l’origine della semiretta, il vertice dell’angolo per la bisettrice, il punto per cui passa la parallela o il punto in cui la perpendicolare interseca la retta.
  • Il punto1 è il secondo punto della retta passante per due punti, il secondo estremo del segmento, il punto per cui passa la semiretta, un punto della bisettrice, un punto della parallela o il punto per cui passa la perpendicolare.

Sintassi

<obj>.point0()
<obj>.point1()

Esempio

Disegna un segmento e scrivi le coordinate dei suoi estremi.

ip = InteractivePlane('33: point0 point1')
seg = Segment(Point(ip, -4, 7, width=5, name='A'),
              Point(ip, 8, 10, width=5, name='B'))
VarText(ip, -5, -5, 'A%s', seg.point0().coords())
VarText(ip, -5, -6, 'B%s', seg.point1().coords())

Polygon

Scopo

Crea un poligono data una sequenza di vertici.

Sintassi

Polygon(points)

Osservazioni

points è una sequenza di punti, può essere una lista (delimitata da parentesi quadre) o una tupla (delimitata da parentesi tonde).

Esempio

Disegna un poligono date le coordinate dei vertici.

ip = InteractivePlane('24: Polygon')
# Lista di coordinate
coords = ((-8, -3), (-6, -2), (-5, -2), (-4, 2), (-2, 3), (0, 4),
          (2, 3), (4, 2), (5, -2), (6, -2), (8, -3))
# Costruzione di una lista di punti partendo da una lista di coordinate:
# listcompreension
ip.defwidth = 5
points = [Point(ip, x, y) for x,y in coords]
Polygon(points, color='HotPink3')

perimeter e surface

Scopo

Sono metodi presenti in tutte le classi figura, restituiscono la lunghezza del contorno e l’area della superficie dell’oggetto.

Sintassi

<figura>.perimeter()
<figura>.surface()

Osservazioni

Sono metodi degli oggetti che sono figure piane e non richiede argomenti.

Esempio

Scrive alcune informazioni relative a un poligono.

ip = InteractivePlane('perimeter, surface')
# I tre vertici del triangolo
poli = Polygon((Point(ip, -7, -3, width=5, name="A"),
                Point(ip, 5, -5, width=5, name="B"),
                Point(ip, -3, 8, width=5, name="C")),
                width=4, color='magenta', intcolor='olive drab')
VarText(ip, -3, -6, "perimetro=%s", poli.perimeter(), color='magenta')
VarText(ip, -3, -7, "area=%s", poli.surface(), color='olive drab')

MidPoints

Scopo

Crea il punto medio tra due punti.

Sintassi

MidPoints(point0, point1)

Osservazioni

point0 e point1 sono due punti.

Esempio

Punto medio tra due punti.

ip = InteractivePlane('14: MidPoints')
# creo due punti
p0=Point(ip, -2, -5)
p1=Point(ip, 4, 7)
# cambio i loro attributi
p0.color="#00a600"
p0.width=5
p1.color="#006a00"
p1.width=5
# creao il punto medio tra p0 e p1
m=MidPoints(p0, p1, name='M')
# cambio gli attributi di m
m.setcolor("#f0f000")
m.setwidth(10)

MidPoint

Scopo

Crea il punto medio di un segmento

Sintassi

MidPoint(segment)

Osservazioni

segment è un oggetto che ha un point0 e un point1.

Esempio

Punto medio di un segmento.

ip = InteractivePlane('15: MidPoint')
# creo un segmento
s=Segment(Point(ip, -2, -1, color="#a60000", width=5),
          Point(ip, 5, 7, color="#6a0000", width=5),
          color="#a0a0a0")
# creo il suo punto medio
MidPoint(s, color="#6f6f00", width=10, name='M')

Line

Scopo

Crea una retta per due punti.

Sintassi

Line(point0, point1)

Osservazioni

point0 e point1 sono, indovina un po’, due punti.

Vedi anche i metodi delle classi linea presentati nella classe Segment.

Esempio

Triangolo delimitato da rette.

ip = InteractivePlane('16: Line')
# creo i 3 punti
a=Point(ip, 0, 0)
b=Point(ip, 1, 5)
c=Point(ip, 5, 1)
# creo i 3 lati
Line(a, b, color="#dead34")
Line(b, c, color="#dead34")
Line(c, a, color="#dead34")

Ray

Scopo

Traccia una semiretta con l’origine in un punto e passante per un altro punto.

Sintassi

Ray(point0, point1)

Osservazioni

point0 è l’origine della semiretta che passa per point1.

Vedi anche i metodi delle classi linea presentati nella classe Segment.

Esempio

Triangolo delimitato da semirette.”“” ip = InteractivePlane(‘17: Ray’) # creo i 3 punti a=Point(ip, 0, 0) b=Point(ip, 1, 5) c=Point(ip, 5, 1) # creo i 3 lati Ray(a, b, color=”#de34ad”) Ray(b, c, color=”#de34ad”) Ray(c, a, color=”#de34ad”)

Orthogonal

Scopo

Crea la retta perpendicolare ad una retta data passante per un punto.

Sintassi

Orthogonal(line, point)

Osservazioni

line è la retta alla quale si costruisce la perpendicolare passante per point.

Vedi anche i metodi delle classi linea presentati nella classe Segment.

Esempio

Disegna la perpendicolare ad una retta data passante per un punto.

ip = InteractivePlane('19: Orthogonal')
retta = Line(ip.newPoint(-4, -1, width=5),
             ip.newPoint(6, 2, width=5),
             width=3, color='DarkOrange1', name='r')
punto = ip.newPoint(-3, 5, width=5, name='P')
Orthogonal(retta, punto)

Parallel

Scopo

Crea la retta parallela ad una retta data passante per un punto.

Sintassi

Parallel(line, point)

Osservazioni

line è la retta alla quale si costruisce la parallela passante per point.

Vedi anche i metodi delle classi linea presentati nella classe Segment.

Esempio

Disegna la parallela ad una retta data passante per un punto.

ip = InteractivePlane('20: Parallel')
retta = Line(Point(ip, -4, -1, width=5),
             Point(ip, 6, 2, width=5),
             width=3, color='DarkOrange1', name='r')
punto = Point(ip, -3, 5, width=5, name='P')
Parallel(retta, punto)

Vector

Scopo

Vettore dati due punti.

Sintassi

<identificatore> = Vector(point0, point1)

Osservazioni

point0 e point1 sono due punti.

Esempio

Disegna un vettore dati i suoi estremi e visualizza le sue componenti.

ip = InteractivePlane(‘Vector’) a = Point(ip, -2, +3, width=5) b = Point(ip, 5, 7, width=5) v = Vector(a, b, color=’saddle brown’) VarText(ip, -5, -1, ‘componenti: %s’, v.components())

Circle

Scopo

Circonferenza dato il centro e un punto.

Sintassi

Circle(center, point)

Osservazioni

center è il centro della circonferenza passante per point.

Vedi anche i metodi delle classi figure piane presentati nella classe Polygon.

Esempio

Circonferenza passante per l’origine e per un punto P.

ip = InteractivePlane('21: Circle')
origine = Point(ip, 0, 0, visible=False, name="O")
p0 = Point(ip, -7, -3, width=5, name="P")
Circle(origine, p0, color="#c0c0de", width=4)

CircleCR

Scopo

Circonferenza dato il centro e il raggio.

Sintassi

CircleCR(center, segment)

Osservazioni

center è il centro della circonferenza che ha raggio lungo come segment.

Vedi anche i metodi delle classi figure piane presentati nella classe Polygon.

Esempio

Circonferenza passante per l’origine e di raggio assegnato.

ip = InteractivePlane('22: CircleCR')
origine = Point(ip, 0, 0, visible=False, name="O")
raggio = Segment(Point(ip, -7, 9, width=5, name="A"),
                 Point(ip, -4, 9, width=5, name="B"),)
CircleCR(origine, raggio, color="#c0c0de", width=4)

radius

Scopo

Restituisce un oggetto dato che contiene la lunghezza del raggio della circonferenza.

Sintassi

<circonferenza>.radius()

Osservazioni

È un metodo degli oggetti circonferenza e non richiede argomenti.

Esempio

Visualizza il raggio di una circonferenza.

ip = InteractivePlane('23: radius')
centro = Point(ip, 3, 4, name="C")
p0 = Point(ip, -5, 4, width=5, name="P")
c = Circle(centro, p0, color="#c0c0de", width=4)
VarText(ip, -5, -1, 'raggio: %s', c.radius())

Intersection

Scopo

Crea il punto di intersezione tra due rette.

Sintassi

Intersection(obj0, obj1)

Osservazioni

obj0 e obj1 possono essere rette o circonferenze.

Esempio

Disegna una circonferenza tangente a una retta.

ip = InteractivePlane('231: Intersection line line')
# Disegno retta e punto
retta = Line(Point(ip, -4, -1, width=5),
             Point(ip, 6, 2, width=5),
             width=3, color='DarkOrange1', name='r')
punto = Point(ip, -3, 5, width=5, name='P')
# trovo il punto di tangenza
perpendicolare = Orthogonal(retta, punto, width=1)
p_tang = Intersection(retta, perpendicolare, width=5)
# disegno la circonferenza
Circle(punto, p_tang, width=4, color='IndianRed')

Disegna il simmetrico di un punto rispetto ad una retta.

ip = InteractivePlane('232: Intersection line circle')
# disegno l'asse di simmetria e il punto
asse = Line(Point(ip, -4, -11, width=5),
            Point(ip, -2, 12, width=5),
            width=3, color='DarkOrange1', name='r')
punto = Point(ip, -7, 3, width=5, name='P')
# disegno la perpendicolare all'asse passante per il punto
perp = Orthogonal(asse, punto, width=1)
# trovo l'intersezione tra la perpendicolare e l'asse
piede = Intersection(perp, asse)
# disegno la circonferenza di centro piede e passante per punto
circ = Circle(piede, punto, width=1)
# trovo il simmetrico di punto rispetto a asse
Intersection(perp, circ, -1, width=5, color='DebianRed', name="P'")

Disegna un triangolo equilatero.

ip = InteractivePlane('233: Intersection circle circle')
# Disegno i due primi vertici
v0=Point(ip, -2, -1, width=5, name='A')
v1=Point(ip, 3, 2, width=5, name='B')
# Disegno le due circonferenze di centro p0 e p1 e passanti per p1 e p0
c0=Circle(v0, v1, width=1)
c1=Circle(v1, v0, width=1)
# terzo vertice: intersezione delle due circonferenze
v2=Intersection(c0, c1, 1, width=5, name='C')
# triangolo per i 3 punti
Polygon((v0, v1, v2), width=4, color='DarkSeaGreen4')

Text

Scopo

Crea un testo posizionato in un punto del piano.

Sintassi

Text(iplane, x, y, text)

Osservazioni

  • iplane è un piano interattivo, gli oggetti liberi, che non dipendono da altri oggetti, devono specificare il piano interattivo in cui devono essere inseriti.
  • x e y sono due numeri interi o razionali relativi x è l’ascissa e y l’ordinata del punto.
  • text è la stringa che verrà visualizzata.

Esempio

Scrive un titolo.

ip = InteractivePlane('25: Text')
Text(ip, -7, 13, "Prove di testo",
     color='blue violet', width=20)

Label

Scopo

Crea un testo legato a un oggetto.

Sintassi

Label(obj, x, y, text)

Osservazioni

La stringa contenuta in text viene posizionata alla distanza di x pixel in orizzontale e di y pixel in verticale dall’oggetto obj.

Esempio

Disegna un punto e gli appiccica un’etichetta.

ip = InteractivePlane('26: Label')
p0 = Point(ip, 7, 3, color='navy', width=10, name='A')
Label(p0, 0, 20, "colore di A = 'navy'")

VarText

Scopo

Crea un testo variabile. Il testo contiene dei “segnaposto” che verranno sostituiti con i valori prodotti dai dati presenti nel parametro variables.

Sintassi

VarText(iplane, x, y, text, variables)

Osservazioni

  • iplane è un piano interattivo, gli oggetti liberi, che non dipendono da altri oggetti, devono specificare il piano interattivo in cui devono essere inseriti.
  • x e y sono due numeri interi o razionali relativi x è l’ascissa e y l’ordinata del punto.
  • text è la stringa che contiene la parte costante e i segnaposto.
  • In genere i segnaposto saranno nella forma: “%s” che indica a Python di convertire in stringa il risultato prodotto dal dato.
  • variables è un dato o una tupla di dati.

Esempio

Un testo che riporta la posizione dei un punto.

ip = InteractivePlane('27: VarText')
p0 = Point(ip, 7, 3, color='green', width=10, name='A')
VarText(ip, -4, -3, "Posizione del punto A: (%s; %s)",
                (p0.xcoord(), p0.ycoord()),
                color='green', width=10)

VarLabel

Scopo

Testo variabile legato ad un oggetto.

Sintassi

VarLabel(obj, x, y, text, variables,)

Osservazioni

vedi le osservazioni di Label e VarText.

Esempio

Disegna un punto con un’ettichetta che riporta la sua posizione.

ip = InteractivePlane('28: VarLabel')
p0 = Point(ip, 7, 3, color='red', width=10, name='A')
VarLabel(p0, 0, -40,
         "A%s", p0.coords(), color='red', width='10')

PointOn

Scopo

Punto disegnato su un oggetto in una posizione fissa.

Sintassi

PointOn(obj, parameter)

Osservazioni

L’oggetto deve essere una linea: o una retta o una circonferenza, parameter è un numero che individua una precisa posizione sull’oggetto. Sia le rette sia le circonferenze hanno una loro metrica che è legata ai punti base dell’oggetto. Su una retta una semiretta o un segmento point0 corrisponde al parametro 0 mentre point1 corrisponde al parametro 1. Nelle circoferenze il punto di base della circonferenza stessa corrisponde al parametro 0 l’intera circonferenza vale 2. Il punto non può essere trascinato con il mouse.

Esempio

Disegna il simmetrico di un punto rispetto ad una retta.

ip = InteractivePlane('29: PointOn')
# disegno l'asse di simmetria e il punto
asse = Line(Point(ip, -4, -11, width=5),
            Point(ip, -2, 12, width=5),
            width=3, color='DarkOrange1', name='r')
punto = Point(ip, -7, 3, width=5, name='P')
# disegno la perpendicolare all'asse passante per il punto
perp = Orthogonal(asse, punto, width=1)
# trovo il simmetrico di punto rispetto a asse
PointOn(perp, -1, width=5, color='DebianRed', name="P'")
Text(ip, -5, -6, """P' è il simmetrico di P.""")

ConstrainedPoint

Scopo

Punto legato ad un oggetto.

Sintassi

ConstrainedPoint(obj, parameter)

Osservazioni

Per quanto riguarda parameter , valgono le osservazioni fatte per PoinOn. Questo punto però può essere trascinato con il mouse pur restando sempre sull’oggetto. Dato che può essere trascinato con il mouse ha un colore di default diverso da quello degli altri oggetti.

Esempio

Circonferenza e proiezioni sugli assi.

ip = InteractivePlane('30: ConstrainedPoint', sx=200)
# Circonferenza
origine = Point(ip, 0, 0, visible=False)
unix = Point(ip, 1, 0, visible=False)
uniy = Point(ip, 0, 1, visible=False)
circ = Circle(origine, unix, color="gray10")
# Punto sulla circonferenza
cursore = ConstrainedPoint(circ, 0, color='magenta', width=20)
# assi
assex = Line(origine, unix, visible=False)
assey = Line(origine, uniy, visible=False)
# proiezioni
py = Parallel(assey, cursore, visible=False)
hx = Intersection(assex, py, color='red', width=8)
px = Parallel(assex, cursore, visible=False)
hy = Intersection(assey, px, color='blue', width=8)

Rette, semirette, segmenti Un altro gruppo di classi hanno in comune la caratteristica di avere un’equazione, una pendenza, uno o due punti. Oltre a tutti i metodi che ereditano dagli oggetti visibili, queste classi, forniscono anche i seguenti metodi che restituiscono oggetti derivati dalla classe Data.

parameter

Scopo

I punti legati agli oggetti hanno un metodo che permette di ottenere il parametro.

Sintassi

<constrained point>.parameter()

Osservazioni

In PointOn il parametro è fissato nel momento della costruzione dell’oggetto. In ConstrainedPoint il parametro può essere variato trascinando il punto con il mouse.

Esempio

Scrivi i dati relativi a un punto collegato a un oggetto.

ip = InteractivePlane('31: parameter')
c0 = Circle(Point(ip, -6, 6, width=6), Point(ip, -1, 5, width=6))
c1 = Circle(Point(ip, 6, 6, width=6), Point(ip, 1, 5, width=6))
a = PointOn(c0, 0.5, name='A')
b = ConstrainedPoint(c1, 0.5, name='B')
ip.newVarText(-5, -1, 'ascissa di A: %s', a.xcoord())
ip.newVarText(-5, -2, 'ordinata di A: %s', a.ycoord())
ip.newVarText(-5, -3, 'posizione di A: %s', a.coords())
ip.newVarText(-5, -4, 'parametro di A: %s', a.parameter())
ip.newVarText(5, -1, 'ascissa di B: %s', b.xcoord())
ip.newVarText(5, -2, 'ordinata di B: %s', b.ycoord())
ip.newVarText(5, -3, 'posizione di B: %s', b.coords())
ip.newVarText(5, -4, 'parametro di B: %s', b.parameter())

Angle

Scopo

Angolo dati tre punti, il secondo punto rappresenta il vertice. Il verso di costruzione dell’angolo è quello antiorario.

Sintassi

Angle(point0, vertex, point1[, sides])

Osservazioni

L’argomento sides può valere:

  • True (o (0, 1)): vengono disegnati i lati;
  • 0: viene disegnato il lato 0;
  • 1: viene disegnato il lato 1;

Angle fornisce i seguenti metodi dal significato piuttosto evidente:

* ``extent``: ampiezza dell'angolo;
* ``bisector``: bisettrice;
* ``vertex``: il vertice;
* ``point0``: il punto base 0;
* ``point1``: il punto base 1;
* ``side0``: il lato 0;
* ``side1``: il lato1.

Esempio

Disegna un angolo e un angolo con i lati.

ip = InteractivePlane('36: Angle')
ip.defwidth = 5
a = Point(ip, -2, 4, color="#40c040", name="A")
b = Point(ip, -5, -2, color="#40c040", name="B")
c = Point(ip, -8, 6, color="#40c040", name="C")
d = Point(ip, 8, 6, color="#40c040", name="D")
e = Point(ip, 5, -2, color="#40c040", name="E")
f = Point(ip, 2, 4, color="#40c040", name="F")
# angolo senza i lati
Angle(a, b, c, color="#40c040")
Angle(d, e, f, color="#c04040", sides=True)

AngleRA

Scopo

Angolo dati una semiretta e un altro angolo. Questo angolo ha il vertice nell’origine della semiretta e l’ampiezza uguale a quella dell’angolo passato come parametro.

Sintassi

AngleRA(ray, angle)

Osservazioni

Vedi le osservazioni di Angle.

Esempio

Somma di due angoli.

ip = InteractivePlane('37: AngleRA')
# i 2 angoli di partenza
a = Angle(Point(ip, -3, 7), Point(ip, -7, 3), Point(ip, -6, 8),
        sides=(0, 1), color="#f09000", name='alfa')
b = Angle(Point(ip, 9, 2), Point(ip, 2, 2), Point(ip, 6, 7),
        sides=(0, 1), color="#0090f0", name='beta')
# Semiretta di base dell'angolo somma di a b
r = Ray(Point(ip, -11, -4), Point(ip, 3, -4), color="#90f000")
# la somma degli angoli
b1 = AngleRA(r, b, color="#0090f0")
l0 = b1.side0(width=3, color="#0090f0")
l1 = b1.side1(width=3, color="#0090f0")
a1 = AngleRA(l1, a, sides=True, color="#f09000")
Text(ip, -4, -7, "Somma di due angoli")

Bisector

Scopo

Retta bisettrice di un angolo.

Sintassi

Bisector(angle)

Osservazioni

Vedi Ray.

Esempio

Disegna l’incentro di un triangolo.

ip = InteractivePlane('36: Bisector')
# I tre vertici del triangolo
a=Point(ip, -7, -3, color="#40c040", width=5, name="A")
b=Point(ip, 5, -5, color="#40c040", width=5, name="B")
c=Point(ip, -3, 8, color="#40c040", width=5, name="C")
# Il triangolo
Polygon((a, b, c))
# Due angoli del triangolo
cba=Angle(c, b, a)
bac=Angle(b, a, c)
# Le bisettrici dei due angoli
b1=Bisector(cba, color="#a0c040")
b2=Bisector(bac, color="#a0c040")
# L'incentro
LineLineIntersection(b1, b2, color="#c040c0", width=5, name="I")

Polygonal

Scopo

Poligonale data una sequenza di vertici.

Sintassi

Polygon(points)

Osservazioni

Vedi Polygon.

Esempio

Disegna una linea spezzata aperta.

ip = InteractivePlane('Polygonal')
points=(Point(ip, -8, -3), Point(ip, -6, -2), Point(ip, -5, -2),
        Point(ip, -4, 2),  Point(ip, -2, 3),  Point(ip, 0, 4),
        Point(ip, 2, 3),   Point(ip, 4, 2),   Point(ip, 5, -2),
        Point(ip, 6, -2),  Point(ip, 8, -3))
Polygonal(points, color='saddle brown', width=4)

CurviLine

Scopo

Linea curva determinata da una sequenza di vertici.

Sintassi

CurviLine(points)

Osservazioni

Vedi Polygon.

Esempio

Disegna una linea spezzata aperta.

ip = InteractivePlane('CurviLine')
points=(Point(ip, -8, -3), Point(ip, -6, -2), Point(ip, -5, -2),
        Point(ip, -4, 2),  Point(ip, -2, 3),  Point(ip, 0, 4),
        Point(ip, 2, 3),   Point(ip, 4, 2),   Point(ip, 5, -2),
        Point(ip, 6, -2),  Point(ip, 8, -3))
CurviLine(points, color='goldenrod', width=4)

Calc

Scopo

Dato che contiene il risultato di un calcolo.

Sintassi

Calc(function, variables)

Osservazioni

  • function è una funzione python, al momento del calcolo alla funzione vengono passati come argomenti il contenuto di variables.
  • variables è un oggetto Data o una tupla che contiene oggetti Data. Il risultato è memorizzato all’interno dell’oggetto Calc e può essere visualizzato con VarText o utilizzato per definire la posizione di un punto.

Esempio

Calcolla il quadrato di un lato e la somma dei quadrati degli altri due di un triangolo.

ip = InteractivePlane('40: Calc')
Circle(Point(ip, 2, 4), Point(ip, -3, 4), width=1)
ip.setdefwidth = 5
a = Point(ip, -3, 4, name="A")
b = Point(ip, 7, 4, name="B")
c = Point(ip, -1, 8, name="C")
ab = Segment(a, b, color="#40c040")
bc = Segment(b, c, color="#c04040")
ca = Segment(c, a, color="#c04040")
q1 = Calc(lambda a: a*a, ab.length())
q2 = Calc(lambda a, b: a*a+b*b, (bc.length(), ca.length()))
VarText(ip, -5, -5, "ab^2 = %s", q1, color="#40c040")
VarText(ip, -5, -6, "bc^2 + ca^2 = %s", q2, color="#c04040")

Elenco Classi, Attributi e Metodi di pyig

Classi di pyig

Angle
Angolo dati tre punti, il secondo punto rappresenta il vertice. Il verso di costruzione dell’angolo è quello antiorario.
AngleRA
Angolo dati una semiretta e un altro angolo. Questo angolo ha il vertice nell’origine della semiretta e l’ampiezza uguale a quella dell’angolo passato come parametro.
Bisector
Retta bisettrice di un angolo.
Circle
Circonferenza dato il centro e un punto.
CircleCR
Circonferenza dato il centro e il raggio.
ConstrainedPoint
Punto legato ad un oggetto.
CurviLine
Linea curva determinata da una sequenza di vertici.
Calc
Dato che contiene il risultato di un calcolo.
InteractivePlane
Crea il piano cartesiano e inizializza gli attributi del piano.
Intersection
Crea il punto di intersezione tra due rette.
Label
Crea un testo legato a un oggetto.
Line
Crea una retta per due punti.
MidPoint
Crea il punto medio di un segmento
MidPoints
Crea il punto medio tra due punti.
Orthogonal
Crea la retta perpendicolare ad una retta data passante per un punto.
Parallel
Crea la retta parallela ad una retta data passante per un punto.
Point
Crea un punto libero date le coordinate della sua posizione iniziale.
PointOn
Punto disegnato su un oggetto in una posizione fissa.
Polygon
Crea un poligono data una sequenza di vertici.
Polygonal
Poligonale data una sequenza di vertici.
Ray
Traccia una semiretta con l’origine in un punto e passante per un altro punto.
Segment
Crea un segmento dati i due estremi, i due estremi sono punti.
Text
Crea un testo posizionato in un punto del piano.
VarText
Crea un testo variabile. Il testo contiene dei “segnaposto” che verranno sostituiti con i valori prodotti dai dati presenti nel parametro variables.
VarLabel
Testo variabile legato ad un oggetto.

Attributi

color
Attributo degli oggetti geometrici: imposta il colore dell’oggetto;
defvisible
Attributo del piano: stabilisce se i nuovi oggetti creati saranno, in modo predefinito, visibili o invisibili;
defwidth
Attributo del piano: imposta la larghezza predefinita;
defwidthtext
Attributo del piano: imposta la larghezza predefinita dei caratteri;
defcolor
Attributo del piano: imposta il colore predefinito degli oggetti;
defdragcolor
Attributo del piano: imposta il colore predefinito per gli oggetti che si possono trascinare con il mouse.
defintcolor
Attributo del piano: imposta il colore predefinito per la superficie di circonferenze e poligoni.
name
Attributo degli oggetti geometrici: imposta il nome dell’oggetto.
visible
Attributo degli oggetti geometrici: stabilisce se l’oggetto sarà visibili o invisibile;
width
Attributi degli oggetti geometrici: imposta la larghezza dell’oggetto.

Metodi

coords
Metodo degli oggetti visualizzabili: restituisce un dato che contiene le coordinate.
equation
Metodo delle classi linea che restituisce un oggetto data contenete l’equazione esplicita della retta a cui appartiene l’oggetto.
length
Metodo della classe Segment che restituisce un oggetto data contenete la lunghezza del segmento stesso.
midpoint
Metodo della classe Segment che restituisce il punto medio del segmento.
parameter
Metodo dei punti legati agli oggetti che restituisce un oggetto data contenete il parametro.
perimeter
Metodo delle classi figura che restituisce un oggetto data contenete la lunghezza del contorno dell’oggetto.
point0
Metodo delle classi linea che restituisce il punto0.
point1
Metodo delle classi linea che restituisce il punto1.
radius
Metodo delle classi circonferenza che restituisce un oggetto data che contiene la lunghezza del raggio della circonferenza.
slope
È il metodo presente in tutte le classi linea che restituisce la pendenza della retta o della retta che a cui appartiene l’oggetto.
surface
Metodo delle classi figura che restituisce un oggetto data contenete l’area della superficie dell’oggetto.
xcoord
Metodo degli oggetti visualizzabili: restituisce un dato che contiene l’ascissa.
ycoord
Metodo degli oggetti visualizzabili: restituisce un dato che contiene l’ordinata.

Indici e tavole