Paola Magillo, Univestita' di Genova,
Corso di Programmazione II per SMID, a.a. 2006-2007.
Lezione 09 b:
UN'INTERFACCIA GRAFICA PER ISTOGRAMMI - PARTE 2
LETTURA / SCRITTURA DI OGGETTI (OGGETTI PERSISTENTI)
Oggetti persistenti =
Oggetti che sono capaci a scriversi su file e rileggersi da file.
Servono per "ricordare" lo stato di un oggetto tra una esecuzione e l'altra
del programma.
Nel nostro caso, l'istogramma deve essere persistente (perche'
voglio poterlo leggere/scrivere) e anche il rettangolo (perche'
le sotto-parti di un oggetto persistente devono essere persistenti).
Classi persistenti implementano l'interfaccia Serializable, che non ha
metodi, ma occorre dichiarare che una classe implementa Serializable
per poterla scrivere e rileggere attraverso i metodi di
ObjectOutputStream e ObjectInputStream.
Nel nostro caso, le interfacce AnyRectangle e AnyIstogram estendevano
Serializable, dunque se la nostra classe per il rettangolo / istogramma
implementa AnyRectangle / AnyIstogram "per la proprieta' transitiva"
implementa anche Serializable.
ObjectOutputStream
Classe le cui istanze rappresentano file e
sono in grado di scrivere oggetti appartenenti a classi che
implementano l'interfaccia Serializable.
Costruzione di un'istanza di ObjectOutputStream,
per es. sia "pippo.txt" il nome del file, e scrittura di un
oggetto sul file:
FileOutputStream fos = new FileOutputStream("pippo.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(...oggetto da scrivere...);
oos.close();
fos.close();
Nota: tutte queste operazioni possono sollevare eccezioni
di classe IOException.
Quindi il codice di solito viene messo all'interno di un blocco
try-catch.
ObjectInputStream
Classe le cui istanze rappresentano file e
sono in grado di leggere oggetti appartenenti a classi che
implementano l'interfaccia Serializable.
Costruzione di un'istanza di ObjectInputStream,
per es. sia "pippo.txt" il nome del file, e lettura di un oggetto dal file:
FileInputStream fis = new FileInputStream("pippo.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
...oggetto da leggere... = (...classe dell'oggetto...)ois.readObject();
ois.close();
fis.close();
Note:
L'oggetto viene restituito dalla funzione read come oggetto di classe
generica Object, bisogna convertirlo forzatamente alla sua classe effettiva
(fare un cast).
Tutte queste operazioni possono sollevare eccezioni
di classe IOException, l'operazione di conversione forzata
puo' sollevare eccezione di classe ClassCastException.
Materiale da realizzare
Nella lezione 9a abbiamo
costruito la parte estetica dell'interfaccia senza
ancora stabilire comportamenti.
I comportamenti vanno associati:
- alle voci di menu' (come ActionListener)
perche' quando vengono azionati facciano quello che devono.
- al pannello disegna-istogramma (come SelectionListener)
perche' quando viene selezionato
/ deselezionato un rettangolo siano abilitate / disabilitate
le voci di menu' per cambiare lunghezza (in menu' Edit)
e per cambiare colore del rettangolo selezionato (in menu' View).
- Comportamenti "facili": non richiedono una finestra di dialogo
- Comportamenti "difficili": richiedono una finestra di dialogo
Comportamenti "facili"
Nel menu' Edit e' facile il comportamento della voce
per cancellare l'ultimo rettangolo:
-
chiama sull'oggetto istogramma (attributo is della classe
ShowIsto) la funzione removeRectangle: is.removeRectangle()
-
guarda il lavore di ritorno (booleano) di questa funzione,
ricordiamo che ritorna false se istogramma era vuoto e dunque
non e' stato possibile rimuovere un rettangolo
-
se e' false (non ha rimosso) riporta l'errore in linea messaggi,
chiamando msg.setText(...meggaggio opportuno...)
-
se e' true (ha rimosso) azzera la linea messaggi,
oppure scrive un messaggio di operazione compiuta.
Nel menu' View e' facile il comportamento delle voci del
sotto-menu'
per decidere se e come mostrare le lunghezze dei rettangoli.
Tutte e tre queste voci possono avere lo stesso listener che
va a vedere quale delle tre e' stata azionata:
- se e e' l'evento, e.getSource() ritorna il dispositivo
su cui si e' verificato
-
se e.getSource() e' uguale (con == identita' tra oggetti) alla
voce corrispondente a sopra/sotto/nascosti, allora chiama sul pannello
disegna-istogramma (attributo ip)
la funzione setValuePos con argomento corrispondente:
ip.setValuePos(IstoPainter.ABOVE) oppure BELOW oppure HIDDEN
E' facile anche il comportamento relativo alla selezione / disselezione
di un rettangolo nel pannello disegna-istogramma:
-
ottiene il rettangolo selezionato chiamando sul pannello
disegna-istogramma (attributo ip)
la funzione getSelectedRectangle
-
se tale funzione ritorna l'oggetto nullo allora
disabilita le voci di menu' relative a cambiare lunghezza e colore
del rettangolo selezionato
-
altrimenti le abilita
Comportamenti "difficili"
Per questa parte bisogna aver visto le finestre di dialogo
(lezione 12 "Interfacce in Java III").
L'operazione ha bisogno di parametri.
Occorre aprire una finestra di dialogo che chiede i parametri all'utente.
L'operazione viene eseguita quando l'utente chiude la finestra di
dialogo premendo il bottone "ok".
La finestra di dialogo e' un oggetto di classe IstoDialog
(ved. lezione 9a).
- il listener della voce di menu' si limita a costruire la
finestra di dialogo e visualizzarla
- l'operazione vera e propria viene eseguita dal listener
del bottone Ok presente nella finestra di dialogo
Vediamo prima in generale che cosa deve fare
l'action listener della voce in questione, e poi
vediamo come esempi quello gia' fatto per l'operazione "Load" e quello
da farsi per l'operazione "Scale factors".
Schema generale:
-
Dichiara array (aux) di stringhe con tanti elementi quanti
sono i parametri da chiedere, i suoi elementi sono le
diciture esplicative dei parametri.
-
Crea nuova finestra di dialogo (oggetto di classe IstoDialog)
passando al costruttore il frame da cui dipende (attributo
thisFrame), il titolo della finestra (corrisponde al nome
dell'operazione), e l'array aux.
-
Associa action listener al bottone Ok della finestra di dialogo.
(Questo action listener e' quello che compie effettivamente l'operazione:
preleva i valori dei parametri dalla finestra di dialogo e invoca
le funzioni necessarie ad eseguire l'operazione, riporta eventuali errori
in linea messaggi).
-
Rende visibile la finestra di dialogo.
Come lo schema e' realizzato dall'action listener relativo all'operazione
"Load":
-
L'operazione ha bisogno di un solo parametro, il nome del file.
Array di stringhe:
String[] aux = {"Name of file to load:"};
-
Crea la finestra di dialogo e la memorizza nell'attributo
fileDialog della classe ShowIsto:
fileDialog = new IstoDialog(thisFrame, "Load", aux);
-
Associa action listener al bottone Ok della finestra di dialogo:
fileDialog.okButton.addActionListener(new ActionListener().........
L'action listener associato al bottone Ok della finestra di dialogo:
- rende invisibile la finestra di dialogo:
fileDialog.setVisible(false);
- preleva dal suo campo numero 0 il nome del file di input:
String fileName = fileDialog.getText(0);
- [A] apre file:
FileInputStream fis = new FileInputStream(fileName);
ObjectInputStream ois = new ObjectInputStream(fis);
- legge istogramma:
is = (AnyIstogram) ois.readObject();
- chiude file: ois.close(); fis.close();
- scrive in linea messaggi che operazione compiuta:
msg.setText("Istogram read from file " + fileName);
- assegna istogramma nel pannello disegna-istogramma ip:
ip.setIstogram(is); [B]
- Le istruzioni da [A] a [B] sono
in un blocco try-catch perche' la lettura
puo' dare eccezione.
- In caso di eccezione scrive in linea messaggi un
messaggio d'errore.
-
Rende visibile la finestra di dialogo, dopo aver
azzerato la linea messaggi:
msg.setText(""); fileDialog.setVisible(true);
Come lo schema deve essere realizzato dall'action listener
relativo all'operazione "Scale factors":
-
L'operazione ha bisogno di due parametri, i due fattori di scala.
Array di stringhe:
String[] aux = {"x factor", "y factor"};
-
Crea la finestra di dialogo e la memorizza in un scaleDialog che
va prima aggiunto tra gli attributi della classe ShowIsto:
scaleDialog = new IstoDialog(thisFrame, "Scale", aux);
-
E' utile anche preimpostare il contenuto dei due campi di testo
(numero 0 e numero 1) nella finestra di dialogo
con la traduzione in stringa dei valori correnti dei fattori di scala,
che si ottengono interrogando il pannello disegna-istogramma ip:
scaleDialog.setText(0, "" + ip.getScaleX());
scaleDialog.setText(1, "" + ip.getScaleY());
-
Associa action listener al bottone Ok della finestra di dialogo:
scaleDialog.okButton.addActionListener(new
ActionListener()........
L'action listener associato al bottone Ok della finestra di dialogo:
- rende invisibile la finestra di dialogo:
scaleDialog.setVisible(false);
- preleva dal suo campo 0 il fattore di scala per la x
(va tradotto da stringa a numero):
int scx = Integer.parseInt(scaleDialog.getText(0));
- e dal suo campo 1 il fattore di scala per la y:
int scy = Integer.parseInt(scaleDialog.getText(1));
- assegna nel pannello disegna-istogramma ip i fattori dopo
aver controllato siano compresi tra 0 e 100:
if ((scx>0)&&(scx<100)) ip.setScaleX(scx);
if ((scy>0)&&(scy<100)) ip.setScaleY(scy);
- Non occorre mettere le istruzioni in un blocco
try-catch perche' qui non sono previste eccezioni
(potrei eventualmente sollevare eccezione se i fattori di
scala non sono corretti).
-
Rende visibile la finestra di dialogo, dopo aver
azzerato la linea messaggi:
msg.setText(""); fileDialog.setVisible(true);
Similmente vanno realizzati i comportamenti di tutte le altre operazioni
che necessitano di parametri nei menu' Edit e View.
Voci del menu' "Function"
Le voci di questo menu' permettono
di calcolare caratteristiche dell'istogramma (media, varianza...).
Gli action listener relativi calcolano la grandezza e la scrivono
sulla linea messaggi.
Esempio per la media:
-
inizializza a zero una variabile double m
che alla fine conterra' la media
-
cicla su tutti i rettangoli presenti nell'istogramma,
per ciascuno prende la lunghezza e la sommo ad m.
Note:
-il numero di rettangoli si ottiene con is.getRectangleNum()
-l'i-esimo rettangolo si ottiene con is.getRectangle(i)
-nel sommare la lunghezza del rettangolo (int) ad
m (double) devo bisogna una conversione esplicita da
int a double.
-
divide m per il numero di rettangoli
-
mostra m (preceduta da opportuna stringa esplicativa)
in linea messaggi