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:

  1. alle voci di menu' (come ActionListener) perche' quando vengono azionati facciano quello che devono.
  2. 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"

Nel menu' Edit e' facile il comportamento della voce per cancellare l'ultimo rettangolo:

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:

E' facile anche il comportamento relativo alla selezione / disselezione di un rettangolo nel pannello disegna-istogramma:

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).

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:

  1. Dichiara array (aux) di stringhe con tanti elementi quanti sono i parametri da chiedere, i suoi elementi sono le diciture esplicative dei parametri.
  2. 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.
  3. 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).
  4. Rende visibile la finestra di dialogo.

Come lo schema e' realizzato dall'action listener relativo all'operazione "Load":

  1. L'operazione ha bisogno di un solo parametro, il nome del file. Array di stringhe:
    String[] aux = {"Name of file to load:"};
  2. Crea la finestra di dialogo e la memorizza nell'attributo fileDialog della classe ShowIsto:
    fileDialog = new IstoDialog(thisFrame, "Load", aux);
  3. 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:
  4. 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":

  1. L'operazione ha bisogno di due parametri, i due fattori di scala. Array di stringhe:
    String[] aux = {"x factor", "y factor"};
  2. 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);
  3. 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());
  4. 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:
  5. 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: