Paola Magillo, Univestita' di Genova, Corso di Interfacce Utente per Informatica, a.a. 2007-2008.

LABORATORIO - INTERFACCE GRAFICHE IN JAVA

Obiettivo

L'obiettivo e' realizzare in Java un'interfaccia che visualizza un grafo piano.

Un grafo piano e' descritto da:

Nota: il sistema di coordinate nel quale e' espresso il grafo e' un sistema a coordinate reali che non ha nessuna relazione coi pixel dello schermo.

L'interfaccia grafica deve fornire le seguenti funzionalita':

Piano di lavoro

Procederemo per gradi:

  1. prima facciamo la progettazione generale dell'interfaccia
  2. quindi la lettura del grafo
  3. e il disegno
  4. poi mettiamo le traslazioni
  5. infine lo zoom

Non occorre consegnare nulla, questo e' solo un esercizio che vi sara' utile per il progetto finale del corso.

SUGGERIMENTO:
E' molto utile programmare tenendo aperta a fianco una finestra sul sito con il manuale on-line delle classi Java: http://java.sun.com/j2se/1.4.2/docs/api/index.html

1) Progettazione generale

L'interfaccia dovra' contenere

Come dispositivi per aprire e chiudere un grafo si possono usare, a scelta:

Bisogna anche disabilitare le operazioni non valide nello stato attuale: "open" se c'e' gia' un grafo caricato, "close" se non c'e' nessun grafo.

A scelta, per controllare lo zoom si possono usare:

Effettuare la scelta, disegnare lo schema dell'interfaccia (che mostra dove sono collocati i vari dispositivi).
Determinare quale gerarchia di contenimento e quali layout manager sono necessari per ottenere la configurazione voluta.

Scrivere il codice Java che ottiene l'interfaccia, per adesso nessun dispositivo reagira' ancora all'interazione con l'utente.

Il pannello in cui si disegna il grafo per adesso sara' una sotto-classe di Panel o di JPanel, dove e' ridefinita getPreferredSize per ottenere dimensioni di default non nulle (come nell'esempio ExDraw1 delle dispense). Non viene disegnato ancora nulla al suo interno.

2) Leggere un grafo

Chiedere all'utente il nome del file usando il dispositivo predefinito Java per chiedere un file (FileDialog o JFileChooser, vedere dispense e manuale in linea di Java).

E' fornita la classe MyReader per leggere da file, tale classe ha le seguenti funzioni:

Tutte le funzioni di lettura possono sollevare eccezione (es. se il file non contiene un elemento con le caratteristiche desiderate).

Il formato del file che contiene il grafo e' il seguente:
elementotipo significato
x0 y0due doublecoordinate min del dominio
deltaX deltaYdue doubleampiezze del dominio
nv nadue interinumero di vertici e archi
x0 y0
x1 y1
...
tante coppie di double quanti i vertici coordinate dei vertici
a0 b0
a1 b1
...
tante coppie di interi quanti gli archi,
un valore k identifica il k-esimo vertice
nella lista di vertici precedente, dove
il primo vertice e' identificato dal numero 0
primo e secondo estremo di ogni arco

Esempi di grafi (il disegno esplicativo inquadra il solo dominio e ha l'asse y girato verso l'alto):

Come struttura dati per il grafo si consiglia di usare:

Schema di frammento di programma che legge un grafo, gestendo anche le eccezioni (le parti tra *** dovranno essere sostituite dai nomi delle vostre variabili):

try
{
  MyReader rd = new MyReader(***nome del file***);
  ***x0*** = rd.readDouble();
  ***y0*** = rd.readDouble();
  ***delta x*** = rd.readDouble();
  ***delta y*** = rd.readDouble();
  ***numero vertici*** = rd.readInteger();
  ***numero archi*** = rd.readInteger();
  int i;
  for (i=0; i<***numero vertici***; i++)
  {
    ***x vertice i-esimo*** = rd.readDouble();
    ***y vertice i-esimo*** = rd.readDouble();
  }
  for (i=0; i<***numero archi***; i++)
  {
    ***primo estremo arco i-esimo*** = rd.readInteger(); 
    ***secondo estremo arco i-esimo*** = rd.readInteger();
  } 
}
catch (Exception excp)
{
  System.err.println("Error in reading: " + excp.getMessage());
}  

3) Disegnare il grafo

L'area di disegno sara' una sotto-classe di pannello dove la funzione paint (in AWT) o paintComponent (in Swing) disegna il grafo.
Il grafo puo' essere memorizzato come attributo di questa classe.

Esempio schematico (dove i nomi MioGrafo e MioPannello indicano la classe grafo e la classe pannello di disegno):

class MioPannello extends Panel
{
  MioGrafo grafo;
  
  public MioPannello(MioGrafo g) { grafo = g; }
  
  public Dimension getPreferredSize()
  {  return new Dimension(500,500);  }
  
  public void paint(Graphics g) { ...... }   
}

Le coordinate in cui e' dato il grafo (user space o coordinate logiche) non hanno relazione con le coordinate della finestra (device space o coordinate fisiche).
Occorre quindi una traduzione.
La traduzione puo' essere fatta in due modi:

Nota: e' preferibile mantenere in device space le stesse proporzioni fra x e y che erano in user space. A questo scopo eseguire la stessa scalatura su x e y, usando il fattore piu' piccolo.

4) Traslazione col mouse

Sul pannello deve essere attivo un mouse motion listener che implementi la funzione di reazione a movimento del mouse con bottone premuto (mouseDragged) traslando il disegno del grafo nel pannello.
L'idea e' che io aggangio la scena e la trascino nella direzione del mouse.
A tale scopo bisogna:

La traslazione si andra' a comporre con la traduzione precedentemente fatta delle coordinate.
E' piu' facile prendere il valore delle coordinate gia' tradotto in pixel e sommare a questo uno spostamento in pixel pari al numero di pixel percorsi dal mouse.

5) Zoom

Se si usa uno slider, i valori ammessi per il fattore di scala possono essere per esempio da 0 a 10, per default 1.

Se si usano bottoni, i due valori devono essere uno l'inverso dell'altro, per es. 0.5 e 2 oppure 0.8 e 1.25.

Applicare lo zoom alle coordinate proprie del grafo (user space) prima di trasportarle nella finestra (device space).
Bisogna nell'ordine:

Attenzione: se si usano le trasformazioni di Java i passaggi vanno specificati in ordine inverso.