LINGUAGGI & TRADUTTORI “Quinta relazione” Albertazzi Luca, matricola 2134230 DESCRIZIONE DEL PROBLEMA Far comparire una finestra centrata nello schermo di dimensioni fisse, ad esempio 400 x 400. Gestire la chiusura della finestra. Il pannello della finestra è diviso in due zone: 1_ Zona centrale: disegnare una crocetta (10x10) in mezzo. 2_ Zona inferiore: un pulsante, centrato orizzontalmente con la scritta “Quit”, quando lo si preme la finestra deve essere chiusa. Nella parte inferiore aggiungere quattro pulsanti (“Up”,”Down”,”Left”,”Right”) tutti allineati a sinistra, ora il pulsante “Quit” allineato a destra. Fissata una certa dimensione di griglia D (es.: 5 pixel), la pressione di “Up” fa salire la crocetta di D pixel, la pressione di “Down” fa scendere la crocetta di D pixel, etc… Evitare di far uscire la crocetta dai margini. Permettere la variazione di D. ANALISI DEL PROBLEMA Il problema in questo caso non è di tipo concettuale, occorre imparare ad utilizzare gli strumenti messi a disposizione dal linguaggio che deciderò di utilizzare: frame, container, pannelli, bottoni, campi di testo, disegni. La complessità risiede nel fare dialogare tutti questi componenti, la cosa potrà risultare più o meno difficile a seconda del tipo di implementazione deciderò di utilizzare. PROGETTO Le classi create sono solamente tre: Ese5.java Panels.java Terminator.java La classe La classe Ese5.java è il componente software, non ha metodi. Panels.java è il cuore del programma, possiede i seguenti metodi: Method Summary void actionPerformed(java.awt.event.ActionEvent e) void paintComponent(java.awt.Graphics g) void setGrid() Setta la dimensione della griglia (in pixel) void setPanel5Down() Sposta la crocetta in basso di n pixel. n = numero di griglia void setPanel5Left() Sposta la crocetta a sinistra di n pixel. n = numero di griglia void setPanel5Right() Sposta la crocetta a destra di n pixel. n = numero di griglia void setPanel5Up() Sposta la crocetta in alto di n pixel. n = numero di griglia Anche la classe Terminator.java non possiede metodi. IMPLEMENTAZIONE file Ese5.java Questo è il componente software. Come primo passo occorre importare due package grafici: java.awt.* e javax.swing.* sono rispettivamente il vecchio package grafico (nato con Java 1.0) e il nuovo package grafico (conosciuto con il nome Swing). La prima cosa che ho fatto nel main è stato creare il JFrame che è la struttura principale sul quale verranno visualizzati tutti gli oggetti successivi. Ogni frame possiede un container richiamabile con il comando f.getContentPane(), tutti i nuovi oggetti verranno aggiunti al container. Successivamente ho creato un oggetto della classe Panels e lo ho aggiunto al container. Le tre righe di comando successive: Dimension d = new Dimension(); Toolkit tk = Toolkit.getDefaultToolkit(); d = tk.getScreenSize(); sono servite ad ottenere la risoluzione dello schermo in modo da potere successivamente posizionare il frame al centro del monitor. L’ultimo passo è quello di mostrare a video il frame contenente tutti gli oggetti sopra descritti, con il comando f.show(). Ho aggiunto anche al frame un WindowListener per gestire la chiusura della finestra (la X in alto a destra), l’ascoltatore è un nuovo oggetto della classe Terminator(), il codice utilizzato è il seguente: /** * * Class Ese5 * */ import java.awt.*; import javax.swing.*; public class Ese5 { public static void main(String[] v){ JFrame f = new JFrame("Esercitazione 5"); Container c = f.getContentPane(); Panels panel = new Panels(); c.add(panel); Dimension d = new Dimension(); Toolkit tk = Toolkit.getDefaultToolkit(); d = tk.getScreenSize(); System.out.println("\n" +d +"\n"); f.setBounds(d.width/3, d.height/4, 400, 400); f.addWindowListener(new Terminator()); f.show(); } } file Terminator.java Questa è la classe che gestisce la finestra di Windows. L’unico aspetto rilevante è la ridefinizione formale del metodo windowClosing, che permette di uscire dal programma ciccando sulla croce in alto a destra nella finestra. Poiché questa classe è un listener occorre importare la classe java.awt.event.*, ecco il codice utilizzato: /** * * Class Terminator * */ import java.awt.event.*; public class Terminator implements WindowListener { public void windowClosed(WindowEvent e){} public void windowClosing(WindowEvent e){ System.exit(0);} public void windowOpened(WindowEvent e){} public void windowIconified(WindowEvent e){} public void windowDeiconified(WindowEvent e){} public void windowActivated(WindowEvent e){} public void windowDeactivated(WindowEvent e){} } file Panels.java Dopo aver importato le classi per la gestione della grafica, delle Swing e degli eventi si procede con la definizione della classe Panels: questa classe estende JPanel acquisendone tutti i metodi e implementa ActionListener in quanto deve fare da ascoltatore per gli eventi legati alla pressione dei bottoni e della pressione del tasto Invio legato al cambiamento della Text Field. Il costruttore è parecchio grande, dopo aver creato un nuovo Layout Manager (BorderLayout) vengono creati tre pannelli: il primo viene aggiunto nella zona inferiore del pannello Panels stesso creato nel main del componente software, il secondo pannello e il terzo vengono aggiunti al primo pannello, rispettivamente nella zona ovest ed est. Dopo aver creato cinque bottoni vengono aggiunti nel secondo pannello. Dopo aver creato un ulteriore pannello, gli vengono aggiunti una JLabel e una JTextField con valore “5” di default. Infine vengono registrati gli ActionListener dei bottoni e della Text Field: l’ascoltatore è uno solo ed è l’oggetto derivato dalla classe Panels stessa. Il metodo paintComponent serve per disegnare la crocetta su un pannello aggiuntivo tramite due drawLine. I quattro interi passati a ciascuna drawLine sono le coordinate degli estremi dei due segmenti. I quattro metodi successivi sono strutturati in maniera simile ed attuano il movimento della crocetta sul pannello nelle quattro direzioni. Ho inserito anche dei controlli (tramite delle strutture if) affinché la crocetta non esca dal pannello. Ciascuno dei quattro metodi funziona in maniera semplice: ricalcola le quattro coordinate dei punti e dice al pannello di ridisegnarsi (repaint) con i nuovi parametri. Il metodo successivo, setGrid, modifica il valore della variabile intera D corrispondente al valore della griglia, cioè al numero di pixel percorsi in una delle quattro direzioni ad ogni pressione di un bottone. Poiché il risulato della getText sulla JtextField restituisce una string inconvertibile in un valore intero, double o float, occorre fare un parsing sul testo: D = Integer.parseInt(txt1.getText()), il risultato di tale operazione è un numero intero che viene memorizzato nella variabile D. L’ultimo metodo è ActionPerformed: poiché questa classe funge da ascolatore degli eventi deve per forza implementare il metodo suddetto. Se il pulsante premuto è Quit viene forzata l’uscita dal programma, a seconda invece che il pulsante premuto sia Up, Down, Left, Right viene invocato il metodo corrispondente per lo spostamento della corocetta. Infine, per gestire il cambiamento della griglia c’è anche l’invocazione del metodo setGrid(). A seguire il codice utilizzato: /** * * Class Panels * */ import java.awt.*; import javax.swing.*; import java.awt.event.*; public class Panels extends JPanel implements ActionListener{ public Panels() { setLayout(new BorderLayout()); panel1 = new Panel(); add(panel1, BorderLayout.SOUTH); panel2 = new Panel(); panel1.add(panel2, BorderLayout.WEST); panel3 = new Panel(); panel1.add(panel3, BorderLayout.EAST); b1 = new JButton("U"); b2 = new JButton("D"); b3 = new JButton("L"); b4 = new JButton("R"); b = new JButton("QUIT"); panel2.add(b1); panel2.add(b2); panel2.add(b3); panel2.add(b4); panel3.add(b); panel4 = new Panel(); add(panel4, BorderLayout.NORTH); JLabel lb1 = new JLabel("GRIGLIA: "); panel4.add(lb1); txt1 = new JTextField("5", 3); panel4.add(txt1); b.addActionListener(this); b1.addActionListener(this); b2.addActionListener(this); b3.addActionListener(this); b4.addActionListener(this); txt1.addActionListener(this); } public void paintComponent(Graphics g){ panel5 = new Panel(); add(panel5, BorderLayout.CENTER); super.paintComponent(g); g.drawLine(x11, y11, x12, y12); g.drawLine(x21, y21, x22, y22); } /** * * Sposta la crocetta in alto di n pixel. n = numero di griglia * */ public void setPanel5Up(){ x11 = x11; x12 = x12; x21 = x21; x22 = x22; y11 -= D; if(y11<37){ y11=37;} y12 -= D; if(y12<37){ y12=37;} y21 -= D; if(y21<32){ y21=32;} y22 -= D; if(y22<42){ y22=42;} repaint(); } /** * * Sposta la crocetta in basso di n pixel. n = numero di griglia * */ public void setPanel5Down(){ x11 = x11; x12 = x12; x21 = x21; x22 = x22; y11 += D; if(y11>320){ y11=320;} y12 += D; if(y12>320){ y12=320;} y21 += D; if(y21>315){ y21=315;} y22 += D; if(y22>325){ y22=325;} repaint(); } /** * * Sposta la crocetta a destra di n pixel. n = numero di griglia * */ public void setPanel5Right(){ y11 = y11; y12 = y12; y21 = y21; y22 = y22; x11 = x11+D; if(x11>380){ x11=380;} x12 = x12+D; if(x12>390){ x12=390;} x21 = x21+D; if(x21>385){ x21=385;} x22 = x22+D; if(x22>385){ x22=385;} repaint(); } /** * * Sposta la crocetta a sinistra di n pixel. n = numero di griglia * */ public void setPanel5Left(){ y11 = y11; y12 = y12; y21 = y21; y22 = y22; x11 = x11-D; if(x11<0){ x11=0;} x12 = x12-D; if(x12<10){ x12=10;} x21 = x21-D; if(x21<5){ x21=5;} x22 = x22-D; if(x22<5){ x22=5;} repaint(); } /** * * Setta la dimensione della griglia (in pixel) * */ public void setGrid(){ D = Integer.parseInt(txt1.getText()); } public void actionPerformed(ActionEvent e){ Object pulsantePremuto = e.getSource(); if (pulsantePremuto == b){ System.out.println("\nQUITTING..."); try { Thread.currentThread().sleep(3000); } catch (Exception ex){ } System.exit(0);} if (pulsantePremuto == b1){ setPanel5Up(); System.out.println("UP " +D +" pixel");} if (pulsantePremuto == b2){ setPanel5Down(); System.out.println("DOWN " +D +" pixel");} if (pulsantePremuto == b4){ setPanel5Right(); System.out.println("RIGHT " +D +" pixel");} if (pulsantePremuto == b3){ setPanel5Left(); System.out.println("LEFT " +D +" pixel");} setGrid(); } //data int x11 int y11 int x12 int y12 int x21 int y21 int x22 int y22 int D = JButton JButton JButton JButton = 190; = 180; = x11+10; = y11; = x11+5; = y11-5; = x21; = y21+10; 5; b1; b2; b3; b4; JButton b; JTextField txt1; Panel panel1; Panel panel2; Panel panel3; Panel panel4; Panel panel5; } CASI D’USO Riporto alcune schermate del programma eseguito: CONCETTI E TECNICHE ACQUISITE Il più importante concetto acquisito è sicuramente stato quello di evento: la grandissima differenza tra i programmi fino ad ora realizzati e quest’ultimo risiede proprio nel concetto di programmazione ad eventi. Analizzando uno qualsiasi dei programmi realizzarti per le esercitazioni precedenti si scopre che la struttura è sempre del tipo: inserimento dati elaborazione dei dati attraverso un algoritmo visualizzazione dei dati manipolati. Con questo tipo di approccio è impossibile realizzare applicazioni moderne come quelle che siamo abituati ad utilizzare ogni giorno (es: Word, Excel, …) in quanto questi programmi non seguono un flusso algoritmico predefinito: deve essere l’utente a decidere cosa fare, non il programma. Grazie a questa esercitazione ho cominciato ad esplorare la programmazione ad eventi: ad ogni oggetto con il quale l’utente può interagire (bottone, text field, …) possono essere associati uno o più eventi. Il concetto base risiede nell’associare ad un oggetto un ascoltatore (una classe che implementi il metodo ActionPerformed), quando questo oggetto viene sollecitato (es: bottone premuto), l’ActionPeformed mette in esecuzione un pezzo di codice in risposta all’evento. Poiché questa esercitazione si basava sull’uso della grafica in Java ho anche imparato ad utilizzare le Swing: ho imparato che alla base di ogni finestra c’è il frame. Non si possono applicare direttamente gli oggetti sul frame ma bisogna richiamare il container associato ad ogni frame e applicare gli oggetti ad esso. Le difficoltà che ho incontrato nella scrittura del programma non sono state di tipo concettuale ma di tipo implementativo. Ho capito che in questo caso era necessario creare meno classi possibile cercando di mantenere tutti gli oggetti che interagivano tra di loro (pannelli, bottoni, textfields) nella stessa classe. L’approccio che avevo deciso di utilizzare all’inizio (data la mia poca esperienza) era quello di creare una classe per ogni pannello, in questo modo non riuscivo a fare in modo che gli oggetti si vedessero tra di loro. La soluzione finale è stata quella di creare una classe Panels che contenesse tutti i pannelli di cui avevo bisogno e che facesse anche da ActionListener. Infine ho voluto convertire il programma in un’applet visibile all’interno di un browser, di come funzionano le applet parlerò nella prossima relazione. Comunque se vuole vedere l’applet di questa esercitazione l’ho caricata nel mio sito internet: http://digilander.iol.it/thunderbird79/