[python] Esercizio su wxPython e PyOpenGL per disegnare un poligono dato il numero di lati

Quello che voglio realizzare oggi è un semplice programma in Python 2.7, con interfaccia grafica e che permetta di disegnare un poligono regolare dato il numero di lati. In questo esercizio vedremo quindi due cose:

  1. L’utilizzo di wxPython per creare un rudimentale programma con interfaccia grafica
  2. L’utilizzo di PyOpenGL per disegnare dentro ad un canvas una figura geometrica

Il programma che andremo a creare avrà alla fine questo aspetto, con uno spazio per inserire il numero di lati ed un canvas su cui disegnare:

Anzitutto assicuriamoci di disporre di entrambe le librerie, per farlo installiamole con pip eseguendo i seguenti due comandi da terminale:

Una volta installate le librerie possiamo cominciare a creare il nostro programma.

Per prima cosa creiamo una classe per la nostra applicazione, utilizzando wx.Frame e avviando la finestra principale. Il codice di cui avremo bisogno sarà il seguente:

In questo modo creiamo una finestra di dimensione 800 x 600 px con titolo Disegnatore. La nostra applicazione è un’estensione di wx.Frame, laddove un Frame è una finestra le cui dimensioni e posizione possono essere modificate dall’utente (insomma la classica finestra in Windows). Inoltre essa può contenere una barra del titolo, dei menu, dello stato e degli strumenti.

Con wx.App avviamo invece l’applicazione vera e propria, questa classe ci serve per lanciare il contenitore principale dell’applicazione e passiamo come argomento False (che sarebbe così anche di predefinito) per non reindirizzare lo sys.stdout e lo sys.stderr. Per maggiori informazioni rimando alla guida ufficiale su App.

A questo punto voglio aggiungere la casella di testo e il pulsante per impostare il numero di lati. Per farlo dobbiamo usare un wx.Panel, che sostanzialmente è il contenitore degli elementi di controllo e si trova tipicamente dentro un Frame. Per maggiori informazioni leggere qui su Panel.

Modifico quindi il codice nella maniera seguente:

E’ molto importante che il Panel venga istanziato prima del metodo aggPulsanti che poi aggiunge elementi al Panel. Arrivati a questo punto lanciamo il nostro programma e verifichiamo che abbia il seguente aspetto.

A questo punto è l’ora di aggiungere anche il Canvas che ci permetterà di mostrare grafica in OpenGL. Per farlo includiamo anzitutto le librerie necessarie:

Modifichiamo poi il codice nel modo seguente:

E’ importante stabilire le dimensioni del canvas in due variabili s.width e s.height per delle correzioni che apporteremo successivamente, per ora prendiamola per buona così.

A questo punto inizializziamo il canvas aggiungendo le seguenti istruzioni:

Faccio notare che il metodo glClearColor(1, 1, 1, 1) imposta il colore dello sfondo su bianco, mentre il metodo glClearColor(0, 0, 0, 1) lo imposta su nero. I primi tre parametri sono i valori di RGB da 0 a 1.

Siamo arrivati a buon punto e siamo praticamente pronti a disegnare il nostro poligono. Il nostro riferimento è uno spazio cartesiano con gli assi che attraversano a metà il canvas e il punto (0, 0) al centro del canvas stesso. Un poligono regolare ha tanti vertici quanti i lati e ogni vertice poggia su una circonferenza di raggio r.

Questo significa che un angolo α che passa da un vertice all’altro è dato dalla seguente equazione:

\alpha = 2 * \pi / n

Dove n è il numero di lati (o vertici) del poligono. Traduciamo questa formula in Python nel modo seguente:

Avremo bisogno della libreria math che importiamo con:

L’angolo di partenza è arbitrario, però per far sì che il lato superiore del poligono sia parallelo all’asse delle ascisse voglio prendere come angolo di partenza:

\beta = \pi / 2 - \pi / n

Quindi la nostra formula sarà:

Attenzione ad inserire i numeri con almeno una cifra decimale, anche se zero, per assicurarci che Python esegua tutti i calcoli con valori decimali e non tronchi ad interi.

Infine vogliamo definire un raggio fisso per i nostri poligoni, per farlo userò come riferimento l’apotema a (anche se sarebbe sufficienti lato e raggio) nel modo seguente:

apotema = 0.5

lato = apotema * ( 2.0 * \tan( \pi / n ) )

raggio = \sqrt{ (lato/2)^2+apotema^2 }

Il primo vertice avrà quindi coordinate:

( raggio * \cos( \beta ) , raggio * \sin( \beta ) )

Il secondo vertice sarà:

( raggio * \cos( \beta + \alpha ) , raggio * \sin( \beta + \alpha ) )

Il terzo vertice sarà:

( raggio * \cos( \beta + 2 * \alpha ) , raggio * \sin( \beta + 2 * \alpha ) )

E così via.

Traduciamo il tutto nel seguente metodo:

Spostiamoci sul metodo onDraw e modifichiamolo nel modo seguente:

Nel costruttore __init__ della classe aggiungiamo una variabile s.n per il numero di lati, subito dopo le dimensioni del canvas, nel modo seguente:

Se abbiamo fatto tutto correttamente dovremmo vedere qualcosa come questo:

Notiamo subito che il pentagono è deformato ed inoltre manca la possibilità di definire una dimensione a piacere.

Anzitutto occupiamoci della deformazione, essa dipende dal fatto che il canvas ha una dimensione fissa di 1 x 1, che poi viene ripartita in parti decimali. Quindi tale dimensione si adatta poi alle proporzioni del canvas. Per ottenere un pentagono regolare anzitutto dobbiamo introdurre un fattore correttivo, che nel nostro caso sarà dato da:

Aggiungiamo la variabile subito dopo larghezza e altezza impostate in __init__.

Modifichiamo il metodo per disegnare il poligono nel modo seguente:

Adesso il poligono risulta regolare:

Infine attribuiamo un valore personalizzato al numero di lati s.n del poligono. Aggiungiamo i seguenti due metodi alla nostra classe:

E dove abbiamo istanziato s.pulsante aggiungiamo:

In questo modo colleghiamo il metodo s.btn_calcola all’evento wx.EVT_BUTTON che corrisponde alla pressione del pulsante. Con il metodo verificaIntero controlliamo se il valore passato dall’utente sia intero. Se non è intero mostriamo, grazie a wx.MessageBox, un messaggio a video con notifica di errore.

Se abbiamo fatto tutto correttamente otterremo il seguente codice finale:

Se lo eseguiamo abbiamo il nostro programma che ci permetterà di disegnare qualunque poligono regolare:

Rispondi

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.