Abbiamo già visto che per scrivere qualcosa sulla finestra di pygame non possiamo usare la print()
di
Python, che continua a scrivere sulla finestra di IDLE. Essendo pygame una libreria orientata alla grafica, anche il testo viene
trattato come se fosse un'immagine: in pratica quando vogliamo scrivere del testo dovremo "disegnare" la nostra scritta su una
Surface.
Per fare questo esiste un oggetto apposito, l'oggetto Font. La prima cosa da fare è quindi creare una variabile di tipo Font: anche in questo caso, come per le immagini e i suoni, il Font deve essere "caricato" a partire da un file che ne contiene i dati codificati. Abbiamo due possibilità:
Font(name, size)
, che ha due parametri:
name
è il nome del file mentre size
è la grandezza del font in punti tipografici. Questa funzione è però
abbastanza difficile da usare, perchè i file dei fonts si trovano nelle cartelle di sistema di Windows o Linux (e non possono essere
spostati!), quindi trovare i loro nomi (con relativo percorso) è un'impresa abbastanza ardua. L'unico impiego pratico di questa
funzione è caricare il Font di default di pygame (piuttosto bruttino, in verità), dando come primo argomento None
(vedi l'esempio successivo).SysFont(name, size, bold=False, italic=False)
che è molto più
facile da usare perchè la stringa name
è il nome del Font nel sisema operativo. Quindi potremo usare i noti
"Times New Roman" oppure "Arial" ecc. Come vedete ci sono anche due argomenti opzionali, bold
per il neretto e
italic
per il corsivo. Se la funzione non trova il font richiesto (di solito perchè abbiamo sbagliato a scrivere il
nome) carica il font di default.Entrambe le funzioni si trovano nel sottomodulo font
. Facciamo qualche esempio:
font1 = pygame.font.Font(None, 24)
font2 = pygame.font.SysFont("Times New Roman", 12)
font3 = pygame.font.SysFont("Arial", 32, bold=True)
font4 = pygame.font.SysFont("Verdana", 12, italic=True)
La #1 carica il font di default di pygame, di grandezza 24 pt, la #2 carica il Times New Roman 12 pt (attenti alle maiuscole e minuscole!), la #3 il font Arial 32 pt neretto e la #4 il Verdana 12 pt corsivo. Notate nelle ultime due chiamate i parametri opzionali chiamati con il loro nome (keyword arguments) in modo da poter scrivere solo i parametri che ci interessano senza tener conto della loro posizione nella funzione.
Una volta caricato un font, per scrivere si usa il metodo render()
, che è così definto:
render(text, antialias, color, background=None) -> Surface
Il metodo accetta quindi quattro parametri:
Parametro | Tipo di dato | Significato |
---|---|---|
text |
string | Il testo da scrivere. Attenzione, la string deve essere unica e non possiamo quindi usarne molte
come facciamo nella print() . Se dobbiamo scrivere molte string di seguito dobbiamo concatenarle in
una sola con l'addizione tra string |
antialias |
booleano | Se è True viene usato l'antialiasing: questo renderà il testo più leggibile ma impegnerà più risorse di calcolo, perchè userà anche colori intermedi tra lo sfondo e il testo. A meno che non dobbiate fare moltissime scritte conviene mettere True per avere un effetto migliore |
color |
tuple (3 valori) o string | Il colore del testo (una tripla di interi in formato RGB o il suo nome) |
background |
tuple (3 valori) o string | Il colore dello sfondo (opzionale): se lo omettiamo lo sfondo risulterà trasparente |
Il metodo restituisce una Surface delle opportune dimensioni che contiene il testo; potremo poi trattarla come sappiamo già fare. Anche qui è meglio vedere subito un esempio:
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 600))
screen.fill((0, 0, 160))
# carichiamo il font e lo assegniamo alla variabile fnt
fnt = pygame.font.SysFont("Times New Roman", 24)
# scriviamo "Hello world!" e otteniamo la Surface surf_text
surf_text = fnt.render("Hello world!", True, "yellow")
# disegniamo surf_text sullo schermo
screen.blit(surf_text, (300, 300))
pygame.display.flip()
# ciclo principale
done = False
while not done:
for ev in pygame.event.get():
if ev.type == QUIT:
done = True
pygame.quit()
Questo semplice programmino scrive "Hello world!" in giallo su una finestra blu. Nella riga #9 abbiamo
caricato il font e lo abbiamo assegnato alla varibile fnt
, nella #12 usiamo la render()
,
che ci restituisce la Surface surf_text
con la scritta. Nella #15 disegniamo la Surface
nella finestra principale. Nel ciclo degli eventi non c'è bisogno di fare nessun aggiornamento perchè l'immagine è fissa.
screen
) ed usare il Rect per posizionare surf_text
nella blit()
(vedi qui).Se vi servono più scritte in diversi colori potete usare lo stesso Font, cambiando i parametri della render()
. Se
invece vi servono scritte di grandezze diverse avete due possibilità: o creare diversi oggetti Font con grandezze diverse o usare
la funzione pygame.transform.scale()
(che abbiamo già visto qui) per ingrandire
o rimpicciolire la Surface. Il primo metodo è più preciso perchè, come già ho detto, scalare una Surface può spesso portare a delle
distorsioni nell'immagine risultante.
Se invece vi serve lo stesso font, prima normale e poi corsivo (o neretto), potete usare alcuni metodi dell'oggetto Font, aggiunti a partire dalla versione 2 di pygame:
Metodo | Esempio | Significato |
---|---|---|
set_underline(bool) |
fnt.set_underline(True) |
Abilita o disabilita il testo sottolineato |
get_underline() |
fnt.get_underline() |
Restituisce True se la prossima render() scriverà il testo sottolineato |
set_bold(bool) |
fnt.set_bold(False) |
Abilita o disabilita il testo in neretto |
get_bold() |
fnt.get_bold() |
Restituisce True se la prossima render() scriverà il testo in neretto |
set_italic(bool) |
fnt.set_italic(True) |
Abilita o disabilita il testo in corsivo |
get_italic() |
fnt.get_italic() |
Restituisce True se la prossima render() scriverà il testo in corsivo |
La documentazione ufficiale avverte però che queste funzioni usano delle trasformazioni di pygame sui caratteri, e quindi non danno gli stessi risultati del font originale corsivo o neretto.
Modifichiamo infine il programma testo1.py per ripassare come vanno concatenate le string e i numeri: per immettere il nostro nome e la nostra età useremo ancora la finestra di IDLE (quando lanciamo il programma dobbiamo cliccare sulla finestra di IDLE e scrivere quello che ci chiede).
import pygame
from pygame.locals import *
name = input("Come ti chiami? ")
age = input("Quanti anni hai? ")
pygame.init()
screen = pygame.display.set_mode((800, 600))
screen.fill((0, 0, 160))
# carichiamo il font e lo assegniamo alla variabile fnt
fnt = pygame.font.SysFont("Times New Roman", 12)
# scriviamo la stringa s
s = name + " ha " + age + " anni"
surf_text = fnt.render(s, True, "yellow")
. . .
Notate nella #15 l'uso dell'addizione tra string e degli spazi (quando concateniamo le string gli
spazi non vengono aggiunti automaticamente come fa la print()
). Notate anche che nella #5
abbiamo immesso direttamente l'età come string, e non come numero (altrimenti avremmo dovuto scrivere
age = int(input("Quanti anni hai? "))
e poi riconvertire il numero in string con str(age)
).
Vediamo adesso come scrivere usando direttamente la tastiera del computer. E' chiaro che in questo caso il testo sarà dinamico, cambiando a seconda dei tasti che premiamo. Quindi dovremo monitorare il solito evento KEYDOWN nel ciclo degli eventi: ogni volta che premiamo un tasto dovremo riconoscere il tasto premuto, trasformare la lettera in una Surface con un oggetto Font e disegnare la Surface sullo schermo.
Dobbiamo ripassare bene (vedi qui) l'evento KEYDOWN. Ricordiamo che esso ha due
distinti attributi che permettono di identificare il tasto: l'attributo unicode
è una string che contiene la lettera
premuta (o è vuota se il tasto non è stampabile), mentre l'attributo key
è un codice numerico che identifica univocamente
ogni tasto, anche quelli non stampabili. Ecco un primo esempio:
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 600))
clk = pygame.time.Clock()
# carichiamo il font e lo assegniamo alla variabile fnt
fnt = pygame.font.SysFont("Times New Roman", 432)
# inizializziamo una string vuota
s = ""
# ciclo principale
done = False
while not done:
# sottociclo degli eventi
for ev in pygame.event.get():
if ev.type == QUIT:
done = True
elif ev.type == KEYDOWN:
s = ev.unicode
# scriviamo s e otteniamo la Surface surf_text
surf_text = fnt.render(s, True, "yellow")
# disegniamo surf_text sullo schermo
screen.fill((0, 0, 160))
screen.blit(surf_text, (100, 100))
pygame.display.flip()
clk.tick(30)
pygame.quit()
Analizziamo il programma:
s
(ricordate che in
Python ogni variabile deve essere inizializzata con un'assegnazione, provate a togliere questa riga ed avrete un errore). s
il
carattere corrispondente al tasto (cioè l'attributo unicode
dell'Event).render()
che ci restituisce una Surface con la scritta, e poi la
disegniamo sullo schermo con la solita blit()
.Se, anzichè stampare solo l'ultimo carattere, voleste stampare tutti i caratteri premuti, dovete fare solo una piccola modifica
alla riga #22: anzichè sostituire il vecchio valore di s
con il nuovo carattere in
ev.unicode
dovete sommarli (potete usare l'operatore +=
).
C'è ancora qualche problema, però. Il nostro programma intercetta anche i tasti non stampabili, come Esc, Ctrl, Invio; alcuni di
essi, come ho detto, producono una string unicode
vuota, mentre altri (che erano usati nelle telescriventi agli albori
dell'informatica) sono stampati come un quadratino. Vorremmo probabilmente che il programma ignorasse i caratteri non stampabilli.
Per migliorare il nostro programma dobbiamo chiedere aiuto non a pygame, ma a Python stesso: l'oggetto
<str>
di Python (cioè la nostra comune string) ha dei metodi che ci aiutano a riconoscere i caratteri presenti
nella string stessa. Eccone alcuni:
.isdigit() |
Restituisce True se la string è composta unicamente da numeri |
.isalpha() |
Restituisce True se la string è composta unicamente da lettere |
.isupper() |
Restituisce True se la string è composta unicamente da lettere maiuscole |
.islower() |
Restituisce True se la string è composta unicamente da lettere minuscole |
.isprintable() |
Restituisce True se la string è composta unicamente da caratteri stampabili |
Ripeto che queste funzioni sono metodi dell'oggetto str
(string), quindi
vanno chiamati usando la sintassi con il punto. Ad esempio, se ev
è un evento KEYDOWN
if ev.unicode.isprintable():
controlla se la string ev.unicode
contiene un carattere stampabile. Notate
di nuovo la "concatenazione" dell'operatore punto: l'istruzione applica il metodo isprintable()
all'attributo
unicode
dell'oggetto ev
.
and
anche la condizione che ev.unicode
sia
stampabile.Il nostro programma precedente potrebbe essere usato in un videogioco, ad esempio per far inserire al giocatore il proprio nome. Manca però ancora una cosa, cioè qualsiasi possibilità di correggere la scritta se dovessimo sbagliare. Cerchiamo di implementre quindi il BackSpace, cioè la possibilità di cancellare l'ultima lettera inserita.
Per controllare se l'utente ha premuto BackSpace non possiamo più utilizzare l'attributo unicode
ma dobbiamo usare
l'attributo key
, che permette di identificare anche i tasti di controllo.
Ricordo che qui http://www.pygame.org/docs/ref/key.html potete trovare l'elenco di tutti i codici dei tasti. A noi interessa il K_BACKSPACE: se esso viene premuto dobbiamo cancellare l'ultimo carattere; ecco il ciclo degli eventi modificato.
. . .
elif ev.type == KEYDOWN: # e' stato premuto un tasto
if ev.key == K_BACKSPACE: # se e' il backspace ...
s = s[0:-1] # slice notation
elif ev.unicode.isprintable() # altrimenti contolliamo se e' stampabile ...
s += ev.unicode # ... e lo aggiungiamo alla nostra stringa
. . .
Nella riga #23 ho usato due sintassi che sono proprie di Python: gli indici negativi
(s[-1]
è l'ultimo carattere della string, s[-2]
il penultimo e così via) e la slice
notation (che significa "notazione a fettine": s[0:-1]
è la string s
dal primo carattere al penultimo).
Se non conoscete bene queste sintassi potete consultare la Lezione 1 del mio tutorial
intermedio. In pratica nella riga #23 prendiamo tutta la string s
meno l'ultimo carattere e la
riassegniamo a sè stessa (se il primo indice è zero si può anche omettere, quindi avremmo potuto anche scrivere
s[:-1]
).
s[:-1]
con s[1:]
e vedete cosa succede. Perchè?text_color
per indicare il colore
di stampa nella render()
;text_color
(ad esempio una string con
il nome di un colore);elif
,
uno per ciascuno dei tasti F1 - F4 (cercate i codici ev.key
nella documentazione). Per ognuno di essi assegnate alla
variabile text_color
un colore diverso.Fine della lezione