\documentclass{howto}
\usepackage[T1]{fontenc}
\usepackage[italian]{babel}

\title{Programmazione in curses con Python}

\release{2.01}

\author{A.M. Kuchling, Eric S. Raymond}
\authoraddress{\email{amk@amk.ca}, \email{esr@thyrsus.com}}

\begin{document}
\maketitle

\begin{abstract}
\noindent
Questo vademecum mostra come scrivere programmi in modalità testuale
con Python 2.x, usando il modulo di estensione \module{curses} per il
controllo dello schermo. Traduzione italiana a cura di Carlos
(enne.enne at tiscalinet.it) e Ferdinando Ferranti (zappagalattica at
inwind.it). 

\`E disponibile, in lingua originale, presso la pagina
dei  Python HOWTO \url{http://www.python.org/doc/howto}, la traduzione
presso la pagina \url{http://www.zonapython.it/doc/howto/}.
\end{abstract}

\tableofcontents

\section{Che cos'è curses?}

La libreria curses consente ai terminali testuali (come VT100, la
console di Linux e i terminali simulati di programmi per X11, quali
xterm e rxvt) di gestire schermo e tastiera indipendentemente dal
terminale. Gli schermi dei terminali supportano vari codici di
controllo per compiere operazioni comuni, come muovere il cursore,
scorrere lo schermo e cancellare aree. Terminali diversi usano codici
molto differenti e spesso hanno i propri piccoli capricci.

In un mondo di schermate di X, qualcuno dirà ``Embè?'' Ora, è vero che
i terminali a caratteri-cella sono tecnologia obsoleta, ma ci sono
ancora nicchie in cui la capacità di fare con essi ciò che si
preferisce è preziosa: per esempio, in piccoli sistemi e macchine con
Unix sprovvisti di serventi X, o quella di strumenti per installazione
di SO e configurazione del kernel, la cui esecuzione potrebbe rendersi
necessaria prima che X sia disponibile. 

La libreria curses nasconde i dettagli di tutti i diversi terminali e
dà al programmatore un porzione di schermo contenente numerose
finestre non sovrapposte. Il contenuto di una finestra può essere
cambiato in vari modi, (aggiungendo o cancellando del testo o
variandone l'aspetto) la libreria calcolerà automaticamente quali
codici di controllo inviare al terminale, per generare il giusto
risultato. 

Essa venne scritta, in origine, per BSD Unix; le successive versioni
System V di Unix, della AT\&T, aggiunsero migliorie e nuove
funzioni. Curses di BSD non è più mantenuta, poiché sostituita da
ncurses, realizzazione a sorgente aperto dell'interfaccia della
AT\&T. Un sistema Unix a sorgente aperto, come Linux o FreeBSD, quasi
sicuramente usa ncurses e, poiché le più diffuse versioni commerciali
di Unix sono basate su codice di tipo System V, probabilmente potrà
disporre di tutte le funzioni qui descritte. Ciononostante, può darsi
che versioni più vecchie di curses fornite da alcuni Unix proprietari
non garantiscano pieno supporto. 

Non esiste un port per Windows del modulo curses; per questa
piattaforma, si provi il modulo Console, scritto da Fredrik Lundh e
disponibile presso il sito \url{http://effbot.org/efflib/console}, che
dà un risultato testuale indirizzabile col cursore e un pieno supporto
per le istruzioni di mouse e tastiera.  


\subsection{Il modulo curses di Python}

Il modulo di Python è un semplicissimo involucro delle funzioni in C
di curses; avendo già nozioni di programmazione di curses in C, è
davvero facile trasferirle in Python: la più grande differenza è che 
l'interfaccia di Python facilita le cose, unendo varie funzioni di C,
come \function{addstr}, \function{mvaddstr}, \function{mvwaddstr}, nel
solo metodo \method{addstr()}. Ma di questo si tratterà oltre.

Questo vademecum, pur fornendo delle idee di base, è semplicemente
un'introduzione alla scrittura di programmi in modalità testuale con
curses e Python e non vuole essere un manuale completo alle API di 
curses; in proposito, cfr. la guida della libreria di Pyhton e le 
pagine di manuale di C su ncurses.


\section{Avviare e fermare un'applicazione in curses}

Anzitutto, curses dev'essere avviata; basta richiamare la funzione
\function{initscr()}, che determina il tipo di terminale, inviando ad
esso il codice di configurazione richiesto e creando varie strutture
di dati interni. In caso di successo, ne risulta un oggetto finestra
che rappresenta l'intero schermo; di solito, questo viene chiamato
\code{stdscr}, dal nome della corrispondente variabile  del linguaggio
C. 

\begin{verbatim}
import curses
stdscr = curses.initscr()
\end{verbatim}

Per lo più, le applicazioni in curses escludono la visualizzazione
automatica su schermo (echo) dei tasti premuti, e li mostra solo in
certe circostanze; a tal fine, bisogna richiamare la funzione
\function{noecho()}.  

\begin{verbatim}
curses.noecho()
\end{verbatim}

Grazie alla modalità chbreak, contrariamente alla consueta modalità
d'inserimento bufferizzata, le applicazioni reagiscono istantaneamente
ai tasti, senza richiedere che venga premuto quello di Invio (Enter).  

\begin{verbatim}
curses.cbreak()
\end{verbatim}

Abitualmente, i terminali convertono la pressione di tasti speciali,
come quelli di direzione o PgUp e Home, in forma di sequenze multiple
di caratteri  escape. Per evitarlo, si dovrebbe scrivere
l'applicazione in modo che le  riconosca e le tratti di conseguenza;
invece, abilitando la modalità keypad, curses provvede a ciò,
restituendo un valore speciale come \constant{curses.KEY_LEFT}. 

\begin{verbatim}
stdscr.keypad(1)
\end{verbatim}

Fermare un'applicazione in curses è più semplice che avviarla, basta
chiamare:

\begin{verbatim}
curses.nocbreak(); stdscr.keypad(0); curses.echo()
\end{verbatim}

per annullare le impostazioni di terminale adatte a curses. Si chiami
\function{endwin()} per ripristinare la modalità operativa d'origine. 

\begin{verbatim}
curses.endwin()
\end{verbatim}

Un problema frequente, in sede di verifica di un'applicazione in
curses, è ritrovarsi un terminale in disordine, quando l'applicazione
muore senza averlo riportato allo stato precedente. In Python, questo
accade quando il codice è bacato e solleva un'eccezione: per esempio
la digitazione potrebbe non produrre più alcun risultato sullo
schermo, e questo renderebbe difficile l'uso della shell. 

In Python si possono evitare tali complicazioni e facilitare di molto
la correzione, importando il modulo \module{curses.wrapper}, un
involucro (\function{wrapper}) per funzioni che richiedono un
argomento d'aggancio (hook). Esso compie l'avvio già descritto
(inizializzando anche i colori, se c'è il relativo supporto), esegue
quindi l'aggancio e, infine, arresta in modo corretto. L'aggancio è
richiamato all'interno di una clausola di verifica che intercetta le
eccezioni e arresta curses, scavalcando le eccezioni medesime. Così si
evita il blocco del terminale in un bizzarro stato interlocutorio.


\section{Finestre e taccuini}

In curses le finestre sono l'idea di base. Un oggetto finestra
rappresenta un'area rettangolare di schermo e permette all'utente di
visualizzare, cancellare, inserire stringhe di testo, e così via, in 
vari modi. 

L'oggetto \code{stdscr} restituito dalla funzione \function{initscr()}
è un oggetto finestra che copre l'intero schermo. A molti programmi
quest'unica finestra basta già, ma può anche darsi che si desideri
dividere lo schermo in finestre più piccole, per ridisegnarle o
svuotarle separatamente. La funzione \function{newwin()} crea una
nuova finestra di una certa dimensione, restituendo il nuovo oggetto
finestra. 

\begin{verbatim}
begin_x = 20 ; begin_y = 7
height = 5 ; width = 40
win = curses.newwin(height, width, begin_y, begin_x)
\end{verbatim}

A proposito del sistema di coordinate usato in curses: sono scritte
sempre nell'ordine \emph{y,x} e la coordinata 0,0 indica l'angolo
superiore sinistro; questo contravviene alla notazione comune, in cui
si segna per prima la coordinata \emph{x}; è un'infelice difformità
rispetto alla maggior parte delle altre applicazioni, ma fa parte di
curses dalla sua prima scrittura ed è tardi per cambiare.

Quando si invoca un metodo per visualizzare o cancellare del testo,
l'effetto non si manifesta immediatamente, poiché curses è stata
scritta, all'origine, pensando alle lente connessioni di terminale a
300 baud: con quei terminali, era molto importante minimizzare il
tempo necessario a ridisegnare lo schermo. Ciò permette a curses di
accumulare cambiamenti sullo schermo e visualizzarli nel modo più
efficiente. Per esempio, se un programma scrive dei caratteri in una
finestra ma la svuota subito, non c'è bisogno di visualizzarli, perché
non persisterebbero che per pochi istanti sullo schermo.

Di conseguenza, curses richiede che gli si dica esplicitamente di
ridisegnare le finestre, mediante il metodo \function{refresh()} degli
oggetti finestra. In pratica, ciò non complica di molto la
programmazione con curses. La maggior parte dei programmi ha
un'attività frenetica e poi si ferma in attesa di una digitazione o di
un'altra azione dell'utente. Bisogna solo assicurarsi che lo schermo
sia stato ridisegnato prima della pausa in attesa di istruzioni,
invocando \code{stdscr.refresh()} o il metodo \function{refresh()} per
qualche altra finestra rilevante. 

Un taccuino è una finestra di tipo speciale: può essere più ampio
dello schermo  reale ed essere visualizzato solo un settore per
volta. Creare un taccuino richiede che se ne stabiliscano l'altezza e
la larghezza, mentre il suo aggiornamento necessita delle coordinate
dell'area dello schermo in cui sia visualizzata una sua sezione. 

\begin{verbatim}
pad = curses.newpad(100, 100)
#  Questi cicli di azioni riempiono il taccuino di lettere; questo è
#  spiegato nella sezione seguente
for y in range(0, 100):
    for x in range(0, 100):
        try: pad.addch(y,x, ord('a') + (x*x+y*y) % 26 )
        except curses.error: pass

#  Visualizza una sezione di taccuino al centro dello schermo
pad.refresh( 0,0, 5,5, 20,75)
\end{verbatim}

L'invocazione di \function{refresh()} visualizza una sezione del
taccuino nel rettangolo fra le coordinate (5,5) e (20,75) sullo
schermo; l'angolo superiore sinistro della sezione visualizzata è il
punto (0,0) del taccuino. Eccettuata questa differenza, i taccuini
sono esattamente come le normali finestre e supportano gli stessi
metodi.

Se sullo schermo ci sono molte finestre e taccuini, un sistema più
efficiente evita il noioso sfarfallio dello schermo ad ogni
aggiornamento di schermo: si usino i metodi \method{noutrefresh()} e/o
\method{noutrefresh()} su ogni finestra, per aggiornare la struttura
di dati che indica lo stato desiderato dello schermo; quindi, si
modifichi di conseguenza lo schermo fisico in una sola volta chiamando
la funzione \function{doupdate()}. Il normale metodo
\method{refresh()} invoca \function{doupdate()} come passo finale. 

\section{Visualizzare del testo}

{}Dal punto di vista di un programmatore in C, curses può talvolta
sembrare un tortuoso dedalo di funzioni sottilmente diverse. Ad
esempio, \function{addstr()} crea una stringa all'attuale posizione
del cursore nella finestra \code{stdscr}, mentre \function{mvaddstr()}
va ad una certa coordinata y,x prima di visualizzare la
stringa. \function{waddstr()} è proprio come \function{addstr()}, ma
permette di specificare la finestra da usare, invece di usare a priori
\code{stdscr}. \function{mvwaddstr()} agisce di conseguenza. 

Fortunatamente, l'interfaccia di Python nasconde tutti i dettagli;
\code{stdscr} è una finestra come qualunque altra e metodi come
\function{addstr()} accettano diverse forme di argomento: di solito ce
ne sono quattro differenti. 

\begin{tableii}{|c|l|}{textrm}{Forma}{Descrizione}
\lineii{\var{str} o \var{ch}}{Visualizza la stringa str \var{str} o il
  carattere \var{ch}}
\lineii{\var{str} o \var{ch}, \var{attr}}{Visualizza la stringa
  \var{str} o il carattere \var{ch}, usando l'attributo \var{attr}}
\lineii{\var{y}, \var{x}, \var{str} o \var{ch}}
{Va alla posizione  \var{y,x} nella finestra e visualizza \var{str} o
  \var{ch}} 
\lineii{\var{y}, \var{x}, \var{str} o \var{ch}, \var{attr}}
{Va alla posizione \var{y,x} nella finestra e visualizza \var{str} o
  \var{ch}, usando l'attributo \var{attr}} 
\end{tableii}

Gli attributi permettono di visualizzare del testo evidenziato, sia
esso grassetto, sottolineato, in negativo o a colori. Cfr. la prossima
sezione per maggiori dettagli. 

La funzione \function{addstr()} assume una stringa di Python come
valore da visualizzare, la funzione \function{addch()} un carattere,
che può essere sia una stringa di Python di lunghezza 1, sia un numero
intero. Se è una stringa, si possono visualizzare caratteri solo fra 0
e 255. Curses SVr4 fornisce, come costanti per caratteri di
estensione, numeri interi maggiori di 255. Ad esempio,
\constant{ACS_PLMINUS} è un simbolo di +/- e \constant{ACS_ULCORNER} è
l'angolo superiore sinistro di una casella (utile, per disegnare
cornici). 

Le finestre conservano la posizione del cursore dopo l'ultima
operazione, così che, se non si danno nuove coordinate \var{y,x}, la
stringa o il carattere saranno visualizzati in quel punto. Si può
spostare il cursore anche con il metodo
\function{move(\var{y,x})}. Poiché alcuni terminali presentano sempre
un cursore lampeggiante, si potrebbe desiderare che lo facciano dove
non costituisca una distrazione, il che è meglio dell'avere un
cursore lampeggiante che appare a casaccio.

Se, poi, esso non è affatto essenziale per l'applicazione, si possono
invocare le funzioni \function{curs_set(0)} o, ciò che ha eguale
effetto, ma è compatibile con versioni più vecchie di curses,
\function{leaveok(\var{bool})}. Quando il valore \var{booleano} è
vero, curses cerca di eliminare il cursore lampeggiante e non ci si
deve più preoccupare di lasciarlo in punti strani.

\subsection{Attributi e colori}

I caratteri possono essere visualizzati in vari modi. Le linee di
stato, in un'applicazione testuale, di solito sono in negativo; ad un
visualizzatore di testo può essere necessario evidenziare certe
parole: curses supporta ciò, permettendo di specificare un attributo
per ogni cella dello schermo. 

Un attributo è un numero intero (ogni bit rappresenta un diverso
attributo); si può provare a visualizzare un testo con una serie di
bit dai molteplici attributi, ma curses non garantisce la
disponibilità o la distinzione visiva di tutte le loro possibili
combinazioni: dipende dalla flessibilità d'uso del terminale, per cui
meglio attenersi alla seguente tabella che elenca quelli più usati.

\begin{tableii}{|c|l|}{constant}{Attributo}{Descrizione}
\lineii{A_BLINK}{Testo lampeggiante}
\lineii{A_BOLD}{Evidenziato o grassetto}
\lineii{A_DIM}{Testo oscurato}
\lineii{A_REVERSE}{Testo in negativo}
\lineii{A_STANDOUT}{La migliore evidenziazione possibile}
\lineii{A_UNDERLINE}{Sottolineato}
\end{tableii}

Così, per visualizzare una linea di stato in negativo alla prima riga
dello schermo, si potrebbe digitare il seguente codice:

\begin{verbatim}
stdscr.addstr(0, 0, "Attuale modalità: Scrittura",
	      curses.A_REVERSE)
stdscr.refresh()
\end{verbatim}

La libreria curses supporta anche il colore, sui terminali che possono
offrirlo, il più comune dei quali è, probabilmente, la console di
linux, seguita dagli xterm a colori. 

Per usare i colori, subito dopo \function{initscr()} bisogna invocare
la funzione \function{start_color()}, che avvia la serie di colori
predefinita - a questo provvede automaticamente la funzione
\function{curses.wrapper.wrapper()}; ciò fatto, la funzione
\function{has_colors()} restituisce il valore TRUE (vero), se il
terminale in uso può visualizzare di fatto i colori. (Nota da AMK:
curses usa la grafia americana 'color', invece della Canadese/Inglese
'colour': ci si rassegni all'errore, in cambio delle funzioni).

La libreria di curses mantiene un numero limitato di coppie di colori,
che comprendono un colore in primo piano (o di testo) e uno di
sfondo. Con la funzione \function{color_pair()} si può ottenere il
valore di attributo corrispondente a una coppia di colori; mediante
bitwise-OR (``|''), questa può essere ancora arricchita di altri attributi
come \constant{A_REVERSE}, ma, ancora una volta, non è detto che tali
combinazioni funzionino su tutti i terminali. 

Un esempio, che visualizza una riga di testo usando la coppia di colori 1:

\begin{verbatim}
stdscr.addstr( "Forza Genoa", curses.color_pair(1) )
stdscr.refresh()
\end{verbatim}

Come già visto, una coppia di colori consta di uno in primo piano e uno
di sfondo. Quando attiva, la modalità colore, \function{start_color()}
avvia gli 8 colori base, cioè: 0:nero, 1:rosso, 2:verde, 3:giallo,
4:blu, 5:magenta, 6:azzurro e 7:bianco. Il modulo di curses definisce
per ognuno dei colori una costante che ne porta il nome inglese: 
\constant{curses.COLOR_BLACK}, \constant{curses.COLOR_RED}, e così
via. 

La funzione \function{init_pair(\var{n, f, b})} passa dalla
definizione della coppia di colori \var{n} ad una con {f} in primo
piano e {b} sullo sfondo. La coppia 0 è collegata rigidamente al
bianco o al nero e non può essere cambiata. 

Ricapitolando, per passare da colore 1 a testo rosso su sfondo bianco,
si dovrebbe invocare:  

\begin{verbatim}
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
\end{verbatim}

Quando si cambia una coppia di colori, qualunque testo visualizzato
con essa passerà ai nuovi, con i quali si può anche creare un nuovo
testo, tramite: 

\begin{verbatim}
stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1) )
\end{verbatim}

Terminali davvero fantastici possono passare dalle definizioni degli
attuali colori a un dato valore RGB; ciò permette di cambiare color 1
(solitamente rosso) in viola o blu o quello che si preferisce, ma
purtroppo la console di linux non lo consente, sicché non posso
provarla, né fornire alcun esempio. Si controlli se il proprio
terminale può farlo, invocando \function{can_change_color()}, che
restituisce TRUE in caso positivo. Se si ha la fortuna di avere un
terminale del genere, per maggiori ragguagli si vedano le pagine man
del sistema.


\section{Input dall'utente}

La libreria curses offre già dei semplici meccanismi di input; il
supporto di Python aggiunge dei widget text-input che ne colmano le
lacune. 

Il modo più comune per catturare un input in una finestra è usare il
suo metodo \method{getch()}, che opera una pausa e attende che
l'utente prema un tasto, visualizzandolo, se prima è stato invocato
\function{echo()}. A scelta, si può specificare la coordinata su cui
spostare il cursore prima della pausa.

Si può cambiare tale comportamento con il metodo
\method{nodelay()}. Dopo \method{nodelay(1)}, \method{getch()} per la
finestra diventa non-bloccante e restituisce ERR (-1) quando non ci
sono istruzioni. C'è anche la funzione \function{halfdelay()}, che può
essere usata per impostare (effettivamente) un intervallo per ogni
\method{getch()}; se entro il numero di millisecondi specificato come
argomento di \function{halfdelay()} non ci sono istruzioni
disponibili, curses solleva un'eccezione. 

Il metodo \method{getch()} restituisce un numero intero che, se
compreso fra 0 e 255, rappresenta il codice ASCII del tasto
premuto. Valori superiori a 255 indicano tasti speciali come Page Up,
Home, o i tasti cursore. Si può confrontare il valore restituito con
costanti come \constant{curses.KEY_PPAGE}, \constant{curses.KEY_HOME},
o \constant{curses.KEY_LEFT}. Di solito, il ciclo principale del
programma è simile a:  

\begin{verbatim}
while 1:
    c = stdscr.getch()
    if c == ord('p'): PrintDocument()
    elif c == ord('q'): break  # Esce dal ciclo while()
    elif c == curses.KEY_HOME: x = y = 0
\end{verbatim}

Il \module{curses.ascii} offre funzioni appartenenti alla classe
ASCII, che assumono come argomenti sia numeri interi, sia stringhe di
un carattere (queste possono essere utili per scrivere test più
leggibili dagli interpreti di comando) e funzioni di conversione che
assumono come argomenti sia numeri interi, sia stringhe di un
carattere ed in più restituiscono lo stesso tipo di dato. Ad esempio,
\function{curses.ascii.ctrl()} restituisce il carattere di controllo
corrispondente al suo argomento. 

Esiste anche un metodo per ritrovare un'intera stringa:
\constant{getstr()}. Non è usato spesso, avendo una funzionalità
limitata: i soli tasti disponibili sono quelli di cancella indietro
(backspace) e Invio (Enter), che chiude la stringa. A scelta, può
essere limitato a un dato numero di caratteri.

\begin{verbatim}
curses.echo()            # Abilita la visualizzazione dei caratteri

# ottiene una stringa di 15 caratteri col cursore sulla prima riga in alto
s = stdscr.getstr(0,0, 15)  
\end{verbatim}

Il modulo di Python \module{curses.textpad} offre qualcosa di meglio:
con esso, si può trasformare una finestra in una casella di testo che
supporta una serie di tasti-funzione in stile Emacs. Vari metodi della
classe \class{Textbox} supportano la modifica con convalida
dell'istruzione e raccolta dei risultati con o senza tenere traccia
degli spazi. Cfr. la documentazione della libreria su
\module{curses.textpad}. 


\section{Per maggiori informazioni}

Questo vademecum non ha affrontato alcuni argomenti avanzati, come il
``raschiare'' lo schermo o catturare gli eventi del mouse da un'istanza
xterm, ma ora la pagina della libreria di Python per il moduli di
curses è abbastanza completa. Sarebbe utile darle un'occhiata, in
futuro. 

In caso di dubbi sul preciso comportamento di qualunque voce di
ncurses, cfr. le pagine del manuale sull'esecuzione di curses, che si
tratti di ncurses o di Unix proprietari. Esse ne documenteranno i vari
ghiribizzi, fornendo un lista completa di tutte le funzioni, gli
attributi e i caratteri \constant{ACS_*} disponibili.

Siccome l'API di curses è così ampia, nell'interfaccia Python
alcune funzioni non sono supportate, non perché difficili da eseguire,
ma perché non se n'è ancora sentito il bisogno. Si aggiungano pure e
si invii la correzione relativa. Ancora, non c'è supporto per i menu o
le librerie per i pannelli associate a ncurses; chi vuole, può
aggiungerle. 

Se qualcuno scrive un programmino interessante, lo mandi come demo
senza alcun timore. Così possiamo usarne sempre di più!  

Le FAQ di ncurses: \url{http://dickey.his.com/ncurses/ncurses.faq.html}

\end{document}
