Wednesday, February 12, 2014

La Canvas, ovvero la 'tela' per disegnare in Tkinter

Quando lavorate in linea di comando, potete sostituire le Entry con le funzioni di input, e le Label con le istruzioni print. Quello che non potete sostituire è il disegno di grafici, disegni o schemi, per cui il widget Canvas risulta insostituibile.

Nella sua forma più semplice una canvas non è che un riquadro di dimensioni scelte in fase di configurazione, che permette di tracciare punti, linee, ovali, rettangoli e anche di renderizzare un testo. Unitamente alla libreria PIL (Python Image Library), consente di importare immagini di vario formato, che possono essere collocate staticamente nella canvas o fatte muovere con tastiera e mouse o cun un algoritmo.

Piuttosto di installare PIL, su Ubuntu, ho preferito un suo fork, pillow, che promette di essere maggiormente user friendly; perchè funzioni correttamente è pero' necessario preliminarmente (re)installare le librerie tk e tcl:

sudo apt-get install tk8.5-dev tcl8.5-dev
sudo pip install pillow

Per esemplificare l'uso della Canvas creiamo una piccola applicazione test con due Frame, la prima contiene una Canvas, la seconda due Button e due Label.

Nella Canvas simuliamo un circuito automobilistico con una Porsche Carrera, ripresa dall'alto, che gira in un'orbita fissa ma di cui possiamo variare la velocità. Non è il gioco più divertente dell'anno ma credo che abbia una discreta efficacia didattica. Rimando le spiegazioni al prossimo post.








  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# -*- coding: utf-8 -*-
import Tkinter
from PIL import Image, ImageTk
import math

class Carrera:
        """
        Classe utile per creare un'oggetto immagine della Porsche Carrera e ruotarla
        in relazione alla posizione angolare sulla traiettoria ellittica
        """
        
        def __init__(self, canvas, image, x, y, angolo):
                self.image = image
                self.image = self.image.rotate(angolo)
                self.car = ImageTk.PhotoImage(self.image)
                canvas.create_image(x, y, image=self.car, tag='myCarrera')

        def update(self, canvas, image, angolo):
                self.image = image
                self.image = self.image.rotate(angolo)
                self.car = ImageTk.PhotoImage(self.image)
                canvas.itemconfig('myCarrera', image=self.car)

                
      
class MyApp:

    def __init__(self):

        self.root = Tkinter.Tk()
        self.root.title('Indianapolis')
        self.coefficiente=1.0
        
        #Frame 1
        self.frame1 = Tkinter.Frame()
        self.frame1.pack(side='left')
        self.width = 600.0
        self.height= 600.0

        #Canvas
        self.canvas = Tkinter.Canvas(self.frame1, background = 'green',
                                     width=self.width, height=self.height)
        self.canvas.pack()
        
        self.eccentricita = .8
        self.angolo = 0
        # disegna il bordo esterno della pista
        coord=self.coord_ell(self.eccentricita, self.width, self.height, self.angolo, 100)
        self.circ1 = self.canvas.create_oval(coord[0],coord[1],coord[2],coord[3],
                                             dash = 10, fill='white', width=2)
        # disegna il bordo interno della pista
        coord=self.coord_ell(self.eccentricita, self.width, self.height, self.angolo, 60)
        self.circ2 = self.canvas.create_oval(coord[0],coord[1],coord[2],coord[3],
                                             dash = 10, fill='grey', width=1)

        # Car
        # usare l'istruzione seguente dopo aver salvato l'immagine della
        # Porsche Carrera che compare nella pagina del blog
        # nella stessa directory del file .py
        photo=Image.open('CarreraTop1.png') 
        self.carrera_photo=photo.resize((50,50),Image.ANTIALIAS)
        # traiettoria dell'auto
        coord=self.coord_ell(self.eccentricita, self.width, self.height, self.angolo, 90)
        self.carrera = Carrera(self.canvas, self.carrera_photo, coord[4], coord[5], self.angolo)
        # inizializza il timer
        self.root.after(50, self.muovi)

        #Frame2
        self.frame2 = Tkinter.Frame()
        self.frame2.pack(side='left')
        self.label1 = Tkinter.Label(self.frame2,
                                   text = 'Indianapolis\nclicca + per accelerare\n- per decelerare',
                                   font = 'Helvetica 14', width=20)
        self.label1.pack()
        # acceleratore
        self.button_acc=Tkinter.Button(self.frame2, text = '+',
                                       font = 'Courier 34', command=self.accelera)
        self.button_acc.pack(fill= 'x')
        # deceleratore
        self.button_dec=Tkinter.Button(self.frame2, text = '-',
                                       font = 'Courier 34', command=self.decelera)
        self.button_dec.pack(fill= 'x')
        # indica la velocità
        self.label2 = Tkinter.Label(self.frame2,
                                    text = 'velocità: 1.0', width=20)
        self.label2.pack()


    def coord_ell(self, e, width, height, angolo, perc):
        """ Funzione che, data l'eccentricità di un'ellisse, le dimensioni x e y della canvas,
        l'angolo al centro e la percentuale dimensionale, restituisce una tupla, con le coordinate
        x1 y1 x2 y2 (bbox) per ovali ed eventualmente rettangoli, le coordinate x e y dell'ellisse
        dato l'angolo al centro    
        """
        a=width/2*perc/100
        c=a*e
        b=math.sqrt(a*a-c*c)
        x1=width/2-a
        y1=(height/2-b)
        x2=width/2+a
        y2=(height/2+b)
        return (x1,y1,x2,y2,a*math.cos(angolo)+self.width/2,b*math.sin(angolo)+self.height/2)
        
    def muovi(self):
        """
        Posizionamento assoluto di un oggetto self.carrera in un orbita ellittica al 90% dell'ampiezza
        """
        self.angolo -= self.coefficiente*math.pi/180
        self.eccentricita = 0.9
        coord= self.coord_ell(self.eccentricita, self.width, self.height, self.angolo, 90)
        self.carrera.update(self.canvas, self.carrera_photo, -self.angolo*180/math.pi)
        self.canvas.coords('myCarrera',coord[4],coord[5])
        self.timer = self.root.after(50, self.muovi)

    def accelera(self):
        self.coefficiente*=1.1
        self.label2.config(text = 'velocità : + %2.2f' % (self.coefficiente))

    def decelera(self):
        self.coefficiente*=0.9
        self.label2.config(text = 'velocità : + %2.2f' % (self.coefficiente))


    def mainloop(self):
        self.root.mainloop()

 


app = MyApp()
app.mainloop()

No comments:

Post a Comment

How to create a virtual linux machine with qemu under Debian or Ubuntu with near native graphics performance

It's been a long time since my latest post. I know, I'm lazy. But every now and then I like to publish something that other people c...