126
126
it
Poste Italiane S.p.ASpedizione in A.P. D.L. 353/2003 (conv.in L.27/02/2004 n.46) art.1 comma 2 DCB ROMA Periodicit mensile APRILE 2007 ANNO XI N.4 (113)
PER ESPERTI E PRINCIPIANTI
RIVISTA+CD E4,90 RIVISTA+LIBRO+CD E7,90
VERSIONE PLUS VERSIONE STANDARD
Poste Italiane S.p.A Spedizione in A.P. D.L. 353/2003 (conv.in L.27/02/2004 n.46) art.1 comma 2 DCB ROMA Periodicit mensile MAGGIO 2008 ANNO XII, N.5 (126)
PER ESPERTI E PRINCIPIANTI
SOLUZIONI RIPORTA IN UN LOG I DATI DI UNA PARTITA DI TENNIS VIRTUALE
ANALIZZALI E RECUPERA LE INFORMAZIONI STATISTICHE SULL'ANDAMENTO DEL GIOCO
DRAG & DROP CON SCRIPTACULOUS
SPOSTA GLI ELEMENTI DI UNA PAGINA INTERNET
SEMPLICEMENTE TRASCINANDOLI
i
o
P
r
o
g
r
a
m
m
o
A
n
n
o
X
I
I
-
N
5
(
1
2
6
)
E
D
I
Z
I
O
N
I
M
A
S
T
E
R
W
O
R
D
&
E
X
C
E
L
L
D
A
L
W
E
B
M
P
3
F
L
A
S
H
P
L
A
Y
E
R
D
R
A
G
&
D
R
O
P
C
O
N
S
C
R
I
P
T
A
C
U
L
O
U
S
S
T
O
P
A
L
L
E
T
A
B
E
L
L
E
.
L
'
O
R
A
D
E
I
C
S
S
VISUAL BASIC.NET
COMPONI REPORT
EFFICACI CON CRYSTAL
REPORTS
Dai dati alla loro rappresentazione
come farlo in modo semplice
ASP.NET
UN SERVIZIO DI FAX
CENTRALIZZATO
Primi passi per costruire
un'applicazione per l'invio
dei documenti via Web
DEPLOYMENT
DELLE APPLICAZIONI
Ti spieghiamo come mettere online
il sito che hai sviluppato in locale
C#
UN CATTURA SCHERMO
TUTTO TUO
Scopri come funzionano le librerie
per la gestione della grafica
in ambiente Windows
RUBY ON RAILS
IL CORSO PER
IMPARARE SUBITO
In questa puntata: database
e relazioni, come gestirli con
poche e chiare righe di codice
MP3 FLASH PLAYER
Come sviluppare il riproduttore perfetto, personalizzando
le funzionalit e la grafica esattamente come piace a te!
TEORIA Anatomia
dei formati XLSX e DOCX
SISTEMA Il setup dell'ambiente
e le operazioni preliminari
CODICE Le librerie da utilizzare
e gli algoritmi di base
ESEMPI Gestisci le celle
di Excel, inserisci immagini
in Word e molto altro ancora...
WORD & EXCEL
DAL WEB
CREA DOCUMENTI OFFICE DIRETTAMENTE
DALLE TUE APPLICAZIONI PHP
JAVA
WICKET: IL NUOVO
WEB FRAMEWORK
Pratico, veloce, innovativo:
noi ti spieghiamo come utilizzarlo
STOP ALLE TABELLE
E L'ORA DEI CSS
Dalla programmazione classica
a quella moderna con i DIV e gli stili
WEB DESIGN
1
2
SN
IP
P
ETT
D
I CO
D
IC
E
da usare subito nei tuoi lavori
TU
TTI i CO
D
ICI
pronti alluso
nel CD
IL CODICE
COMPLETO
NEL CD
Per questo m
ese
ioProgram
m
o + CD
a soli 4,90 anzich 6,90
PREZZO PROM
OZIONE
PREZZO PROM
OZIONE
001_ioprog126.indd 1 2-04-2008 17:21:29
Anno XII - N.ro 05 (126) - Maggio 2008 -
Periodicit: Mensile
Reg. Trib. di CS al n.ro 593 del 11 Febbraio 1997
Cod. ISSN 1128-594X
E-mail: [email protected]
http://www.edmaster.it/ioprogrammo
http://www.ioprogrammo.it
Direttore Editoriale: Massimo Sesti
Direttore Responsabile: Massimo Sesti
Responsabile Editoriale: Gianmarco Bruni
Vice Publisher: Paolo Soldan
Redazione: Fabio Farnesi
Collaboratori: R. Allegra, O Peli, F. Grimaldi,
C. Lollo, E. Bottari, P. Perrotta, G. Fiore
Consulenza Redazionale: SET S.r.l.
G. Forlino, P. Mannelli
Segreteria di Redazione: Emanuela Giordano
Realizzazione grafica: Cromatika S.r.l.
Art Director: Paolo Cristiano
Responsabile grafico di progetto: Salvatore Vuono
Responsabile area tecnica: Giancarlo Sicilia
Illustrazioni: M. Veltri
Impaginazione elettronica: Francesco Cospite, Lisa Orrico,
Nuccia Marra, Luigi Ferraro
Realizzazione Multimediale: SET S.r.l.
Realizzazione CD-Rom: Paolo Iacona
Pubblicit: Master Advertising s.r.l.
Via C. Correnti, 1 - 20123 Milano
Tel. 02 831212 - Fax 02 83121207
e-mail [email protected]
Sales Director: Max Scortegagna
Editore: Edizioni Master S.p.a.
Sede di Milano: Via Ariberto, 24 - 20123 Milano
Sede di Rende: C.da Lecco, zona ind. - 87036 Rende (CS)
Presidente e Amministratore Delegato: Massimo Sesti
Direttore Generale: Massimo Rizzo
ABBONAMENTO E ARRETRATI
ITALIA: Abbonamento Annuale: IOPROGRAMMO (11 NUMERI)
E59,90 SCONTO 21% SUL PREZZO DI COPERTINA DI E75,90
IOPROGRAMMO CON LIBRO (11 NUMERI) E75,90 SCONTO 30%
SUL PREZZO DI COPERTINA DI E108,90
Abbonamento Biennale: IOPROGRAMMO (22 NUMERI) E75,90
SCONTO 50% SUL PREZZO DI COPERTINA DI E151,80
IOPROGRAMMO CON LIBRO (22 NUMERI) E108,90 SCONTO 50%
SUL PREZZO DI COPERTINA DI E217,80 OFFERTE VALIDE FINO AL
30/06/2008.
Costo arretrati (a copia): il doppio del prezzo di copertina + E
5. 32 spese (spedizione con corriere). Prima di inviare i
pagamenti, verificare la disponibilit delle copie arretrate allo 02
831212.
La richiesta contenente i Vs. dati anagrafici e il nome della riv-
ista, dovr essere inviata via fax allo 02 83121206, oppure via
posta a EDIZIONI MASTER via C. Correnti, 1 - 20123 Milano, dopo
avere effettuato il pagamento, secondo le modalit di seguito
elencate:
Tempo di realizzazione
REQUISITI
020-026:072-080 1-04-2008 16:09 Pagina 20
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
21
G
I nuovi formati di Microsoft gestiti da PHP
ESTENSIONE DELLA
CLASSE PHPDOCX PER
GESTIRE UNA TABELLA
In base alla sequenza vista, la classe verr estesa con
un meccanismo analogo a quello usato per la gestio-
ne del testo, ovvero avremo: un metodo per inizializ-
zare la tabella, uno per inizializzare la riga, uno per
inizializzare la singola cella ed i corrispondenti meto-
di che chiudono correttamente i tag aperti. Comin-
ciamo a vedere il primo:
function start_table($n_colonne,$borders) {
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
// Apertura del file in append
$handle = fopen($newfile, 'a');
$this->tabelle = 1;
$content = "<w:tbl>\n<w:tblPr>\n<w:tblStyle
w:val=\"TableGrid\"/>\n<w:tblW w:w=\"0\"
w:type=\"auto\"/>\n";
if ( $borders == 0 ) {
$content .= "<w:tblBorders>\n";
$content .= "<w:top w:val=\"none\" w:sz=
\"0\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:left w:val=\"none\" w:sz=
\"0\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:bottom w:val=\"none\" w:
sz=\"0\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:right w:val=\"none\" w:
sz=\"0\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:insideH w:val=\"none\" w:
sz=\"0\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:insideV w:val=\"none\" w:
sz=\"0\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "</w:tblBorders>\n";
} else {
$content .= "<w:tblBorders>\n";
$content .= "<w:top w:val=\"single\" w:sz=
\"4\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:left w:val=\"single\" w:sz=
\"4\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:bottom w:val=\"single\"
w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:right w:val=\"single\" w:
sz=\"4\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:insideH w:val=\"single\"
w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "<w:insideV w:val=\"single\"
w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>\n";
$content .= "</w:tblBorders>\n";
}
$content .= "</w:tblPr>\n";
$n_colonne = intval($n_colonne);
$dimensione_cella = 9780/$n_colonne;
$dimensione_cella = intval($dimensione_cella);
$content .= "<w:tblGrid>\n";
for ( $i = 0; $i < $n_colonne; $i++ ) {
$content .= "<w:gridCol w:w=\"" .
$dimensione_cella . "\"/>\n";
}
$content .= "</w:tblGrid>\n";
$this->dimensione_cella = $dimensione_cella;
fwrite($handle, $content);
fclose($handle);
}
La funzione vista apre il tagdella tabella e definisce, tra-
mite la sequenza
<w:tblPr>
...
</w:tblPr>
una serie di propriet. La prima di questa lo stile del-
la tabella, che prevede una griglia fissa,
<w:tblStyle w:val="TableGrid"/>
seguita da una indicazione della larghezza, che faremo
in modo sia auto-adattante rispetto alla pagina.
<w:tblW w:w="0" w:type="auto"/>
Come detto, vogliamo poi poter definire la presenza dei
bordi. In base al parametro passato attiveremo quin-
di una sequenza di tag, dove possiamo indicare i vari
tipi di bordi possibili e per ognuno di questi sar pos-
sibile indicare il tipo di linea del bordo (w:val), quan-
to questa deve essere spessa (w:sz), il colore (w:color)
e lo spazio di offset (w:space). Per fare in modo di ave-
re una griglia fissa a celle tutte eguali, la successiva se-
quenza di tag <w:tblGrid> </w:tblGrid> deve conte-
nere le indicazioni di dimensioni delle colonne con
larghezza data dalla dimensione massima possibile
per una riga, divisa per il numero di colonne. In ter-
mini del WordprocessingML, una pagina A4 larga
10296 ventesimi di pollice (tale lunit di misura).
Considerando per i margini standard con cui si scri-
ve, tale larghezza si abbassa a 9780 ventesimi di polli-
ce. Per cui in una tabella di N colonne le celle avranno
una larghezza di 9780/N.
Il secondo metodo, per definire la riga, conterr sem-
plicemente il tag di apertura della riga stessa ed un
tag delle propriet della riga che permette di indicare
che non deve essere possibile suddividere la stessa ri-
ga in pagine diverse (cosa che accade in caso di celle
con contenuto troppo lungo, che possono eventualmente
finire su due pagine diverse):
function add_row() {
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
020-026:072-080 1-04-2008 16:09 Pagina 21
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web M
G
22
/Maggio 2008
I nuovi formati di Microsoft gestiti da PHP
// Apertura del file in append
$handle = fopen($newfile, 'a');
$content = "<w:tr> <w:trPr><w:cantSplit />
</w:trPr>";
fwrite($handle, $content);
fclose($handle);
}
Mentre quello che definisce una cella, considerando
il requisito di poter gestire un bordo inferiore pi mar-
cato, sar:
function add_cell($border) {
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
// Apertura del file in append
$handle = fopen($newfile, 'a');
$content = "<w:tc>\n";
$content .= "<w:tcPr>\n";
$content .= "<w:tcW w:w=\"" . $this->
dimensione_cella . "\" w:type=\"dxa\"/>\n";
if ( $border == 1 ) {
$content .= "<w:tcBorders>\n";
$content .= "<w:bottom w:val=\"thinThick
SmallGap\" w:sz=\"24\" w:space=\"0\"
w:color=\"auto\"/>\n";
$content .= "</w:tcBorders>\n";
}
$content .= "</w:tcPr>\n";
fwrite($handle, $content);
fclose($handle);
}
In pratica, possibile ridefinire il parametro del bor-
do inferiore (w:bottom) andando cos in override ri-
spetto a quello impostato per tutta la tabella. Nel ca-
so particolare abbiamo scelto una linea di tipo
thinThickSmallGap, ovvero una linea composta
da una pi sottile sopra una pi spessa.
I corrispondenti metodi di chiusura dei tag saranno
semplicemente:
function end_cell() {
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
// Apertura del file in append
$handle = fopen($newfile, 'a');
$content = "</w:tc>\n";
fwrite($handle, $content);
fclose($handle);
}
function end_row() {
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
// Apertura del file in append
$handle = fopen($newfile, 'a');
$content = "</w:tr>\n";
fwrite($handle, $content);
fclose($handle);
}
function end_table() {
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
// Apertura del file in append
$handle = fopen($newfile, 'a');
$content = "</w:tbl>\n";
fwrite($handle, $content);
fclose($handle);
}
ESEMPIO DI USO
DELLA CLASSE ESTESA
PER GESTIRE LE TABELLE
In base a quanto definito, per poter inserire la seguente
tabella in un documento DOCX:
La sequenza dei comandi per la prima riga (suppo-
nendo che la classe sia instanziata dalloggetto doc)
sar:
$doc->start_table(4,1);
$doc->add_row();
$doc->add_cell(1);
$doc->start_paragraph(0,center);
$doc->add_text(Titolo 1,bold);
$doc->end_paragraph();
$doc->end_cell();
$doc->add_cell(1);
$doc->start_paragraph(0,center);
$doc->add_text(Titolo 2,bold);
$doc->end_paragraph();
$doc->end_cell();
$doc->add_cell(1);
$doc->start_paragraph(0,center);
NOTA
Alla definizione dello
standard TC45 (Office
Open XML File
Formats) non ha
ovviamente
partecipato solo la
Microsoft, ma anche le
seguenti
aziende/organizzazioni
: Apple, Barclays
Capital, BP, The British
Library, Essilor, The
Gnome Foundation,
Intel, The Library of
Congress, NextPage,
Novell, Statoil, and
Toshiba. Lo standard in
se stato approvato
da una assemblea
generale dellEcma nel
dicembre 2006.
A11
A21
A31
A12
A22
A32
A13
A23
A33
A14
A24
A34
Titolo 1 Titolo 2 Titolo 3 Titolo 4
020-026:072-080 1-04-2008 16:09 Pagina 22
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
23
G
I nuovi formati di Microsoft gestiti da PHP
$doc->add_text(Titolo 3,bold);
$doc->end_paragraph();
$doc->end_cell();
$doc->add_cell(1);
$doc->start_paragraph(0,center);
$doc->add_text(Titolo 4,bold);
$doc->end_paragraph();
$doc->end_cell();
$doc->end_row();
Per la seconda riga avremo:
$doc->add_row();
$doc->add_cell(0);
$doc->start_paragraph(0,center);
$doc->add_text(A11,null);
$doc->end_paragraph();
$doc->end_cell();
$doc->add_cell(0);
$doc->start_paragraph(0,center);
$doc->add_text(A12,null);
$doc->end_paragraph();
$doc->end_cell();
$doc->add_cell(0);
$doc->start_paragraph(0,center);
$doc->add_text(A13,null);
$doc->end_paragraph();
$doc->end_cell();
$doc->add_cell(0);
$doc->start_paragraph(0,center);
$doc->add_text(A14,null);
$doc->end_paragraph();
$doc->end_cell();
$doc->end_row();
E cos via per le altre righe. Quando avremo finito di ag-
giungere righe e celle, potremo chiudere la tabella con
$doc-> end_table();
GESTIONE
DELLE IMMAGINI
La gestione delle immagini allinterno di un do-
cument DOCX prevede dei passi aggiuntivi a
quanto visto per testo e tabelle. Infatti fino ad
ora stato necessario e sufficiente agire su un
singolo file (document.xml) di quelli che com-
pongono il documento DOCX, mentre
lintroduzione di una immagine prevede sia la
modifica di altri file, sia linserimento dellim-
magine stessa nel file ZIP (ovvero limmagine
viene embedded nel file). Cominciamo col no-
tare come le varie immagini verranno archivia-
te anche loro allinterno del file ZIP nella sotto-
directory word/media ed previsto che queste sia-
no in formato PNG. A prescindere dal nome ori-
ginale dellimmagine, nellarchiviazione queste
prendono tutte nome imageX.png (dove X un nu-
mero intero a partire da 1), in modo che possa-
no poi essere facilmente gestite.
Andiamo a vedere quali sono i file che vengono
modificati con linserimento di una immagine. Il
primo [Content_Types].xml, che si trova nella root
del file e contiene una serie di tag che indicano
diversi tipi di elementi contenuti nel documen-
to. Nel momento in cui abbiamo una immagine
dovremo aggiungere alla lista dei tipi il seguen-
te:
<Default Extension="png" ContentType=
"image/png"/>
Il secondo il file delle relazioni, ovvero il file
che associa i tipi del file [Content_Types].xml con
degli elementi allinterno dello ZIP, il cui nome
document.xml.rels e si trova nella sottodirectory
word/_rels. Per le varie immagini presenti do-
vremo prevedere di aggiungere una riga del tipo
<Relationship Id="rId4"
Type="http://schemas.openxmlformats.org/officeD
ocume
nt/2006/relationships/image"
Target="media/image1.png"/>
Come si vede, stiamo indicando che me-
dia/image1.png un tipo immagine e sar rico-
nosciuta da un ID pari a rId4, pertanto allinterno
del file document.xml ci aspettiamo di ritrovare
tale ID quando andremo a specificare linserimento
dellimmagine allinterno del documento. Rela-
tivamente ad i tag da usare, lapproccio scelto
per gestire una immagine molto simile a quel-
lo usato per il testo. Mentre in questultimo caso
prevista la sequenza PARAGRAPH/RUN/TEXT,
per le immagini vi la sequenza PARA-
GRAPH/RUN/DRAWING.
Si noti come la parte di DRAWING fa riferimen-
to ad un markup-language, detto DrawingML;
mentre per il WordprocessingML abbiamo dei
tag che iniziano per w, il DrawingML prevede
dei tag che iniziano per a. Allinterno di DRAW-
ING andremo a specificare che si tratta di una
PICTURE e che quindi i tag si devono riferire ad
un altra sezione del DrawingML i cui tag inizia-
no per pic.
Per brevit non si riporta tutta la lunga sequen-
za di tag necessaria per inserire una immagine
(sequenza che possibile vedere nel codice del-
la classe).
E per bene rilevare alcuni elementi specifici;
ad esempio, nella sezione PICTURE, i tag che
fanno riferimento allimmagine prima inserita
sono i seguenti:
020-026:072-080 1-04-2008 16:09 Pagina 23
ht t p: / / www. i opr ogr ammo. i t
<p:pic>
..
<p:blipFill>
<a:blip r:embed="rId4"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</p:blipFill>
..
</p:pic>
Si noti la presenza dellID prima definito nel punto
dove viene specificata limmagine da inserire. La sezione
stretch quella che indica a Word di posizionare
limmagine in linea col testo.
Altri tag da esaminare sono i tag che specificano da
dimensione del paragrafo e dellimmagine stessa: in ge-
nere dovrebbero coincidere, a meno che non si voglia
avere dei margini del paragrafo pi grandi, per avere
una sorta di bordo rispetto allimmagine. Tali tag sono
rispettivamente:
<wp:extent cx="X" cy="Y" />
<a:ext cx="X" cy="Y" />
Dove al posto di X ed Y vi devono essere delle dimen-
sioni in DPI. Per una immagine larga orizzontalmen-
te come i margini standard di una pagina A4, il valore
di X in DPI di 6120130; (quindi se vogliamo inserire
una immagine larga la un quarto della pagina avre-
mo una X pari a 6120130/4).
ESTENSIONE DELLA
CLASSE PHPDOCX PER
GESTIRE LE IMMAGINI
In base a quanto visto, laggiunta di una imma-
gine prevede radicali modifiche al file ZIP, che si
riflettono in non trascurabili modifiche alla no-
stra classe. Per prima cosa, sar necessario di-
sporre di una nuova propriet della classe: un
intero che chiameremo images che conterr il
numero delle immagini inserite. Nel costruttore
inizializzeremo questa propriet a zero. Altra
modifica al costruttore da apportare sar quella
di costruire i file [Content_Types].xml e docu-
ment.xml.rels con i tag standard che si avrebbe-
ro in assenza di immagini, senza per chiudere
lultimo tag (cosa che dovr essere fatta dal di-
struttore).
Inoltre, bene disporre di un metodo per inserire
le immagini allinterno del file ed uno per spe-
cificare dove limmagine va messa allinterno del
testo. In questo modo, una immagine potr essere
facilmente messa in pi punti del documento.
Nel metodo di inserimento immagini, oltre a por-
re limmagine PNG nel punto corretto della sta-
ge area, dovremo
1) Aggiornare la propriet del numero corrente
di immagini inserite.
2) Se la prima volta che si inserisce una im-
magine, allora necessario aggiornare il con-
tenuto del file [Content_Types].xml.
3) Per ogni nuova immagine creata, necessario
inserire un ID nel file document.xml.rels. A
tal pro, sar necessario poter generare auto-
maticamente i vari ID.
Infine, il distruttore deve essere esteso in modo
da
1) Chiudere correttamente anche i due nuovi
file da trattare,
2) Aggiungere al file ZIP sia i nuovi file, sia i file
delle immagini.
Nella sostanza, al costruttore per gestire [Con-
tent_Types].xml e per inizializzare la nuova pro-
priet verranno aggiunte le seguenti righe:
$this->images = 0;
$newfile = "./stage_area/" . $this->current_file .
"/[Content_Types].xml";
$handle = fopen($newfile, 'w');
$content = "<?xml version="1.0" encoding="iso-
8859-1" ?>\n";
$content .= " <Types xmlns=\"http://schemas.
openxmlformats.org/package/2006/
content-types\">\n;
$content .= "<Default Extension=\"rels\" Content
Type=\"application/vnd.openxmlformats-
package.relationships+xml\" />\n";
$content .= "<Default Extension=\"xml\" Content
Type=\"application/xml\" />\n";
$content .= "<Override PartName=\"/word/
document.xml\" ContentType=\"application/
vnd.openxmlformats-officedocument.
wordprocessingml.document.main+xml\" />\n";
$content .=<Override PartName=\"/word/styles.
xml\" ContentType=\"application/vnd.
openxmlformats-officedocument.
wordprocessingml.styles+xml\" />\n";
$content .= "<Override PartName=\"/docProps/
app.xml\" ContentType=\"application/vnd.
openxmlformats-officedocument.extended-
properties+xml\" />\n";
$content .= "<Override PartName=\"/word/
settings.xml\" ContentType=\"application/
vnd.openxmlformats-officedocument.word
processingml.settings+xml\" />\n";
ioProgrammo Web M
G
24
/Maggio 2008
I nuovi formati di Microsoft gestiti da PHP
NOTA
Se si dispone solo di
Word 2003 (o 2002 o
2000), per poter aprire
un file in formato
DOCX necessario
installare un pacchetto
aggiuntivo della
Microsoft. Tramite
questo sar anche
possibile salvare da un
vecchio Word nel
nuovo formato. Tale
pacchetto
installabile a patto di
avere un sistema
Windows ed Office
pienamente
aggiornati. Per
trovarlo sufficiente
andare sul sito
Microsoft e cercare il
Microsoft Office
Compatibility Pack.
020-026:072-080 1-04-2008 16:09 Pagina 24
ht t p: / / www. i opr ogr ammo. i t
$content .= "<Override PartName=\"/word/theme/
theme1.xml\" ContentType=\"application/
vnd.openxmlformats-officedocument.
theme+xml\" />\n;
$content .= "<Override PartName=\"/word/fontTable.
xml\" ContentType=\"application/vnd.
openxmlformats-officedocument.wordprocessingml.
fontTable+xml\" />\n";
$content .= "<Override PartName=\"/word/
webSettings.xml\" ContentType=\"application/
vnd.openxmlformats-officedocument.
wordprocessingml.webSettings+xml\" />\n";
$content .= "<Override PartName=\"/docProps/core.
xml\" ContentType=\"application/vnd.
openxmlformats-package.core-properties+
xml\" /> \n";
fwrite($handle, $content);
fclose($handle);
Mentre per gestire document.xml.rels le righe sono
le seguenti:
$newfile = "./stage_area/" . $this->current_file .
"/word/_rels/" . "/document.xml.rels";
$handle = fopen($newfile, 'w');
$content = "<?xml version="1.0" encoding=
"iso-8859-1" ?>\n";
$content .= "<Relationships xmlns=\"http://
schemas.openxmlformats.org/
package/2006/relationships\">\n";
$content .= "<Relationship Id=\"rId3\" Type=\"http:
//schemas.openxmlformats.org/officeDocument/
2006/relationships/webSettings\"
Target=\"webSettings.xml\" />\n";
$content .= "<Relationship Id=\"rId2\" Type=\"http:
//schemas.openxmlformats.org/officeDocument/
2006/relationships/settings\" Target=\
"settings.xml\" />\n";
$content .= "<Relationship Id=\"rId1\" Type=\"http:
//schemas.openxmlformats.org/officeDocument/2006
/relationships/styles\" Target=\"styles.xml\" />\n";
$content .= "<Relationship Id=\"rId6\" Type=\"http:
//schemas.openxmlformats.org/officeDocument/
2006/relationships/theme\" Target=\
"theme/theme1.xml\" />\n";
$content .= "<Relationship Id=\"rId5\" Type=\
"http://schemas.openxmlformats.org/office
Document/2006/relationships/fontTable\"
Target=\"fontTable.xml\" />\n";
fwrite($handle, $content);
fclose($handle);
Il metodo di aggiunta di una immagine sar invece il
seguente:
function insert_image($image_file,$image_id) {
$newdir = "./stage_area/" . $this-
>current_file;
$newimagedir = $newdir . "/media";
mkdir($newimagedir);
$this->images++;
$newfile = $newimagedir. "/image" . $this-
>images ".png";
copy($image_file,$newfile);
if ( $this->images == 1 ) {
$newfile = "./stage_area/" . $this-
>current_file . "/[Content_Types].xml";
$handle = fopen($newfile, 'w');
$content =" <Default Extension=\"png\"
ContentType=\"image/png\" />\n";
fwrite($handle, $content);
fclose($handle);
}
$newfile = "./stage_area/" . $this->current_file
. "/word/_rels/" . "/ document.xml.rels";
$handle = fopen($newfile, 'a');
$content = <Relationship Id=\"" . $image_id .
"\" Type=\"http://schemas.openxmlformats.
org/officeDocument/2006/relationships/image\"
Target=\"media/image" . $this->images . ".png"
/>\n";
fwrite($handle, $content);
fclose($handle);
}
Mentre quello che inserisce effettivamente limmagine
nel documento sar:
function put_image($vdim,$hdim,$image_id) {
// Gestione variabili
if ( $vdim == "" || is_null($vdim) ) {
$vdim = 6120130;
}
if ( $hdim == "" || is_null($hdim) ) {
$hdim = 2209165;
}
// Variabile del file XML corrente
$newfile = "./stage_area/" . $this->current_file
. "/word" . "/document.xml";
// Apertura del file in append
$handle = fopen($newfile, 'a');
$content = "<w:p>\n";
$content .= "<w:r><w:rPr><w:noProof/
><w:lang w:eastAsia=\"it-IT\"/>\n";
$content .= "</w:rPr><w:drawing>\n";
$content .= "<wp:inline distT=\"0\" distB=
\"0\" distL=\"0\" distR=\"0\">\n";
$content .= "<wp:extent cx=\"" . $vdim . "\"
M
ioProgrammo Web
Maggio 2008/
25
G
I nuovi formati di Microsoft gestiti da PHP
LAUTORE
Guido Pennella un
ingegnere esperto di
informatica, laureato
allUniversit di Roma
La Sapienza. Si
occupa principalmente
dello sviluppo di
sistemi sia via Web, sia
di applicazioni
embedded real time
distribuite. E possibile
contattarlo
allindirizzo di posta
elettronica
guidopennella@
virgilio.it
020-026:072-080 1-04-2008 16:09 Pagina 25
ht t p: / / www. i opr ogr ammo. i t
cy=\"" . $hdim . "\"/>\n";
$content .= "<wp:effectExtent l=\"19050\"
t=\"0\" r=\"0\" b=\"0\"/>\n";
$content .= "<wp:docPr id=\"" . $this->images
. "\" name=\"Picture " . $this->images . "\" descr=
\"Immagine " . $this->images . " aggiunta
automaticamente\"/>\n";
$content .= "<wp:cNvGraphicFramePr><a:
graphicFrameLocks xmlns:a=\"http://schemas.
openxmlformats.org/drawingml/2006/main\"
noChangeAspect=\"1\"/>\n";
$content .= "</wp:cNvGraphicFramePr>
<a:graphic xmlns:a=\"http://schemas.
openxmlformats.org/drawingml/2006/main\">\n";
$content .= "<a:graphicData uri=\"http://
schemas.openxmlformats.org/drawingml/
2006/picture\">\n";
$content .= "<pic:pic xmlns:pic=\"http://
schemas.openxmlformats.org/drawingml/
2006/picture\">\n";
$content .= "<pic:nvPicPr><pic:cNvPr id=\"0\"
name=\"intestazione.png\"/><pic:cNvPicPr/>
</pic:nvPicPr>\n";
$content .= "<pic:blipFill><a:blip r:embed=\""
. $image_id. "\"/>\n";
$content .= "<a:stretch><a:fillRect/></a:
stretch></pic:blipFill>\n";
$content .= "<pic:spPr><a:xfrm><a:off x=\
"0\" y=\"0\"/>\n";
$content .= "<a:ext cx=\"" . $vdim . "\" cy=
\"" . $hdim . "\"/>\n";
$content .= "</a:xfrm><a:prstGeom prst=\
"rect\">\n";
$content .= "<a:avLst/></a:prstGeom>
</pic:spPr></pic:pic>\n";
$content .= "</a:graphicData></a:graphic>
</wp:inline></w:drawing></w:r></w:p>\n";
fwrite($handle, $content);
fclose($handle);
}
Infine, nel distruttore sar necessario inserire le se-
guenti righe:
$newfile = "./stage_area/" . $this->current_file .
"/word/_rels/" . "/document.xml.rels";
$handle = fopen($newfile, 'a');
$content .= "</Relationships>\n";
fwrite($handle, $content);
fclose($handle);
chdir($newdir);
$to_run = '7za a ' . $this->current_file . '.docx
word/_rels/ document.xml.rels';
$newfile = "./stage_area/" . $this->current_file .
"/[Content_Types].xml";
$handle = fopen($newfile, 'a');
$content .= "</Types>\n";
fwrite($handle, $content);
fclose($handle);
$to_run = '7za a ' . $this->current_file . '.docx
word/[Content_Types].xml ';
for ( $i = 0; $i < $this->images; $i++ ) {
$to_run = '7za a ' . $this->current_file . '.docx
word/media/image' . $i . '.png ';
CONCLUSIONI
Come si potuto vedere, estendere la classe
PHP di gestione di documenti DOCX per poter
aggiungere delle tabelle non presenta reali dif-
ficolt. Estendere invece la classe per poter ge-
stire linserimento di immagini prevede modi-
fiche di entit non trascurabile (anche se non
vi sono particolari difficolt in tali modifiche).
Grazie a tali estensioni, siamo ora in possesso di
una classe che permette di creare documenti
DOCX sufficientemente strutturati.
E fondamentale rilevare come inserire testo e
immagini allinterno di una cella analogo ad
inserirle allinterno del corpo del documento.
Anche con tali estensioni, dato che non si sono
usate caratteristiche peculiari del PHP, la clas-
se facilmente portabile in altri ambienti e lin-
guaggi. Si noti infine come la gestione delle im-
magini proposta nellarticolo prevede il sem-
plice inserimento delle stesse, mentre lo stan-
dard prevede anche la possibilit di effettuare del-
le manipolazioni, come ad esempio reimpo-
stare i colori ed inserire ombreggiature sul bor-
do dellimmagine, sempre inserendo una serie
di appositi tag, che saranno esaminati in suc-
cessivi articoli.
A tutto ci si pu aggiungere una buona dose di Ajax,
che pu essere utilizzata per limmissione di conte-
nuti. A tal proposito esistono moltissime librerie che
possono essere utilizzate, alcune delle quali vengono
analizzate anche allinterno di questo stesso numero
di ioProgrammo. A titolo di esempio citiamo le yahoo
library, oppure scriptaculous. Entrambe presentano
numerosi metodi per limmissione dei contenuti sia in
forma tabellare sia sotto forma di un completo editor
di testi che emula in tutto e per tutto il formato di word.
Unendo le tecniche ajax a quelle espresse in questo
articolo, possibile quasi completamente svincolar-
si dallinstallare software in locale, piuttosto possibile
utilizzare direttamente applicazioni localizzate sul
web. il concetto di applicazione in ASP di cui si par-
la ormai da moltissimo tempo
Guido Pennella
ioProgrammo Web
M
G
26
/Maggio 2008
I nuovi formati di Microsoft gestiti da PHP
020-026:072-080 1-04-2008 16:09 Pagina 26
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web M
G
28
/Maggio 2008
Usare i servizi di Windows
C
ostruire unapplicazione che gestisca in
modo centralizzato linvio dei Fax, una
attivit facile da immaginare ma molto
meno semplice da realizzare. In questo artico-
lo analizzeremo una soluzione che si basa sul
Servizio Fax di Windows Server 2003 (o Windows
XP nel caso in cui volessimo implementare una
postazione di test).
La soluzione composta dai seguenti componenti:
Un servizio Windows che tiene traccia delle
attivit del Fax, implementato mediante gli
oggetti Fax Service Extended COM API;
Una classe FaxHelper che espone tutti i me-
todi relativi allinvio dei Fax;
Un controllo FaxMonitor, basato su AJAX, che
mostra lo stato del Fax;
Una pagina web che rappresenta linterfaccia
utente dellapplicazione.
A causa della complessit della soluzione, larticolo
verr suddiviso in due parti; nella prima parte avre-
mo modo di analizzare lintera infrastruttura
dellapplicazione e i primi due componenti.
Nella seconda parte analizzeremo il controllo
che traccia lo stato del Fax e la pagina web at-
traverso cui avremo modo di effettuare alcuni te-
st.
LINFRASTRUTTURA
In Windows Server 2003 ed in Windows XP, se
apriamo la cartella Stampanti e fax, presente
nel gruppo Impostazioni, possiamo trovare una
icona Fax che rappresenta la console del servi-
zio Fax
Qualora non sia presente la suddetta icona, pos-
siamo installare il servizio Fax attraverso lutilit
Installazione applicazioni del Pannello di
Controllo. Il servizio Fax fa parte dei Compo-
nenti di Windows quindi facilmente instal-
labile (Figura 2).
Linstallazione del servizio Fax e del relativo mo-
dem (locale o di rete), sono argomenti che esu-
lano dalla trattazione (anche per motivi di spa-
zio); nel caso in cui sia necessario eseguire an-
che queste operazioni preventive, si consiglia
di seguire la documentazione in linea del siste-
ma operativo.
Se il servizio installato e funzionante, un dop-
pio clic sullicona del Fax ci consente di acce-
dere alla consolle del servizio Fax.
Questo servizio rappresenta una soluzione com-
pleta per la gestione dei Fax; gli utenti possono
inviare e ricevere Fax, monitorare le attivit del-
lapparecchiatura ed accedere allarchivio dei
Fax. Il servizio consente di inviare e ricevere Fax
attraverso un dispositivo direttamente connesso
alla postazione o attraverso un dispositivo re-
moto, raggiungibile via rete. Una descrizione
completa del servizio Fax e della sua configu-
razione, esula dagli obiettivi della trattazione;
per approfondire largomento, per, possibi-
le far riferimento alla documentazione in linea.
Per sviluppare il presente articolo, la configu-
INVIA I TUOI FAX
TRAMITE ASP.NET
ALLA SCOPERTA DEL SERVIZIO FAX INTEGRATO IN WINDOWS. VEDREMO COME UTILIZZARE
INTERFACCE SPECIFICHE PER ACCEDERE ALLE FUNZIONI SUPPORTATE. INOLTRE GETTEREMO
LE BASI PER COSTRUIRE UNAPPLICAZIONE WEB CHE NE FACCIA USO
Conoscenze richieste
ASP.NET, C#
Software
Visual Studio 2005
Impegno
Tempo di realizzazione
REQUISITI
Figura 1: Il servizio Fax gi installato.
028-036:072-080 2-04-2008 11:52 Pagina 28
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
29
G
Usare i servizi di Windows
razione di base utilizzata, composta da un
computer portatile con Windows XP Professio-
nal con modem/fax incorporato.
Dato che dobbiamo costruire una applicazio-
ne che interagisca con il servizio Fax, abbiamo
bisogno di una interfaccia di programmazione
al fine di poter operare a livello di codice. Ci
che fa al nostro caso sono le Fax Service Ex-
tended COM API (FAXCOMEXLib) ovvero le in-
terfacce di programmazione COMdel servizio
Fax. La libreria FAXCOMEXLib, inclusa come ri-
sorsa nella dll Fxscomex.dll, una risorsa ricca
di oggetti che ci consentono di gestire tutti gli
aspetti del servizio Fax. Gli oggetti sono rag-
gruppati in insiemi funzionali a partire dal-
loggetto principale, FaxServer, che ci consen-
te di connetterci ad un Fax Server attivo. Pos-
siamo individuare altre famiglie di oggetti per ge-
stire la messaggistica, la configurazione del Fax
e la gestione delle notifiche.
La libreria FAXCOMEXLib la base dei due pri-
mi componenti della soluzione; il primo un
servizio Windows che tiene traccia delle attivit
del Fax e memorizza tutti i documenti inviati
tramite il servizio Fax; questo servizio neces-
sario per gestire la differente natura di una
applicazione web rispetto ad un servizio Fax.
Una applicazione web, infatti, un processo
sincrono privo di stato; il servizio Fax, al con-
trario, un processo asincrono basato su code.
In altre parole, possiamo facilmente inviare un
Fax attraverso una applicazione web, ma ab-
biamo, altres, bisogno di un meccanismo che
ci consenta di controllare lo stato della tra-
smissione del documento finch questo non sia
stato effettivamente inviato.
Il secondo componente della soluzione una
classe FaxHelper, basata sulla libreria FAX-
COMEXLib; utilizzeremo la classe per incapsu-
lare la complessit della libreria e per esporre
solo quelle funzionalit necessarie per inviare
un Fax.
Il terzo componente della soluzione il con-
trollo FaxMonitor, che viene utilizzato per in-
terrogare la lista delle registrazioni effettuate
dal servizio Windows; in questo modo avremo
la possibilit di verificare lo stato di avanza-
mento della trasmissione. Al fine di aumentare
la fruibilit della soluzione, il controllo utiliz-
zer AJAX per aggiornare la lista delle registra-
zioni relative al nostro documento; in questo
modo lutente non dovr ricaricare la pagina
per controllare lo stato di avanzamento della
trasmissione.
Lultimo componente che andremo ad analizzare
una pagina web che funge da tester della so-
luzione; vedremo come selezionare un docu-
mento dal nostro disco e come riusciremo ad
inviarlo via Fax.
IL SERVIZIO
FAX MONITOR
Iniziamo con lanalizzare a fondo il primo com-
ponente: il Servizio Fax Monitor.
Ci di cui abbiamo bisogno un servizio che
registri le attivit del Fax; a tal proposito, quin-
di, abbiamo bisogno di individuare un suppor-
to su cui effettuare le registrazioni.
Ci sono diverse alternative per risolvere questo
NOTA
FAX SERVICE
EXTENDED
COM API
Per approfondire la
conoscenza delle
interface di
programmazione del
servizio Fax,
possibile far
riferimento alla library
msdn allindirizzo:
http://msdn2.microsoft.co
m/en-
us/library/ms684513(VS.8
5).aspx.
Figura 2: Il servizio Fax non installato quindi lo selezioniamo dai componenti di
Windows.
Figura 3: La consolle del servizio Fax.
028-036:072-080 1-04-2008 18:44 Pagina 29
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web M
G
30
/Maggio 2008
Usare i servizi di Windows
problema; tutte hanno lati positivi e negativi;
prima di effettuare la scelta, dobbiamo anche
tenere a mente che le registrazioni verranno in-
terrogate pesantemente dalle pagine web, al fi-
ne di mostrare lo stato di avanzamento della
trasmissione.
Consideriamo le seguenti alternative:
Event Log: il gestore degli eventi il supporto
utilizzato normalmente dai servizi per regi-
strare le rispettive attivit; laspetto negativo
di questa modalit risiede nella difficile in-
terrogazione delle registrazioni.
File di testo: i file di testo sono stati utilizzati
per anni come supporti per memorizzare da-
ti non strutturati; proprio la mancanza di qual-
siasi infrastruttura per gestire i dati ci costringe
a costruire da zero un meccanismo che ci con-
senta una facile interazione sia nella regi-
strazione che nellinterrogazione.
Base Dati: indubbiamente il miglior suppor-
to per memorizzare informazioni strutturate
una base dati; allo stesso tempo, indubbia-
mente, questa soluzione la pi onerosa dal
punto di vista della gestione e della configu-
razione della soluzione, dato che ci costrin-
ge a prevedere anche un ulteriore prodotto
come SQL Server piuttosto che MySql, con tut-
te le sue problematiche.
In questo articolo utilizzeremo lEvent Log da-
to che ci consente di organizzare i dati in modo
strutturato, ma allo stesso tempo ci evita di in-
stallare ulteriori prodotti (come SQL Server o
MySql).
Per implementare un servizio Windows in Vi-
sual Studio 2005, aggiungiamo un nuovo pro-
getto alla soluzione; nel dialogo New Project,
selezioniamo il linguaggio che preferiamo dal
pannello Project types, quindi selezioniamo
il ramo Windows. Scegliamo il template Win-
dows Services dal pannello Templates ed in-
dichiamo come nome del progetto FaxMoni-
torServiceCs se abbiamo scelto di utilizzare C#
come linguaggio di sviluppo, oppure FaxMon-
itorServiceVb se utilizziamo Visual Basic.NET.
In funzione del linguaggio scelto, Visual Studio
2005 aggiunge alcuni file alla nostra soluzione.
Rinominiamo il file Service1.cs (o Service1.vb)
in FaxMonitorService.cs (o FaxMonitorService.vb)
quindi passiamo alla visualizzazione del codi-
ce. Come possiamo vedere dal codice che se-
gue, lo strumento di sviluppo ha gi compilato
alcuni metodi; a noi resta da completare il mo-
dello gi impostato.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using FAXCOMEXLib;
namespace FaxMonitorServiceCs
{
public partial class FaxMonitorService :
ServiceBase
{
FaxServer oFaxServer;
public FaxMonitorService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-
down
// necessary to stop your service.
}
}
}
Innanzitutto dobbiamo aggiungere un riferi-
mento al namespace System.Diagnostics ed al- Fig.4: Aggiungiamo un progetto per lo sviluppo di un servizio Windows.
NOTA
IL CODICE
DELLA
SOLUZIONE
Nel corso della
trattazione verranno
descritti gli esempi
sviluppati utilizzando
il linguaggio C#. Gli
sviluppatori che
prediligono il
linguaggio Visual
Basic.NET, possono far
riferimento ai file di
supporto allegati al
presente articolo per
analizzare il codice
sviluppato in Visual
Basic.NET. Le
funzionalit, i
componenti ed il
comportamento della
soluzione sono
analoghi nei due
linguaggi.
028-036:072-080 1-04-2008 18:44 Pagina 30
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
31
G
Usare i servizi di Windows
la libreria FAXCOMEXLib (ricordiamoci di ag-
giungere, preventivamente, un riferimento alla
libreria Fxscomex.dll nel Solution Explorer); in
questo modo ci predisponiamo allutilizzo del-
lEvent Log e delle interfacce Fax Service Ex-
tended COM API.
Quindi dobbiamo definire una variabile globa-
le, ovvero un oggetto di tipo FaxServer che uti-
lizzeremo per connetterci al dispositivo fisico.
Come possiamo notare dal codice precedente,
Visual Studio 2005 predispone due metodi, On-
Start ed OnStop, che verranno eseguiti, rispet-
tivamente, nel momento in cui il servizio vie-
ne avviato e nel momento in cui il servizio vie-
ne fermato.
In fase di avvio del servizio, dobbiamo connet-
terci al Fax Server e dobbiamo sottoscrivere al-
cune notifiche al fine di eseguire del codice al mo-
mento opportuno.
In fase di arresto del servizio, al contrario, non
dobbiamo eseguire nessuna particolare opera-
zione.
protected override void OnStart(string[] args)
{
oFaxServer = new FaxServer();
//Connettiamo il Fax Server locale.
oFaxServer.Connect("");
//Sottoscriviamo le notifiche.
oFaxServer.ListenToServerEvents(
FAX_SERVER_EVENTS_TYPE_ENUM.fsetOUT_
QUEUE);
//Aggiungiamo gli Event Handlers
oFaxServer.OnOutgoingJobAdded +=
new IFaxServerNotify_
OnOutgoingJobAddedEventHandler(
oFaxServer_OnOutgoingJobAdded);
oFaxServer.OnOutgoingJobChanged +=
new IFaxServerNotify_
OnOutgoingJobChangedEventHandler(
oFaxServer_OnOutgoingJobChanged);
}
protected override void OnStop()
{
// TODO: Add code here to perform any
tear-down
// necessary to stop your service.
}
La configurazione predefinita delloggetto
FaxServer, non comporta la ricezione di alcun
evento scaturito dal Server. Al fine di ricevere i
messaggi di notifica scaturiti dalle attivit del
Fax, necessario invocare il metodo Listen-
ToServerEvents delloggetto di tipo FaxServer
passando in ingresso i tipi di eventi che ci ver-
ranno notificati.
Ci sono diversi tipi di evento che il servizio Fax
pu notificare alle applicazioni in ascolto; que-
sti tipi sono definiti dallenumeratore
FAX_SERVER_EVENTS_ TYPE_ENUMi cui mem-
bri sono definiti come maschere di bit che pos-
sono essere utilizzati in combinazione.
Dopo aver sottoscritto uno o pi eventi, dob-
biamo implementare i relativi gestori (hand-
lers) che saranno invocati allesecuzione di par-
ticolari attivit. In questo modo potremo gesti-
re levento, effettuando una registrazione o, co-
munque, eseguendo del codice.
Nellesempio che stiamo analizzando, dobbia-
mo sottoscrivere solo levento fsetOUT_QUEUE
ovvero richiediamo una notifica quando un do-
cumento viene inviato alla coda dei Fax in usci-
ta.
Quando cambia lo stato di un Fax in uscita, il
servizio Fax invia una notifica ed invoca i se-
guenti gestori: OnOutgoingJobAdded, OnOut-
goingJobRemoved e OnOutgoingJobChanged. Il
metodo OnOutgoingJobAdded viene invocato
ogniqualvolta viene inviato un documento alla
coda dei Fax in uscita mentre il metodo OnOut-
goingJobChanged viene invocato ogniqualvolta
un documento in coda, cambia il suo stato.
Lobiettivo del nostro servizio proprio quello
di registrare questo tipo di attivit quindi, pre-
supponendo di incapsulare la logica di regi-
strazione in un metodo privato WriteLog, il cor-
po dei suddetti gestori sar composto da una
sola invocazione del suddetto metodo.
public void oFaxServer_OnOutgoingJobAdded(
FaxServer pFaxServer, string sJobId)
{
//Registriamo lattivit
WriteLog(sJobId, "Added To Queue");
}
public void oFaxServer_OnOutgoingJobChanged(
FaxServer pFaxServer, string sJobId,
FaxJobStatus pFaxJobStatus)
{
//Registriamo lattivit
WriteLog(sJobId,pFaxJobStatus.Status.
ToString());
}
Quando trasmettiamo un documento, il servi-
zio Fax crea un job a cui assegna un identifica-
tivo univoco (JobId). Il suddetto identificativo
passato ai gestori degli eventi; ci ci consen-
te, come vedremo tra un attimo, di organizzare
le registrazioni in modo strutturato. Il gestore
OnOutgoingJobChanged riceve in input anche un
oggetto di tipo FaxJobStatus che contiene le
NOTA
IL TIPO
FAX_SERVER_
EVENTS_TYPE_
ENUM
Per comprendere il
significato di tutti i
componenti del tipo
FAX_SERVER_EVENTS_T
YPE_ENUM, possibile
far riferimento alla
library msdn
allindirizzo:
http://msdn2.microsoft.
com/en-us/library/ms689
206(VS.85).aspx.
028-036:072-080 1-04-2008 18:44 Pagina 31
ht t p: / / www. i opr ogr ammo. i t
informazioni relative al cambiamento di stato del
relativo job. Loggetto contiene lo stato corren-
te del job, la pagina che attualmente in fase
di trasmissione ed il numero di tentativi di tra-
smissione effettuati dal servizio. Nella soluzio-
ne che stiamo analizzando, abbiamo bisogno
di gestire solo la propriet Status delloggetto
FaxJobStatus, che un enumeratore che speci-
fica lo stato attuale del job in coda.
Lultimo frammento di codice da analizzare il
metodo privato che incapsula la logica di regi-
strazione delle attivit. Possiamo interagire con
lEvent Log di Windows attraverso loggetto
EventLog appartenente al namespace Sys-
tem.Diagnostics. Attraverso loggetto EventLog
possiamo leggere o scrivere i valori dei log esi-
stenti, piuttosto che scrivere o cancellare nuo-
vi log.
Al fine di isolare la gestione delle registrazioni del-
la nostra applicazione dal resto dei log del si-
stema, creeremo uno spazio dedicato, chiama-
to FaxMonitor; ogni trasmissione sar registra-
ta nel nuovo log e nominata con lidentificativo
del job (JobId), in modo da poter individuare
univocamente le registrazioni relative ad una
particolare trasmissione.
Il metodo WriteLog riceve in input due strin-
ghe: lidentificativo univoco del job (JobId) ed
il suo stato. In primo luogo dobbiamo verifica-
re se abbiamo gi effettuato registrazioni per il
suddetto job; se non esistono registrazioni dob-
biamo invocare il metodo statico Cre-
ateEventSource, delloggetto EventLog, che as-
socia la nostra applicazione allEvent Log chia-
mato FaxMonitor per mezzo della sorgente spe-
cificata come primo parametro.
Da notare che se lEvent Log FaxMonitor non
esiste, allatto dellinvocazione del metodo, que-
sto ne crea uno al momento.
Se la sorgente gi presente nellEvent Log (ov-
vero avevamo gi effettuato qualche registra-
zione), dobbiamo creare un oggetto di tipo Event-
Log che punti allEvent Log FaxMonitor, quin-
di, dopo aver definito su quale sorgente effet-
tuare la registrazione, andiamo a scrivere lo sta-
to del job per mezzo del metodo
WriteEntry.
private void WriteLog(string sJobId, string
sJobStatus)
{
// Verifichiamo se sono gi state effettuate
registrazioni
if (!EventLog.SourceExists(sJobId))
{
// Creiamo una nuova sorgente ed,
eventualmente, un nuovo log
// se "FaxMonitor" non esiste
EventLog.CreateEventSource(sJobId,
"FaxMonitor");
}
EventLog oEventLog = new
EventLog("FaxMonitor");
oEventLog.Source = sJobId;
// Effettuiamo la registrazione
oEventLog.WriteEntry("Your fax reached
this status: "
+ sJobStatus);
}
Dopo aver scritto il codice funzionale del no-
stro servizio, prima di poter effettuare la creazione
del pacchetto, dobbiamo eseguire alcune altre
operazioni; in particolare dobbiamo aggiunge-
re un installer per il nostro servizio. Questa ope-
razione necessaria per far si che il sistema ope-
rativo sappia che il programma che scaturir
dalloperazione di build, deve essere eseguito
proprio come servizio.
In Visual Studio 2005, attiviamo la modalit de-
sign e facciamo clic con il tasto destro sul pan-
nello di design in modo da visualizzare il men
contestuale; quindi facciamo clic sulla voce Add
Installer. Visual Studio 2005 aggiunge un nuo-
vo file, ProjectInsaller.cs (o ProjectInsaller.vb se
ioProgrammo Web M
G
32
/Maggio 2008
Usare i servizi di Windows
Figura 5: Le propriet delloggetto ServiceInstaller.
028-036:072-080 1-04-2008 18:44 Pagina 32
ht t p: / / www. i opr ogr ammo. i t
abbiamo scelto il Visual Basic.NET come lin-
guaggio di sviluppo), che include due oggetti:
ServiceProcessInstaller e ServiceInstaller. Il pri-
mo oggetto gestisce linterazione con le utilit
di installazione come Windows Installer o in-
stallutil.exe. Il secondo oggetto conserva i dati
specifici del servizio che saranno necessari al
ServiceProcessInstaller. Selezioniamo loggetto
ServiceInstaller e portiamoci sul pannello delle
propriet (Figura 3); qui definiamo alcune pro-
priet come DisplayName e ServiceName (va-
lorizziamole FaxMonitorCs o FaxMonitorVb in
funzione del linguaggio scelto per lo sviluppo
dellapplicazione). Possiamo definire anche ul-
teriori propriet come la descrizione del servi-
zio piuttosto che la modalit di avviamento
Manual/Automatic/Disabled); troveremo que-
ste propriet nel Windows Service Control Ma-
nager.
Relativamente alloggetto ServiceProcessInstaller,
lunica propriet che dobbiamo valorizzare
Account, che porremo pari a LocalSystem, in
modo che il servizio abbia accesso a tutte le ri-
sorse di cui necessita (Figura 4).
TESTIAMO IL NOSTRO
SERVIZIO
Ora possiamo lanciare il build della soluzione,
quindi installare e testare il nostro servizio.
Apriamo una finestra di commando e posizio-
niamoci nella cartella Bin\Debug allinterno del-
la cartella del progetto. Lanciamo il program-
ma installutil.exe per installare il nostro servi-
zio:
installutil.exe FaxMonitorServiceCs.exe
oppure
installutil.exe FaxMonitorServiceVb.exe
Controlliamo che il programma abbia esegui-
to la procedura di installazione in maniera cor-
retta quindi andiamo a verificare che il nostro ser-
vizio sia effettivamente presente nella lista dei
servizi installati sulla nostra macchina.
Possiamo avviare lo snap-in Servizi dal grup-
po Strumenti di amministrazione a sua volta
contenuto nel gruppo Programmi.
M
ioProgrammo Web
Maggio 2008/
33
G
Usare i servizi di Windows
Figura 6: Le propriet delloggetto ServiceProcess
Installer.
Figura 8: Nel Visualizzatore Eventi presente il nuovo Event Log.
Figura 7: Il nostro servizio stato installato e avviato correttamente.
NOTA
INTERAZIONE
CON LEVENT
LOG DI
WINDOWS
Per ottenere la
descrizione completa
della classe EventLog e
di tutti i suoi
componenti,
possibile far
riferimento alla library
msdn allindirizzo:
http://msdn2.microsoft.
com/en-us/library/system.
diagnostics.eventlog(vs.
71).aspx.
028-036:072-080 2-04-2008 11:57 Pagina 33
ht t p: / / www. i opr ogr ammo. i t
Se linstallazione andata a buon fine, possia-
mo posizionarci sulla voce FaxMonitorCs (o Fax-
MonitorVb), quindi possiamo avviare il servi-
zio.
Se il servizio si avvia correttamente, avremo mo-
do di visualizzare qualcosa di simile alla Figu-
ra 5.
Ora che il servizio attivo sulla nostra mac-
china, possiamo testarlo immediatamente
dato che ci che abbiamo realizzato un
componente autonomo dal resto dellapplica-
zione. Ci significa che non abbiamo la
necessit di sviluppare ulteriore codice per
provarlo; se ci pensiamo, in effetti, il servizio
effettua il monitoraggio di ogni Fax inviato
tramite il servizio Fax, quindi non necessaria-
mente inviato tramite la nostra applicazione.
Per fare una prova, allora, possiamo tentare di
inviare un Fax attraverso la console del servi-
zio Fax vista in Figura 1. Possiamo effettuare
la prova anche senza essere effettivamente
connessi ad una linea telefonica. Ci che
vogliamo verificare che il servizio cominci a
registrare le attivit del servizio nellapposito
Event Log. Per verificare quanto sopra, pos-
siamo aprire il Visualizzatore Eventi per
accertare se il nuovo Event Log, FaxMonitor,
stato creato e se, eventualmente contiene
delle registrazioni. Se il servizio ha funzionato
correttamente, potremo allora vedere, come
da Figura 6, che le registrazioni sono effettiva-
mente presenti.
Se facciamo doppio clic su una delle registra-
zioni, potremo visualizzare il relativo detta-
glio, che ci informa in merito allo stato rag-
giunto dal job.
LA CLASSE FAXHELPER
Terminata lanalisi del servizio FaxMonitor, ini-
ziamo lo sviluppo della classe FaxHelper che
il secondo componente che si basa sulla libre-
ria FAXCOMEXLib. Vogliamo sviluppare questa
classe per incapsulare la complessit della li-
breria FAXCOMEXLib, in modo da esporre so-
lo i metodi che ci servono per inviare un Fax. Al
fine di semplificare il processo di trasmissione
dei documenti, implementeremo la classe im-
ponendo alcuni vincoli; chiaramente i suddet-
ti vincoli potranno essere modificati a cura del-
lo sviluppatore che volesse implementare so-
luzioni differenti.
I vincoli che imponiamo sono:
Restrizione del tipo di documento inviabile
via Fax; i tipi concessi sono pdf, tiff e txt;
I documenti che potremo inviare devono es-
sere memorizzati in una sotto-cartella della
nostra applicazione.
Il primo vincolo stabilito in funzione del pro-
cesso di conversione che il servizio Fax attua
prima di trasmettere il documento se questo
in formato differente da quello predefinito, ov-
vero tiff. I documenti pdf e txt possono essere tra-
smessi senza particolari operazioni di conver-
sione, quindi il processo gestito automatica-
mente senza aver bisogno dellausilio di parti-
colari programmi (a differenza dei documenti
Word, ad esempio). Nel caso di documenti pdf
necessario che sulla macchina sia installato
lAdobe Reader scarcabile gratuitamente dal si-
to del produttore.
Considerando, tra laltro, che attualmente esi-
stono diversi plug-in gratuiti che consentono
la conversione in formato pdf di praticamente
ogni formato di documento, questo vincolo
da considerarsi molto lasco. Il secondo vincolo
ioProgrammo Web
M
G
34
/Maggio 2008
Usare i servizi di Windows
LA CLASSE FAXDOCUMENT
La classe FaxDocument un
altro componente della libreria
FAXCOMEXLib e, come vedremo,
ci sar utile per comporre un
documento Fax da sottoporre al
servizio Fax per la trasmissione.
La classe molto ricca e
consente di specificare ogni
aspetto del documento da
trasmettere; possibile definire
dettagliate informazioni sul
mittente, possibile inviare lo
stesso documento a pi
destinatari, possibile altres
definire una copertina per il
Fax.
Per approfondire la conoscenza
della classe FaxDocument
possibile far riferimento alla
library msdn allindirizzo
http://msdn2.microsoft.com/en-
us/library/ms685958(VS.85).aspx.
Figura 9: Il Fax stato accodato.
028-036:072-080 1-04-2008 18:44 Pagina 34
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
35
G
Usare i servizi di Windows
, in verit, un puro compromesso per rendere
lo sviluppo dellapplicazione pi rapido e, so-
prattutto, consente al lettore di concentrarsi
sulle parti rilevanti ed originali della trattazio-
ne. La classe FaxHelper eredita dalla classe ba-
se Control dato utilizza il View State per me-
morizzare i valori delle propriet. Le due pro-
priet della classe sono DocToFax, che contie-
ne il nome del documento da trasmettere e
RecipentNumber che contiene il numero a cui in-
viare il Fax.
namespace FaxHelperCs
{
public class FaxHelperClass : Control
{
public string DocToFax
{
get
{
object o = ViewState["DocToFax"];
return (o != null ? o.ToString() : "");
}
set { ViewState["DocToFax"] = value; }
}
public string RecipientNumber
{
get
{
object o = ViewState["RecipientNumber"];
return (o != null ? o.ToString() : "");
}
set { ViewState["RecipientNumber"] = value; }
}
}
}
La classe espone solo un metodo, SendFax che
utilizzeremo per trasmettere un documento.
I passi necessari per trasmettere un Fax sono
relativamente semplici; in primo luogo creia-
mo una istanza della classe FaxServer ed una
istanza della classe FaxDocument (che utiliz-
zeremo a breve); quindi dobbiamo verificare se
il documento che stiamo per trasmettere di
tipo corretto; in caso negativo terminiamo il
metodo impostando un messaggio di errore.
Nel caso in cui il tipo di documento che stiamo
per trasmettere di tipo corretto, ci connettia-
mo al servizio Fax tramite linvocazione del me-
todo Connect delloggetto di tipo FaxServer. Di
seguito verifichiamo se il documento presen-
te nella sotto-cartella Faxes; in caso positivo va-
lorizziamo la propriet Body delloggetto di ti-
po FaxDocument con il percorso al suddetto
documento. Dobbiamo ricordarci che con la
propriet Body indichiamo le pagine del Fax a me-
no della copertina; sebbene nessuno ci vieti di
incorporare la copertina del Fax allinterno del
documento stesso, bene precisare che la clas-
se FaxDocument espone due propriet Cover-
Page e 7 che consentono di definire in modo
opportuno la copertina del Fax da trasmettere.
La descrizione dettagliata delle due propriet
reperibile, rispettivamente agli indirizzi:
http://msdn2.microsoft.com/en-us/library/ ms687493
(VS.85).aspx e http://msdn2.microsoft.com/en-
us/library/ms686003 (VS.85).aspx.
LA PROPRIET BODY
Un altro dettaglio importante da tenere a men-
te quando vogliamo trasmettere un documen-
to via Fax, riguarda il tipo di documento che as-
sociamo alla propriet Body. Abbiamo gi ac-
cennato, quando abbiamo parlato dei vincoli
del progetto, che il tipo di documento che vo-
gliamo trasmettere influisce in modo determi-
nante sul processo di trasmissione. Ebbene a
questo punto possiamo enunciare la regola ge-
nerale che deve essere soddisfatta affinch il
documento venga trasmesso correttamente: il
tipo di documento che intendiamo trasmette-
re deve essere associato ad una applicazione,
installata sulla stessa macchina in cui instal-
lata la nostra applicazione, e la suddetta appli-
cazione deve supportare il verbo PrintTo.
Continuando nella descrizione del metodo Send-
Fax, ci resta da vedere come indicare il numero
del destinatario e come attivare il processo di
trasmissione. Nel primo caso, per rendere pi
semplice la soluzione, imponiamo un solo de-
stinatario il cui numero memorizzato nella
propriet RecipientNumber. I destinatari ven-
gono indicati attraverso la collezione Recipients
delloggetto di tipo FaxDocument; laggiunta di
ogni destinatario la si effettua invocando il me-
todo Add della collezione, a cui passiamo il nu-
mero del destinatario. Il primo parametro del
metodo Add rappresenta il numero del desti-
natario, il secondo parametro rappresenta il no-
me del destinatario (per semplicit passiamo
al metodo lo stesso valore per i due parametri).
Dopo aver definito tutti gli aspetti del Fax da
trasmettere, possiamo effettivamente inviare il
comando di trasmissione al servizio Fax invocando
il metodo ConnectedSubmit delloggetto di ti-
po FaxDocument a cui passiamo, come argo-
mento, proprio listanza delloggetto ti tipo
FaxServer che abbiamo creato in testa al meto-
do SendFax.
028-036:072-080 1-04-2008 18:44 Pagina 35
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web M
G
38
/Maggio 2008
Usare le tecnologie Microsoft per il Web
C
ome si evince dal titolo, lo scopo principa-
le di questo articolo trattare in modo
sistematico le fasi di deploying e installa-
zione di una Web Application sul server di desti-
nazione. Tuttava, evidente che largomento
Asp.Net non si pu considerare esaurito con le
puntate precedenti, vista la cospicua mole di
funzionalit di cui dispone il framework (alcune
delle quali, come i temi, le web parts, i servizi
web, ecc, non potremo neppure affrontare in
questa sede per mancanza di spazio o perch
fuori tema rispetto allo scopo dellarticolo): ci
riserviamo, pertanto, di accennare brevemente
ad alcuni di questi argomenti nei box laterali.
Un argomento che dobbiamo, invece, affrontare
un po pi da vicino lo sviluppo dei controlli
personalizzati. Se, infatti, abbiamo bisogno di un
controllo con particolari caratteristiche (non
presente tra i componenti standard .Net), possia-
mo allora pensare di svilupparne uno persona-
lizzato (quindi crearlo da zero), oppure possiamo
limitarci ad estenderne uno gi presente, inte-
grandolo con nuove funzionalit. Lo User
Control il modo pi semplice di realizzare un
controllo personalizzato: esso, infatti, un con-
trollo che si limita ad incorporare, a sua volta,
vari altri componenti. Ciascuno di questi com-
ponenti, in particolare, associato a determinati
eventi, che possono modificare lo stato dello
User Control stesso e, ovviamente, anche del
resto della pagina. Costruire uno User Control
molto facile: si tratta di un file .ascx, che comin-
cia di solito con una riga simile alla seguente:
<%@ Control Language="C#"
AutoEventWireup="true" %>
Il resto del controllo , invece, costituito da altri
componenti .Net (anche associati a eventi), codi-
ce HTML, codice lato-server, ecc Creare uno
User Control unoperazione in tutto simile alla
realizzazione di una pagina .aspx: nel file .ascx
trasciniamo i componenti, nel .ascx.cs inseriamo
il codice lato-server.
Linclusione del controllo nella pagina si effettua
aggiungendo le seguenti righe (naturalmente,
prima dobbiamo registrare il controllo, e solo
dopo lo possiamo istanziare):
<%@ Register Src="UserControl.ascx"
TagName="banner" TagPrefix="uc1" %>
Tempo di realizzazione
REQUISITI
052-059:072-080 1-04-2008 16:31 Pagina 52
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Maggio 2008/
53
G
Guida ad Apache Wicket
viene effettuato il submit. E proprio alla fine dellese-
cuzione di questo metodo che il framework ridisegna
tutti i componenti grafici controllando che i model a
loro associati non abbiano cambiato valore. Ecco per-
ch proprio in questo metodo che calcoliamo un
numero random e lo utilizziamo per cambiare la strin-
ga associata al modello numModel. A questo punto
non ci resta che aggiungere la Label alla Form e la Form
alla nostra WebPage. Lesempio non ancora com-
pleto, infatti Wicket prevede una parte implementati-
va riservata al codice html da associare ad ogni pagi-
na; quindi nello stesso package in cui collocato il fi-
le HelloNumber.java bisogna editare ed inserire un fi-
le HelloNumber.html.
<html><head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<title>HelloNumber with Wicket 1.3</title>
</head><body>
<form wicket:id="webForm">
<h2>Hello <span wicket:id="numLabel"/></h2>
<input type="submit" value="random"/>
</form></body></html>
Come possiamo notare ci troviamo alle prese con un
semplice file html che presenta alcuni wicket:id valo-
rizzati in maniera opportuna. I valori associati sono e
devono essere le stringhe utilizzate come primo pa-
rametro nella costruzione dei vari componenti grafi-
ci. Oltre a questo primo vincolo bisogna notare che i
tag hrml, che utilizzano il parametro wicket:id, ri-
specchiano la struttura utilizzata dai corrispondenti com-
ponenti nel codice Java. Infatti, nel file html, il tag form
(con wicketd:id=webForm) racchiude un tag span
che contiene anchesso un parametro wicket:id; nel
codice Java corrispondente notiamo che la Label ini-
zializzata con la stringa numLabel viene aggiunta,
tramite il metodo add, alloggetto istanza di Form. In
questo caso sarebbe stato rilevato un errore se avessimo
aggiunto direttamente alla WebPage la Label in questione.
UNAPPLICAZIONE CHE
GESTISCE I TALK
Visti i caratteri salienti di questo framework possia-
mo cimentarci nellimplementazione di unapplica-
zione web un po pi complessa. Ci che vogliamo
implementare unapplicazione che gestisca i talk,
ovvero le conferenze o le presentazioni, tramite fun-
zionalit quali linserimento, la modifica, la visualiz-
zazione e la ricerca degli stessi.
Il nostro ambiente di sviluppo sar composto da Wicket
1.3 e da Eclipse per lediting del codice Java e delle pa-
gine HTML; per quanto riguarda la persistenza dei da-
ti utilizzeremo HSQLDB come DBMS e Hibernate per
il mapping. Lapplicazione inoltre presenter i cano-
nici tre livelli; il presentation layer sar composto da
wicket, il business layer sar rappresentato da una clas-
se che espone i servizi essenziali, mentre il data layer
da Hibernate e da un DAO (Data Access Object) che
ne filtra le funzionalit.
Lo schema dei dati da utilizzare sar semplificato al
massimo riducendo il tutto ad una tabella avente la
seguente struttura:
ID: chiave della tabella
SPEAKER: Stringa che indica la persona che presenter
la conferenza
PLACE: il posto in cui si terr la conferenza
TOPIC: largomento del talk
ABSTRACT: un descrizione abbastanza dettagliata
della presentazione
STARTDATE: il giorno in cui si terr
STARTHOUR: lora di inizio della presentazione
DURATION: la durata in minuti del talk
Tale tabella sar mappata tramite Hibernate su una
semplice classe Java.
public class TalkDTO implements Serializable{
private String id=null;
private String speaker=null;
private Date startDate=null;
private String startHour=null;
private String place=null;
private String topic=null;
private String abstractText=null;
private Integer duration=null;
. . .
//metodi set e get
. . .
}
La classe un semplice Java Bean che contiene tanti
campi quante sono le colonne della tabella vista in
precedenza. Ovviamente tali campi sono utilizzabili tra-
mite i rispettivi metodi set e get ad essi associati.
IMPLEMENTAZIONE
DEL BUSINESS LAYER
Il business layer espone i servizi che sono utilizzati dal
presentation layer; nel nostro caso linterfaccia che
ne riassume le funzionalit TalkService.
public interface TalkService {
public void addTalk(TalkDTO talk) throws
TalkServiceException;
public TalkDTO getTalkByID(String talkID) throws
TalkServiceException;
public List<TalkDTO> findAllTalks() throws
TalkServiceException;
public void updateTalk(TalkDTO talk) throws
052-059:072-080 1-04-2008 16:31 Pagina 53
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
54
/Maggio 2008
Guida ad Apache Wicket
TalkServiceException;
public void deleteTalk(TalkDTO talk) throws
TalkServiceException;
public List<TalkDTO> findTalksBE(TalkDTO tEx)
throws TalkServiceException;
}
I metodi e la loro semantica sono di immediata com-
prensione; infatti addTalk viene utilizzato per ag-
giungere un oggetto istanza di TalkDTO, getTalkByID
serve a leggere un TalkDTO usando la sua chiave, find-
AllTalks serve a leggere tutti i TalkDTO presenti sul DB.
updateTalkinvece invocato dal presentation layer per
modificare un TalkDTO gi esistente e deleteTalk per
cancellarlo. Il metodo findTalksBE implementa una
ricerca by example; questa tipologia di ricerca preve-
de lutilizzo di una entit del dominio di ricerca come
esempio da utilizzare nella ricerca stessa. In pratica si
usa una istanza di TalkDTO con un sottoinsieme dei cam-
pi valorizzati e si usa questo sottoinsieme nella sezio-
ne where dellinterrogazione sql. Ovviamente esiste
una classe concreta che implementa linterfaccia Talk-
Service.
public class TalkServiceImpl implements TalkService{
public static TalkService ts=new TalkServiceImpl();
private TalkServiceImpl(){}
public static TalkService sing(){
return ts;
}
. . .
}
La classe in questione stateless , quindi ne pu esse-
re condivisa una sola istanza da chiunque la voglia
utilizzare. Questa caratteristica spiega il perch si sia
scelto di implementarla come singleton; nel nostro
caso realizzato un costruttore privato ed un metodo
statico sing che ne restituisce lunica istanza statica a
disposizione.
PRESENTATION LAYER:
IL LAYOUT
Ma come si presenter la nostra applicazione agli oc-
chi degli utenti? Per rispondere a questa domanda bi-
sogna progettare sin da ora il layout da utilizzare nel-
limplementazione del presentation layer. Allora stabiliamo
una volta per tutte che le pagine che stiamo per im-
plementare saranno divise verticalmente in tre zone.
In alto prevista una zona contenente il logo dellap-
plicazione, un po pi in basso ci sar un pannello con-
tenente il men delle funzionalit principali; sotto il me-
nu e per tutto il resto della pagina ci sar la zona variabile
riservata alla funzionalit corrente. Quindi sono pre-
viste due zone che saranno presenti in tutte le pagine
ed una variabile. Per implementare questo requisito sfrut-
teremo una delle caratteristiche pi avanzate di Wicket
ovvero la markupinheritance; noi la chiameremo ere-
ditariet tra pagine web. Questa feature consente di
definire pagine web come template (modelli) per altre
pagine web; a livello di classi Java si sfrutta lereditariet
tra classi propria del linguaggio.
public class TalkPageTemplate extends WebPage{
public TalkPageTemplate(){
Label title=new Label("titleLabel","Wicket4Talk");
this.add(title);
TopMenuPanel tmp=new TopMenuPanel("panelMenu");
this.add(tmp);
}
}
Il template in questione molto semplice, infatti de-
finiamo una label per il titolo delle pagine web; il logo
dellapplicazione come vedremo lo definiremo diret-
tamente nellHTML, invece utilizzeremo un TopMe-
nuPanel per linserimento del menu delle funzionalit.
Il file HTML associato al template deve avere lo stes-
so nome (TalkPageTemplate.html) del file Java (cam-
bia lestensione ovviamente) e deve contenere i com-
ponenti definiti nel file Java.
<html><head>
<title wicket:id="titleLabel"></title>
</head>
<body>
. . .
<table><tr>
<td><img src="wicket4t.jpg"/></td>
</tr><tr>
<td><span wicket:id="panelMenu"/></td>
</tr><tr>
<td><wicket:child/>
</td></tr>
</table>
. . .
</body></html>
Il titolo della pagina viene definito tramite il wicket:id
valorizzato con la stringa titleLabel, il tutto serve a
referenziare la stringa Wicket4Talk passata come se-
condo parametro nel costruttore della Label title al-
Figura 1: Lapplicazione Wicket4Talks
052-059:072-080 1-04-2008 16:31 Pagina 54
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Maggio 2008/
55
G
Guida ad Apache Wicket
linterno del file Java. Il resto della pagina presenta
una <table> HTML avente tre righe; nella prima viene
introdotta unimmagine rappresentante il logo del-
lapplicazione. Nella seconda riga viene inserito, tra-
mite il parametro wicket:id valorizzato con la stringa
panelMenu, il pannello con il menu associato alle
funzionalit dellapplicazione stessa. Nella terza riga
possiamo notare lutilizzo del tag <wicket:child/>; que-
sto tag delimita la zona della pagina html che verr
riempita con il contenuto previsto dalle pagine wicket
che estendono la pagina che funge da modello. In pra-
tica ogni pagina, che utilizza il template in questione,
andr a riempire solo questa parte del template.
Anche limplementazione del pannello che contiene
il menu orizzontale molto semplice.
public class TopMenuPanel extends Panel{
public TopMenuPanel(String id){
super(id);
PageLink talksLink=new
PageLink("talks",ListTalksPage.class);
PageLink editTalkLink=new
PageLink("createTalk",CreateTalkPage.class);
PageLink searchTalksLink=new
PageLink("searchTalks",SearchTalksPage.class);
this.add(talksLink);
this.add(editTalkLink);
this.add(searchTalksLink);
}
}
La classe TopMenuPanel estende Panel, come para-
metro del costruttore viene utilizzata una stringa che
deve coincidere con il valore che si d al wicket:id as-
sociato al pannello. Successivamente vengono istan-
ziati ad aggiunte alla pagina tre istanze della classe Pa-
geLink. Loggetto talksLink serve a definire un colle-
gamento ipertestuale con la pagina che visualizza tut-
ti talks memorizzati. Ce da notare che il costruttore del-
la classe PageLink accetta in input due parametri: il
primo, come al solito, determina il valore con cui va-
lorizzare il wicket:id associato, il secondo invece spe-
cifica la classe rappresentante la pagina da visualiz-
zare cliccando sul link. Loggetto editTalkLink con-
sente di accedere alla pagina che implementa
linserimento di un nuovo talk. Invece searchTalksLink
definisce un link alla pagina contenente la funziona-
lit di ricerca. Ovviamente i tre oggetti una volta istan-
ziati vanno aggiunti al Panel tramite il metodo add.
Per quanto riguarda la parte HTML del pannello, ab-
biamo tre semplici tag <a> che utilizzano il wicket:id
valorizzato ognuno con la corrispondente stringa usa-
ta nel codice Java.
<wicket:panel>
<a wicket:id="talks">All Talks</a><a
wicket:id="createTalk">Create Talk</a><a
wicket:id="searchTalks">Search</a>
</wicket:panel>
Il codice HTML delimitato dal tag <wicket:panel>
VISUALIZZARE I DATI
La nostra applicazione prevede sia una funzionalit
che mostra un elenco dei talk presenti sul DB, sia una
funzionalit di ricerca che visualizza un elenco dei talk
che rispondono ai requisiti della ricerca stessa. Le due
fasi di visualizzazione possono essere implementate
dalla stessa classe. Nel nostro caso si tratta della clas-
se ListTalksPage.
public class ListTalksPage extends TalkPageTemplate{
public ListTalksPage(){
super();
try {
List<TalkDTO> talks =
TalkServiceImpl.sing().findAllTalks();
init(talks);
} catch (TalkServiceException e) {
e.printStackTrace();
}
}
public ListTalksPage(List<TalkDTO> talks){
super();
init(talks);
}
. . . metodo init . . .
}
La classe estende il template di pagina che abbiamo ana-
lizzato in precedenza; il primo costruttore viene utilizzato
quando si vogliono visualizzare tutti i talk. I dati ne-
cessari vengono recuperati tramite il metodo find-
AllTalks della classe TalkServiceImpl. Il secondo co-
struttore accetta come parametro una List<TalkDTO>;
questo costruttore si presta ad essere utilizzato per vi-
Figura 2: Il progetto visto da Eclipse
052-059:072-080 1-04-2008 16:31 Pagina 55
ht t p: / / www. i opr ogr ammo. i t
sualizzare una lista di talk recuperati come risultato
di una ricerca. Vediamo adesso come stato imple-
mentato il metodo init.
private void init(final List<TalkDTO> talks){
PageableListView listView=new
PageableListView("talks",talks,10){
public void populateItem(final ListItem item){
final TalkDTO talk =
(TalkDTO)item.getModelObject();
item.add(new Label("speaker", talk.getSpeaker()));
item.add(new Label("place", talk.getPlace()));
item.add(new Label("topic", talk.getTopic()));
item.add(new Label("startDate",
ClientUtils.toString(talk.getStartDate())));
item.add(new Label("startHour",
talk.getStartHour()));
Per prima cosa viene creato un oggetto di tipo Pagea-
bleListView; questa classe, definita nel framework
Wicket, permette di visualizzare tabelle di dati e pa-
ginarli in base alle esigenze del programmatore. Il pri-
mo parametro passato rappresenta la stringa che do-
vr valorizzare il parametro wicket:id allinterno del
codice HTML. Il secondo parametro la lista di og-
getti da visualizzare; il terzo rappresenta il numero
massimo di oggetti che si possono visualizzare con la
paginazione. Per personalizzare il rendering dellog-
getto listView necessario effettuare loverriding del
metodo populateItem. Questo metodo viene invoca-
to dal framework tante volte quanti sono gli oggetti
contenuti in talks. Al suo interno vengono creati cin-
que oggetti istanze di Label, c da notare che i para-
metri utilizzati nei loro costruttori sono la solita strin-
ga da associare ai wicket:id ed i valori da visualizzare
nella tabella recuperandoli dalloggetto talk. Questi
oggetti una volta creati vanno aggiunti tramite il me-
todo addal ListItempassato come parametro nel me-
todo populateItem.
Link viewLink=new Link("viewLink"){
public void onClick(){
setResponsePage(new
ViewTalkPage(talk.getId()));
}
};
item.add(viewLink);
Link updateLink=new Link("updateLink"){
public void onClick(){
setResponsePage(new
UpdateTalkPage(talk.getId()));
}
};
item.add(updateLink);
Link deleteLink=new Link("deleteLink"){
public void onClick(){
try {
TalkServiceImpl.sing().deleteTalk(talk);
talks.remove(talk);
} catch (TalkServiceException e) {
e.printStackTrace();
}
setResponsePage(new ListTalksPage(talks));
}
};
item.add(deleteLink);
}
};
this.add(listView);
this.add(new PagingNavigator("navigator", listView));
}
Ogni riga della tabella di visualizzazione conterr tre
link denominati view, update e delete. La semantica e
la creazione dei tre link abbastanza semplice, c da
notare come per ogni Link venga effettuato loverriding
del metodo onClick. Inoltre tramite il metodo setRe-
sponsePage viene rediretto il browser alla pagina cor-
rispondente al parametro passato al metodo stesso.
Lultima istruzione presente nel metodo init evidenzia
lutilizzo di un PagingNavigator, questo oggetto con-
sente di navigare la tabella quando gli elementi con-
tenuti superano la capacit massima prevista per la
paginazione. Ovviamente nello stesso package va de-
finito un file ListTalksPage.html che viene utilizzato
per visualizzare i dati gestiti dalla classe appena ana-
lizzata.
<wicket:extend>
. . .
<table><tr>
. . .
<tr wicket:id="talks">
<td><span wicket:id="topic"></span></td>
<td><span wicket:id="speaker"></span></td>
<td><span wicket:id="place"></span></td>
SISTEMA M
G
56
/Maggio 2008
Guida ad Apache Wicket
DEPLOYARE LE DUE APPLICAZIONI
Sotto la directory wicket/src
troverete le due applicazioni
descritte in questo articolo; sotto
la directory wicket/wicketone
troverete invece la web-app che
contiene le due applicazioni.
Per veder funzionare le due
applicazioni necessario copiare
la webapp sotto la cartella
webapps della propria istanza di
Tomcat; a questo punto bisogna
configurare in modo opportuno
il file
<tomcat>/webapps/wicketone/WE
B-INF/classes/hibernate.cfg.xml
Supponiamo che il path di
Tomcat sia C:\tomcat, allora
bisogna modificare il suddetto
file in modo che contenga il
seguente tag xml:
. . .
<property
name="hibernate.connection.
url">jdbc:hsqldb:file:/tomcat/
webapps/wicketone/WEB-
INF/db/wicketone</property>
. . .
A questo punto sufficiente
avviare Tomcat e visitare gli url
http://127.0.0.1:8080/wicketone/app/ e
http://127.0.0.1:8080/wicketone/hello/
per veder funzionare le due
applicazioni.
052-059:072-080 1-04-2008 16:31 Pagina 56
ht t p: / / www. i opr ogr ammo. i t
<td><span wicket:id="startDate"></span></td>
<td><span wicket:id="startHour"></span></td>
<td><a wicket:id="viewLink">view</a></td>
<td><a wicket:id="updateLink">update</a></td>
<td><a wicket:id="deleteLink">delete</a></td>
</tr>
</table>
<span wicket:id="navigator"/>
</wicket:extend>
I tag HTML sono delimitati dal tag <wicket:extend>
che necessario ad implementare lereditariet tra
pagine web. In pratica tutto ci che delimitato da
questo tag viene sostituito al tag <wicket:child/> pre-
sente nel codice html del template (TalkPageTempla-
te.html). Allinterno del tag <tr> troviamo il parametro
wicket:id=talks; esso indica che le righe della tabel-
la in questione saranno popolate con loggetto Pa-
geableListView. Ovviamente lassociazione dovuta
al fatto che loggetto stato costruito con lo stesso va-
lore passatogli come primo parametro nel costrutto-
re. Analogamente le colonne delle righe prodotte sa-
ranno associate agli oggetti istanze di Label e di Link
create nel metodo populateItem. Sotto la tabella sta-
to collocato il PagingNavigator referenziato dal para-
metro wicket:id=navigator.
RIUTILIZZO
DEI COMPONENTI
Tra le funzionalit che ci siamo prefissati di imple-
mentare ci sono la creazione, la modifica e la ricerca;
queste funzionalit hanno in comune lutilizzo di una
form per limmissione dei dati relativi ad un talk, ov-
vio che lazione da eseguire dopo la submit della form
diversa. Con le dovute accortezze possiamo imple-
mentare una sola form che di volta in volta esegua
azioni diverse in base al contesto in cui utilizzata.
Noi ci concentreremo sulla funzionalit di modifica
tralasciando lanalisi delle altre in quanto i cambia-
menti riguardano solo linterazione con il Business
Layer. Partiamo questa volta dallimplementazione
delle pagine html, iniziando da UpdateTalkPage.html.
<wicket:extend>
<div align="center"><b>Update Talk</b></div>
<span wicket:id="updatePanel"/>
</wicket:extend>
Ovviamente il tag <wicket:extend> serve a definire
una pagina che ne usa unaltra come template; men-
tre wicket:id=editPanel definisce lutilizzo di un Pa-
nel. La corrispondente classe (UpdateTalkPage.java),
che estende TalkPageTemplate, presenta soltanto
limplementazione del costruttore; loggetto id pas-
sato come parametro serve per recuperare loggetto
tdtodal DB . Successivamente viene costruito un Edi-
TalkFormPanel e lo si aggiunge alla pagina.
public class UpdateTalkPage extends TalkPageTemplate{
public UpdateTalkPage(String id){
super();
UpdateTalkOperation uOperation=new
UpdateTalkOperation();
TalkDTO tdto=null;
try {
tdto=TalkServiceImpl.sing().getTalkByID(id);
} catch (TalkServiceException e) {
e.printStackTrace();
}
EditTalkFormPanel etfp=new
EditTalkFormPanel("updatePanel",true,
uOperation,tdto);
add(etfp);
}
}
Come possiamo notare la stringa updatePanel, pas-
sata come primo argomento al costruttore, corrisponde
al valore associato al parametro wicket:id presente nel
file HTML. Il secondo parametro invece definisce qua-
le operazione deve essere eseguita al submit della form.
La sua implementazione prevede la definizione di un
solo metodo come previsto dallinterfaccia Stra-
tegyOperation.
public class UpdateTalkOperation implements
StrategyOperation{
public void exeOperation(Component
source,TalkDTO tdto) {
try {
TalkServiceImpl.sing().updateTalk(tdto);
source.setResponsePage(new
ViewTalkPage(tdto.getId()));
catch (TalkServiceException e) {
e.printStackTrace();
}
}
}
La prima istruzione effettua lupdate delloggetto sul
DB, successivamente si forza la risposta http verso la
pagina ViewTalkPage.
La classe EditTalkFormPanel estende Panel ed il suo co-
struttore accetta tre parametri.
public class EditTalkFormPanel extends Panel{
public EditTalkFormPanel(String
panelID,StrategyOperation so,TalkDTO td){
super(panelID);
EditTalkForm etf=new
EditTalkForm("editForm",true,so,td);
this.add(etf);
}
M SISTEMA
Maggio 2008/
57
G
Guida ad Apache Wicket
052-059:072-080 2-04-2008 12:11 Pagina 57
ht t p: / / www. i opr ogr ammo. i t
}
Al suo interno viene istanziato un oggetto di tipo Edit-
TalkForm e successivamente viene aggiunto al pan-
nello stesso. La classe EditTalkForm prevede un co-
struttore ed il metodo onSubmit.
class EditTalkForm extends Form{
private StrategyOperation so=null;
private TalkDTO tdto=null;
public EditTalkForm(String formID,boolean
checkFields,StrategyOperation so,TalkDTO td){
super(formID);
this.tdto=(td!=null?td:new TalkDTO());
setModel(new CompoundPropertyModel(tdto));
TextField place=new TextField("place");
TextField speaker=new TextField("speaker");
TextArea abstractText=new TextArea ("abstractText");
TextField topic=new TextField("topic");
DateTextField startDate = new
DateTextField("startDate", new
PrortyModel(tdto,"startDate"), new
StyleDateConverter("S-", true));
startDate.add(new DatePicker());
TextField startHour=new TextField("startHour");
TextField duration=new TextField("duration");
this.add(place);
this.add(speaker);
this.add(abstractText);
this.add(topic);
this.add(startDate);
this.add(startHour);
this.add(duration);
this.so=so;
this.add(new FeedbackPanel("message"));
if(checkFields){
startDate.setRequired(true);
place.setRequired(true);
topic.setRequired(true);
}
}
. . .
}
Una parte cruciale del costruttore quando viene in-
vocato il metodo setModel; infatti viene stabilito che
il Model per la Form in questione loggetto tdtoistan-
za di TalkDTO. In seguito vengono creati ed aggiunti al-
la Form tanti oggetti quanti sono i campi della classe
TalkDTO. Questi Component non vengono inizializzati
con un Model in quanto utilizzano il CompoundPro-
pertyModel passato nel metodo setModel. Loggetto start-
Date costituisce una eccezione a quanto appena det-
to; infatti per i textfield, che contengono delle date, il
framework prevede una specializzazione molto utile:
DateTextField. Questa consente di definire anche lo
stile con cui vengono convertite le date in stringhe. Al
DateTextField stato associato un DatePicker ovvero
un widget grafico che consente di scegliere una data da
un Calendario navigabile che appare come popup.
Nella form viene inserito, tramite loggetto istanza di
FeedbackPanel, anche un campo di testo visualizzato
nel caso in cui ci sia un campo di input che non sta-
to validato. Nel nostro esempio abbiamo utilizzato un
RequiredValidator (il framework ne prevede altri, mol-
to pi complessi); questo validator viene sfruttato so-
lo per i campi in cui viene invocato il metodo setRe-
quired(true). Infatti nel caso in cui lutente non inse-
risce i suddetti campi, viene visualizzato il messaggio
presente nel file EditTalkFormPanel.properties.
RequiredValidator=${label} is a required field
SISTEMA M
G
58
/Maggio 2008
Guida ad Apache Wicket
Figura 3: La form di immissione dati arricchita dal
DatePicker.
Figura 4: Il sito di riferimento del framework Wicket.
PROPERTYMODEL
E COMPOUNDPROPERTYMODEL
Il framework Wicket
fortemente basato sul concetto
di Model; per questo motivo ne
sono previsti di vario tipo e con
caratteristiche molto diverse
tra loro. PropertyModel
definisce un tipo di Model che
associa ad un Component la
propriet (utilizzando i metodi
set e get) di un oggetto
corrispondente alla stringa
passata come secondo
argomento al suo costruttore:
DateTextField startDate = new
DateTextField("startDate", new
PrortyModel(tdto,"startDate"));
Mentre la classe
CompoundPropertyModel
associa ad un insieme di
Component le propriet di un
oggetto utilizzato come
parametro nel suo costruttore, in
questi casi il nome del
Component deve corrispondere
alla propriet delloggetto.
NOTA
IL SITO DI
RIFERIMENTO
Da quasi un anno
Wicket un progetto
open source della
comunit Apache;
pertanto il suo sito di
riferimento
raggiungibile
allindirizzo
http://wicket.apache.
org
052-059:072-080 1-04-2008 16:31 Pagina 58
Il messaggio viene associato al particolare Validator
utilizzato nel file Java che porta lo stesso nome del fi-
le properties. Il metodo onSubmit non fa altro che in-
vocare il metodo exeOperationsulloggetto istanza di
una classe che implementa StrategyOperation.
public final void onSubmit(){
so.exeOperation(this,tdto);
}
Ormai abbiamo imparato che le classi che estendo-
no direttamente o indirettamente Panel o WebPage
devono avere un file html associato. Analizziamo le
caratteristiche salienti di EditTalkFormPanel.html.
<wicket:panel>
<span wicket:id="message"/>
<form wicket:id="editForm">
<table>
<tr><td>TOPIC</td><td>
<input wicket:id="topic"/>
</td></tr>
<tr><td>SPEAKER</td>
<td><input wicket:id="speaker"/>
</td></tr>
<tr><td>ABSTRACT</td>
<td> <TEXTAREA
wicket:id="abstractText"></TEXTAREA></td></tr>
. . .
</table>
</form>
</wicket:panel>
Il tag <wicket:panel> racchiude il contenuto html re-
lativo ad un panel. Il wicket:id=message serve a vi-
sualizzare gli eventuali messaggi di errore commessi
dallutente nel compilare la form; mentre il
wicket:id=editForm collega la form html alla classe
EditTalkForm. La stessa form, come avevamo gi visto
nel codice Java, contiene tutti i campi di input asso-
ciati ai campi della classe TalkDTO.
CONCLUSIONI
In questo articolo abbiamo visto le caratteristiche prin-
cipali di Wicket; ci siamo resi conto che il suo utilizzo
prevede una netta separazione tra la logica di presen-
tazione dei dati, realizzata in puro HTML, e la logica ap-
plicativa implementata in Java. Per quanto riguarda
lapproccio imposto dal framework palese che i con-
cetti fondamentali sono quello di model e quello di
componente grafico; questi delineano un alto grado di
riutilizzo del codice che va al di la dei principi base
della programmazione OO.
Roberto Sidoti
M SISTEMA Guida ad Apache Wicket
LAUTORE
Roberto Sidoti
Ingegnere
Informatico, in
passato si occupato
di servizi VoIP;
attualmente lavora
come Functional
Designer per Herzum
Software, una
multinazionale di
consulenza
specializzata nello
sviluppo di
componenti per
applicazioni SOA.
052-059:072-080 1-04-2008 16:31 Pagina 59
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
63
G
Progettare una pagina web
A
l terzo appuntamento del nostro mini-
corso sui fogli di stile siamo arrivati ad
affrontare uno dei pi affascinanti (e anche
complessi) temi con cui si trova a che fare chi lavora
con i CSS: il layout.
IL LAYOUT
DELLA PAGINA WEB
Per layout della pagina Web intendiamo tutto quel-
l'insieme di tecniche che riguardano il posiziona-
mento degli elementi nella pagina e la struttura della
stessa. Agli albori del web il problema non si poneva
nemmeno : la struttura era pi o meno quella di un
documento di testo, al massimo si poteva agire sulla
dimensione ed il colore dei caratteri. I Menu erano
rappresentati come elenchi puntati. Poi, a partire
dalla versione 3.2 di HTML (rilasciata dal W3C nel
1997), venne rilasciato un nuovo elemento ovvero
<table>. Nelle intenzioni dei progettisti delle specifi-
che di HTML 3.2 il tag <TABLE> doveva servire a rap-
presentare dei dati in forma di struttura riga/colonna
(come una tabella di database o di excel per capirsi).
Ad esempio per rappresentare alcuni dati statistici in
HTML si pu usare un markup come questo:
<table border="1"><caption><em>Dati
popolazione</em> </caption><tr>
<th rowspan="2"></th>
<th colspan="2">Media </th>
<th rowspan="2">Occhi<br />
celesti </th></tr><tr>
<th>altezza </th><th>peso </th>
</tr><tr> <th>Maschi </th>
<td>1,78 </td>
<td>75 kg </td>
<td>20% </td>
</tr><tr><th>Femmmine </th>
<td>1,70 </td><td>60 kg </td>
<td>23% </td></tr>
</table>
Che d luogo ad un output come quello mostrato in
figura 1. Questo tag ha da subito attirato l'attenzione
dei web designer. L'elemento table infatti consente
di disporre a piacimento gli elementi nella pagina
controllandone in modo molto preciso la posizione.
Uno dei principali utilizzi di table il posizionamen-
to dei menu del sito a destra o sinistra del contenu-
to principale della pagina web. Poniamo ad esempio
di dover realizzare un layout del tipo di quello
mostrato in figura 2. Utilizzando una tabella di
layout si sarebbe impostato con :
<table border="0" cellspacing="2" cellpadding="2"
width="100%">
<tr valign="top"><td bgcolor="#EEEEEE"
width="180px">
<h2>Menu</h2><div><a href="#">Chi siamo</a>
</div><div><a href="#">Dove siamo</a></div>
<div><a href="#">I prodotti</a></div><div>
<a href="#">Contatti</a></div></td><td>
<h2>Contenuti</h2><p><!testo -->
</p>
</td>
</tr>
</table>
Conoscenze richieste
Conoscenza di base di
HTML
Software
Nessuno
Impegno
Tempo di realizzazione
REQUISITI
STOP ALLE TABELLE
PAGINE WEB CON I CSS
PENULTIMO APPUNTANTAMENTO CON I FOGLI DI STILE. ABBANDONIAMO L'USO DELLE TABELLE
PER DISEGNARE IL LAYOUT DELLA PAGINA WEB E VEDIAMO COME UTILIZZARE I CSS PER CREARE
SITI WEB PI ACCESSIBILI E GESTIBILI. DIMENTICHIAMO IL TAG TABLE.
Fig. 1: Una <table> nel browser
Fig. 2: Un semplice layout con menu laterale creato
utilizzando <table>
063-067:032-035 1-04-2008 16:54 Pagina 63
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web
M
G
64
/Maggio 2008
Progettare una pagina web
Questo solo un esempio semplicistico, in realt le
table sono state spesso usate (e abusate) dai web
designer per comporre delle pagine ad elevato con-
tenuto grafico. Prendiamo ad esempio l'orribile
layout grafico mostrato in figura 3 A prescindere
dalla sua bruttezza, pone comunque problemi teori-
ci di non poco conto : elementi sovrapposti, semitra-
sparenze e altro ancora.Il web designer non faceva
che disegnare la pagina in un programma grafico
come Photoshop e poi, usando gli strumenti messi a
disposizione degli stessi tool grafici, suddivideva
l'immagine in tanti "pezzetti" che venivano riassem-
blati in una table. Il programma si occupava di split-
tare l'immagine in vari pezzetti e di produrre il codi-
ce HTML. Il codice HTML corrispondente all'output
di figura 3 qualcosa come:
<table id="Table_01" width="801" height="601"
border="0" cellpadding="0" cellspacing="0">
...
<tr>
<td colspan="8">
<img
src="images/Splitted_table_06.jpg" width="687"
height="1" alt=""></td>
<td>
<img
src="images/spacer.gif" width="1" height="1"
alt=""></td>
</tr>
<tr>
<td rowspan="6">
<img
src="images/Splitted_table_07.jpg" width="1"
height="157" alt=""></td>
<td colspan="2" rowspan="2"
background="images/Splitted_table_08.jpg"
width="194" height="76">
<div
id="azienda">l'azienda</div></td>
<td rowspan="11">
<img
src="images/Splitted_table_09.jpg" width="2"
height="500" alt=""></td>
<td colspan="2" valign="top"
rowspan="7" width="269" height="354"
background="images/Splitted_table_10.jpg">
<!-- <img
src="images/Splitted_table_10.jpg" width="269"
height="354" alt=""> -->
<p>
<!--testo-->
</p>
</td>
<td colspan="2">
<img
src="images/Splitted_table_11.jpg" width="221"
height="1" alt=""></td>
<td>
<img
src="images/spacer.gif" width="1" height="1"
alt=""></td>
</tr>
...
</table>
si pensi che ne abbiamo riportato solo un minimo
estratto!
I PROBLEMI DI TABLE
Da quanto abbiamo visto risulta evidente che
l'utilizzo delle tabelle per l'impaginazione era diven-
tato pressoch insostenibile. I problemi che si evi-
denziano in questa tecnica sono:
1. Leggendo la pagina con un browser testuale il
testo contenuto negli elementi grafici si perde ed
il menu si trova mischiato al testo
Questo fatto rende difficile l'accessibilit al sito ai
disabili che utilizzano strumenti diversi dai nor-
mali browser.
2. Lo spezzettamento delle immagini e il riassembla-
mento utilizzando una tabella porta a produrre
un codice HTML pressoch illeggibile, con molte
piccole immagini da scaricare e quindi a proble-
mi notevoli per la manutenzione e le prestazioni
del sito.
Ma allora come evitare tutto questo? Naturalmente
con i CSS!
LA SOLUZIONE DEI CSS
Sempre prendendo a modello il layout che abbiamo
visto, potremmo ottenere un effetto analogo con il
seguente markup HTML:
<div id="main">
<img src="images/testo.png" id="testo"
width="260" height="100" alt="Sito Web" />
<ul class="menu">
<li><a
href="#">l'azienda</a></li>
<li><a href="#">chi
siamo</a></li>
Fig. 3: Il layout grafico ottenuto da immagini suddivi-
se e da tabelle
063-067:032-035 1-04-2008 16:54 Pagina 64
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web
Maggio 2008/
65
G
Progettare una pagina web
<li><a
href="#">prodotti</a></li>
<li><a
href="#">contatti</a></li>
</ul> <div class="content"> <!--testo-->
</div> </div>
collegato al foglio di stile che definisce queste regole:
body {background-color: #C9D3E2; }
DIV#main {
width: 695px; height: 447px;
margin: 30px auto; position: relative;
background: url(images/ovale.jpg) no-repeat left top;
}
IMG#testo{
position: absolute; top: -40px; left: 35px;
}
UL.menu {
margin: 0; padding: 0; list-style: none;
position: absolute; left: 30px; top: 70px;
}
UL.menu LI {
width: 176px; height: 35px;display: block; margin: 0;
background: url(images/button.png) no-repeat left top;
padding: 15px 0 0 20px;
font: bold 12px Arial, Helvetica, sans-serif;
}
UL.menu A {
text-decoration: none; color: #FFF;
}
DIV.content{
position: absolute; top:76px; left: 250px;
width:269px; height:300px;
font: normal 12px 'Lucida Sans Unicode','Lucida
Grande', Arial, Helvetica, sans-serif;
}
Il risultato identico a quello ottenuto con la tabel-
la (sempre orribile, ma questo non colpa dei CSS).
I vantaggi sono evidenti:
1. HTML comprensibile e leggero (27 kb contro i 38
kb della versione precedente, il 30% in meno).
2. Separazione tra grafica e struttura. Che vuol dire
anche riusabilit sia della veste grafica (applica-
bile ad altre pagine simili) che della struttura
(alla quale possono essere applicate altre vesti
grafiche)
3. Accessibilit anche da parte di browser testuali o
vocali.
Abbiamo visto quindi che, con l'aiuto dei CSS, possi-
bile ottenere risultati analoghi, e anche superiori, a
quanto possiamo realizzare usando le tabelle.
Vediamo adesso, in pratica, quali sono gli attributi CSS
maggiormente coinvolti nella realizzazione dei layout.
TECNICHE CSS PER LAYOUT
Per controllare il layout sono utilizzati alcuni attribu-
ti CSS, di alcuni ne abbiamo gi parlato nell'articolo
precedente, ma comunque opportuno ricordarli :
G border disegna un bordo intorno all'elemento.
border dispone a sua volta di tre sotto-attributi:
border-width lo spessore del bordo espres-
so in px o con una delle costanti : thin, medium
e thick.
border-color il colore del bordo
border-style lo stile del bordo : solid (conti-
nuo), dotted (tratteggiato), dashed (tratteggiato
pi ampio), double (doppia linea), groove (corni-
ce 3D), inset (incassato 3D), outset (in rilievo 3D).
Quindi, ad esempio:
border:1px solid #CCCCCC;
oppure:
border-width:1px;
border-style:solid;
border-color: #CCCCCC;
border tuttavia consente anche una maggiore flessi-
bilit, ovvero l'impostazione di un bordo diverso
per ognuno dei quattro lati dell'elemento attraverso
gli attributi:
border-top lato superiore
border-right lato destro
border-bottom lato inferiore
border- left lato sinistro
ognuno di questi attributi ha poi, a sua volta, dei
sotto-attributi corrispondenti a spessore, colore e
stile, quindi possiamo avere:
border-top:1px solid #CCCCCC;
o anche:
border-top-width:1px;
border-top-style:solid;
border-top-color: #CCCCCC;
e cos via anche per gli altri tipi di bordo.
G margin imposta il margine esterno dell'elemen-
to rispetto all'elemento contenitore.
Il valore espresso nelle unit di misura valide.
Come valore possibile anche usare auto oppure
0 (il numero zero senza unit di misura).
Margin una propriet composta, nel senso che
NOTA
MIMARE IL
COMPORTA-
MENTO DELLE
TABELLE
Nella versione 2 dei
CSS possibile
riprodurre
esattamente il
comportamento dei
tag table, tr, td ecc.
attraverso i valori
dell'attributo display
(table, table-row,
table-cell ecc.). Se
siamo costretti a
ricorrere ad altri
stratagemmi perch
Internet Explorer,
nemmeno nella
versione 7, supporta
queste propriet (lo
far solo dalla
versione 8).
063-067:032-035 1-04-2008 16:54 Pagina 65
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web
M
G
66
/Maggio 2008
Progettare una pagina web
accetta fino a quattro valori separati da spazi (per
indicare, rispettivamente, il margine superiore,
destro, inferiore e sinistro) con una sintassi del tipo:
margin:<top> <right> <bottom> <left>;
ad esempio:
margin:0 auto 12px 30%;
se viene indicato un solo valore il margine si
applica a tutte le direzioni, ad esempio:
margin:4px;
Anche per margin poi possiamo esprimere valori
per uno dei quattro lati:
margin-top
margin-right
margin-bottom
margin-left
G padding imposta il margine interno dell'elmen-
to, riferito cio rispetto agli elementi contenuti in
esso. Il valore espresso nelle unit di misura
valide che abbiamo visto per margin.
Anche qui possiamo usare padding sia come pro-
priet composta:
padding:0 auto 12px 30%;
che come propriet singole:
padding-top:0;
padding-right:auto;
padding-bottom:12px;
padding-left:30%;
oppure riferendosi a tutte le direzioni:
padding:4px;
G left , top, bottom e right impostano la distanza
(sinistra, superiore, inferiore e destra) dell'ele-
mento dall'intera pagina se position absolute.
G min-width, min-height, max-width, max-height
impostano larghezza e altezza minima e massi-
ma dell'elemento. In Internet Explorer queste
propriet sono supportate solo dalla versione 7 in
standard mode (cio, come abbiamo detto nel-
l'articolo precedente quando viene dichiarato un
DOCTYPE), la versione 6 supporta solo min-
height per gli elementi TD,TH e TR.
Per gli attributi position, float, display, visibility fac-
ciamo riferimento a quanto illustrato nell'articolo
precedente.
LA STRUTTURA
GENERALE DELLA PAGINA
La prima cosa da impostare la disposizione gene-
rale della pagina, ovvero le "macro aree" in cui si
presenta la pagina Web. Gli elementi principali
sono schematizzati nella figura 5:
container elemento che contiene tutti gli altri, a
rigor di logica potrebbe sembrare logico che sia il
tag HTML body. Tuttavia pi spesso di ricorre a un
div che ci consente una maggiore flessibilit come
impostare i margini, centrare il contenuto rispetto
alla pagina, dargli una larghezza fissa ecc.
header la testata della pagina, in genere conte-
nente il logo e le informazioni o i link di carattere
generale.
menu il menu del sito, posizionato di norma a
destra o sinistra del contenuto e contenente i link
alle altre pagine.
content il contenuto vero proprio della pagina
footer il pi di pagina, contenente di solito le infor-
mazioni come indirizzo, contatti ecc.
Ovviamente in una struttura possono anche non
essere presenti tutti questi elementi o esservene
anche altri, ma di norma questi sono i pi utilizzati.
Come buona pratica in genere si tende a contrasse-
gnare nel codice HTML questi elementi con un ID
corrispondente piuttosto che usare una classe CSS,
questo perch si tratta comunque di elementi che
non si ripetono (unici) e usare un selector di tipo #ID,
nel foglio di stile, ci aiuta a rintracciare subito gli ele-
menti. Basiamoci su una struttura come questa:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-
transitional.dtd">
<html><head>[...]</head>
<body>
<div id="container"><div id="header">[...]</div>
<div id="main"><div id="menu">[...]</div>
<div id="content">[...]</div>
</div>
<div id="footer">[...]</div>
</div>
</body>
</html>
vediamo come impostare il layout. Cominciamo col
Fig. 4: Lo stesso layout, ma creato con i CSS
063-067:032-035 1-04-2008 16:54 Pagina 66
ht t p: / / www. i opr ogr ammo. i t
M
ioProgrammo Web Progettare una pagina web
preparare un foglio di stile dove definiremo:
G il container fisso al centro con larghezza di 780
pixel
G il menu a sinistra di 180px
G il footer fisso a fondo pagina
Per prima cosa definiamo quindi le regole per gli ele-
menti di insieme HTML e BODY:
html,body {
margin:0;
padding:0;
height:100%; /* necessario per calcolare
min-height di container*/
background-color: #CCC;
font-family:Arial, Helvetica, sans-serif;
font-size:small;}
da notare come abbiamo impostato height al 100%
ci consentir il rendering corretto della propriet
min-height nel container. Quindi passiamo ad
impostare le regole per il DIV contenitore che abbia-
mo contrassegnato con l'ID "container":
DIV#container {position:relative; /* necessario per il
posizionamento del footer*/
margin: 0 auto;width:780px;
background-color: #FFF;
height:auto !important;
height:100%; /* IE6: lo interpreta come
min-height*/
min-height:100%;}
qui importante sottolineare che :
G abbiamo centrato l'elemento attribuendogli una
width di 780px e un margindestro settato ad auto
(il margine destro ad auto centra orizzontalmente
l'elemento).
G abbiamo dapprima definito height come auto
(contrassegnandolo con !important per evitare
ogni possibile ridefinizione) in modo da consenti-
re al DIV di ereditare il 100% di height degli ele-
menti padri.
G per evitare problemi con IE6 abbiamo poi ridefi-
nito height al 100% (gli altri browser avevano gi
ricevuto questa impostazione da auto).
G abbiamo impostato anche min-height al 100%
Definiamo poi l'header e il blocco main (che contie-
ne a sua volta menu e content):
DIV#header {border-bottom: 1px solid #999;
background-color: #FFF;}
DIV#main {position: relative;
padding-left: 200px;
padding-right: 10px; }
da notare padding-left di main a 200px che crea un
margine interno per ospitare il menu posizionato in
modo assoluto. Passiamo quindi al menu che viene
appunto definito come assoluto e allineato in alto a
sinistra di larghezza 180px:
DIV#menu {position: absolute;
top: 0; left: 0; width: 180px;}
Impostiamo anche alcune regole per il content e per
i paragrafi in esso contenuti:
div#content {padding:2px 2px 40px 2px; /* bottom
padding per footer */}
div#content p {margin: 0;
text-align:justify;
padding:10px;}
da notare qui il margine inferiore per il content in
modo da evitare che vi si sovrapponga il footer che
viene posizionato in modo assoluto. Ed infine pas-
siamo al footer che viene ancorato in fondo al con-
tainer e posizionato in modo assoluto:
DIV#footer {border-top: 1px solid #999;
background-color: #FFF;
text-align: center;
color:#999;
font-weight: bold;
position:absolute;
width:100%;
bottom:0; /* attaccato al bottom */}
CONCLUSIONI
In questo terzo appuntamento abbiamo cominciato
a vedere l'applicazione delle regole CSS che ci con-
sentono di superare il layout ottenuto con <TABLE>,
partendo dalla definizione della struttura della
pagina. Nel prossimo appuntamento vedremo, altri
due elementi molto utilizzati nel web : i menu, le
colonne e i box.
Francesco Smelzo
Fig. 5: Elementi strutturali della pagina web
Maggio 2008/
67
G
063-067:032-035 1-04-2008 16:54 Pagina 67
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
72
/Maggio 2008
Usare Crystal Report
M
icrosoft fornisce, allinterno di Visual Studio.NET,
un tool di grande qualit per la reportistica. Si trat-
ta di Crystal Reports che, come viene illustrato nel-
larticolo, perfettamente integrato in Visual Basic.NET, con-
sentendo di gestire con facilit le stampe allinterno delle ap-
plicazioni gestionali che sviluppate.
Una degli aspetti pi interessanti di Visual Basic.NET
, infatti, la possibilit di costruire report con sempli-
cit, utilizzando uno strumento che dalla versione 6.0
fa parte dellambiente di sviluppo. Il Crystal Reports
un prodotto di facile utilizzo che consente di creare
report personalizzati, elenchi e prospetti di stampa di
vario tipo utilizzando le informazioni contenute al-
linterno di un database. Il funzionamento si basa sul-
la creazione di connessioni con uno o pi database e
sullutilizzo di questi link per ricavare valori dai cam-
pi dei database selezionati, che possono poi essere in-
seriti nei report.
Lobiettivo principale di questo articolo quello di il-
lustrare lutilizzo di Crystal Reports in ambiente Vi-
sual Basic. Esistono, infatti, degli appositi ActiveX che
consentono di attivare da unapplicazione VB uno o
pi report.
DESTINATARI
DEL REPORT
I report sono strumenti di supporto alle decisioni e
destinati a chiunque debba gestire qualcosa. Il loro
principale obiettivo consiste nel permettere agli uten-
ti di apprendere rapidamente gli elementi e le rela-
zioni fondamentali esistenti tra i dati, in modo che sia
possibile prendere decisioni appropriate. La validit del
report dipende dalla presentazione logica di dati cor-
retti, proprio perch un report che contiene informa-
zioni presentate in maniera non chiara o addirittura er-
rate non solo pu rallentare il processo decisionale,
ma pu condurre a conclusioni sbagliate. Se, ad esem-
pio, il direttore commerciale di unazienda desume,
da uno o pi report errati, che il volume delle vendite
ha un trend crescente pu ritenere necessaria una po-
litica dinvestimenti e di costi ben differente se avesse
ricevuto dati veritieri sullattuale realt aziendale.
Prima di creare un report consigliabile identificar-
ne brevemente per iscritto gli obiettivi, in maniera ta-
le da poter realizzare uno strumento che persegua
proprio quanto definito in fase di analisi. In questa fa-
se sar necessario spendere un po di tempo con chi ci
ha commissionato il lavoro per evitare di prendere
cantonate. Ad esempio lobiettivo del report pu essere:
1. La visualizzazione delle vendite mensili ed an-
nuali per agente di vendita, paragonare le cifre del-
lanno corrente con quelle dellanno precedente e con-
trassegnare gli agenti con valori delle vendite che
non corrispondono agli standard societari.
2. La visualizzazione delle attivit di vendita per ogni
voce dellinventario e la definizione del punto di
riordino delle quantit in base a tale attivit.
3. Il calcolo e la successiva visualizzazione delle am-
monizioni e delle espulsioni per ogni squadra che par-
tecipa al campionato di calcio di serie A e B.
Dopo la definizione dellobiettivo importante capi-
re chi sono gli effettivi destinatari del nostro lavoro.
Un singolo report viene solitamente utilizzato da pi
persone. Tornando al caso delle vendite, un report det-
tagliato sulle vendite potrebbe essere utile agli agen-
ti di vendita, ai responsabili di area e magari anche al
direttore generale, anche se ognuno potrebbe essere
interessato ad aspetti diversi.
KICK OFF
DEL PROGETTO
La prima operazione che dovete effettuare durante la
fase di creazione di un report quella di attivare VB.NET,
per poi fare clic sul menu File-New-Project, selezio-
nare Windows Application nella sezione Templates,
digitare il nome dellapplicazione in Name e, poi, fate
clic sul pulsante OK. Successivamente, facendo clic
destro nella finestra Solution Explorer sul nome del
progetto, scegliere Add, poi New Iteme nella finestra
Add New Itemscegliete Crystal Reports. A questo pun-
to compare automaticamente il wizard Crystal Reports
Gallery dove si possono scegliere le seguenti opzioni:
G Using the Report Wizard, per creare un report in ma-
CREARE REPORT CON
VISUAL BASIC.NET
GRAFICI E REPORTISTICA RAPPRESENTANO UNA PARTE IMPORTANTE DI OGNI APPLICAZIONE.
VISUAL STUDIO OFFRE STRUMENTI AVANZATI PER LA CREAZIONE DI REPORT PROFESSIONALI.
IN QUESTO ARTICOLO SPIEGHEREMO PASSO DOPO PASSO COME USARLI
Conoscenze richieste
Visual Studio e VB.NET
Software
Windows XP Service
Pack 2, Windows
Server 2003 o
Windows Vista e
Visual Studio 2005
versione Standard o
Professional
Impegno
Tempo di realizzazione
REQUISITI
072-078:072-080 1-04-2008 16:35 Pagina 72
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Maggio 2008/
73
G
Usare Crystal Report
niera guidata ed ex-novo.
G As a Blank Report, per generare un report vuoto,
senza alcun campo, n connessioni a database.
G From an Existing Report, per effettuare una copia
di un report gi creato in precedenza.
In questa esemplificazione utilizzerete la prima op-
zione. Il Report Wizard permette di procedere in ma-
niera guidata partendo dalla conessione al database.
Nella sezione Data selezionate Create New Connec-
tion e fare clic su OLE DB (ADO) per connettervi im-
mediatamente ad un database server come Microsoft
SQL Server o Oracle, scegliendo, poi, nella finestra di
dialogo che appare Microsoft OLE DB Provider for SQL
Server. Quindi indicate il nome del computer su cui
risiede il database, se la macchina corrente basta di-
gitare localhost, utente e password ed infine il nome
della fonte dati, ossia del database. A questo punto
lorigine dei dati disponibile ed necessario indica-
re gli oggetti da inserire nel report, che possono esse-
re tabelle, viste o stored procedure.
La successiva sezione Link rappresenta graficamen-
te i collegamenti tra gli oggetti selezionati in base alle
varie chiavi primarie e straniere presenti sulle tabelle.
Qui necessario eliminare i link sbagliati o che non
sono necessari per la costruzione del report finale.
Inoltre anche possibile verificare quali indici sono
presenti grazie alla legenda attivabile con il pulsante
Index Legend.
Nella successiva sezione Fields dovete indicare i cam-
pi che includerete nel report, che solitamente sono
solo una parte di quelli presenti nelle tabelle selezio-
nate.
In seguito nella sezione Grouping dovete individuare
le cosiddette Group By per raggruppare i dati secon-
do determinati campi. Facendo clic sul pulsante Next
si passa alla sezione Summaries, dove vengono indi-
viduati i campi di riepilogo e cio quelli che vengono
sommati in base ai criteri di raggruppamento. Nella
sezione Group Sortingi gruppi sono ordinati in base ai
totali di riepilogo.
Avete, poi, la possibilit dinserire un grafico che pu
essere a barre verticali, a linee orizzontali o a torta.
Nella successiva schermata, Record Selection, potete fil-
trare le informazioni, selezionando un sottoinsieme
dei campi presenti nel database, grazie alla combo
box posta a sinistra in basso; l si pu, ad esempio, spe-
cificare che devono essere visualizzati solo i computer
con scadenza inferiore o uguale al 31 dicembre 2008
(data digitata sulla seconda combo box, che appare
in seguito). Infine la sezione conclusiva Report Style per-
mette di scegliere il modello da utilizzare per visua-
lizzare il report tra dieci differenti alternative. A destra
il wizard Crystal Reports visualizza ogni volta lanteprima
di ci che state scegliendo.
OGGETTI
DEL CRYSTAL REPORTS
La finestra di progettazione di ciascun report, de-
nominata per default CrystalReport1.rpt, simile
ad una form di Visual Basic, ed include una fine-
stra di progettazione nella quale viene creato il
layout del report. Essa, inoltre, composta dagli
oggetti riportati di seguito.
Section. Ciascuna sezione della finestra di progetta-
Figura 1: Aggiungete nel progetto il componente Crystal
Reports
Figura 2: Il wizard consente di selezionare il database
di riferimento.
Figura 3: Le relazioni tra le tabelle utili al vostro report
possono essere modificate.
NOTA
UNA VERA
E PROPRIA
JOINT
VENTURE
Crystal Reports un
prodotto con
copyright Crystal
Decisions ed dal
1992 che diventato
lo strumento per la
creazione di report
pi utilizzato su
sistemi operativi
Microsoft Windows.
Questa
collaborazione tra
Microsoft e Crystal
Decisions
continuata fino ai
nostri giorni, anche
dopo lacquisizione
da parte di Business
Objects di Crystal
Decisions.
072-078:072-080 1-04-2008 16:35 Pagina 73
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
74
/Maggio 2008
Usare Crystal Report
zione Crystal Reports rappresentata da un oggetto
Section. In fase di progettazione, ogni sezione rap-
presentata da unintestazione, su cui potete fare clic
per selezionare la sezione, e da un riquadro in cui
possibile inserire e posizionare controlli. Tramite que-
sto oggetto e le relative propriet possibile riconfi-
gurare un report in modo dinamico prima delleffet-
tiva creazione.
Controlli di Crystal Reports. Si tratta di controlli speciali
che funzionano soltanto nella finestra di progettazio-
ne Crystal Reports. In questultima finestra non pos-
sibile utilizzare i controlli intrinseci di Visual Basic o
anche controlli ActiveX. I controlli specifici di Crystal
Reports sono inclusi nella casella degli strumenti di
Visual Basic nella scheda Crystal Reports. In partico-
lare quando in un progetto si aggiunge una finestra di
progettazione di un report, nella nuova scheda Cry-
stal Reports della casella degli strumenti vengono ag-
giunti automaticamente i seguenti controlli:
G Text Object, che consente di formattare testo.
G Line Object. Consente di disegnare righe nel report
per contraddistinguere in modo pi chiaro le varie
sezioni.
G Box Object. Consente di inserire nel report rettan-
goli, cerchi e ovali.
I DATI DA INCLUDERE
Lintestazione non deve venire trascurata, in quanto con-
tiene le informazioni necessarie allidentificazione del
report stesso. In particolare dovreste sempre include-
re i seguenti elementi: nome dellente responsabile
del report, titolo ed eventualmente anche gli obietti-
vi del report.
Il numero di pagina, invece, dovrebbe sempre fare
parte del pi di pagina come anche il termine confi-
denziale se necessario e la data e lora del report.
I tipi di dati che si possono includere in un report pro-
dotto con Crystal Reports sono i seguenti:
1. Provenienti da campi di un oggetto (tabella, vista,
ecc) di un database e sono quelli pi diffusi.
2. Calcolati ricavandoli in parte da campi di databa-
se.
3. Digitati direttamente dallautore in caselle di te-
sto (intestazioni, etichette, note e cos via).
Un altro concetto da tenere presente il raggruppamento
delle informazioni. Non una novit, in quanto pre-
sente nel linguaggio SQL ed un modo molto poten-
te che potete utilizzare per creare dei gruppi di record
in base al cliente, alla data o a qualsiasi altro criterio.
A rottura del gruppo potrete inserire dati calcolati, ti-
picamente percentuali, totali parziali/generali o me-
die.
LE SEZIONI
DI CIASCUN REPORT
Osservando un report possibile distinguere almeno
sette sezioni che potete utilizzare in fase di progetta-
zione. Si tratta di aree di default che sono destinate a
contenere i dati e le intestazioni del report. Tutte le
aree possono contenere controlli personalizzati pre-
levati dalla toolbox e, quindi, descrizioni statiche, da-
ti estratti dai database collegati e immagini grafiche.
Report Header. E una sezione che pu contenere il ti-
tolo del report e magari unimmagine grafica che ren-
de pi piacevole il prospetto di stampa.
Page Header. Contiene le intestazioni che vengono ri-
petute ogni volta che avviene un salto pagina.
Group Header Section1. Qui si deve indicare il campo
(o i campi) che fa parte della tabella master.
Details. Nella sezione corrente vengono specificati i
campi che sono presenti nella tabella detail. Ad esem-
pio si possono considerare le due tabelle Publishers e
Titles, che sono relazionate tra loro tramite il campo Pu-
bID. Infatti le tabelle sono legate con una relazione
uno a molti. Ci significa che per ogni record della ta-
bella master Publishers sono presenti uno o pi re-
cord sulla tabella detail Titles.
Group Footer Section1. Qui vengono specificati even-
tuali campi che devono essere riportati alla fine della
visualizzazione degli elementi di ciascun gruppo.
Report Footer. il cosiddetto pi di pagina del report,
ossia lultima sezione del report che viene visualizza-
ta sullultima pagina dello stesso. In questa area pos-
sono venire riportati i totali generali del report. Se, ad
esempio, le tabelle master e detail fossero rispettiva-
mente banche e conti correnti, evidente che in que-
sta sezione sarebbe possibile indicare la formula che
effettua la sommatoria degli importi dei saldi relativi
a tutte le banche considerate.
Page Footer. Si tratta dellarea del report che viene
ripetuta nella parte inferiore di ogni pagina. Soli-
tamente qui si indica la data e il progressivo della
pagina corrente.
NOTA
REPORT NON
CORRELATI
Crystal Reports
consente anche di
stampare due o pi
report non correlati
tra loro, luno di
seguito allaltro. Per
ottenere ci
necessario creare il
report che sintende
stampare per primo
ed inserirlo nella
sezione Details.
Successivamente
importare un report
gi esistente o creare
un subreport e
posizionarlo nella
sezione Report
Footer. Gli altri,
eventuali subreport,
dovranno essere
inseriti in ulteriori
Report Footer creati
con il Section Expert
(tasto destro del
mouse, opzione di
menu Insert Section).
Figura 4: La finestra di progettazione di Crystal Reports.
072-078:072-080 1-04-2008 16:35 Pagina 74
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Maggio 2008/
75
G
Usare Crystal Report
UN REPORT DI SINTESI
Nellesempio presentato con questo articolo viene in-
cluso il database Access che contiene i dati caricati dal
report. Prodotti.mdb composto da 5 tabelle: 1.Or-
dineProdotto, che la traduzione della relazione con
molteplicit N x M che mette in collegamento le entit
ordine e prodotto. Lunico campo non chiave la quan-
tit di prodotto venduta per ogni ordine. 2.Ordine, che
contiene tutti gli attributi propri (come data dellor-
dine e data di consegna) e le chiavi straniere (foreign
keys) che la collegano alle tabelle Cliente e Venditore.
3.Prodotto, che contiene la descrizione del prodotto
e il costo unitario. 4.Cliente, che contiene attributi co-
me nome di riferimento, fax, indirizzo, ed altri anco-
ra. 5.Venditore, che la tabella relativa alla forza ven-
dita.
Lobiettivo del report che si vuole creare quello di
mettere a confronto le vendite effettuate da ciascun
agente di vendita, evidenziando una serie di campi di
dettaglio tra cui i prodotti venduti, le quantit vendu-
te, il prezzo unitario e quello totale (prezzo per quan-
tit). Inoltre si vogliono evidenziare anche i seguenti cam-
pi calcolati: fatturato totale realizzato da ciascun agen-
te di vendita e totale fatturato realizzato da tutta la for-
za vendita.
I destinatari possono essere i responsabili di area, il
direttore commerciale e qualsiasi manager che si oc-
cupa della politica delle vendite aziendale, ma anche
della gestione del personale.
Prima di tutto si deve creare un nuovo progetto di ti-
po Windows Application ed inserire sulla form il con-
trollo CrystalReportViewer. Bisogna, poi, ricordarsi
dimpostare a Fill la propriet Dock per ancorare il
controllo a tutti i lati della form corrente e, successi-
vamente, in ReportSource necessario specificare per-
corso e nome del report da visualizzare.
Qualora vogliate utilizzare lo stesso CrystalReport-
Viewer per pi report dovrete impostare la propriet
ReportSource dinamicamente da menu o tramite un
pulsante di comando. Il codice che segue serve pro-
prio a fare lo switch tra 2 report:
Private Sub Cambia_Click(...) Handles Cambia.Click
Static flgCR As Boolean = False
flgCR = Not flgCR
If Not flgCR Then
CrystalReportViewer1.DisplayGroupTree = True
Me.Text = "Comparazione delle vendite per
agente"
CrystalReportViewer1.ReportSource =
myCrystalReport1
Else
CrystalReportViewer1.DisplayGroupTree = False
Me.Text = "Ordini evasi per ogni cliente"
CrystalReportViewer1.ReportSource = CRcliente
End If
End Sub
Al fine di evitare errori, si ricorda anche che deve essere
anche incluso il codice che segue (nellapplicazione
desempio presente nella sezione generata automa-
ticamente da Progettazione Windows Form):
Friend WithEvents myCrystalReport1 As
WindowsApplication2.CrystalReport1
Figura 5: Le sezioni di un report a design time consento-
no di definire la logica di elaborazione: intestazioni, rag-
gruppamenti, dati calcolati e non, ecc.
Figura 6: I vincoli relazionali presenti nel database
Microsoft Access Prodotti.mdb.
Figura 7: Loggetto CrystalReportViewer, una volta inseri-
to nel progetto, si fonde con la form corrente.
072-078:072-080 1-04-2008 16:35 Pagina 75
ht t p: / / www. i opr ogr ammo. i t
Friend WithEvents CRcliente As
WindowsApplication2.CRcliente
Me.myCrystalReport1 = New
WindowsApplication2.CrystalReport1
Me.CRcliente = New WindowsApplication2.CRcliente
Aggiungere un componente Crystal Report come spie-
gato in precedenza e procedete allutilizzo del wizard
individuando passo dopo passo tutte le opzioni che
interessano. In particolare si deve scegliere un report
standard e identificare il database Prodotti.mdb co-
me quello di riferimento dopo avere fatto clic su File di
database. In questo modo si rendono disponibili tut-
ti gli oggetti del file Access allapplicazione.
IL WIZARD EXPERT
Nella sezione Links sono ora disponibili i collega-
menti che evidenziano le tabelle master e di detta-
glio ed in generale i vincoli dintegrit referenziale im-
postati sul database. Se volete aggiungere o rimuo-
vere collegamenti questa la sede giusta per farlo.
In effetti in questo caso il Report Wizard potrebbe
aver definito un link tra il campo Region della ta-
bella Venditore e il campo Regiondella tabella Clien-
te che non interessando bene rimuovere. Aggiun-
gete, poi, manualmente un link tra il campo SalesI-
NI della tabella Venditore e il campo SalesINI della
tabella Ordine, necessario per collegare la forza ven-
dita alle altre tabelle del database.
Successivamente individuate i seguenti campi da vi-
sualizzare: Venditore.Lastname, Venditore.Region,
Ordine.Shipping Date, Cliente.Contact Name,
Cliente.City, Cliente.Contract Date, Prodotto.Product
Description, Prodotto.Unit Price e OrdineProdot-
to.Quantity. Seguendo il wizard potrete impostare rag-
gruppamenti, ad esempio in questo report nella se-
zione Group By scegliete Venditore.Lastname per
raggruppare le righe in base al cognome degli agen-
ti di vendita.
La sezione successiva consente di definire il grafico
da visualizzare nel report. Fate clic sullopzione Bar
Chart.
Nella sezione Details aggiungete un campo calcolato
ottenuto dalla moltiplicazione di Prodotto.Unit Price
per OrdineProdotto.Quantity, al fine di identificare il fat-
turato totale di ogni prodotto di una certa categoria
agente per agente. Per inserire campi calcolati fate clic
su Formula Fields nel Field Explorer, scegliete Newe do-
po avere inserito il nome Prezzo x QTA viene visua-
lizzata la finestra di dialogo Formula Editor. Qui ne-
cessario scegliere Prodotto.Unit Pricepoi tra gli operatori
aritmetici Multiplyed infine OrdineProdotto.Quantity.
Dopo avere salvato le modifiche il nuovo campo pu
essere inserito tra i campi da visualizzare nel report.
In seguito facendo clic destro sul grafico e scegliendo
Chart Expert potete personalizzare il grafico creato
con il wizard. In Type scegliete Horizontal mentre in
Dataimpostate Once per report nel campo Place Chart
e a destra lopzione Header. In On change of scegliete
Venditore.Lastnamee in Showimpostate Sum of @Prez-
zo x QTA. In questo modo il grafico in grado di vi-
sualizzare un confronto tra il fatturato realizzato da
ogni agente. Nella sezione Text digitate, Venditore nel
campo Group titlee Guadagno realizzatonel campo Da-
ta title.
Nella sezione Group Footer dovete fare clic con il pul-
sante destro del mouse e selezionare Insert-Summa-
ry. Nella finestra di dialogo che appare operate come
segue: nella prima casella selezionate Prezzo x QTA,
nella seconda Sume, poi, scegliete OK. In questo mo-
do il campo Prezzo x QTA verr sommato per dare il
fatturato totale dellagente di vendita. Fate la stessa
cosa nella sezione Report Footer per calcolare il fat-
turato globale della forza vendita.
A questo punto potete eseguire il report (ricordarsi
della propriet ReportSource) e visualizzare le infor-
mazioni selezionate e presenti nel database Prodot-
ti.mdb.
Probabilmente sar opportuno procedere ad alcune ri-
finiture, come dimensionare manualmente alcuni
campi che risultano troppo piccoli, o posizionarli me-
glio, inserire le intestazioni e i pi di pagina, ed altro an-
cora.
SISTEMA M
G
76
/Maggio 2008
Usare Crystal Report
Figura 8: I dati vengono raggruppati in base al campo
Lastname della tabella Venditore.
Figura 9: Creazione di un campo calcolato con lo stru-
mento Formula Editor.
SUL WEB
possibile reperire
utili informazioni,
consigli e
suggerimenti su
Crystal Reports
visitando i seguenti
siti:
www.developers.net
(esempi da scaricare
nella sezione Crystal
Reports in basso a
sinistra)
www.businessobjects.co
m/products/reporting/cr
ystalreports
www.crystalreports.com
www.codeguru.com
(cercare Crystal
Reports)
msdn.microsoft.com/libr
ary (sezione
Development Tools
and Languages)
msdn2.microsoft.com/e
n-us/vbasic
072-078:072-080 1-04-2008 16:35 Pagina 76
ht t p: / / www. i opr ogr ammo. i t
IL CONTRIBUTO DI OLE
Per rendere pi significativo il report possibile inse-
rire oggetti elaborati allinterno di altre applicazioni, qua-
li Microsoft Access o Microsoft Excel. In particolare la
tecnologia OLE consente di ottenere molto sempli-
cemente questo risultato.
La prima versione di Object Linking and Embedding
(OLE1) nata da una collaborazione tra Microsoft, Al-
dus, Lotus, WordPerfect ed altri produttori di softwa-
re, con lobiettivo di rendere disponibile un meccani-
smo standard per consentire la gestione di Documenti
Composti (Compound Documents) in ambiente Win-
dows; le prime realizzazioni di OLE1 sono apparse gi
agli inizi del 1991. Lidea base di questa tecnologia era
quella di consentire ad unapplicazione (applicazio-
ne Client) dincludere un oggetto generato da unal-
tra applicazione (applicazione Server) tramite un par-
ticolare riferimento alloggetto da gestire (Object
Linking) o incorporando loggetto nel formato richie-
sto dallapplicazione Server allinterno del documen-
to gestito dallapplicazione Client (Object Embed-
ding).
In questo modo , per esempio possibile, incorpora-
re (Embedding) in un prospetto di stampa di Crystal
Reports un foglio elettronico di Excel o connettersi
(Linking) ad unapplicazione Server, ad esempio rea-
lizzata con Access. Nel primo caso loggetto incorpo-
rato, essendo solo una copia delloriginale, non in-
fluenzato dalle modifiche che, dopo loperazione di
embedding, vengono effettuate nellapplicazione Ser-
ver, mentre nel secondo caso le modifiche apportate
allinterno del Server saranno comunque riflesse an-
che allinterno del Client. Con la successiva introdu-
zione di OLE versione 2 (OLE2) si raffinata la capa-
cit di gestire oggetti composti e si sono poste le pre-
messe per consentire la realizzazione di un Windows
Object Oriented Environment. Nel caso dellesempio
stato incorporato un grafico Excel che, affiancato a
quello ottenuto con il Wizard di Crystal Reports, d
una visione anche delle commissioni che ciascun for-
nitore ottiene sui prodotti venduti. Se in sede di stam-
pa non si pu assolutamente percepire la differenza tra
i due grafici, tuttavia dovrebbe essere chiaro che il se-
condo frutto dellelaborazione effettuata allinterno
di unapplicazione esterna a Crystal Reports, appun-
to Excel. Procedendo in questo modo ovviamente
possibile rendere sempre pi chiari i dati contenuti
nel report. Ovviamente un oggetto pu essere inseri-
to in Crystal Reports in due modi, ovvero incorpo-
randolo o collegandolo. In entrambi casi si crea un
documento composto secondo OLE. Se si utilizza
lincorporamento, nel report viene memorizzata una
copia delloggetto originale. Gli oggetti incorporati
non hanno alcun collegamento con i relativi oggetti
di origine e pertanto esistono solo nel documento con-
tenitore. Se sincorpora lo stesso oggetto in report di-
versi, loggetto in ogni report non ha alcuna relazio-
ne con gli altri oggetti. Le modifiche apportate ad un
oggetto incorporato non verranno applicate agli altri
oggetti. Le modifiche apportate ad un oggetto nel-
lapplicazione di origine non vengono applicate al-
loggetto nellapplicazione contenitore. Sar quindi
necessario aggiornare manualmente loggetto nel-
lapplicazione contenitore. Ricordate che tutte le vol-
te che ricorrete a OLE appesantite notevolmente il re-
port, perch un oggetto incorporato parte integran-
te del report e pertanto aumenta in modo considere-
M SISTEMA
Maggio 2008/
77
G
Usare Crystal Report
Figura 10: Il report a run time evidenzia il grafico a barre
e i dati cos come selezionati durante la fase di disegno.
Figura 11: Relazione tra oggetti client collegati, incorpo-
rati e server OLE.
NOTA
VERIFICARE
IL DATABASE
Eseguendo
lapplicazione
allinterno
dellambiente di
sviluppo pu
accadere che il report
non venga
visualizzato a causa
di una richiesta di
login. Per evitare ci
dovete effettuare la
verifica del database
su tutti i report,
facendo clic destro
sul report stesso e
scegliendo Database-
Verify database.
Nella finestra che
appare fate clic sul
piccolo pulsante
corrispondente al
campo Database
Name, selezionate il
database di
riferimento, fate clic
su Open ed, infine, su
Finish. Al termine
dovr comparire il
messaggio The
database is up to
date.
Figura 12: Il secondo grafico presente nel report un
oggetto OLE.
072-078:072-080 1-04-2008 16:35 Pagina 77
ht t p: / / www. i opr ogr ammo. i t
vole le dimensioni del filedi riferimento. Al contrario,
se si utilizza il collegamento, nel report viene memo-
rizzata unimmagine delloggetto con le informazioni
di riferimento, mentre loggetto vero e proprio rima-
ne memorizzato nel documento server.
I SUBREPORT
Un subreport un report inserito allinterno di un re-
port principale e per crearlo bisogna procedere come
nel caso di un normale report. Un subreport si differenzia
dal report principale in quanto inserito al suo inter-
no come oggetto e non esiste da solo; pu essere po-
sizionato in qualsiasi sezione del report e, allinterno
di tale sezione, viene stampato per intero; non pu
contenere a sua volta un subreport. Tipicamente un
subreport viene creato per combinare report non cor-
relati in un unico prospetto di stampa oppure per pre-
sentare diverse visualizzazioni degli stessi dati in un
unico report. Per inserire un subreport basta visualiz-
zare la form che ospita il report e fare clic destro con il
mouse scegliendo, poi, lopzione di menu Insert-Sub-
report. Dopo aver posizionato il subreport nella se-
zione opportuna del report principale, necessario
decidere se scegliere un report gi esistente oppure se
crearne uno nuovo con il Report Wizard.
I subreport possono essere collegati oppure no al re-
port principale. Quelli non collegati sono autonomi: i
dati non sono collegati ai dati del report primario in al-
cun modo. Tra laltro subreport di questo tipo posso-
no anche utilizzare origini di dati completamente di-
verse da quelle del report principale. Al contrario i su-
breport collegati son esattamente lopposto, in quan-
to ic dati sono coordinati tra loro e il programma fa
corrispondere i record del subreport a quelli del re-
port principale. Per esemplificare se si crea un report
principale con informazioni sui clienti e un subreport
con informazioni sugli ordini e li si collega, il pro-
gramma crea un subreport per ciascun cliente e in-
clude anche tutti gli ordini ad essi relativi. Nellesem-
pio che vi presentiamo stato creato un report prin-
cipale che contiene nome e dati relativi a ciascun ven-
ditore, per ciascuno dei quali, grazie ad un subreport,
vengono specificati i dati di vendita. Al fine di colle-
gare report principale e subreport dovete specificare
un campo in entrambi i report in maniera tale che
contengano dati comuni. Crystal Reports utilizza tali
campi per coordinare i dati e permette di definirli se-
lezionando Change Subreport Links nel menu che
compare facendo clic destro sul subreport stesso. A
questo punto necessario evidenziare il campo che
si desidera utilizzare come campo di collegamento
nel report principale che lo contiene; fate, poi, clic sul
pulsante con la freccia rivolta verso destra. Il campo verr
aggiunto alla casella di riepilogo Field(s) to link to e
selezionato come campo di collegamento. Successi-
vamente dovete fare la stessa cosa per il subreport in-
dividuando lo stesso campo scelto per il report prin-
cipale in Subreport parameter field to use e i dati da vi-
sualizzare nel subreport Select data in subreport based
on field. Queste operazioni devono essere eseguite per
ciascun collegamento aggiuntivo che si vuole creare.
Al temine scegliete OK.
UNOCCHIATA
AL REPORTVIEWER
Ai pi attenti utilizzatori di Visual Basic.NET non pu
essere sfuggito il controllo ReportViewer. Si tratta di
un ulteriore strumento che Microsoft fornisce agli svi-
luppatori VB per creare report che, tuttavia, non fa
parte del voluminoso pacchetto di Crystal Reports.
Per utilizzare il ReportViewer dovete creare, al solito,
un progetto di tipo Windows Application, inserire il
controllo di riferimento ed, infine, creare una fonte di
dati, aggiungendo un nuovo DataSet. Ricordatevi, poi,
di definire la query SQL che costituir il riferimento
sorgente per il report finale. Linterfaccia del ReportViewer
costituita da pi sezioni ed ha una struttura simile a
quella delle prime versioni di Crystal Reports.
Sebbene si tratti di uno strumento abbastanza flessi-
bile ed utile a creare report collegati a database, il mag-
giore punto debole la mancanza di un wizard per la
creazione veloce del prospetto di stampa e la conseguente
necessit di realizzare il report passo dopo passo in
maniera del tutto manuale.
Enrico Bottari
SISTEMA M
G
78
/Maggio 2008
Usare Crystal Report
Figura 13: Creazione di un report che contiene un subre-
port.
Figura 14: Il controllo ReportViewer fornisce uno stru-
mento alternativo a Crystal Reports.
LAUTORE
Enrico Bottari lavora
da 18 anni nel
mondo ICT. Ha avuto
esperienze
significative nel
campo dello sviluppo
software su
piattaforme
Microsoft,
dellamministrazione
di database Oracle e
della gestione di
sistemi UNIX Solaris
System V.
Attualmente
responsabile delle
attivit operazionali
(LAN, Office
Automation e servizi
Desktop) presso la
sede italiana di
unimportante
organizzazione
internazionale.
072-078:072-080 1-04-2008 16:35 Pagina 78
ht t p: / / www. i opr ogr ammo. i t
MULTIMEDIA M
G
80
/Maggio 2008
Player MP3 in una pagina Web con Flash
P
er prima cosa apriamo il nostro software
Flash 8, creiamo un nuovo documento e
impostiamo le sue dimensioni a 300 x 200
pixel (le dimensioni possono essere modificate a
piacimento). A questo punto iniziamo a creare i
livelli di cui abbiamo bisogno rinominandoli come
in figura
Questo importante in quanto sar pi facile
individuare e capire su quale parte stiamo lavo-
rando. Selezioniamo il primo fotogramma del
livello Sfondo e disegniamo un rettangolo che
sar lo sfondo del nostro lettore (volendo possia-
mo anche utilizzare unimmagine dopo averla
importata nella libreria), ovviamente il rettango-
lo (o limmagine) avr la stessa grandezza del
nostro documento.
Ora nel primo fotogramma del livello Azioni
andremo a scrivere il nostro codice stop(); per
bloccare la riproduzione del filmato flash su quel
fotogramma, clicchiamo quindi col tasto destro
del mouse sul primo fotogramma del suddetto
livello e selezioniamo la voce Azioni e nel pan-
nello che si aprir scriveremo il seguente codice:
stop();
REALIZZAZIONE
DEI PULSANTI
A questo punto iniziamo ad occuparci dellaspet-
to grafico del nostro lettore, che potremmo
modificare come pi ci aggrada. Posizioniamoci
sul livello player e cominciamo col creare i pul-
santi di cui abbiamo bisogno. Se non vogliamo
perdere troppo tempo a disegnarne di nostri o
non si molto ferrati con la grafica, possiamo
utilizzare i pulsanti gi pronti che si trovano in
libreria; quindi dal menu Finestra andiamo col
cursore sulla voce Librerie comuni e selezionia-
mo la voce pulsanti:
Ora scegliamo i pulsanti pi adatti allo stile che
DOTARE UNA PAGINA
WEB DI UN MP3 PLAYER
IN QUESTO ARTICOLO DESCRIVEREMO COME REALIZZARE UN LETTORE MP3 PER I NOSTRI SITI
WEB. UTILIZZEREMO LA TECNOLOGIA MESSA A DISPOSIZIONE DA MACROMEDIA FLASH E INTE-
GREREMO IL TUTTO CON UNA STRUTTURA XML DI SUPPORTO.
Conoscenze richieste
Principi di XML e
utilizzo base di Flash
Software
Macromedia Flash
Impegno
Tempo di realizzazione
REQUISITI
Fig.1: In figura possiamo vedere come sono strutturati
i livelli.
Fig.3: Accesso alla libreria dei pulsanti.
Fig.2: Lo sfondo utilizzato per il nostro player.
080-089:072-080 2-04-2008 12:04 Pagina 80
ht t p: / / www. i opr ogr ammo. i t
M
MULTIMEDIA
Maggio 2008/
81
G
Player MP3 in una pagina Web con Flash
abbiamo pensato per il nostro lettore guardando
tra le librerie denominate Playback. Dopo aver
scelto i nostri pulsanti e trascinati sullo stage
dovremo trovarci nella seguente situazione:
Dopo aver posizionato i pulsanti, selezioniamoli
uno ad uno tenendo premuto il tasto SHIFT dalla
tastiera, in modo da selezionarli tutti, e poi clic-
chiamo su un solo pulsante col tasto destro e
selezioniamo la voce converti in simbolo, conver-
tiremo cosi in clip filmato il gruppo di pulsanti e
gli daremo come nome player
Ora diamo al gruppo di pulsanti appena creato,
come nome distanza, comandi e poi saremo
pronti per il passo successivo.
A questo punto clicchiamo due volte sul clip fil-
mato comandi e ci ritroveremo nella seguente
situazione:
CREAZIONE DEL
CONTROLLO VOLUME
Prima di procedere alla realizzazione del coman-
do volume bene creare tutti i livelli di cui avre-
mo bisogno, quindi per prima cosa rinominiamo
il livello 1, che quello che contiene i nostri pul-
santi, e chiamiamolo pulsanti, poi assegniamo
i rispettivi nomi distanza: al pulsante play dare-
mo come nome distanza playbtn, a stop dare-
mo stopbtn, a next daremo nextbtn ed infine
a back daremo backbtn. Adesso possiamo crea-
re i livelli di cui abbiamo bisogno per realizzare
gli altri comandi del lettore. Disponiamo e rino-
miniamo i livelli come in figura:
Come possiamo notare dalla figura precedente
abbiamo il livello volume_mascherato a cui
stata applicata una maschera. Per ora limitiamo-
ci a dare ai livelli gli stessi nomi di quelli in figu-
ra e procedendo dal basso verso lalto, realizzia-
mo il contenuto dei nostri livelli. Quindi selezio-
niamo il primo fotogramma di livello_sound e
nello stage andremo a posizionare un movie clip
vuoto che sar il nostro livello dappoggio in cui
verr creata, grazie al codice che dopo vedremo,
listanza per il caricamento della canzone che
verr riprodotta dal nostro lettore. Per realizzare
un clip filmato vuoto premiamo ctrl+F8, oppu-
re dal menu Inserisci selezioniamo la voce
Nuovo Simbolo , e chiamiamolo livel-
lo_appoggio clicchiamo su ok e ci ritroveremo
allinterno del movie clip appena creato.
Rinominiamo il livello 1 del suddetto clip filma-
NOTA
UN PLAYER
MP3 PRONTO
ALLUSO
Se non si vuole creare il
player ex-novo per
mancanza di voglia o
tempo, esistono in Rete
molti player gratuiti
pronti alluso. Uno su
tutti XSPF Web Music
Player, scaricabile
allindirizzo
http://musicplayer.source
forge.net/ .
Il player altamente
personalizzabile grazie
ai numerosi parametri
di configurazione, ed
in grado di gestire le
playlist.
Fig.4: Linsieme dei pulsanti di controllo
Fig.7: Ora siamo allinterno del clip filmato
comandi
Fig.8: Disposizione dei livelli e rispettivi nomi.
Fig.5: Il menu che consente di convertire in clip filmato.
Fig.6: Il campo in cui indicare il nome distanza del
clip filmato player.
080-089:072-080 2-04-2008 12:18 Pagina 81
ht t p: / / www. i opr ogr ammo. i t
MULTIMEDIA
M
G
82
/Maggio 2008
Player MP3 in una pagina Web con Flash
to, e chiamiamolo sound_temp, possiamo
anche chiamarlo diversamente in quanto il
nome del livello serve semplicemente come
descrizione del suo contenuto o delle sue funzio-
ni. A questo punto torniamo indietro al livello
player e, dopo aver selezionato il primo foto-
gramma di livello_sound trasciniamo dalla
libreria il clip filmato appena creato, ossia livel-
lo_appoggio. Posizioniamolo dove ci pare,
anche fuori dal nostro stage, in quanto come ho
gi specificato solo un livello dappoggio per la
canzone in riproduzione, e infine assegniamogli
come nome distanza appoggio. Passiamo ora
alla parte delicata del nostro progetto: creare una
barra per il volume utilizzando le maschere.
Selezioniamo il primo fotogramma del livello
guida_volume e creiamo un nuovo clip filmato,
premendo ctrl+F8, in cui andremo a disegnare
e posizionare come in figura la nostra linea guida
per lo scorrimento della barra di volume:
Assicuriamoci di aver posizionato la nostra guida
volume a destra del crocino dorigine come
abbiamo visto in figura; questa azione ci torner
utile in seguito per la realizzazione del codice per
il controllo della barra del volume. Torniamo
indietro al livello player e trasciniamo sullo
stage il clip filmato contenente la nostra guida
volume. Infine, assicurandoci di aver selezionato
il primo fotogramma del livello guida_volume,
selezioniamo il clip filmato della nostra guida e
diamogli come nome distanza guida_vol. Ora
passiamo alla realizzazione della nostra barra di
volume che, dovr avere la stessa forma e dimen-
sione della nostra guida volume, quindi posizio-
niamoci sul primo fotogramma del livello vol-
ume_mascherato e ancora una volta premiamo
ctrl+F8 per realizzare il nostro clip filmato, che
chiameremo volume_masch. Disegniamo la
nostra barra di volume e posizioniamola come in
figura facendo sempre attenzione al crocino
dorigine, perch questa volta esso si dovr trova-
re a destra della barra di volume, mentre prima,
per la guida, si trovava a sinistra.
Ora prima di tornare indietro al livello player
avremo bisogno di realizzare il pulsante che ci
permetter di trascinare la barra di volume appe-
na creata. Selezioniamo quindi la nostra barra di
volume e cliccando col tasto destro la trasforme-
remo in un clip filmato (cos da avere due clip fil-
mato annidati un nellaltro) che chiameremo
volum:
Dopo aver convertito la nostra barra di volume,
aggiungiamo un livello alla linea temporale e
chiamiamolo pulsante, questo conterr il pul-
sante che ci permetter di trascinare la barra
Per realizzare ora il nostro pulsante premiamo
ancora ctrl+F8, ma stavolta, invece della spun-
ta sulla voce clip filmato la metteremo sulla voce
pulsante, diamo il nome di volume_btn e clic-
chiamo su ok. A questo punto ci ritroveremo
davanti ad una situazione come in figura seguen-
te:
Guardando la linea temporale noteremo le voci:
- Su
- Sopra
- Gi
- Premuto
Queste voci corrispondo agli stati di un pulsante
e quindi a come il pulsante dovr comportarsi ad
ogni evento. Per ora lasciamo stare tutti gli stati e
concentriamoci su quello di cui abbiamo biso-
Fig.9: Ecco la nostra linea guida su cui scorrer la
barra di volume.
Fig.10: La barra di volume si trova a sinistra del cro-
cino dorigine rispetto alla guida volume che si trova-
va a destra del crocino.
Fig.11: Convertiamo la barra di volume appena realiz-
zata in clip filmato.
Fig.12: I livelli allinterno del clip filmato
volume_masch
Fig.13: Allinterno del pulsante appena realizzato.
080-089:072-080 1-04-2008 18:38 Pagina 82
ht t p: / / www. i opr ogr ammo. i t
M
MULTIMEDIA
Maggio 2008/
83
G
Player MP3 in una pagina Web con Flash
gno e cio un pulsante invisibile che ci per-
metter di trascinare la barra del volume.
Posizioniamoci sul fotogramma corrisponden-
te alla voce Premuto e clicchiamo col tasto
destro selezionando dal menu a tendina la voce
inserisci fotogramma chiave. In questo foto-
gramma andremo a disegnare e posizionare
come in figura 13 il nostro pulsante che sar
semplicemente un quadrato di colore nero (o
qualsiasi altro colore). Se tutto andato a buon
fine la situazione sar la seguente:
Ora torniamo indietro al livello vol-
ume_mascherato e trasciniamo dalla libreria
allo stage il pulsante appena creato nel primo
fotogramma del livello pulsante, gli daremo
come nome distanza volume_btn ed andre-
mo a posizionarlo sulla nostra barra volume e
pi precisamente sullestremit destra della
nostra barra.
Torniamo ora ancora pi indietro al nostro
livello player per andare a realizzare lultima
parte del nostro controllo volume, ovvero la
maschera. Posizioniamoci sul primo fotogram-
ma del livello maschera e premiamo
ctrl+F8. Questa volta andremo a realizzare un
oggetto di tipo grafico, quindi spuntiamo
appunto la voce Grafico, diamo come nome
maschera e clicchiamo su ok. Ora disegnere-
mo la nostra maschera che avr la grandezza e
la forma dellarea che vogliamo sia visibile. Per
il nostro caso la maschera avr la stessa forma
della barra del volume e della guida, ma ho
deciso di farla un po pi grande per assicurar-
mi la visibilit della barra. Stavolta non dobbia-
mo preoccuparci del crocino di origine in
quanto questa solo una maschera che posi-
zioneremo noi manualmente sullarea da ren-
dere visibile. Dopo aver disegnato la nostra
maschera, torniamo indietro al livello player
e nel primo fotogramma del livello vol-
ume_mascherato andiamo a trascinare dalla
libreria il nostro clip filmato, contenente la
barra di volume e il pulsante, che abbiamo
chiamato volume_ma
scherato, gli daremo come nome distanza
volum e lo sovrapporremo precisamente alla
nostra barra della guida. Passiamo poi al primo
fotogramma del livello maschera e trascinia-
mo dalla libreria allo stage il nostro oggetto
grafico maschera, sovrapponendolo alla barra
del volume facendo combaciare precisamente
lestremit sinistra e il lato superiore della
maschera con la barra. Clicchiamo quindi con
il tasto destro sul nome del livello maschera
nella barra temporale e selezioniamo la voce
maschera cos da rendere una maschera il
nostro oggetto grafico e un oggetto mascherato
la nostra barra di volume.
Prima di proseguire con la realizzazione del
nostro lettore, possiamo assicurarci che il con-
trollo volume appena creato funzioni nel modo
corretto dal punto di vista grafico; andiamo
quindi a cliccare col tasto destro sul primo
fotogramma del livello azioni ed aggiungia-
mo questo codice che servir per il controllo
del volume:
volum.volume_btn.onPress = function() {
sinistra = guida_vol._x;
sopra = guida_vol._y;
destra =
guida_vol._x+guida_vol._width;
Fig.14: La struttura del pulsante appena realizzato.
Fig.15: Barra e pulsante invisibile
Fig.16: Il menu che permette di creare un livello
maschera
080-089:072-080 1-04-2008 18:38 Pagina 83
ht t p: / / www. i opr ogr ammo. i t
sotto = guida_vol._y;
volum.startDrag(false, sinistra,
sopra, destra, sotto);
};
volum.volume_btn.onRelease = function() {
volum.stopDrag();
};
volum.volume_btn.onReleaseOutside =
function() {
volum.stopDrag();
};
dopo aver aggiunto il codice premiamo
ctrl+Invio e facciamo partire il filmato. Se
abbiamo fatto tutto come si deve, andando a
cliccare e trascinando dallestremit destra la
barra del volume, la vedremo accorciarsi
verso sinistra. In realt la barra non si accorcia
realmente ma trasla soltanto verso sinistra.
Leffetto labbiamo dato grazie alla maschera
creata che nasconde la parte di barra che supe-
ra la grandezza della maschera stessa. Andiamo
ora ad analizzare il codice per traslare la barra;
abbiamo richiamato il pulsante volume_btn
allinterno di volum, che il nome distanza
che abbiamo assegnato alla nostra barra, ed
essendo un pulsante gli abbiamo assegnato il
gestore onPress(). Quindi abbiamo che al clic-
care del pulsante volum_btn si da il via ad una
serie di processi contenuti nella funzione asse-
gnata a quellazione sul pulsante; in questo
caso si da il via alla funzione start Drag(), che
rende trascinabile il clip filmato a cui stata
assegnata durante la riproduzione. possibile
trascinare solo un clip filmato alla volta. Dopo
l'esecuzione di un'operazione startDrag(), il
clip filmato rimane trascinabile finch non
viene esplicitamente arrestato da stop Drag()
oppure finch non viene chiamata un'azione
startDrag() per un altro clip filmato. I parametri
per la suddetta funzione saranno per noi un
Valore booleano che specifica che il clip filma-
to trascinabile bloccato al centro della posi-
zione del mouse (se assegniamo true) oppure
bloccato nel punto in cui l'utente aveva fatto
clic sul clip filmato la prima volta (se assegnia-
mo false), come abbiamo fatto nel nostro caso.
Gli altri parametri che abbiamo indicato deli-
mitano lo spazio entro il quale il clip filmato
pu muoversi. Il valore di guida_vol._x vale per
il lato sinistro, guida_vol._y per il lato superiore
e quello inferiore ed infine guida_vol._width
per il lato destro. Continuando ad analizzare il
codice troviamo altri due gestori sempre asse-
gnati al pulsante, ovvero, onRelease(); e
onReleaseOutside(); questi due gestori servono
rispettivamente a controllare il rilascio del pul-
sante. Il primo controlla il rilascio entro larea
del pulsante stesso, mentre il secondo viene
richiamato quando il pulsante viene rilasciato
mentre il puntatore si trova fuori dallarea del
pulsante stesso. Ad ognuno dei due gestori
assegnata la funzione stop Drag(); che termina
il trascinamento del clip filmato. Detto ci,
prima di passare allimplementazione del codi-
ce in actionscript avremo bisogno di creare un
file xml che sar la nostra playlist e lo salvere-
mo nella stessa cartella del nostro player dove
creeremo inoltre una directory che chiamere-
mo canzoni che ospiter tutti i nostri file mp3.
IL FILE XML, OVVERO
LA NOSTRA PLAYLIST
Apriamo il blocco note e scriviamo il seguente
codice per creare il nostro file xml:
<?xml version="1.0"?>
<ARCHIVE>
<SONG title="Titolo"
file="url/../canzoni/song1.mp3"
artist="Artista"></SONG>
<SONG title="Titolo"
file="url/../canzoni/song2.mp3"
artist="Artista"></SONG>
</ARCHIVE>
Ovviamente dovremmo aggiungere una riga di
codice per ogni canzone che vogliamo ascolta-
re col nostro player. Ora salviamo il nostro file
chiamandolo listaCanzoni.xml e chiudiamo
il blocco note per tornare cos a lavorare al
nostro file in flash.
TESTI DINAMICI PER
VISUALIZZARE LE
INFORMAZIONI
Tornati ora alla nostra area di lavoro, clicchiamo
su Scena 1 nella linea temporale per tornare
alla scena principale, quindi dopo aver selezio-
MULTIMEDIA
M
G
84
/Maggio 2008
Player MP3 in una pagina Web con Flash
Fig.17: Ecco i nuovi livelli.
NOTA
PROGRAMMARE
CON XML.
I PORTATILI
Dedicato sia a utenti
principianti che non,
questo libro insegna
come utilizzare il
linguaggio XML ormai
sempre pi diffuso e in
continua evoluzione.
Partendo dalle basi
dell'XML, il testo
approfondisce
argomenti come la
validazione tramite
XSD, con qualche
accenno a DTD, all'XSD,
all'XSLT, all'XSL-FO e a
XPath, oltre a
sviluppare applicazioni
concrete di XML con
Visual Basic 6.0, il .NET
Framework, SQLXML e
JSP.
Autore: Paolo Pialorsi
Prezzo: 8.56 euro
Editore: Mondadori
Informatica
080-089:072-080 1-04-2008 18:38 Pagina 84
ht t p: / / www. i opr ogr ammo. i t
nato il primo fotogramma del livello player
andremo a creare delle caselle di testo dinamiche
che visualizzeranno informazioni sulla canzone
in esecuzione. Prendiamo lo strumento di testo e
dal pannello propriet apriamo il menu a tendi-
na e selezioniamo la voce testo dinamico sce-
gliamo come colore del testo quello che voglia-
mo, clicchiamo poi sulla casella per il bordo
intorno al testo e andiamo a disegnare tre caselle
di testo come in figura:
Dopo aver disegnato le tre caselle di testo, asse-
gniamo ad ognuna un nome nella casella var;
la prima la chiameremo errore e questa ci
segnaler leventuale mancanza del file xml, la
seconda la chiameremo contatore questa inve-
ce conterr informazioni riguardo il tempo di
esecuzione, e lultima la chiameremo titolo
conterr il nome dellartista e il titolo del brano
in esecuzione. Ora facciamo doppio click sul clip
filmato contenente i pulsanti per rientrare nel
livello player dove andremo ad aggiungere altri
due livelli sopra al livello Azioni, questi altri due
livelli andranno a contenere il codice per il con-
trollo dei pulsanti e per le impostazioni iniziali
del lettore, quindi li chiameremo rispettivamen-
te Azioni pulsanti e Impostazioni come in
figura:
IMPLEMENTAZIONE
DEL CODICE
Iniziamo dal livello impostazioni, quindi clic-
chiamo col tasto destro sul primo fotogramma
e selezioniamo la voce Azioni per aprire il
nostro pannello dove inseriremo il seguente
codice:
traccia = 1; // settiamo il valore di traccia ad uno per
segnalare la prima traccia del file xml
autoplay = false; // inizializziamo a false una
variabile autoplay
newSong = false; /*newSong viene settata a true
durante la riproduzione fino a che non si raggiunge la
fine della playlist*/
musicaOn = true;//musicaOn viene settata a true
allinizio ma se autoplay false musicaOn=false
songList = new Array();
Lista = "listaCanzoni.xml";
playList = new XML();
playList.ignoreWhite = true;
playList.onLoad = function(success) {
if (!success) {
_root.errore = "Playlist non
trovata";
return;
}
nodi = playList.firstChild.childNodes;
for (var k = 0; k<nodi.length; ++k) {
songList[k] =
nodi[k].attributes.file;
}
if (autoplay) {
appoggio.music = new Sound();/*creiamo un nuovo
oggetto sound di nome music, nel clip filmato
appoggio*/
appoggio.music.loadSound(songList[traccia-1],
true);/*carichiamo la nostra traccia dallarray
songList e con true segnaliamo che laudio in
streaming*/
appoggio.music.onSoundComplete
= function() {
traccia = traccia+1;
if
(traccia>songList.length) {
traccia = 1;
}
newSong = true;
};
} else {
musicaOn = false;
}
}
Questa la prima parte di codice. Oltre ad alcu-
ne variabili per il controllo automatico della
riproduzione come autoplay, in questa parte di
codice carichiamo il file xml, che sar la nostra
playlist, e creiamo larray per contenerne i
nodi. Dichiariamo quindi un nuovo array col
nome songList e gli assegniamo listruzione
new Array(); per la creazione di uno spazio di
memoria per larray che poi andremo a riempi-
re con i valori dal file xml. Carichiamo il file
listaCanzoni.xml in una variabile che abbiamo
chiamato Lista in questo modo: Lista = lista
Canzoni.xml ovviamente il file deve trovarsi
nella stessa cartella del nostro lettore. Se
vogliamo che il file della lista risieda in un altro
posto dovremo specificarne il percorso tra le
Maggio 2008/
85
G
Player MP3 in una pagina Web con Flash M
MULTIMEDIA
Fig.18: La struttura dei nuovi livelli.
080-089:072-080 1-04-2008 18:38 Pagina 85
ht t p: / / www. i opr ogr ammo. i t
virgolette oltre al nome del file. Dopo ci creia-
mo un nuovo oggetto XML prima di chiamare i
metodi della classe, in questo modo playList =
new XML(); cos abbiamo creato il nostro
oggetto che avr il nome di playList ed a questo
andremo ad assegnare eventuali metodi della
classe; del suddetto oggetto. Allinizio richia-
miamo il metodo ignoreWhite e lo impostiamo
come true in modo da eliminare eventuali nodi
di testo contenenti solo spazi vuoti, poi richia-
miamo il gestore onLoad(); il cui parametro
success risulta true se il file viene caricato cor-
rettamente. Viceversa risulter false se il file
non venisse caricato; nel secondo caso abbia-
mo fatto si che lutente venga avvertito con un
messaggio che apparir nella casella di testo
dinamico che abbiamo creato precedentemen-
te per questo scopo. Assegniamo ora ad una
variabile nodi il valore delloggetto XML playlist
con i seguenti metodi: firstChild, che fa riferi-
mento al primo elemento secondario nell'elen-
co degli elementi secondari del nodo principa-
le, questa propriet null se il nodo non ha ele-
menti secondari ed undefined se il nodo un
nodo di testo. Assegniamo anche il metodo
childNodes, che un array degli elementi
secondari dell'oggetto XML specificato, e ogni
elemento dell'array un riferimento a un
oggetto XML che rappresenta un nodo secon-
dario. Cos facendo abbiamo accesso agli ele-
menti del file xml, quindi a questo punto utiliz-
zeremo un ciclo for() per scorrere un ad uno gli
elementi dellarray songList[]; ed assegneremo
al k-esimo elemento del nostro array il valore
nodi[k].attributes.file;attributes, esso si riferi-
sce ad un oggetto che contiene tutti gli attribu-
ti dell'istanza XML specificata, in questo caso
nodi[k].attributes, dove k il nostro indice che
incrementa ogni volta di k+1, contiene una
variabile per ciascun attributo dell'istanza
XML.
Dal momento che queste variabili vengono
definite come parte dell'oggetto, a esse si fa
generalmente riferimento come propriet del-
l'oggetto. Il valore di ogni attributo viene
memorizzato nella propriet corrispondente
sotto forma di stringa, quindi per prelevare il
valore di un attributo presente in nodi[k] biso-
gna richiamare il nome dellattributo come
propriet in questo modo: oggettoXML.
firstChild.attributes.propriet. Dopo aver crea-
to larray da cui preleveremo le tracce musicali
passiamo alla creazione di un nuovo oggetto
Sound di nome music allinterno del nostro clip
filmato appoggio e carichiamo la traccia dal-
larray songList con il gestore loadSound che
permette di caricare un file mp3 e richiede un
valore booleano per verificare se laudio in
streaming o interno al filmato. In questo caso il
parametro assegnato sar true, in quanto i
nostri file vengono caricati in streaming. Per
passare alla traccia successiva utilizziamo il
gestore onSoundComplete chiamato automati-
camente quando termina la riproduzione di un
suono. Passiamo quindi alla traccia successiva
se il valore di traccia minore della lunghezza
dellarray, altrimenti il valore viene resettato ad
1 e la riproduzione parte di nuovo dalla prima
traccia.
Finite le nostre impostazioni iniziali passiamo
al codice per le azioni dei pulsanti, quindi clic-
chiamo col tasto destro sul primo fotogramma
del livello Azioni pulsanti e selezioniamo
ancora una volta la voce azioni per aprire il
nostro panello in cui scriveremo il seguente
codice:
volum.onEnterFrame = function() {
vol_uno = volum._x-guida_vol._x;
vol_due = guida_vol._width/100;
vol =
Math.round(vol_uno/vol_due);
appoggio.music.setVolume(vol);
};
volum.volume_btn.onPress = function() {
sinistra = guida_vol._x;
sopra = guida_vol._y;
destra =
guida_vol._x+guida_vol._width;
sotto = guida_vol._y;
volum.startDrag(false, sinistra,
sopra, destra, sotto);
};
volum.volume_btn.onRelease = function() {
volum.stopDrag();
};
volum.volume_btn.onReleaseOutside =
function() {
volum.stopDrag();
};
nextbtn.onRelease = function() {
traccia = traccia+1;
if (traccia>songList.length) {
traccia = 1;
}
if (musicaOn) {
newSong = true;
}
};
nextbtn.onReleaseOutside = function() {
traccia = traccia+1;
if (traccia>songList.length) {
traccia = 1;
}
if (musicaOn) {
newSong = true;
MULTIMEDIA
M
G
86
/Maggio 2008
Player MP3 in una pagina Web con Flash
080-089:072-080 1-04-2008 18:38 Pagina 86
ht t p: / / www. i opr ogr ammo. i t
}
};
backbtn.onRelease = function() {
traccia = traccia-1;
if (traccia<1) {
traccia = songList.length;
}
if (musicaOn) {
newSong = true;
}
};
backbtn.onReleaseOutside = function() {
traccia = traccia-1;
if (traccia<1) {
traccia = songList.length;
}
if (musicaOn) {
newSong = true;
}
};
playbtn.onRelease = function() {
if (!musicaOn) {
appoggio.music = new Sound();
appoggio.music.loadSound(songList[traccia-1], true);
appoggio.music.onSoundComplete
= function() {
traccia = traccia+1;
if
(traccia>songList.length) {
traccia = 1;
}
newSong = true;
};
musicaOn = true;
}
};
playbtn.onReleaseOutside = function() {
if (!musicaOn) {
appoggio.music = new Sound();
appoggio.music.loadSound(songList[traccia-1], true);
appoggio.music.onSoundComplete
= function() {
traccia = traccia+1;
if
(traccia>songList.length) {
traccia = 1;
}
newSong = true;
};
musicaOn = true;
}
};
stopbtn.onRelease = function() {
if (musicaOn) {
appoggio.music.stop();
musicaOn = false;
}
};
stopbtn.onReleaseOutside = function() {
if (musicaOn) {
appoggio.music.stop();
musicaOn = false;
}
};
questa parte di codice la pi semplice in
quanto controlla le azioni sui pulsanti con due
semplici gestori, onRelease e onReleaseOutside.
Come abbiamo gi visto per il volume questi
due gestori si occupano del punto in cui viene
rilasciato il click del mouse per poi eseguire le
azioni associate ai rispettivi pulsanti.
Per quanto riguarda invece la parte di codice
dedicata al controllo del volume, noteremo
delle righe di codice che, quando abbiamo
provato il funzionamento della barra volume,
non abbiamo utilizzato perch questa parte si
occupa del volume vero e proprio utilizzando il
metodo setVolume e richiede come parametri
un valore da 0 a 100 che ricaviamo dal rappor-
to tra la differenza tra la posizione sullasse x
della barra di volume, e la posizione sullasse x
della guida, e il rapporto tra la larghezza del
pulsante espressa in pixel e il valore massimo
di volume 100.
Visto che il risultato di questa operazione non
sar un numero intero, prima di passarlo come
parametro al metodo setVolume utilizziamo la
funzione math con il metodo round che arro-
tonda il valore del parametro vol al numero
intero pi vicino per eccesso o per difetto e
restituisce il valore. Se il parametro vol equi-
distante dai due numeri interi pi vicini (ovve-
ro termina con ,5), il valore viene arrotondato
al numero intero pi alto. A questo punto se
abbiamo scritto anche questo codice ci avvia-
mo alla fine della nostra realizzazione. Infatti
non rimane che aggiungere il codice per cari-
care e visualizzare le informazioni sui file che
verranno riprodotti. Clicchiamo col tasto
destro sul primo fotogramma del livello
Azioni e come sempre apriamo il pannello
per il codice selezionando la voce Azioni, quin-
di cancelliamo il codice che prima abbiamo
provato per il volume e scriviamo il seguente:
playList.load(Lista);
appoggio.onEnterFrame = function() {
const_uno = 1000;
const_due = 60;
minuti_pos =
posizione/const_uno/const_due;
secondi_pos = posizione/const_uno;
minuti_dur = durata/const_uno/const_due;
M
MULTIMEDIA
Maggio 2008/
87
G
Player MP3 in una pagina Web con Flash
080-089:072-080 1-04-2008 18:38 Pagina 87
ht t p: / / www. i opr ogr ammo. i t
secondi_dur = durata/const_uno;
if (musicaOn) {
posizione =
Math.round(appoggio.music.position);
durata =
Math.round(appoggio.music.duration);
minuti_trascorsi =
Math.floor(minuti_pos);
secondi_trascorsi =
Math.round(secondi_pos)-
minuti_trascorsi*const_due;
minuti_tot =
Math.floor(minuti_dur);
secondi_tot =
Math.round(secondi_dur)-minuti_tot*const_due;
if (posizione != 0 && durata != 0)
{
if
(secondi_trascorsi>59) {
minuti_trascorsi = minuti_trascorsi+1;
}
if (secondi_tot>59) {
minuti_tot =
minuti_tot+1;
}
if (secondi_tot>59) {
secondi_tot =
0;
}
if
(secondi_trascorsi>59) {
secondi_trascorsi = 0;
}
if (secondi_tot<10) {
secondi_tot =
"0"+secondi_tot;
}
if
(secondi_trascorsi<10) {
secondi_trascorsi = "0"+secondi_trascorsi;
}
if (minuti_trascorsi<10)
{
minuti_trascorsi = "0"+minuti_trascorsi;
}
if (minuti_tot<10) {
minuti_tot =
"0"+minuti_tot;
}
_root.contatore =
"Trascorsi:
"+minuti_trascorsi+":"+secondi_trascorsi+" - Durata:
"+minuti_tot+":"+secondi_tot;
}
} else if (!musicaOn) {
_root.contatore = "Trascorsi:
00:00 - Durata: 00:00";
}
if (newSong) {
appoggio.music = new Sound();
appoggio.music.loadSound(songList[traccia-1], true);
appoggio.music.onSoundComplete
= function() {
traccia = traccia+1;
if
(traccia>songList.length) {
traccia = 1;
}
newSong = true;
};
newSong = false;
}
_root.titolo = "Artista:
"+playList.firstChild.childNodes[traccia-
1].attributes.artist+" Titolo:
"+playList.firstChild.childNodes[traccia-
1].attributes.title;
};
in questultima parte di codice ci occupiamo
della visualizzazione delle informazioni riguar-
do al file in esecuzione. Guardando il codice
possiamo notare come anche qui sia stata uti-
lizzata la funzione math con il metodo round,
ma inoltre vediamo anche lutilizzo di un altro
metodo, ossia, il metodo floor. Questo metodo
restituisce il primo intero inferiore o uguale al
numero o all'espressione specificata; nel
nostro caso restituir il valore dei minuti tra-
scorsi e quello dei minuti totali del file in ese-
cuzione.
Continuando a spulciare il codice, oltre a tutti i
controlli per le varie visualizzazioni che dovre-
mo avere, arriveremo alle ultime righe nelle
quali viene assegnato alla variabile titolo il
valore dellattributo contenente il nome del-
lartista e il titolo della canzone in esecuzione.
Qui vediamo un esempio pratico di quello che
abbiamo gi detto durante la spiegazione della
prima parte di codice, ossia la sintassi per pre-
levare il valore degli attributi allinterno del file
xml.
CONCLUSIONI
Ora potrete divertirvi ad abbellire come vole-
te il vostro bel lettore mp3 pronto e funziona-
le per dilettare i visitatori dei nostri websites e
blog personali. In futuro vedremo come rea-
lizzare un player video per la riproduzione dei
filmati .flv.
Giovanni Fiore
MULTIMEDIA
M
G
88
/Maggio 2008
Player MP3 in una pagina Web con Flash
SUL WEB
Per approfondire la
conoscenza di Flash:
http://www.actionscript.it
http://flash.html.it
http://www.v2online.it
http://www.flashzone.com
http://www.flashcomguru.
com/
http://www.kloonigames.
com/
FORMATI AUDIO
Per conoscere i pi
diffusi formati audio e
confrontarne la qualit:
http://tinyurl.com/2a8nc8
http://tinyurl.com/337qvp
http://tinyurl.com/2e2hq2
http://tinyurl.com/2fgvfg
080-089:072-080 1-04-2008 18:38 Pagina 88
ht t p: / / www. i opr ogr ammo. i t
GRAFICA M
G
90
/Maggio 2008
Realizzare un cattura schermo
N
el redigere una relazione, un manuale,
unofferta o qualsivoglia documento
quasi sempre richiesto laggiunta di una
qualche immagine del prodotto trattato, dellin-
terfaccia o di un dettaglio della medesima per
meglio chiarire il senso di quello che si vuole
comunicare. La situazione nella quale ci trovia-
mo spesso quella in cui non disponiamo di una
versione su file dellimmagine pronta alluso ma
viceversa disponiamo di una versione creata a
run time sullo schermo da parte di qualche
applicazione o strumento.
Il metodo classico per catturare limmagine e al
quale ci si orienta pi frequentemente quello di
premere sulla tastiera il pulsante Print Screen
(stampa schermata) o Alt-Print Screen per cat-
turare solo la finestra attiva aprire Paint o qual-
siasi altro programma di grafica, incollare
limmagine e ritagliare la porzione che ci interes-
sa per poi salvarla nel formato che preferiamo.
Infine, completata loperazione, possiamo alle-
garla o incollarla al documento corrente.
Diversi sono i software commerciali che permet-
tono di automatizzare tale procedura e, vista
lutilit di un tale strumento, nellultima versione
del sistema operativo di casa Microsoft, Windows
Vista, stata giustamente aggiunta come utility.
Fino a poco tempo fa realizzare da zero una tale
tipologia di applicazione era un'operazione
alquanto elaborata e lunga e implicava chiamate
di API e notevoli sforzi di programmazione. In
realt, oggi, creare un programma che permetta di
catturare lo schermo o porzioni dello stesso in
modo semplice e veloce unoperazione abba-
stanza rapida se fatta con il supporto tecnologico
offerto dal .Net Framework. Per renderlo evidente,
nel prosieguo dellarticolo vedremo come realiz-
zare un programma fatto in C# che permetta di
catturare lo schermo o porzioni dello stesso per
poi salvarlo in un formato a scelta: BMP, JPEG,
TIFF o GIF. Per impreziosire il tutto vedremo
anche come visualizzare larea catturata in forma
di preview mediante una thumbnail con una serie
di informazioni aggiuntive che ne indichino le
dimensioni e leventuale spazio su disco occupato
se salvato in uno dei vari formati supportato.
DEFINIZIONE
DELLINTERFACCIA
La prima fase nello sviluppo della nostra piccola
applicazione consiste nella definizione dellinter-
faccia utente. Creiamo un nuovo progetto di tipo
Windows Application e rinominiamolo in
ScreenCapture. Decidiamo che la nostra interfac-
cia sar composta da quattro pulsanti, quattro
radio button e unarea dedicata allanteprima del-
limmagine catturata. Due pulsanti li utilizzere-
mo per la gestione della cattura dello schermo ed
in particolare il primo per la cattura dellintera
area di lavoro mentre il secondo per la cattura
delle singole porzioni. Gli altri due pulsanti servi-
ranno rispettivamente per salvare limmagine
catturata e per la chiusura dellapplicazione. I
quattro radio button li impiegheremo invece per
definire il formato con cui salvare limmagine
medesima. Per dettagliare meglio le caratteristi-
che dellimmagine mostrata nellanteprima
aggiungiamo una serie di label per rappresentare:
La profondit di colore dellimmagine
La larghezza
Laltezza
ANDIAMO A CACCIA
PER IL DESKTOP
CREIAMO, IN MODO SEMPLICE E VELOCE, UN PROGETTO C# IN GRADO DI CATTURARE
E SALVARE, NEL FORMATO CHE PREFERIAMO, SINGOLE PORZIONI O TUTTA LAREA
DI LAVORO DEL NOSTRO DESKTOP.
Fig.1: Risultato finale del nostro Cattura Schermo
personalizzato.
Conoscenze richieste
C#
Software
Windows XP/Vista,
Microsoft Visual Studio
2005
Impegno
Tempo di realizzazione
REQUISITI
090-095:066-071 2-04-2008 12:06 Pagina 90
ht t p: / / www. i opr ogr ammo. i t
M GRAFICA
Maggio 2008/
91
G
Realizzare un cattura schermo
Aggiungiamo ancora unetichetta per ogni singo-
lo formato individuato che permetta di visualiz-
zare loccupazione su disco nel caso venga salva-
ta con quel formato specifico. Come prima evi-
denziato i formati supportati sono: BMP, JPEG,
TIFF e GIF. Tuttavia con pochissime modifiche al
codice sar possibile aggiungere e gestire anche
gli altri formati non trattati come EXIF, PNG ecc.
Trasciniamo quindi sulla form tutti gli oggetti
necessari, introduciamo qualche dettaglio esteti-
co di personalizzazione e il risultato dovrebbe
essere simile a quello mostrato in figura 1. Creata
linterfaccia entriamo nel dettaglio della gestione
grafica del nostro personal screen capture.
CATTURA
DELLO SCHERMO
Per la cattura dello schermo e la gestione della
visualizzazione della thumbnail utilizzeremo
GDI+ (Graphics Device Interface), la nuova ver-
sione della libreria grafica di Windows, wrappata
dal Microsoft .Net Framework. E possibile sfrut-
tare le funzionalit grafiche di GDI+ mediante un
insieme di classi esposte presenti nel namespace
System.Drawing. Di tale namespace fanno parte
le classi:
System.Drawing.Drawing2D
System.Drawing.Imaging
System.Drawing.Text
System.Drawing.Printing
Il namespace System.Drawing consente di acce-
dere alle funzioni grafiche di base GDI+. Lo spa-
zio dei nomi System.Drawing.Graphics fornisce i
metodi per il disegno di varie forme, quali cerchi,
triangoli, archi, nonch metodi per la visualizza-
zione di testo, caratteri, font e cos via. Funzioni
pi avanzate vengono fornite invece negli spazi
dei nomi System.Drawing.Drawing2D, System.
Drawing.Imaging, System.Drawing.Text e System.
Drawing.Printing.
La classe Graphics fra i tanti metodi che espone
presenta anche il metodo CopyFromScreen che,
come dal nome stesso si evince, il metodo che
andremo ad utilizzare per i nostri scopi. Tale
metodo, a fronte di un oggetto di tipo Bitmap
definito con dimensioni pari allarea da copiare,
trasferisce limmagine dallarea del desktop ad
unarea di memoria specifica individuata dalla
Bitmap.
Tale metodo richiede come parametri:
Il punto dellangolo superiore sinistro del ret-
tangolo di origine
Il punto dellangolo superiore sinistro del ret-
tangolo di destinazione
Le dimensioni dellarea da trasferire
Quindi, al metodo, sufficiente passare le coor-
dinate del punto di partenza e le dimensioni di
unarea specifica da trasferire. Il secondo para-
metro, il punto iniziale dellarea di destinazione,
lo impostiamo al punto di coordinate (0,0).
Adesso che abbiamo visto quale metodo utilizza-
re volendo copiare tutto il contenuto del desktop
dobbiamo semplicemente fornire al metodo un
rettangolo le cui dimensioni siano pari a quelle
dello schermo oltre che definire una Bitmap di
dimensioni pari alla risoluzione impostata.
Possiamo ricavare le stesse tramite le propriet
Width e Height della classe Screen mediante il
metodo GetBounds(Point.Empty).
Laccortezza di cui vogliamo tener conto che
non desideriamo che linterfaccia della nostra
applicazione appaia in primo piano sullarea cat-
turata. Per fare ci minimizziamo linterfaccia
con listruzione this. WindowState =
FormWindowState.Minimized, diamo un attimo
di tempo al metodo redraw per il completamen-
to delloperazione (attivando una sleep di 200
millisecondi), copiamo larea e alla fine ripristi-
niamo linterfaccia rimettendo il WindowState a
Normal.
Per finire, avendo adesso loggetto Bitmap cari-
cato con limmagine catturata, possiamo rende-
re la medesima disponibile alla Clipboard, al fine
di poter effettuare il copia e incolla su qualsiasi
documento aperto, semplicemente scrivendo
listruzione Clipboard.SetImage((Image)bitmap).
//Viene catturato l'intero schermo dopo aver
minimizzato l'applicazione
private void btnCatturaSchermo_Click(object sender,
EventArgs e)
{
try
Fig.2: Risultato del click sul pulsante Cattura Schermo.
NOTA
LE BITMAP
Il termine bitmap
significa letteralmente
mappa di bit. La
qualit dell'immagine
di una bitmap dipende
strettamente da due
fattori: la risoluzione e
la profondit di colore.
La risoluzione indica il
numero di pixel che la
compongono, mentre
la profondit di colore
indica il numero di bit
utilizzati per
descrivere il colore di
ogni singolo punto. Il
numero di colori che
possibile assegnare ad
ogni pixel
determinato dal
numero di bit
destinati al singolo
pixel. Se ad esempio
ogni pixel
rappresentato da 4 bit,
sar possibile
assegnare a un dato
pixel uno dei 16 colori
diversi disponibili (2^4
= 16). La profondit di
colore pu variare da 1
fino a 64 bit.
Naturalmente con
profondit 1 possiamo
rappresentare solo
immagini in bianco e
nero.
090-095:066-071 1-04-2008 17:21 Pagina 91
ht t p: / / www. i opr ogr ammo. i t
GRAFICA M
G
92
/Maggio 2008
Realizzare un cattura schermo
{
this.WindowState =
FormWindowState.Minimized;
System.Threading.Thread.Sleep(200);
copyFromScreen(new Point(0, 0), new
Rectangle(0, 0, Screen.GetBounds
(Point.Empty).Width, Screen.GetBounds
(Point.Empty).Height));
this.WindowState = FormWindowState.Normal;
}
catch { }
}
//Viene catturata l'area disegnata
private void copyFromScreen(Point topPoint,
Rectangle capturedArea)
{
try
{
bitmap = new Bitmap(capturedArea.Width,
capturedArea.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(topPoint, Point.Empty,
capturedArea.Size);
Clipboard.Clear();
Clipboard.SetImage((Image)bitmap);
getImageInformation();
g.Dispose();
}
catch { }
}
CATTURA
DI UNAREA SPECIFICA
Risolto il problema della cattura di tutta larea del
desktop vediamo come fare se il nostro interesse
si concentra solo su unarea specifica.
Premesso che al metodo CopyFromScreen dob-
biamo fornire il punto di partenza e le dimensio-
ni di unarea ben definita la questione si sposta
su come individuare tale regione. Dobbiamo tro-
vare una strategia per lavorare sul desktop.
Banalmente se noi espandiamo tutta larea della
form impostando il WindowState a Maximized
siamo gi in grado di poter gestire quasi tutto lo
schermo, ed esattamente cos che faremo. Per
completare lopera eliminiamo i bordi della
form, rendiamo invisibili tutti gli oggetti visualiz-
zati e per finire rendiamo trasparente la form
mediante limpostazione del valore di opacit ad
un numero abbastanza basso. Avviando adesso
lapplicazione quello che vedremmo a video il
desktop leggermente opaco. Se ora facciamo
diventare la form massimizzata unarea di dise-
gno per il tracciamento di rettangoli vari, tramite
lausilio del mouse facilmente possiamo indivi-
duare unarea specifica, evidenziarla e infine
copiarla in memoria.
//Viene modificato il layout dell'applicazione per poter
catturare un'area specifica
private void btnCatturaArea_Click(object sender,
EventArgs e)
Per il salvataggio di bitmap
con il .Net Framework sono
disponibili diversi formati:
BMP un formato standard
utilizzato da Windows per
la memorizzazione di
immagini indipendenti da
periferiche e da
applicazioni. Il numero di
bit per pixel viene
specificato nell'intestazione
del file. I file BMP non sono
compressi ed per questo
che occupano una gran
quantit di spazio su disco.
GIF (Graphics Interchange
Format) il formato che in
genere si usa per le
immagini visualizzate nelle
pagine Web. Tale formato
risulta ottimale per disegni
costituiti da linee, per
immagini con blocchi di
colore a tinta unita e
immagini con colori ben
definiti e distinti
diversamente si riscontra
una notevole perdita di
definizione. Il formato GIF
compresso e da anche la
possibilit di impostare un
colore della tavolozza come
trasparente. E altres
possibile gestire GIF
animate salvando sullo
stesso file pi immagini.
JPEG (Joint Photographic
Experts Group) uno
schema di compressione
particolarmente adatto per
le immagini fotografiche. Il
livello di compressione
delle immagini JPEG
configurabile, ma ad alti
livelli di compressione
corrisponde una maggiore
perdita di informazioni
mentre a bassi livelli tale
perdita quasi sempre
impercettibile per l'occhio
umano. I file JPEG non
supportano la trasparenza o
le animazioni.
EXIF (Exchangeable Image
File) un formato utilizzato
per immagini acquisite con
fotocamere digitali. In un
file EXIF contenuta
un'immagine compressa in
base alle specifiche JPEG
con laggiunta di una serie
di informazioni relative alla
fotografia (data in cui
stata scattata, velocit
dell'otturatore, esposizione
ecc.) e alla fotocamera
(produttore, modello ecc.).
PNG (Portable Network
Graphics) consente di
usufruire di molti dei
vantaggi offerti dal
formato GIF, ma fornisce
anche ulteriori funzionalit
rispetto a tale formato.
Analogamente ai file GIF, i
file PNG vengono compressi
senza alcuna perdita di
informazioni. Il formato
PNG rappresenta un
miglioramento della
capacit del formato GIF di
visualizzare
progressivamente
un'immagine. In altre
parole permette di
visualizzare
approssimazioni sempre
migliori di un'immagine
mentre viene trasmessa
attraverso una connessione
di rete.
TIFF (Tag Image File Format)
un formato di file
flessibile ed adattabile,
supportato da una vasta
gamma di piattaforme e di
applicazioni per
l'elaborazione di immagini.
possibile memorizzare nei
file TIFF immagini con un
numero arbitrario di bit per
pixel ed consentito
l'utilizzo di svariati
algoritmi di compressione.
FORMATO DEI FILE GRAFICI
090-095:066-071 1-04-2008 17:21 Pagina 92
ht t p: / / www. i opr ogr ammo. i t
M GRAFICA
Maggio 2008/
93
G
Realizzare un cattura schermo
{
try
{
this.SuspendLayout();
this.Cursor = Cursors.Cross;
this.FormBorderStyle = FormBorderStyle.None;
this.Opacity = 0.01;
this.WindowState =
FormWindowState.Maximized;
this.ResumeLayout(false);
graphArea = this.CreateGraphics();
isCaptureActive = true;
setVisibleFalse();
}
catch { }
}
Per rendere un p pi professionale il nostro
Screen Capture facciamo si che venga disegnato
un rettangolo sullarea che viene man mano evi-
denziata per la cattura. Modifichiamo anche il
layout del cursore per rendere loperazione di
disegno pi precisa e puntuale.
Per la gestione del tracciamento dellarea dob-
biamo far ricorso a vari eventi. In particolare
avremo necessit di gestire gli eventi:
ScreenCapture_MouseDown
ScreenCapture_Up
ScreenCapture_Move
Nellevento ScreenCapture_MouseDown intercet-
tiamo il punto del click del mouse, che servir
come riferimento iniziale per tracciare larea del
rettangolo da evidenziare. Per individuare le
coordinate (x,y) del punto cliccato sfruttiamo la
classe Control.MousePosition.
Nellevento ScreenCapture_Move teniamo invece
traccia delle coordinate correnti del mouse. Ad
ogni movimento del mouse ridisegniamo il ret-
tangolo cancellando il precedente non pi attua-
le. Otteniamo lo scopo colorando lo stesso con il
colore di sfondo adottato.
Infine nellevento ScreenCapture_Up catturiamo
concretamente la porzione di schermo in base
alle coordinate ultime tracciate e ripristiniamo
linterfaccia utente.
//Viene attivato il tracciamento dell'area da catturare
private void ScreenCapture_MouseDown(object
sender, MouseEventArgs e)
{
try
{
if (isCaptureActive)
{
this.Opacity = 0.25;
graphArea.Clear(Color.DimGray);
isMouseClicked = true;
startPoint = new Point(Control.Mouse
Position.X, Control.MousePosition.Y);
}
}
catch { }
}
//Al rilascio del mouse viene ripristinato il layout
originale
private void ScreenCapture_Up(object sender,
MouseEventArgs e)
{
try
{
if (isCaptureActive)
{
System.Threading.Thread.Sleep(200);
this.WindowState =
FormWindowState.Minimized;
copyFromScreen(new Point(topRect.X,
topRect.Y), new Rectangle(topRect.X, topRect.Y,
bottomRect.X - topRect.X, bottomRect.Y -
topRect.Y));
setVisibleTrue();
this.SuspendLayout();
this.WindowState =
FormWindowState.Normal;
this.FormBorderStyle =
FormBorderStyle.FixedDialog;
this.Cursor = Cursors.Default;
this.Opacity = 1;
this.ResumeLayout(true);
isCaptureActive = false;
isMouseClicked = false;
}
}
catch { }
}
//Viene tracciato passo passo il rettangolo da
catturare
private void ScreenCapture _Move(object sender,
MouseEventArgs e)
{
try
NOTA
LA CLIPBOARD
La "Clipboard" il
termine utilizzato per
descrivere lo spazio in
memoria destinato al
copia e incolla.
Quando si copia,
loggetto copiato
trasferito nella
Clipboard. Viceversa,
quando si incolla
loggetto copiato
dalla Clipboard nella
sua destinazione
finale.
Fig.3: Cattura di una porzione ben definita dello
schermo.
090-095:066-071 1-04-2008 17:21 Pagina 93
ht t p: / / www. i opr ogr ammo. i t
GRAFICA M
G
94
/Maggio 2008
Realizzare un cattura schermo
{
if (isMouseClicked)
{
graphArea.DrawRectangle(new
Pen(Color.DimGray), topRect.X, topRect.Y,
bottomRect.X - topRect.X, bottomRect.Y - topRect.Y);
getBorderImage();
Pen pen = new Pen(Color.Black);
pen.DashStyle = DashStyle.Dash;
graphArea.DrawRectangle(pen, topRect.X,
topRect.Y, bottomRect.X - topRect.X, bottomRect.Y -
topRect.Y);
}
}
catch { }
}
GESTIONE
INFORMAZIONI
DI SINTESI
Unutility interessante che possiamo aggiungere
al nostro cattura schermo consiste nel visualizza-
re lanteprima dellimmagine catturata diretta-
mente sulla form con laggiunta di una serie di
informazioni di dettaglio che meglio individuano
le caratteristiche della stessa, come indicato alli-
nizio dellarticolo.
Al fine di visualizzare limmagine sempre della
stessa dimensione e nello spazio ad essa dedica-
to utilizziamo il metodo GetThumbnailImage
della classe Bitmap per estrarre dallimmagine
catturata la versione in miniatura o cosiddetta
thumbnail. Cosi facendo otteniamo
unimmagine rimpicciolita se la stessa molto
estesa e viceversa unimmagine ingrandita se la
stessa piccola. Per la visualizzazione a video
effettuiamo loverride del metodo OnPaint e
allinterno serviamoci del metodo DrawImage
per disegnarla.
Vediamo adesso come calcolare le informazioni
da allegare allimmagine. Per calcolare la lar-
ghezza e laltezza nonch la profondit di colore
sufficiente accedere alloggetto Bitmap in cui
limmagine stessa contenuta e leggerli. Invece il
calcolo delloccupazione dello spazio su disco
richiede qualche passaggio in pi. In particolare
il metodo che ho individuato consiste nel salvare
in un oggetto di tipo MemoryStream limmagine
impostando di volta in volta il parametro
ImageCodecInfo al formato che vogliamo (BMP,
JPEG, TIFF, GIF) e il parametro Encoder
Parameters, che rappresenta la qualit o fattore
di compressione con la quale si vuole salvare
limmagine (dove applicabile), al valore di
default che 75.
Trovato il sistema per salvare limmagine in
memoria la dimensione effettiva in Kilo Byte la
ricaviamo facilmente dividendo la propriet
Length delloggetto imageStreamper 1024.
//Gestione visualizzazione thumbnail
protected override void OnPaint(PaintEventArgs e)
{
try
{
base.OnPaint(e);
e.Graphics.DrawImage(thumbnailImage, new
Rectangle(15, 125, 381, 221), 0, 0, 380, 220,
GraphicsUnit.Pixel);
if (bitmap != null) btnSalva.Enabled = true;
}
catch { }
}
//Vengono visualizzate le informazioni associate
all'immagine
private void getImageInformation()
{
try
{
thumbnailImage = bitmap.GetThumbnailImage
(380, 220, null, IntPtr.Zero);
label2.Text = "Color Depth: " + Image.Get
PixelFormatSize(bitmap.PixelFormat) + " bit";
label3.Text = "Width: " + bitmap.Width + "
pixel";
label4.Text = "Height: " + bitmap.Height + "
pixel";
label5.Text = getSize("bmp");
label6.Text = getSize("jpeg");
label7.Text = getSize("tiff");
label8.Text = getSize("gif");
}
catch { }
}
//Viene calcolato lo spazio su disco richiesto
dall'immagine in funzione del tipo
private string getSize(string type)
{
try
{
ImageCodecInfo ici = GetEncoderInfo("image/"
+ type);
MemoryStream imageStream = new
MemoryStream();
EncoderParameters ep = new
EncoderParameters(1);
ep.Param[0] = new EncoderParameter
(Encoder.Quality, (long)75);
bitmap.Save(imageStream, ici, ep);
string value = (Math.Ceiling((double)image
Stream.Length / 1024) + " KB");
imageStream.Dispose();
return value;
}
NOTA
E possibile avere
qualche informazione
di dettaglio sul nuovo
strumento Cattura
Schermo introdotto in
Windows Vista
allindirizzo:
http://windowshelp.
microsoft.com/Windows/
it-IT/help/1337cdba-52a2-
4704-ad4d-2d7bace605b
41040.mspx
090-095:066-071 1-04-2008 17:21 Pagina 94
ht t p: / / www. i opr ogr ammo. i t
GGG GGG
M GRAFICA
Maggio 2008/
95
G
Realizzare un cattura schermo
catch
{
return string.Empty;
}
}
private ImageCodecInfo GetEncoderInfo(String
mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
GESTIONE SALVATAGGIO
IMMAGINE
Per concludere la nostra applicazione non rima-
ne che scrivere le poche righe di codice che ci
permettono di salvare limmagine nel formato
desiderato. In funzione del radio button selezio-
nato cliccando sul pulsante Salva viene imposta-
to il filtro delloggetto saveFileDialog che per-
mette di accedere al file system e scegliere il
nome con cui salvare o sovrascrivere limmagine
nella cartella individuata.
//L'immagine catturata viene salvata
private void btnSalva_Click(object sender, EventArgs e)
{
try
{
string savePath = getFileName();
if (savePath != string.Empty)
if (BMP.Checked)
bitmap.Save(savePath,
ImageFormat.Bmp);
else if (JPEG.Checked)
bitmap.Save(savePath,
ImageFormat.Jpeg);
else if (TIFF.Checked)
bitmap.Save(savePath,
ImageFormat.Tiff);
else
bitmap.Save(savePath,
ImageFormat.Gif);
}
catch { }
}
//Gestione finestra di dialogo per salvare l'immagine
su file
private string getFileName()
{
if (BMP.Checked)
{
saveFileDialog1.DefaultExt = "bmp";
saveFileDialog1.Filter = "bmp files
(*.bmp)|*.bmp";
}
else if (JPEG.Checked)
{
saveFileDialog1.DefaultExt = "jpeg";
saveFileDialog1.Filter = "jpeg files
(*.jpeg)|*.jpeg";
}
else if (TIFF.Checked)
{
saveFileDialog1.DefaultExt = "tiff";
saveFileDialog1.Filter = "tiff files (*.tiff)|*.tiff";
}
else
{
saveFileDialog1.DefaultExt = "gif";
saveFileDialog1.Filter = "gif files (*.gif)|*.gif";
}
saveFileDialog1.Title = "Save Image As ";
saveFileDialog1.ShowDialog();
return saveFileDialog1.FileName;
}
Volendo aggiungere ulteriori formati grafici per il
salvataggio su disco sufficiente aggiungere alla
form qualche ulteriore radio button indicante il
tipo di formato richiesto e qualche ramo in pi
alla serie di if precedenti per definire lestensione
e il filtro.
CONCLUSIONI
Il semplice programma esposto un elegante
strumento per la cattura e il salvataggio di imma-
gini. Abbiamo visto come con il .Net Framework
e C# sia facile la gestione delle librerie grafiche
esposte dal GDI+ per creare utility anche com-
plesse e che sul mercato hanno un certo costo,
con un impiego di tempo abbastanza ragionevo-
le. Sicuramente molto altro ancora si pu
aggiungere e migliorare per rendere il nostro cat-
tura schermo pi professionale ed efficiente.
Tuttavia il risultato raggiunto, anche se con un
numero limitato di funzionalit, sicuramente
permette di fare gran parte del lavoro che si
farebbe con lacquisto di uno strumento com-
merciale con lindubbio vantaggio di non dover
spendere nulla.
Carlo Lollo
LAUTORE
Lautore, consulente
informatico, si occupa
di progettazione e
sviluppo software in
Java e .NET, sia nel
campo industriale con
applicazioni in real
time sia nelloffice
automation con
applicazioni stand-
alone, distribuite e
Web. Le principali aree
di interesse sono
lanalisi dei requisiti,
la progettazione, lo
sviluppo di sistemi ad
oggetti, la qualit e
lusabilit del
software, il testing e
la progettazione di
base di dati e relativa
gestione. Per
commenti, critiche e
suggerimenti
contattabile
allindirizzo
[email protected].
090-095:066-071 1-04-2008 17:21 Pagina 95
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
96
/Maggio 2008
Un sistema di talk con Rails
I
l nostro progetto, l'applicazione web Soap-
box, un semplice network sociale con tre
funzionalit:
1) Un utente pu tenere un discorso, cio in-
serire la propria opinione.
2) Un utente pu lasciare un commento e un
voto, positivo o negativo, al discorso di qual-
cun altro.
3) Tutti i discorsi sono elencati sulla prima pa-
gina di Soapbox, con quelli pi votati elencati
pi in alto.
Il mese scorso abbiamo usato Ruby on Rails per
sviluppare la versione 0.1 di Soapbox (se avete
perso il numero scorso di ioProgrammo, leggete
il box Arnesi da lavoro). Soapbox 0.1 implemen-
ta la prima funzionalit: un utente pu scrivere,
modificare o cancellare uno speech, cio un di-
scorso. Oggi ci occuperemo della seconda fun-
zionalit: i voti e i commenti.
UN GIRO DI RAILS
Per rinfrescarci la memoria, e per chi ha perso
l'articolo del mese scorso, torniamo al modello
Model-View-Controller di Rails. Cosa succede se
lanciamo Soapbox e visitiamo, ad esempio,
l'indirizzo http://localhost:3000/speeches/show/1? La
prima parte dell'indirizzo, fino alla porta 3000 in-
clusa, serve solo a identificare il server. Una vol-
ta che la chiamata HTTP arriva sul server, Rails
legge il resto della URL e ne estrae il primo segmento,
in questo caso speeches. Questa stringa viene
convertita nel nome di un Controller, che per con-
venzione si chiamer SpeechesController. Il seg-
mento successivo, show, il nome di una action
in quel controller. Questo significa che nella directory
soapbox/app/controllers esiste un file speech-
es_controller.rb, che contiene una classe Speech-
esController, che contiene un metodo show(), e
questo il metodo che viene invocato dalla URL
/speeches/show/1. Ecco una parte di questa classe,
commenti esclusi:
class SpeechesController < ApplicationController
...
def show
@speech = Speech.find(params[:id])
respond_to do |format|
format.html
format.xml { render :xml => @speech }
end
end
...
Il metodo show() legge da un oggetto di nome
params. Questo oggetto un hash, che il ter-
mine Ruby per indicare una mappa, o un di-
zionario. Rails usa params per passare alle ac-
tion i parametri della chiamata HTTP. La chia-
mata http://localhost:3000/speeches/show/1 non con-
tiene parametri, ma per una convenzione di Rails
il numero 1 alla fine della URL stato assegnato
ad un parametro di nome id. Quindi la prima ri-
ga di show() equivale a:
@speech = Speech.find(1)
Apriamo la classe Speech per capire cosa fa que-
sta riga. Si tratta di una classe del modello, quin-
di la troveremo nella directory soapbox/app/models,
in un file di nome speech.rb:
class Speech < ActiveRecord::Base
end
La classe vuota, ma eredita da ActiveRecord::Base.
ActiveRecord la libreria di Rails che si occupa
di sincronizzare oggetti e database. La classe si
chiama Speech, quindi ActiveRecord la mappa
su una tabella speeches nel database. Le colonne
della tabella diventano automaticamente pro-
priet della classe. Ad esempio: la tabella ha una
colonna di nome title, quindi tutti gli Speechricevono
automaticamente una propriet di nome title.
RUBY ON RAILS:
OGGETTI E RELAZIONI
CONSERVARE DATI OBJECT-ORIENTED IN UN DATABASE RELAZIONALE SPESSO UN LAVORO
DIFFICILE E COMPLICATO. MA NON CON LA LIBRERIA ACTIVERECORD DI RAILS, CHE RENDE
QUESTO COMPITO GRAVOSO ADDIRITTURA DIVERTENTE
J CD J WEB
soapbox-0.1.zip, soapbox-0.2.zip
cdrom.ioprogrammo.it
Conoscenze richieste
Basi di programmazione
object-oriented,
database e sviluppo
web.
Software
Ruby, Rails, SQLite3.
Impegno
Tempo di realizzazione
REQUISITI
096-103:072-080 1-04-2008 17:23 Pagina 96
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Maggio 2008/
97
G
Un sistema di talk con Rails
Tutti gli oggetti ActiveRecord ricevono, tra gli al-
tri metodi, un metodo di classe di nome find().
Ad esempio, Speech.find(1) significa: vai nel da-
tabase, trova un record nella tabella speeches il
cui campo idsia uguale a 1, e convertilo in un og-
getto di classe Speech. Nella riga successiva, il
Controller assegna lo Speechappena recuperato
ad una variabile di nome @speech:
@speech = Speech.new
In Ruby, le variabili che iniziano con una @ sono
variabili di istanza (cio campi) di un oggetto,
quindi questa riga fa nascere una nuova variabi-
le di istanza nel Controller. I campi del Control-
ler hanno un ruolo speciale, che vedremo tra po-
co. Finiamo prima di esplorare il codice di show():
respond_to do |format|
format.html
format.xml { render :xml => @speech }
end
Queste righe selezionano la View (la vista) che
deve essere restituita all'utente. Non entreremo nei
dettagli, ma cerchiamo di spiegarle in poche pa-
role. La riga che inizia con format.xml vuol dire:
se ti stato richiesto il formato XML, prendi lo
Speechche hai appena creato, convertilo in XML
e mandalo all'utente. Se volete vedere questa ri-
ga in azione, aprite la URL
http://localhost:3000/speeches/1.xml.
La URL http://localhost:3000/speeches, invece, non
specifica alcuna estensione quindi chiede im-
plicitamente una pagina HTML. Il codice qui so-
pra non dice cosa restituire all'utente nel caso
venga richiesto dell'HTML, quindi Rails si affida
ad una convenzione di default: seleziona la View
che ha lo stesso nome della action (in questo ca-
so show) e l'estensione di default .html.erb. Il suf-
fisso .erb sta per Embedded RuBy, che come ve-
dremo tra poco un sistema di template, e .html
indica che il template deve generare un file HTML.
Andiamo nella directory soapbox/app/views e cu-
riosiamo nel file show.html.erb:
<p>
<b>Author:</b>
<%=h @speech.author %>
</p>
<p>
<b>Title:</b>
<%=h @speech.title %>
</p>
<p>
<b>Text:</b>
<%=h @speech.text %>
</p>
<%= link_to 'Edit', edit_speech_path(@speech)
%> |
<%= link_to 'Back', speeches_path %>
Questo un template HTML: un pezzo di HTML
punteggiato da tag speciali che contengono codice
Ruby. I tag <% ... %> significano esegui questo
codice, e i tag <%= ... %> significano esegui que-
sto codice e infilane il risultato nell'HTML. <%=h
... %> significa che la stringa deve essere codificata
in HTML, per evitare di usare caratteri riservati
come le parentesi angolari.
Qui possiamo vedere come fa il Controller a co-
municare dati alla View. Semplicemente, tutte le
variabili di istanza nel Controller (quelle che ini-
ziano per @) sono accessibili anche nella View.
Ad esempio, questa vista accede ai campi author,
title e text della variabile @speech, che corri-
spondono ad altrettante colonne nella tabella
speeches.
Le ultime due righe della View usano istruzioni
come link_to() e speeches_path() per generare i
link ad altre pagine dell'applicazione. In realt
non si tratta di istruzioni, ma di semplici metodi
(in Ruby si possono omettere le parentesi tonde
quando si chiama un metodo). I metodi che si
usano nelle View, come questi, si chiamano hel-
per.
Potreste chiedervi come mai questo HTML non con-
tiene tag come <html> o <body>. Troverete i tag
scomparsi nel file soapbox/app/views/layouts
/speeches.html.erb. Per una delle sue tante con-
venzioni, Rails avvolge tutte le viste invocate dal-
lo SpeechesController in questo file (che in gergo
Rails si chiama un layout), in modo da non du-
plicare l'inizio e la fine della pagina in ciascuna vi-
sta.
Ora il giro completo. La URL stata interpreta-
ta e inviata alla action di un Controller. Il Con-
troller si rivolto al Model per calcolare un risul-
tato, l'ha infilato nelle sue variabili di istanza e
ha selezionato una View. La View ha letto i dati
preparati dal Controller, li ha formattati in HTML,
ed ha usato qualche helper per calcolare i link ad
altre pagine. L'HTML risultante stato rispedito
all'utente. Quasi tutte le chiamate ad una appli-
cazione Rails funzionano in questo modo.
Ma ora basta studiare: ora di metterci al lavoro
e sviluppare la versione 0.2 di Soapbox.
UN PROBLEMA
DI FILOSOFIA
Ecco la funzionalit che vogliamo sviluppare
oggi:
096-103:072-080 1-04-2008 17:23 Pagina 97
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
98
/Maggio 2008
Un sistema di talk con Rails
- Un utente pu inserire un voto (+1 oppure -1)
sull'opinione di qualcun altro, e lasciare un com-
mento.
Pensiamo prima al posto dove risiede la logica
del sistema: il Model. Un commento sar proba-
bilmente un oggetto di classe Comment, che in-
clude un testo (il commento vero e proprio), il
nome dell'autore e un voto. Per semplificare, an-
zich usare letteralmente i valori +1 e -1, possia-
mo dare al voto un valore booleano: true per so-
no d'accordo e false per non sono d'accordo.
Potremo sempre convertire questi valori in +1 e -
1 in seguito.
Tutti i commenti si riferiscono ad una opinione,
quindi dobbiamo fare in modo che ciascun Com-
ment abbia la propria Speech. Questa quello che
si chiama una relazione 1 a N, nel senso che cia-
scun commento si riferisce ad una opinione, e
ciascuna opinione pu avere pi commenti. Man-
tenere questa relazione pu suonare semplicissimo,
fin quando non ci ricordiamo che sia i commen-
ti che le opinioni vivono contemporaneamente in
due mondi diversi: in memoria sotto forma di og-
getti, e nel database sotto forma di record. Le re-
lazioni tra un commento e la sua opinione, e tra
un'opinione e i suoi commenti, devono essere
mantenute in entrambi i casi.
Uno dei problemi con cui ci si scontra spesso
quando si conservano gli oggetti in un database
che gli oggetti e le tabelle hanno idee comple-
tamente diverse di come si gestiscono le relazio-
ni. Tra oggetti, si usano semplici riferimenti. Se
un Comment deve avere una Speech corrispon-
dente, basta che ciascun Comment conservi un
riferimento ad una Speech, probabilmente in una
variabile di istanza. Se vogliamo navigare la rela-
zione in entrambe le direzioni, allora anche la
Speechdeve avere un riferimento ai propri Com-
ment. Ma dato che una Speechpu avere pi Com-
ment, dovr conservarli in qualche tipo di colle-
zione, come ad esempio un array.
Il database non ha nessun concetto di direzio-
ne o collezione, e nemmeno di riferimento.
Per dire che una tabella comments ha una rela-
zione 1 a N con una tabella speeches, basta che
una colonna di comments sia una foreign key
ai corrispondenti record di speeches. Per trovare
lo speech di un comment, o i comment di uno
speech, dobbiamo fare un join tra le tabelle.
E' difficile mettere d'accordo queste due filosofie,
ma Rails semplifica tutto nel suo solito modo:
con una serie di convenzioni.
CIASCUNO DICA LA SUA
Ricordate il generator? Il mese scorso l'abbiamo
usato per creare l'impalcatura completa degli
Speech. Questa volta non vogliamo un'impalca-
tura completa ad esempio, non vogliamo che i
commenti abbiano un Controller e delle View.
Tutto quello che ci serve il Model:
ruby script/generate model comment author:string
text:text vote:boolean speech_id:integer
Abbiamo detto che vogliamo un modello per qual-
cosa che si chiama un comment. Questo mo-
dello deve avere un autore (una stringa), un te-
sto (di tipo text), un voto (booleano) e una fo-
reign key ad un record della tabella speeches.
Quest'ultima un semplice intero, che secondo
le convenzioni di Rails si chiama con il nome al sin-
golare della destinazione seguita da _id - in que-
sto caso, speech_id.
Il generator ha scritto per noi quattro o cinque fi-
le, ma i due pi importanti sono la migrazione e
il modello ActiveRecord. La migrazione si chia-
ma 002_create_comments.rb, e contiene le istru-
zioni che preparano il database ad accogliere i
commenti. Lanciamo la migrazione con Rake:
rake db:migrate
Il risultato sar qualcosa come:
= 2 CreateComments: migrating
================================-
create_table(:comments)
-> 0.0052s
== 2 CreateComments: migrated (0.0054s)
================================
NOTA
IL TOCCO
DELL'ARTISTA
Se conoscete l'HTML,
giocate un po' con i
template della View
per migliorare la
grafica di Soapbox.
Ricordate che tutte le
viste sono avvolte
nel file
soapbox/app/views/la
youts
speeches.html.erb.
RAILS IN PILLOLE
- Ruby on Rails un framework
per sviluppare applicazioni
web basate su database. In
altre parole, serve per scrivere
quel genere di sito web che
spesso si scrive con PHP,
ASP.NET o Java EE. Molti
ritengono che Rails sia di gran
lunga pi produttivo di questi
suoi concorrenti.
- Rails usa un linguaggio di
programmazione chiamato
Ruby. Ruby un linguaggio
dinamico come Python o
Perl. E' anche rigorosamente
object-oriented e
straordinariamente sintetico
ed espressivo.
- Molti siti di successo sono
scritti in Rails. Alcuni, come
Twitter, generano un'enorme
quantit di traffico e dati.
- Rails corredato da una serie
di script, tra i quali un
generatore di codice che
permette di sviluppare
rapidamente un prototipo di
applicazione da rifinire in
seguito.
- Rails implementa il pattern
Model-View-Controller. In
un'applicazione Rails, il
controller, il modello e le
viste risiedono fisicamente
in directory separate.
- Rails ha una sua filosofia,
basata su concetti come le
convenzioni sono meglio della
configurazione, o non
ripetere la stessa informazione
in due punti diversi. Un
esempio di entrambe queste
idee il modo in cui Rails
mappa gli oggetti sul
database, di cui parliamo in
questo articolo.
096-103:072-080 1-04-2008 17:23 Pagina 98
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Maggio 2008/
99
G
Un sistema di talk con Rails
Quindi ora abbiamo una tabella comments dove
conservare i commenti. Andiamo a verificarlo
aprendo il database con SQLite3:
sqlite3 db/development.sqlite3
Il comando .tables mostra che il database con-
tiene le tabelle speeches, comments e schema_info
(Rails usa quest'ultima tabella per gestire le mi-
grazioni). Con il comando .schema comments
possiamo vedere il DDL usato per creare la ta-
bella comments:
CREATE TABLE comments ("id" INTEGER PRIMARY
KEY AUTOINCREMENT NOT NULL, "author"
varchar(255) DEFAULT NULL, "text" text DEFAULT
NULL, "vote" boolean DEFAULT NULL, "speech_id"
integer DEFAULT NULL, "created_at" datetime
DEFAULT NULL, "updated_at" datetime DEFAULT
NULL);
Come al solito, Rails ha fatto un po' di lavoro ex-
tra per conformare la tabella alle proprie con-
venzioni: ha aggiunto una chiave primaria di no-
me id, ha convertito i tipi in quelli nativi di SQ-
Lite3 (ad esempio, la string diventata un var-
char), ha assegnato NULL come default a tutti i
campi e ha persino creato due nuovi campi che ge-
stir automaticamente: created_at e updated_at,
che contengono le date di creazione e di ultima mo-
difica di ciascun record.
Ora abbiamo una relazione nel database, ma il
Model non ne sa ancora niente. Apriamo i file
comment.rb e speech.rb e aggiungiamo una riga
di codice a ciascuna classe:
class Comment < ActiveRecord::Base
belongs_to :speech
end
class Speech < ActiveRecord::Base
has_many :comments
end
Abbiamo appena detto a Rails che un Comment
appartiene ad uno Speech (belongs_to), e che
uno Speechha molti Comment (has_many). Che
ci crediate o no, queste due righe di codice sono
magiche: creano nuovi metodi nelle classi Com-
ment e Speech. Vediamo quali.
VIAGGIO AL CENTRO
DELL'APPLICAZIONE
Cerchiamo di capire come funziona il nostro nuo-
vo modello. Per interagire con il modello dob-
biamo scrivere un Controller e una serie di viste...
Aspettate un momento. Esiste un modo molto
pi semplice per giocare col modello: uno script
di nome console, che possiamo lanciare come al
solito dalla directory soapbox.
ruby script/console
Il risultato sar qualcosa di simile a questo:
Loading development environment (Rails 2.0.2)
>>
Strano ma vero: siamo appena entrati nell'appli-
cazione Soapbox. Abbiamo davanti un interpre-
te Ruby interattivo che ci permette di accedere
al modello e al sottostante database, proprio co-
me se fossimo un Controller. Ogni volta che dia-
mo un comando, la console ne stampa il risulta-
to. Lo script console carica uno degli ambienti de-
finiti nel file soapbox/config/database.yml. Non
abbiamo specificato quale ambiente ci interes-
sa, quindi Rails ha caricato per default l'ambiente
di sviluppo, che usa il database soapbox/db/ de-
velopment.sqlite3. Giochiamo un po' con la clas-
se Command. Con il metodo find() e il parametro
:all possiamo leggere tutti i Comment che sono
gi nel database:
>> Comment.find :all
=> []
In Ruby gli array sono delimitati da parentesi qua-
drate, quindi quello che vediamo nel risultato un
array vuoto. Giusto: non abbiamo ancora creato
alcun commento. Ora recuperiamo il primo re-
cord dalla tabella speeches usando il parametro
:first:
>> s = Speech.find :first
=> #<Speech id: 1, author: "Paolo", title: "Non ci
sono pi le mezze stagioni", text: "Anche senessuno
ne parla mai, ho notato che le mez...", created_at:
"2008-02-03 17:09:16", updated_at: "2008-03-16
16:30:25">
ActiveRecord andato nel DB, ha recuperato il
primo record della tabella speeches e l'ha avvol-
to in un oggetto di classe Speech. Ma il meglio
deve ancora venire.
Quando abbiamo scritto che uno Speechha mol-
ti Comment e un Comment appartiene a uno
Speech, Rails ha creato automaticamente una se-
rie di nuovi metodi nel modello. In particolare
ora abbiamo un metodo Speech.comments(), che
restituisce un array di commenti, e un metodo
Comment.speech(), che restituisce un singolo
Speech (notate i singolari e i plurali). Vediamo
Speech.comments() in azione:
096-103:072-080 1-04-2008 17:23 Pagina 99
ht t p: / / www. i opr ogr ammo. i t
>> s.comments
=> []
Come ci potevamo aspettare, questo Speechnon
ha alcun Comment. Proviamo a creare un Comment
come se fosse un normale oggetto Ruby, con il
metodo new() della sua classe:
>> c = Comment.new
=> #<Comment id: nil, author: nil, text: nil, vote:
nil, speech_id: nil, created_at: nil, updated_at: nil>
Le propriet di questo commento sono ancora
vuote. Riempiamole:
>> c.author = "Gino"
>> c.text = "Finalmente un'opinione originale!"
>> c.vote = true
Ora che il nostro Comment ha un autore, un testo
e un voto, possiamo aggiungerlo ai commenti
dello Speech. Per aggiungere un elemento ad un
array possiamo usare l'operatore <<:
>> s.comments << c
=> [#<Comment id: 1, author: "Gino", text:
"Finalmente un'opinione originale!", vote: true,
speech_id: 1, created_at: "2008-03-16 17:06:31",
updated_at: "2008-03-16 17:06:31">]
Questa semplice riga di codice ha avuto molte
conseguenze. La console ci dice che la propriet
comments dello Speech ora un array che con-
tiene un Comment con id uguale a 1. Gli id sono
assegnati dal database, quindi Rails deve aver sal-
vato il commento nel database. Possiamo anche
chiedere al Comment a quale Speechappartiene:
>> c.speech
=> #<Speech id: 1, author: "Paolo", title: "Non ci
sono pi le mezze stagioni", text: "Anche se
nessuno
ne parla mai, ho notato che le mez...", created_at:
"2008-02-03 17:09:16", updated_at: "2008-03-16
16:36:51">
Il commento appartiene allo Speechcon idugua-
le a 1, lo stesso che abbiamo messo da parte nel-
la variabile s. Quindi, nel database, la tabella com-
ments contiene ora un singolo record, il cui id
uguale a 1 e il cui campo speech_id anch'esso
uguale a 1.
La sincronia tra oggetti e database pu sembra-
re quasi magica, ma non sempre cos traspa-
rente. Proviamo a creare un secondo commen-
to. Questa volta usiamo una sintassi pi sintetica
per valorizzare i suoi campi nel momento stesso
in cui lo creiamo:
c2 = Comment.new :author => "Il Polemico", :text
=> "Non sono d'accordo", :vote => false
Assegniamo il commento ad s. Questa volta usia-
mo per l'operazione inversa. Anzich assegna-
re il Comment allo Speech, assegniamo lo Speech
al Comment:
>> c2.speech = s
Sembrerebbe tutto a posto, ma se andiamo a con-
trollare la lunghezza dell'array s.comments tro-
veremo che contiene ancora un solo elemento:
>> s.comments.size
=> 1
Il problema che (per alcuni buoni motivi sui
quali non ci soffermeremo) Rails salva gli ogget-
ti automaticamente solo se vengono assegnati
SISTEMA M
G
100
/Maggio 2008
Un sistema di talk con Rails
ARNESI DA LAVORO
Se avete seguito lo sviluppo di
Soapbox sin dal mese scorso,
allora avete gi tutto quello che
vi occorre. Se iniziate da questo
mese, ecco cosa vi serve.
Installate Ruby e il package
manager RubyGems. Se usate
Windows, potete installarli
entrambi con il One-Click Ruby
Installer che trovate su
http://www.ruby-
lang.org/en/downloads. Poi
copiate da qualche parte nel
path la DLL e l'eseguibile del
database SQLite3. Nel momento
in cui scriviamo, i file sono
sqlitedll-3_5_6.zip e sqlite-
3_5_6.zip, e potete scaricarli da
http://www.sqlite.org/download.
html. Infine installate i pacchetti
RubyGems che ci servono (Rails,
il driver per SQLite3 e il web
server Mongrel) con questo
comando:
gem install rails sqlite3-ruby mongrel -y
Vediamo se quello che abbiamo
installato funziona. Scompattate
da qualche parte il file soapbox-
0.1.zip che trovate sul CD allegato
alla rivista, aprite il prompt dei
comandi, entrate nella directory
soapbox e scrivete:
ruby script\server
Questo comando lancia il web
server Mongrel. Aprite un browser
e andate all'indirizzo
http://locahost:3000/speeches.
Dovreste vedere la prima pagina
di Soapbox: una lista editabile di
discorsi come quella
dell'immagine: primapagina.
Figura 1: Un semplice esempio di come appare la pagina
096-103:072-080 1-04-2008 17:23 Pagina 100
ht t p: / / www. i opr ogr ammo. i t
dal lato della relazione che non contiene la fo-
reign key. Dato che i Comment contengono la
foreign key, Rails non li salva fino a quando non
aggiorniamo il corrispondente Speech. Per evita-
re questo problema dobbiamo ricordarci di ge-
stire le relazioni dal lato has_many, non dal lato
belongs_to. In alternativa, possiamo salvare noi
stessi il nuovo Comment e rinfrescare lo Speech
con i dati del database:
>> c2.save
>> s.reload
>> s.comments.size
=> 2
Ora uno degli Speechnel nostro sistema ha due Com-
ment al seguito. Come ultima verifica, control-
liamo che questa relazione sia davvero bidire-
zionale. Lo Speechdi uno dei due Comment di s de-
ve essere proprio s:
>> s.comments[1].speech == s
=> true
Perfetto! Avete ancora il server di Soapbox atti-
vo? In caso contrario lanciatelo, perch il momento
di tornare sul web.
CAMERA CON VISTA
Aprite l'applicazione e cliccate sul link show ac-
canto a uno degli speech sulla prima pagina. Ve-
drete i dettagli dello speech che avete seleziona-
to, come nella figura
Vogliamo che i commenti degli utenti siano visi-
bili sul sito, quindi ciascuno Speechdovrebbe mo-
strare anche tutti i commenti relativi. Sappiamo
gi come viene costruita questa pagina: Rails
mappa una URL come http://localhost:3000/speeches/1
sulla action show() dello SpeechesController. Il
Controller recupera lo Speechcon l'id richiesto e
lo passa ad una View di nome show.html.rb nel-
la directory soapbox/app/views/speeches. Visto che
uno Speech si porta dietro tutti i suoi commenti,
possiamo gi accedere ai commenti dalla vista.
Dobbiamo solo visualizzarli. Apriamo il templa-
te show.html.erb e aggiungiamogli qualche riga:
<p>
<b>Author:</b>
<%=h @speech.author %>
</p>
<p>
<b>Title:</b>
<%=h @speech.title %>
</p>
<p>
<b>Text:</b>
<%=h @speech.text %>
</p>
<h3>Comments:</h3>
<% for comment in @speech.comments %>
<p>
<%= comment.vote ? "+1" : "-1" %><br/>
On <%=h comment.created_at.to_s(:short) %>,
<b><%=h comment.author %></b> says:<br/>
<%=h comment.text %>
</p>
<% end %>
<%= link_to 'Edit', edit_speech_path(@speech)
%> |
<%= link_to 'Back', speeches_path %>
Prima che tutti questi caratteri buffi ci facciano gi-
rare la testa, cerchiamo di distinguere subito tra
tag HTML e pezzi di codice Ruby. Abbiamo aper-
to l'elenco dei commenti con un normale titolo in
HTML. La riga successiva codice Ruby: un ci-
clo for su tutti i commenti contenuti nell'array
@speech.comments. Il ciclo termina con la paro-
la chiave end. Tutto quello che c' in mezzo fa
parte del ciclo, e ad ogni giro la variabile comment
assume il valore di un nuovo commento. (Se co-
noscete Ruby, sapete certo che esiste un modo
pi elegante per scrivere i cicli, ma in questo ar-
ticolo abbiamo preferito usare un pi tradizio-
nale for).
Vediamo cosa succede nel ciclo. Saltando la pri-
ma riga, sulla quale torneremo tra poco, il corpo
del ciclo :
M SISTEMA
Maggio 2008/
101
G
Un sistema di talk con Rails
Figura 2: Ed ecco i commenti!
NOTA
CENSURA!
Soapbox 0.2 non
permette di cancellare
i commenti attraverso
l'interfaccia web.
Provate ad aggiungere
un po' di commenti
inutili all'applicazione
(particolarmente
consigliati quelli che
pubblicizzano Viagra o
giochi d'azzardo). Poi
lanciate script/console
e cancellare i
commenti indesiderati
attraverso la riga di
comando. Potete
cancellare un oggetto
dal database
chiamando il suo
metodo destroy(), e
cancellare tutti gli
oggetti di una certa
classe con il metodo di
classe destroy_all().
096-103:072-080 1-04-2008 17:23 Pagina 101
ht t p: / / www. i opr ogr ammo. i t
On <%=h comment.created_at.to_s(:short) %>,
<b><%=h comment.author %></b> says:<br/>
<%=h comment.text %>
Questo HTML punteggiato da pezzi di Ruby che
leggono le propriet di comment. La propriet
comment.created_at una data, quindi l'abbiamo
convertita in una stringa breve e leggibile con il me-
todo to_s(), che sta per to string. Abbiamo anche
codificato in HTML i campi che contengono te-
sto.
La prima riga del ciclo contiene un pezzo di codice
un po' pi complicato:
comment.vote ? "+1" : "-1"
Se conoscete il C o qualcuno dei suoi derivati
avrete certo riconosciuto il famigerato operato-
re ternario. Questa riga dice: se comment.vote
true, stampa +1, altrimenti stampa -1. E' un
modo pi sintetico di scrivere if..else.
Salvate il file e fate un refresh della pagina nel
browser (non c' bisogno che riavviate il server).
Il risultato dovrebbe essere quello dell'immagi-
ne: comments:
lo Speech seguito dalla lista dei suoi commenti.
Proprio quello che volevamo.
UN PO'
DI REFACTORING
Il codice che abbiamo appena scritto funziona,
ma non del tutto pulito. Il problema in quel-
la brutta riga della View:
<%= comment.vote ? "+1" : "-1" %>
Se avete esperienza di applicazioni MVC, sapete
che le View dovrebbero essere stupide e conte-
nere solo la logica di presentazione. La logica di
business, cio il comportamento dell'applica-
zione, dovrebbe essere nel Model. Secondo voi,
questa riga logica di presentazione o logica di bu-
siness? Comunque la pensiate, questo genere di
codice rende il template pi difficile da leggere -
quindi per ora lasceremo da parte le discussioni
accademiche e ne approfitteremo per rimpolpa-
re il nostro Model anemico. Apriamo il file com-
ment.rb e aggiungiamo un metodo alla classe
Comment:
class Comment < ActiveRecord::Base
belongs_to :speech
def ranking
vote ? "+1" : "-1"
end
end
Il metodo ranking() restituisce +1 o -1 a se-
conda che la propriet vote del Comment sia ve-
ra o falsa. In un altro linguaggio avremmo usato
la parola chiave return, ma in Ruby non ce n' bi-
sogno: in assenza di return, un metodo restitui-
sce semplicemente il valore dell'ultima espres-
sione che ha calcolato. Ora possiamo semplifi-
care il codice della vista:
<% for comment in @speech.comments %>
<p>
<%= comment.ranking %><br/>
...
Abbiamo soddisfatto il nostro perfezionismo. Ora
ci resta solo un pezzo per completare questo puzz-
le.
QUESTIONE DI FORM
Finora gli utenti di Soapbox possono visualizza-
re i commenti, ma non inserirli. Per inserire un
commento ci serve una nuova action nel Con-
troller:
class SpeechesController < ApplicationController
def comment
s = Speech.find params[:id]
s.comments <<
Comment.new(params[:comment])
redirect_to :action => :show, :id => s
end
...
La action comment() inizia come la action show():
si aspetta che la chiamata HTTP contenga come
parametro l'id di uno Speech, e legge lo Speech
dal database. Inoltre si aspetta che la chiamata
contenga un parametro comment, che a sua vol-
ta contiene tutte le propriet necessarie per crea-
re un nuovo Comment (come vedremo tra poco,
Rails costruir questo parametro per noi). La ac-
tion usa questo parametro per creare un Com-
ment che poi aggiunge allo Speech. Se ricordae i
nostri esperimenti con la console di poco fa, sa-
pete che questa operazione salva automatica-
mente gli oggetti nel database.
Normalmente, al termine di un'azione viene vi-
sualizzata una View. Noi invece vogliamo che, do-
po aver inserito un Comment, l'utente veda la pa-
gina dello Speechcorrispondente. Quindi l'ultima
SISTEMA M
G
102
/Maggio 2008
Un sistema di talk con Rails
NOTA
PER I PI
AVVENTUROSI
Se ve la sentite,
provate a scrivere
una pagina di
amministrazione che
permette di
cancellare alcuni
Comment. Potete
scrivere tutto il
codice a mano,
oppure usare
script/generator per
costruire uno
scaffold per i
Comment, come
abbiamo fatto il
mese scorso per gli
Speech. In
quest'ultimo caso
potete modificare il
Controller e le viste
generate perch
rendano possibile
eliminare un
commento, ma non
aggiungerlo. Se ce la
fate, potete gi
considerarvi
programmatori Rails!
096-103:072-080 1-04-2008 17:23 Pagina 102
ht t p: / / www. i opr ogr ammo. i t
riga una redirezione alla action show(). Questa
action vuole un parametro id, quindi dobbiamo
assegnare a questo parametro il valore dell'iddel-
la Speech. Grazie ad una delle tante scorciatoie
di Rails, possiamo semplicemente assegnare al
parametro l'intero oggetto s, che verr converti-
to automaticamente nel suo id durante la tra-
sformazione in HTTP.
Ora ci manca solo l'interfaccia utente. Torniamo
alla vista show.html.erb e aggiungiamo queste ri-
ghe subito prima dei due link in fondo alla pagi-
na:
<h3>Add your comment:</h3>
<% form_for :comment, :url => { :action =>
:comment, :id => @speech } do |f| %>
<p>
By: <%= f.text_field :author %>
</p>
<p>
Vote: <%= f.check_box :vote %>
</p>
<p>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit "Add comment" %>
</p>
<% end %>
Questo codice pu sembrare complesso se non
conoscete ancora bene Ruby. In realt molto
semplice, ma il modo migliore per imparare co-
me funziona quello di scrivere due o tre form
per conto vostro - quindi per ora ci limiteremo a
scorrere velocemente queste righe senza con-
centrarci sui dettagli. L'helper form_for costrui-
sce un tag HTML <form>. Il codice all'interno del-
la form definisce tre campi: un campo di testo
breve di nome author, una checkbox di nome
vote (che indica un voto positivo se seleziona-
ta, negativo se non lo ), un'area di testo di no-
me text e un pulsante per inviare i dati. La chiamata
a form_for precisa che la form impacchetta i cam-
pi in un parametro di nome comment, e invia
questo parametro alla action comment().
Dato che non abbiamo specificato un Control-
ler, il Controller lo stesso che ha generato la pa-
gina. Attenzione a non confondervi: sia la action
che il parametro si chiamano si chiamano com-
ment, ma avremmo potuto anche usare due no-
mi diversi.
Oltre al parametro comment, la form infila nella
richiesta anche un parametro id, che contiene
l'id dello Speechattualmente visualizzato. Anco-
ra una volta, Rails converte automaticamente lo
Speechnel suo id ma nessuno ci avrebbe impe-
dito di scrivere esplicitamente @speech.id.
Ricapitoliamo il flusso della vita di un commen-
to. L'utente compila questa form, e il risultato una
chiamata HTTP POST alla action comment(). Que-
sta chiamata ha due parametri: id, che contiene
l'identificativo di uno Speech, e comment, un pa-
rametro che contiene i valori di author, text e
vote. La action comment() usa i dati nel parame-
tro comment per creare un nuovo Comment e as-
segnarlo allo Speech identificato dal parametro
id. Poi la action redirige l'utente alla action show()
dello stesso controller, che visualizza nuovamente
lo Speech, i suoi Comment aggiornati, e la form
per aggiungere un altro Comment. Il risultato do-
vrebbe essere quello dell'immagine: add_comments.
Fatto!
CONCLUSIONI
Soapbox 0.2 pronto. Certo, c' ancora tanto
lavoro da fare. La grafica ignobile, le funzio-
nalit sono pi o meno le stesse di un qualsia-
si blog, e comincia a farsi sentire l'esigenza di
un sistema di autenticazione degli utenti (non
bello che chiunque possa modificare o can-
cellare i discorsi di chiunque altro).
Ma noi siamo seguaci del motto: prima fallo
funzionare, poi fallo bene. Perch Soapbox fun-
zioni come dicono le specifiche, ci resta anco-
ra un'ultima caratteristica: dobbiamo ordina-
re le opinioni sulla prima pagina in base ai vo-
ti che hanno ricevuto.Ce ne occuperemo il me-
se venturo. Nel frattempo, buon divertimento
con Rails!
Paolo Perrotta
M SISTEMA
Maggio 2008/
103
G
Un sistema di talk con Rails
Figura 3: La form per limmissione
NOTA
ERRATA
CORRIGE
Nell'articolo del
mese scorso abbiamo
scritto che potete
installare il driver
SQLite3 per Ruby con
il comando:
gem install sqlite3
Questo comando
sbagliato. Quello
giusto :
gem install sqlite3-
ruby
Chiediamo scusa per
l'errore.
096-103:072-080 1-04-2008 17:23 Pagina 103
ht t p: / / www. i opr ogr ammo. i t
SOFTWARE SUL CD M
G
106
/Maggio 2008
Librerie e Tool di sviluppo
XAMPP 1.5.5
IL TUO SERVER PERSONALE!
Installando questo software avremo a
disposizione un completo ambiente
per lo sviluppo di applicazioni web ba-
sate su PHP e MySQL, ossia una delle
piattaforme pi usate del web. I pro-
grammi installati saranno: Apache 2.2.3
(server web), MySQL 5.0.27 (motore
SQL), PHP 5.20/4.4.4 (linguaggio di
scripting), phpMyAdmin 2.9.1.1 (tool
web per amministrare MySQL), File-
Zilla FTP Server 0.9.20 (server FTP),
OpenSSL 0.9.8.d (librerie per
limplementazione del protocollo SSL).
La presenza di un pannello di control-
lo, inoltre, semplificher lavvio e larresto
dei vari server.
Directory: XAMPP 1.5.5.zip
PYTHON 2.5
L'EX GIOVANE RAMPANTE
Python stato considerato per lungo
tempo il nuovo che avanza. Attual-
mente non lo si pu pi definire in que-
sto modo, Python ormai un linguag-
gio stabile e completo che trova appli-
cazione in un gran numero di proget-
ti. Se ne parla sempre di pi in campo
industriale come su Internet. Soprat-
tutto un gran numero di applicazioni an-
che in ambiente Windows girano or-
mai grazie a Python e presentano in-
terfacce grafiche ottimamente strut-
turate. Ci nonostante Python rimane
un grande linguaggio di scripting adat-
to a gestire in modo completamente
automatico buona parte di un sistema
operativo sia esso Linux o Windows
Directory: python-2.5.msi
ANKHSETUP 0.5.5
PER UTILIZZARE SUBVERSION
DI VISUAL STUDIO.NET
SubVersion uno strumento di con-
trollo della revisione che si sta affer-
mando rapidamente nel momento del-
la programmazione. Consente di te-
nere sotto controllo le modifiche ef-
fettuate al codice generando una serie
di log che consentono di capire come
quando e come determinate parti del-
l'applicazione siano state modificate.
Eventualmente possibile tornare a
versioni precedenti oppure generare il
changelog automaticamente. Questo
Add In consente ai programmatori .NET
di utilizzare subversion direttamente
dai propri progetti e dall'ambiente di
programmazione Visual Studio. Sub-
version sta lentamente soppiantando
il fratello maggiore CVS che nel tem-
po aveva conquistato larghe fette di
mercato. Alcuni progetti molto attivi
come ad esempio il kernel di Linux so-
no passati a subversion
Directory: AnkhSetup 0.5.5.zip
REGULATOR 20B
IL COSTRUTTORE DI REGEX
un tool ricco di funzioni e semplice
da usare, che permette di testare le
espressioni regolari. Le REGEX non
sempre sono facili da manipolare,
Regulator ci sa una mano semplifi-
candoci il lavoro consentendoci di
verificare il buon funzionamento di
un espressione regolare prima di in-
serirla nel nostro codice
Directory: Regulator 20B
BUGZILLA 2.23.3
PER TENERE SOTTO CONTROLLO
I BACHI DEL SOFTWARE
Un buon software evolve nel tempo-
sulla base delle indicazioni dei suoi
utenti. E tutti i software nascono con
qualche imperfezione che solitamen-
te viene fuori proprio in fase di utiliz-
zo intensivo dell'applicazione.Ma co-
me tener traccia delle segnalazioni ef-
fettuate dagli utenti?Bugzilla una Web
Application checonsente di memoriz-
zare i bug, ordinarli secondo precisi
ticket e cos garantisce al programma-
tore di poter intervenire in modo or-
ganico, sistemando i bachi, elaboran-
do patch, eventualmente segnalando
all'utente falsi bug.Un'applicazione
senza dubbio utile che risolve uno dei
maggiori problemidel ciclo di svilup-
po. Attualmente bugzilla lo strumento
pi diffuso per il controllo dei bug sui
progetti opensource e molto spesso
viene utilizzato anche in caso di pro-
getti commerciali
Directory: Bugzilla 2.23.3.zip
WIRESHARK 0.99
LO SPIONE DELLA RETE
Wireshark uno dei software pi di-
scussi dell'ultimo anno. Non perch in
se contenga qualcosa di realmente dan-
noso ma perch spesso viene utilizza-
to da malintenzionati per recuperare
dal traffico di rete informazioni non
criptate.Wireshark infatti un analiz-
zatore di traffico di rete. Intercetta tut-
ti i pacchetti che passano nella vostra
lan restituendo una serie di informa-
zioni completa, quantitativa e quali-
tativa del materiale che passa attra-
verso le schede di rete della Lan. Si trat-
ta di uno strumento utilissimo per ca-
pire come ottimizzare le vostre con-
nessioni, ma deve essere utilizzato con
cautela
Directory: wireshark 0.99
NUNIT 2.4.3
IL COSTRUTTORE DI TEST
Qualche volta, anzi molto spesso vi tro-
verete a costruire dei test per "stressa-
re" la vostra applicazione .NET e ve-
dere come reagisce. Nunit un tool
opensource che vi mette a disposizio-
ne un linguaggio per scrivere proce-
dure di test. Inoltre consente di visua-
SOFTWARE
SUL CD
106-109:106-108-software 1-04-2008 17:12 Pagina 106
ht t p: / / www. i opr ogr ammo. i t
M SOFTWARE SUL CD
Maggio 2008/
107
G
Librerie e Tool di sviluppo
lizzare i dati tramite un'interfaccia gra-
fica
Directory: Nunit 2.4.3
DAYPILOT 2.1. SP3
UN CONTROLLO ASP.NET PER LA
GESTIONE DEGLI APPUNTAMEN-
TI
Intelligente questo controllo! Consen-
te di creare un'applicazione ASP .NET
che offre funzionalit del tutto simili
a quelle utilizzate da Outlook nella sua
parte relativa alla gestione degli impe-
gni.Il controllo molto solido e ben
strutturato e offre una serie di funzio-
ni che risultano piuttosto comode per
un programmatore, molto pi evolute
di un normale calendario
Directory: Daypilot 2.1. SP3
NANT 0.86
IL COMPILATORE BATCH
Il vostro progetto in continua fase di
sviluppo. Avete deciso di rilasciare al
pubblico ogni sera ad una certa ora una
release binaria contenente gli ultimi
aggiornamenti. Come automatizzare
il processo di compilazione? Facile! ef-
fettuate uno scheduling indicando a
nant cosa e come volete compilare e
lui pensa al resto!
Directory: Nant 0.86
DPACK 2005-2008
UN PACCHETTO COMPLETO PER
VS 2008
Il DPack pi che un tool un Add-In
per le versioni di Visual Studio dal 2005
al 2008 e che si integra perfettamente
nell'ambiente. Aggiunge funzioni co-
me Code Browser, File Browser, Solution
Browser, Statistiche, Backup e molto
altro
Directory: Dpack 2005-2008
REFLECTOR 5.1.0
IL DECOMPILATORE PER .NET
forse il tool pi conosciuto tra gli svi-
luppatori .NET e non solo perch uti-
lissimo ma anche perch costante-
mente aggiornato e gratuito. Si tratta
di un software che sfrutta la reflection
di .NET per risalire al contenuto degli
assembly .NET. Questo consente di ri-
salire alla logica di funzionamento di
un codice anche quando non se ne han-
no a disposizione i sorgenti
Directory: Reflector 5.1.0
ASP.NET VERSION
SWITCHER 2008
MODIFICARE AL VOLO LA VER-
SIONE DI ASP.NET
Semplicissimo tool il cui unico scopo
quello di modificare la versione di
ASP.NET utilizzata da una particolare vir-
tual directory di IIS. Questa variazio-
ne in realt potrebbe essere fatta di-
rettamente dalla console di ammini-
strazione ma avere a disposizione una
piccola applicazione che lo fa per voi al
volo, potrebbe essere una comodit
non da poco
Directory: ASP.NET Version Switcher
2008
REFLECTOR 5.1.0
IL DECOMPILATORE PER .NET
forse il tool pi conosciuto tra gli svi-
luppatori .NET e non solo perch uti-
lissimo ma anche perch costante-
mente aggiornato e gratuito. Si tratta
di un software che sfrutta la reflection
di .NET per risalire al contenuto degli
assembly .NET. Questo consente di ri-
salire alla logica di funzionamento di
un codice anche quando non se ne han-
no a disposizione i sorgenti
Directory: Reflector 5.1.0
SNIPPET COMPI-
LER 2.0
PER COMPILARE UN PICCOLO
PEZZO DI CODICE
Un utility che consente di srivere, com-
pilare e fare girare piccoli spezzoni di co-
dice. Molto utile se si vuole provare il fun-
zionamento di una porzione di codi-
ce senza per questo volere creare un
completo progetto con Visual Studio
prima di eseguire il tutto
Directory: Snippet Compiler 2.0
DEV C++ 4.9.9.2
UN EDITOR C++ A BASSO CO-
STO
Dev C++ un editor distibuito su li-
cenza GPL, come tale non ha costi re-
lativi al diritto d'autore. Come tutto o
quasi il software GPL la sua economi-
cit non affatto sinonimo di scarsa
qualit. Al contrario DEV C++ uno
degli editor C++ pi amati ed utilizza-
ti da chi sviluppa in C++. Le caratteri-
stiche sono notevoli. Si va dal debugger
integrato, al project management, al
class browser, al code completion.
L'insieme di queste caratteristiche uni-
te all'eccezionale leggerezza dell'am-
biente lo rende particolarmente co-
modo da utilizzare per sviluppare pro-
getti C++ anche di grandi dimensioni
Directory: Dev C++ 4.9.9.2
WXWIDGETS 2.8.4
COMODE LIBRERIE PER LO SVI-
LUPPO DI INTERFACCE GRAFI-
CHE
Interessantissime queste librerie, pi
volte le abbiamo utilizzate all'interno
di ioProgrammo per realizzare degli
esempi. Si tratta di librerie che con-
sentono la creazione di interfacce gra-
fiche, possono essere utilizzate da C++
ma anche da altri linguaggi come ad
esempio Python. La cosa estremamente
interessante che consentono lo svi-
luppo di applicazioni completamente
multipiattaforma, sono disponibili in-
fatti sia in ambiente unix che in am-
biente Windows.
Directory: wxWidgets 2.8.4
ULTIMATE ++ 2008
IL NUOVO IDE PER C++
Completo e ben fatto questo IDE che
si affaccia alla programmazione ten-
tando di rubare la scena al ben pi no-
to DEV C++. Dotato di code comple-
tion e sintax highlighting, un ottimo
debugger e votato persino ad essere un
ambiente RAD programmabile a mez-
zo form come accade negli IDE pi bla-
sonati. Certo in questo senso ha anco-
ra molto da crescere, tuttavia si pone
come un ottimo IDE freeware e alter-
nativo a DEV C++
Directory: Ultimate ++ 2008
YUI - YAHOO
LIBRARY 2.4.1
IL FRAMEWORK FACILE PER JA-
VASCRIPT
Bottoni, Tabsheet, Menu a scomparsa,
effetti speciali, il Web 2.0 costellato
di interfacce simili a quelle lato desk-
top. Come possibile tutto ci? la ri-
sposta semplice: JavaScript. YUI un
framework JavaScript sponsorizzato
da Yahoo e che rende semplice la pro-
grammazione di interfacce per il web in
linguaggio JavaScript. Il framework
veramente ben fatto e contiene una se-
rie di funzionalit che coprono quasi
tutto lo specchio possibile dei compo-
106-109:106-108-software 1-04-2008 17:12 Pagina 107
ht t p: / / www. i opr ogr ammo. i t
SOFTWARE SUL CD M
G
108
/Maggio 2008
Librerie e Tool di sviluppo
nenti utili nella progettazione grafica
di un'applicazione. Si tratta di una di
quelle librerie che realmente accelera il
lavoro di qualunque programmatore
Web.
Directory: YUI - Yahoo Library 2.4.1
RICO 1.1.2
DRAG & DROP CON AJAX
Un altro interessante framework Java-
Script orientato ovviamente al Web 2.0.
Offre alcune particolarit interessanti co-
me il drag & drop ed effetti particolar-
mente spettacolari per arricchire
l'interfaccia. Lo stile quello dei beha-
viour gi utilizzati in Flex e Openlaszlo.
E' sufficiente inserire in qualche div al-
cune righe di JavaScript et voil il gio-
co fatto!
Directory: Rico 1.1.2
TOMCAT 6.0.9
IL SERVLET CONTAINER PER JA-
VA E JSP
L'idea molto semplice. Sviluppare im
Java pagine Web. Ad un primo sguardo,
Tomcat potrebbe sembrare un norma-
le Web Server.
Ed in effetti un normale Web Server! In
grado di soddisfare le richieste per qua-
lunque pagina HTML. In realt per
Tomcat anche qualcosa in pi, ovve-
ro la capacit di soddisfare le richieste
per applicazioni Java. Potrebbe sem-
brare complesso, in realt lo meno di
quanto sembri. Immaginate Tomcat co-
me un grande contenitore al cui inter-
no ci sono altri contenitori ciascuno
dei quali rappresenta un'applicazione
Java, che una volta richiamata costrui-
sce una pagina Web interpretabile da
un browser. Questo consente di svi-
luppare pagine Web utilizzando tutta
la potenza della normale gerarchia del-
le classi Java e la sintassi e il linguaggio
che qualunque programmatore Java co-
nosce bene.
Directory: Tomcat 6.0.9
ZKOSS 3.0.0
IL FRAMEWORK AJAX IN TECNO-
LOGIA MOZILLA
ZK un Ajax web framework che facili-
ta lo sviluppo di Rich Internet Applica-
tion aderenti alle indicazioni del Web
2.0. Il progetto Open Source ed pos-
sibile scegliere tra due diverse tipolo-
gie di licenza (stessa politica adottata
da MySQL): GPL o commerciale. La co-
struzione dellinterfaccia grafica rea-
lizzata tramite il linguaggio di markup
ZUL, un diretto discendente del pi
famoso XUL utilizzato nella costruzio-
ne di applicazioni basate su Mozilla.
Directory: ZKOss 3.0.0
ANIMADEAD 2.0
PER CREARE ANIMAZIONI PAR-
TENDO DA UNO SCHELETRO
Una libreria che sfrutta il concetto di
skeletal animation. Si inizia disegnan-
do con un qualunque ambiente 3D lo
scheletro di un soggetto, su questo sche-
letro si costruiscono i movimenti che
saranno pilotati dalla libreria. Si tratta
di un progetto per alcuni versi ancora em-
brionale, ma che lascia intravedere un
approccio piuttosto innovativo all'ani-
mazione di modelli tridimensionali
Directory: Animadead 2.0
PIRCBOT 1.4.4
UN BOT IRC PROGRAMMABILE
IN JAVA
Interessante questa idea di fornire agli
amanti di IRC una serie di API che con-
sentono di programmare un bot utiliz-
zando Java.Normalmente chi si dedica
a questo tipo di applicazioni utilizza
Eggdrop e programma in TCL, in tutti e
due i casi non si tratta di tecniche di
larga diffusione, viceversa Java un lin-
guaggio che gode di un'immensa po-
polarit, per cui il poter programmare
dei bot attraverso la flessibilt di Java
rappresenta senza dubbio un'oppor-
tunit interessante
Directory: Pircbot 1.4.4
J2ME POLISH 2.0
IL COSTRUTTORE DI GUI PER DE-
VICE MOBILI
J2ME polish forse un precursore dei
tempi. Si tratta di un software il cui sco-
po supportare il programmatore nel-
la creazione di interfacce destinate a
dispositivi portatili quali ad esempio
cellulari o palmari. Ovviamente la ba-
se su cui si fonda J2ME ormai onni-
presente in qualsiasi applicazione per
dispositivi del genere, tuttavia invece
la definizione dell'interfaccia basa le
sue caratteristiche su semplici file HTML.
Interessante il fatto che J2MEpolish
sia distribuito sotto licenza GPL
Directory: J2me Polish 2.0
JAVA SE DEVELOP-
MENT KIT 6 6.0
IL COMPILATORE INDISPENSABI-
LE PER PROGRAMMARE IN JAVA
Se avete intenzione di iniziare a pro-
grammare in Java oppure siete gi dei
programmatori esperti avete bisogno
sicuramente del compilatore e delle li-
brerie indispensabili. Sotto il nome di Ja-
va SE Development Kit vanno appunto
tutti gli strumenti e le librerie nonch le
utility necessarie per programmare in
Java. L'attuale versione la 6.0 molto
pi legata al desktop di quanto non fos-
sero tutte le precedenti
Directory: Java SE Development Kit
6 6.0
ECLIPSE SDK 3.2.2
L'IDE TUTTOFARE
Eclipse un progetto completo porta-
to avanti da Eclipse Foundation con la
collaborazione di una miriade di azien-
de fra cui IBM, Adobe, Sun e che si
prefissata lo scopo di creare un IDE
estendibile per plugin adattabile a qua-
lunque tipo di linguaggio o tecnologia.
Di default Eclipse si propone come IDE
per Java ed qui che da il meglio di se.
Ma proprio grazie ai suoi plugin pos-
sibile utilizzarlo come ambiente di pro-
grammazione per PHP, per C++, per Flex
e per molti altri linguaggi ancora. Inol-
tre sempre grazie per ciascun linguag-
gio sono disponibili altri plugin ad esem-
pio per rendere l'ambiente RAD o per fa-
vorire lo sviluppo dei Web Services o
altro.
Insomma lo scopo stato raggiunto
completamente. Eclipse realmente
un IDE tuttofare, ormai maturo, e che ser-
ve una miriade di programmatori gra-
zie alle sue caratteristiche di affidabilit
e flessibilit. Unica nota negativa: una
certa pesantezza che lo rende idoneo
ad essere usato solo su PC con una do-
tazione hardware minima di tutto ri-
spetto
Directory: Eclipse SDK 3.2.2
PHP 5.2.1
IL LINGUAGGIO DI SCRIPTING
PI AMATO DEL WEB
Sono tre le colonne portanti di Inter-
net: PHP, APACHE e MySQL. Certo la
concorrenza forte. Asp.NET e SQL Ser-
ver avanzano con celerit, ma a tutt'og-
gi non si pu affermare che i siti svi-
106-109:106-108-software 1-04-2008 17:12 Pagina 108
ht t p: / / www. i opr ogr ammo. i t
M SOFTWARE SUL CD
Maggio 2008/
109
G
Librerie e Tool di sviluppo
luppati in PHP costituiscano la stra-
grande maggioranza di Internet. Qua-
li sono le ragioni del successo di co-
tanto linguaggio? Prima di tutto la com-
pletezza.
PHP ha di base tutto quello che serve
ad un buon programmatore, raramen-
te necessario ricorrere a librerie ester-
ne, e quando proprio indispensabile
farlo esistono comunque una serie di
repository che rendono tutto imme-
diatamente disponibile ed in forma gra-
tuita.
Il secondo punto di forza del linguaggio
sta nella sua capacit di poter essere
utilizzato sia in modo procedurale che
nella sua forma ad oggetti certamente
pi potente e completa. Esiste un terzo
di punta di forza essenziale che quel-
lo riguardante la curva di apprendi-
mento.
PHP in assoluto uno dei linguaggi con
la curva di apprendimento pi bassa
nel panorama degli strumenti di pro-
grammazione. Si tratta perci di uno
strumento indispensabile per chi si avv-
vicina alla programmazione web, a me-
no che non intendiate scegliere strade
diverse quali possono essere ASP.NET o
JSP
Directory: PHP 5.2.1
SIMPLE MACHINE
FORUM 1.1.4
PER CREARE AGEVOLMENTE SE-
ZIONI FORUM
Per anni su Internet hanno dominato la
scena forum del calibro di PHPbb, VBul-
lettin, Invision, da qualche tempo a que-
sta parte a contendere lo scettro del mi-
gliore a questi mostri sacri arrivato
Simple Machine Forum, per gli amici:
SMF.Si tratta di un forum che ancora
non ha tutte le caratteristiche dei suoi
rivali ma che procede a ritmi serrati nel-
lo sviluppo e promette realmente bene.
La versione 1.1.4 che vi presentiamo
contiene gi un nutrito numero di fun-
zionalit ed un wizard realmente semplice
per l'installazione di addon. La 2.0 pros-
sima ventura annunciata con tutte le
caratteristiche degne dei suoi rivali e an-
che qualcosa in pi. Si tratta di un fo-
rum su cui scommettere se si vuole in-
stallare qualcosa di realmente innova-
tivo e con larghi orizzonti.
Directory: Simple Machine Forum
1.1.4
MYSQL 5.1.15
IL PRINCIPE DEI DATABASE
Indispensabile per programmare we-
bapplication in tecnologia PHP. Non-
che non sia possibile utilizzare altrida-
tabase, ma MySQL e PHP rappresenta-
no veramente un binomioinscindibi-
le. L'integrazione fra questo database
e il linguaggio di scripting pi usato sul-
la rete talmente alta da fare divenire
quasi un obbligo l'uso congiunto di
questi due strumenti
Directory: MySQL 5.1.15
DADABIK 4.2
UN CREATORE DI INTERFACCE
VERSO DATABASE
Si tratta di una web application scrit-
ta in PHP che consente di costruire
altre web application basate su data-
base. Ad esempio se volete costruire
un sito che esponga un catalogo mul-
timediale, non vi resta che informare
Dadabik di quali campi si compone il
catalogo in questione, di quali funzio-
nalit volete che il vostro sito sia
dotato, e lasciare a DadaBik il compi-
to di generare la vostra interfac-
cia.Non necessario avere compe-
tenze estese di programmazione,
l'uso di DadaBik piuttosto semplice.
Directory: Dadabik 4.2
POSTGRESSQL
8.2.5
IL DATABASE DELLE MERAVIGLIE
Postgres a pieno titolo una delle
meraviglie del mondo dei database.
Completo, performante, dotato di
tutte le funzioni principali che si
attribuiscono ad un database profes-
sionale. Stored Procedure, triggers,
gestione avanzata della sicurezza con
controllo fino al singolo campo, tutta
una serie di caratteristiche che lo
posizionano al top della categoria. In
pi Postgres realmente estendibile.
Contiene in se meccanismi per creare
funzioni e procedure che costituisco-
no il valore aggiunto di questo gi
dotatissimo database
Directory: Postgressql 8.2.5
MYSQL
WORKBENCH 5.0.12
BETA
IL PROGETTISTA DI DATABASE
Quante volte usando MySQL avete
rimpianto quella parte di Access che
vi consente di disegnare e collegare
graficamente le tabelle fra di loro?
Sicuramente tante. E non perch
Access sia minimamente comparabi-
le con MySQL ma solo e soltanto per-
ch realmente progettare un database
un'operazione che prende molto
meno tempo se possibile disegnare
un grafico di correlazione delle tabel-
le. MySQL Workbench assolve proprio
a questa funzione, si tratta di un uti-
lissimo programma che rende pi
comoda la progettazione di un DB,
mettendo a disposizione del progetti-
sta una sorta di lavagna in cui dise-
gnare graficamente le tabelle e le loro
relazioni, oltre alle viste e alle altre
molte cose che MySQL consente di
fare
Directory: MySQL Workbench 5.0.12
beta
MYSQL GUI 5.0
IL GESTORE DI MYSQL
Gestire MySQL un problema? Creare
nuovi database? Creare nuovi uten-
ti?In soccorso vi viene questa ottima
Gui attraverso la quale potete gestire
in modo realmente semplice tutte le
funzionalit del vostro database pre-
ferito.Si tratta di una Gui realizzata
proprio da MySQL AB per cui decisa-
mente affidabile e completa
Directory: MySQL GUI 5.0
PGADMIN III
3.1.8.2.2
L'INTERFACCIA DI GESTIONE DI
POSTGRES
PostgreSQL un database eccezional-
mente potente, forse il pi completo
oggi disponibile. Il suo tallone
d'achille risiede probabilmente nella
difficolt del linguaggio che ne la
base. Per evitare lo scontro con una
sintassi non sempre chiara c'
pgAdmin, una comoda interfaccia
grafica attraverso la quale si possono
gestire tutte le funzionalit di un ser-
ver Postgres in modo semplice ed
ottimale. Postgres rimane un databa-
se incredibilmente evoluto, che fa
dell'estendibilit uno dei dei suoi
punti di forza grazie al suo linguaggio
interno
Nome Programma: PGadmin III
3.1.8.2.
106-109:106-108-software 1-04-2008 17:12 Pagina 109
ht t p: / / www. i opr ogr ammo. i t
M
Soluzioni
Maggio 2008/
111
G
Analisi dei log e simulazione dellazione
A
scopo di intrattenimento sono sempre pi
diffusi sul web animazioni che simulano
uno sport, come una partita di calcio, una
corsa di cavalli, un incontro di tennis o quantaltro.
Non solo. Spesso si incontrano giochi e applicazioni
che consentono di indicare dei parametri per il
gioco o sport che si intende rappresentare, come
labilit dei singoli atleti per compiti specifici, ed in
funzione di routine che usano componenti random.
Si pu assistere cos ad una partita dove non vi
alcuna interazione con lutente ma che fornisce un
risultato. In altre parole come una procedura
batch in cui si stabiliscono inizialmente alcuni
aspetti generali e in un secondo momento si avvia la
simulazione. Ad esempio se parliamo di calcio, si
possono a priori stabilire le abilit dei singoli calcia-
tori, come la forza di gioco, ma anche secondo
aspetti particolari come: lagilit, la precisione, il tiro
e cos via. Inoltre, si possono impostare alcuni
aspetti generali come la condizione del campo.
Stabilito ci, si avvia la simulazione che avr il com-
pito di produrre un file di log. Questo file descriver
lintero incontro, azione per azione. A questo punto
affinch un opportuno client di un programma di
animazione come flash possa interpretare il risulta-
to ottenuto necessario trasformare il file di log
prodotto in un opportuno file, utile ad essere inter-
pretato. In questo articolo affronteremo le prime
parti riferite ad uno sport relativamente semplice
per essere trattato, come il tennis, e per esso vedre-
mo come si possano stabilire e strutturare dei para-
metri che descrivono le condizioni iniziali del gioco.
Infine, daremo indicazioni su come costruire il
codice che simula lincontro producendo un file di
log. Tale file potr facilmente essere tradotto in un
altro file secondo la sintassi del client di animazione
che si sta usando.
UN ESEMPIO
CONCRETO: IL TENNIS
La scelta del tennis individuale come sport per effet-
tuare un concreto esempio, stata fatta per la rela-
tiva semplicit che esso esprime, dovuta principal-
mente al fatto che non si tratta di uno sport di squa-
dra. In realt come vedremo, nonostante considere-
voli semplificazioni al fine di rendere pi chiaro e
comprensibile il percorso di costruzione della simu-
lazione, la struttura del programma tuttaltro che
semplice. Ad ogni modo compresa la filosofia che
sottende il ragionamento sar facile per il lettore
ampliare lapplicazione con ulteriori aspetti tecnici.
A tale proposito anticipo che non sono un esperto
di tale sport e quindi inevitabilmente mi capiter di
tralasciare alcuni aspetti tecnici sicuramente signi-
ficativi. Ogni riga del file di log, uno file di testo stan-
dard, contiene unazione completa. Ogni azione
consiste di due semi azioni, associate alle attivit dei
due tennisti. Entreremo successivamente sugli
aspetti squisitamente tecnici della questione, per
adesso utile ricordare che ogni giocatore pu tro-
varsi ad effettuare una tra diverse attivit come la
battuta, la corsa o spostamento, il rovescio, il dritto,
la schiacciata e cosi via. Ed ognuna di queste attivit
accompagnata da parametri come la forza e la
precisione per la battuta, il dritto, la schiacciata ed il
rovescio; e come la velocit per altri come la corsa o
spostamento. Risulta evidente che nel caso del ten-
nis i parametri che vanno settati inizialmente
riguardano le caratteristiche di abilit del singolo
giocatore che possono esprimersi con
unopportuna scala numerica. Come nella realt il
giocatore pi forte ha pi chance di vincere, ma
analogamente alla realt vedremo come giocher
un ruolo importante anche la variabile aleatoria.
STRUTTURA DEI DATI
In questo paragrafo individueremo le informazioni
per descrivere una incontro completo, una scelta
del tutto personale, ovviamente modificabile ed
ampliabile. Inoltre, stabiliremo oltre alle attivit
possibili quelle previste, ed in che scala possono
essere descritte. Le prime informazioni riguardano
SIMULAZIONE
RANDOM DI GIOCHI
COME COMANDARE DEI CLIENT DI ANIMAZIONE COME FLASH SIMULANDO GIOCHI O SPORT. LA
SOLUZIONE PRODURRE FILE DI LOG COSTRUITI INTORNO A DEI PARAMETRI DI GIOCO E CON
UNA FORTE COMPONENTE CASUALE
Conoscenze richieste
Basi di
programmazione sul
web
Software
nessuno
Impegno
Tempo di realizzazione
REQUISITI
110-114:032-035 1-04-2008 17:18 Pagina 111
ht t p: / / www. i opr ogr ammo. i t
Soluzioni
M
G
112
/Maggio 2008
Analisi dei log e simulazione dellazione
il contendenti. Come per le altre che esamineremo
le descriveremo in forma tabulare, sapendo che
questi dati possono essere memorizzati in modo
permanente su un file o meglio in una tabella di un
db. I dati salienti rispetto ai giocatori sono:
la prima riga che indica il tracciato record relativo
al tennista. Da notare che la lista di giocatori pu
essere corposa. Nel momento in cui si dovr dispu-
tare un incontro bisogner scegliere due tennisti.
Sar lindice (identificativo numerico del tennista -
IDGT) lelemento a cui fare riferimento successiva-
mente per distinguere un tennista. Un altro fonda-
mentale nucleo informativo riguarda lattivit che
un singolo tennista pu effettuare. Quelle previste
per una basilare simulazione sono:
Esisteranno informazioni che mettono in relazione
il singolo giocatore con le attivit da svolgere, si trat-
ta delle abilit. Come detto per ogni giocatore biso-
gna prestabilire le skills che verranno indicate
rispetto ad una scala di valori. Una scala particolar-
mente adatta e intuitiva si sviluppa su 101 valori da
0, che indica assenza di abilit a 100 che invece spe-
cifica il massimo delle capacit. Cosicch ogni gio-
catore avr la sua scheda informativa, che esiste
ovviamente anche nella realt, dove si desume dai
risultati medi rispetto a quella attivit. Nella scheda
per ogni attivit e rispettivo parametro si indicher
un valore. Ecco un esempio per il giocatore 2.
facile intuire come leggere tale tabella. Il giocato-
re con identificativo 2, che nel nostro esempio
Walter Beltrame ha come abilit media per
lattivit 1, ossia la battuta: 70 di forza e 50 di preci-
sione; e cos via. Da notare che per lo spostamento
i parametri indicano altri aspetti che non sono le
solite forza, e precisione, bens come si nota dalla
tabella attivit direzione e velocit. Di questi due il
secondo parametro pu essere espresso come
unabilit del tennista, il primo no cosicch contie-
ne il valore -1, il che indica appunto che non si trat-
ta di unabilit. Stessa cosa per lattesa. Questa
tabella sar il punto di partenza quando bisogner
produrre unazione, vedremo che si applicher una
funzione casuale a partire per dalle abilit posse-
dute. Quindi se ad esempio la forza con cui un ten-
nista tira il rovescio mediamente 80 (ricordo che
riferita in una scala da 0 a 100), allora ci saranno
buone possibilit che la forza effettiva della perfor-
mance sia 80 o un valore vicino ad esso, non si
esclude per, anche se con probabilit minore, che
si producano valori sensibilmente diversi da 80.
Inoltre, va considerato che non sempre valori alti
indicano ottimi risultati. Per la volet ad esempio, il
tocco leggero sotto rete, la forza deve essere bassa,
altrimenti il tiro risulta sbagliato, mentre la precisio-
ne deve essere alta. Le informazioni fino ad ora esa-
minate riguardano i giocatori e le loro caratteristi-
che, ma non entrano nel merito di una sfida, una
reale partita. Per esprimere una partita bisogner
sequenziare delle azioni, viste come un insieme di
semi azioni, svolte cio da un tennista e dal suo
avversario. Se ad esempio uno dei due giocatori
risponde ad un tiro di rovescio, laltro verosimil-
mente si sposter per riconquistare una posizione
ottimale. Unazione che sar lelemento centrale di
cui tenere traccia per descrivere una partita pu
essere espressa da un tracciato record. Come al soli-
to lo accompagniamo ad un esempio in cui si istan-
ziano valori.
Come si legge? La prima colonna scandisce i tempi
delle azioni che potrebbero essere espressi in deci-
mi di secondo. Le successive quattro colonne fanno
riferimento ad informazioni per un tennista, in par-
ticolare il codice del tennista, il codice dellazione
20
42
39
1
2
3
70
80
70
90
80
90
2
1
2
8
6
7
0
40
45
0
20
50
1
2
1
Tempo IDNT IDNA Par_1 Par_2 IDNT IDNA Par_1 Par_2
Tabella 4 - Azioni Alcune azioni di un incontro
1
2
3
Gianni Potente
Walter Beltrame
Gaspare Soffritti
Gpot.jpg
Walterb.jpg
Gasparr.ipg
IDNT Nominativo Pic
Tabella 1 - Tennisti - Elenco dei tennisti
1
2
3
4
5
6
7
8
Forza
Forza
Forza
Forza
Forza
Forza
Direzione
Battuta
Rovescio
Diritto
Schiacciata
Volet
Seconda Battuta
Spostamento
Attesa
Precisione
Precisione
Precisione
Precisione
Precisione
Precisione
Velocit
IDNA Nome_at Par 1 Par 2
Tabella 2 - Attivit - Elenco delle possibili attivit
1
2
3
4
5
6
7
8
70
80
60
70
20
50
-1
-1
50
90
60
50
70
30
60
-1
IDNA Par 1 Par 2
Tabella 3 - Abilit- Parametri riferiti ad uno specifico giocatore
110-114:032-035 1-04-2008 17:18 Pagina 112
ht t p: / / www. i opr ogr ammo. i t
che ha eseguito, i parametri riferiti allazione espres-
si secondo la scala citata, come la forza e la precisio-
ne. Analogamente le altre quattro colonne sono rife-
rite allazione dellavversario. Di questo e di come
tenerne traccia tratteremo pi approfonditamente
avanti. Per la direzione il numero indicato specifica
in quale parte del campo muoversi. Si pu pensare
di partizionare il campo in 101 parti e numerarle.
Unaltra importante nozione riguarda il punteggio. Il
risultato corrente di un incontro si pu mantenere
in un tabellone fatto semplicemente come segue:
Dal tabellone oltre al punteggio al momento acqui-
sito dai due tennisti si riesce a dedurre in quale fase
della partita ci si trova, ossia in quale: set, gioco e
punto. Nellesempio riportato ci troviamo al set 2
(secondo), gioco 8 (ottavo) e punto 4 (quarto).
Questi ultimi dati servono ad esempio a stabilire chi
deve battere. Le informazioni di cui tener conto
sono tante, nonostante come detto si voluto espli-
citamente mantenere il problema ad un livello di
astrazione tale da renderlo semplice. Si capisce ad
esempio facendo un paragone rispetto al calcio, che
nel tennis sebbene possa essere relativamente pi
semplice la gestione delle azioni di gioco, le cose si
complicano leggermente quando si deve gestire il
punteggio.
LA PRODUZIONE DI FILE
Il programma che si intende costruire deve produr-
re dei file. Un file tiene traccia di tutte le azioni svol-
te nel campo da tennis. Abbiamo visto con la tabella
4 come descrivere unazione, ossia con linsieme di
due semi azioni ottenute come attivit dei due con-
tendenti. Il file principale di log conterr quindi una
sequenza di righe che rappresentano azioni cos
come descritte nella tabella citata. Nel file ci sar
una prima riga di intestazione che contiene gli iden-
tificativi dei due giocatori, il luogo, la data, lora e il
turno. Il nome assegnato al file sar incontro seguito
da informazioni che lo contraddistinguono da altri
incontri, come un numero progressivo o insiemi di
altre informazioni legate allevento ma che comun-
que indichino in modo univoco la partita. Il file
dovr essere di testo per il suo carattere universale,
facile da essere trattato da terze parti. Un esempio di
questo file riportato di seguito (figura 1), si tratta di
una sequenza cos come riportata in tabella 2.
Bisogner per memorizzare in modo permanente
anche altre informazioni generali, come la lista dei
giocatori a disposizione, seguendo le indicazioni di
tabella 1; la lista delle azioni e dei significati, tabella
2 e un file di abilit in cui sono riportate in sequenza
una serie di righe che indicano le abilit dei singoli
tennisti, come una serie di tabelle 3, una per ogni
giocatore.
Un altro file che bisogna associare ad un singolo
incontro il punteggio, in modo da tenere traccia di
tutte le variazioni. necessario poich il tabellone
fotografa la sola situazione attuale e non rappresen-
ta come si arrivati a tale punteggio. Il file punteggio
pu essere espresso come mostrato in figura 2.
Individuata unopportuna struttura dati da realizza-
re sia per luso run-time, quindi come una serie di
vettori di record; che per la memorizzazione perma-
nente, appunto come files, ma si potrebbe pensare
anche ad un database, bisogna passare allimple-
mentazione.
IMPLEMENTAZIONE
Il programma principale sar scandito da una serie
di cicli innestati, riferiti ai quattro diversi livelli di
punteggio che si possono ottenere: partita, set, gio-
chi e punti. Nel ciclo pi interno si produrr una
nuova azione. Ecco come si pu costruire in C++ il
programma principale.
void partita ()
{ int ris;
tennista1=caricatennista();
tennista2=caricatennista();
M Soluzioni
Maggio 2008/
113
G
Analisi dei log e simulazione dellazione
1
2
1
1
3
5
3
5
IDNT Set Gioco Punto
Tabella 5 - Tabellone Punteggio attuale
Figura 1: Il file che rappresenta lincontro con le sue
azioni.
Figura 2: Il file che rappresenta il punteggio e la sua
evoluzione.
110-114:032-035 1-04-2008 17:18 Pagina 113
ht t p: / / www. i opr ogr ammo. i t
int set=1;
int azio=0;
bool fineset=false;
bool finepartita=false;
do // ripete fin quando non finisce la partita
{ int gioco=1;
bool finegioco=false;
do // ripete fin quando non finisce il set
{ int punto=0;
do // ripete fin quando non finisce il gioco
{ do // ripete fin quando non si
fa un punto
{ ris=fai_azione(++azio,
set, gioco, punto);
} while (ris!=0) ;
aggiorna_punteggio(ris, set,
finepartita, gioco, finegioco, punto);
} while (!finegioco)
} while (!fineset)
} while (!finepartita) }
Con le tre variabili: set, gioco e punto si riesce a sta-
bilire in che fase del gioco ci si trova, ossia in quale
set, gioco e punto. Le tre variabili booleane: finepar-
tita, finegioco e finepunto invece permettono di
indicare la fine delle rispettive cadenze di gioco.
Tutto ruota intorno al risultato della funzione
fai_azione(..) che produce un risultato. Il risultato
non necessariamente coincider con lassegnazione
di un punto. In altre parole unazione non altro che
linsieme di due semiazioni svolte dai due antagoni-
sti. Cos al termine di essa pu accadere che si pro-
dotto un punto oppure no. Per convenzione diremo
che se il valore restituito zero allora non si pro-
dotto un punto, quindi continueranno gli scambi tra
i tennisti. Se il valore 1 si intende punto per il
primo giocatore, se il valore 2 allora punto per il
secondo giocatore. Si rimane nel ciclo pi interno
fino a quando lazione non produce un punto. Qui si
aggiorna il punteggio con lomonima funzione.
utile ricordare che lassegnazione di un punto pu
modificare la situazione dei giochi, dei set e della
partita. La variabile azio conteggia tutte le azioni
giocate. Ritorniamo sulla funzione pi importante.
Analizziamo in sintesi cosa deve fare fai_azione:
- Produrre unazione come insieme di due semi
azioni;
- Gestire la produzione di un risultato conclusivo,
che non necessariamente si verifica;
- Registrare nel file di log, chiamato incontroxxx
(figura 1) tutti i particolari dellazione secondo la
sintassi stabilita;
Ecco come implementare:
int fai_azione(int pazio, int pset, int pgioco, int
ppunto)
{ int pris=0;
rec1=fai_semiazio(pazio,ppunto);
rec2=fai_semiazio(pazio,ppunto, rec1);
ris=risultato(rec1,rec2);
registra(rec1,rec2);
return pris;
}
Due funzioni con overloading con due e tre parame-
tri producono sulla base delle caratteristiche dei gio-
catori due semi azioni. La seconda dipende anche
dalla semiazione prodotta dal primo giocatore. I
risultati saranno due record chiamati rec1 e rec2 che
contengono tutte le informazioni necessarie per la
produzione del risultato (funzione risultato) e per la
registrazione nel file di log (funzione registra).
Merita un approfondimento la funzione
fai_semiazio nella fase in cui deve produrre
unattivit con le relative caratteristiche.
Supponiamo che il tennista debba fare un tiro dirit-
to, la funzione deve produrre in output la forza e la
precisione del tiro. Per farlo si pu usare una funzio-
ne random gaussiana, ossia una funzione che abbia
maggiori probabilit di produrre un valore prestabi-
lito che nel caso specifico labilit media, mentre
con meno probabilit valori lontano dalla media, sia
che si tratti di un colpo sbagliato (lontano a sinistra
della media) sia che si tratti di un colpo ottimo (lon-
tano a destra della media).
CONCLUSIONI
Lultimo anello della catena prevede la traduzione
dei file prodotti dallapplicazione in file comprensi-
bili dai client di animazione. A tale proposito biso-
gna studiare la sintassi dei vari client e produrre
delle animazioni adeguate per essere interpretate. Si
devono tradurre in animazione tutte le attivit pre-
viste e parametrizzarle come spiegato. Ad esempio
bisogna sviluppare un animazione di un tennista
che fa la battuta, che sar precisa e forte come indi-
cata nel file di log.
Fabio Grimaldi
Soluzioni M
G
114
/Maggio 2008
Analisi dei log e simulazione dellazione
DISTRIBUZIONE GAUSSIANA
Molti fenomeni della natura
vengono descritti dalla
distribuzione di Gauss. Nel caso
specifico, attraverso la
distribuzione, si possono
rendere pi probabili i valori
intorno alla media, nel grafico
indicati dalla banda blu, e
meno probabili gli altri valori.
110-114:032-035 2-04-2008 12:09 Pagina 114