Quinta esercitazione di Linguaggi e Traduttori Primo Problema Descrizione del problema: Far comparire una finestra centrata nel centro dello schermo e divisa in due parti, una centrale in cui disegnare una crocetta di dimensioni 10x10 pixiel posta al centro e la parte sud con un pulsante “Quit” centrato orizzontalmente. L’applicazione si deve chiudere se si preme il pulsante Quit o tramite il bottone di chiusura della finestra. Analisi: La finestra deve essere di dimensioni fisse, date al momento della costruzione della finestra stessa, deve essere formata da due pannelli uno in cui disegnare la crocetta, che deve essere al centro del pannello, l’altro in cui posizionare il pulsante con la scritta Quit: quando viene premuto il pulsante Quit deve essere il pannello stesso ad accorgersene e a chiudere la finestra e l’applicazione. Progetto: Per la realizzazione della finestra usiamo la classe JFrame che fa parte dei package java.awt e javax.swing che sono i package grafici di java. Ho creato una mia finestra MyFrame che eredita da JFrame e nel costruttore richiama il costruttore JFrame(string s), che crea una finestra vuota di titolo s, e poi setta le dimensioni della finestra con il metodo setBounds(int x, int y, int width, int height) dove x e y sono le coordinate dello spigolo in alto a sinistra della finestra, width è la larghezza e height è l’altezza. Per centrare la finestra nello schermo si usa il metodo getScreenSize() da invocare su Toolkit ma, dato che non è un metodo statico e non può essere invocato su di una classe, bisogna prima creare un oggetto di tipo Toolkit, cosa che si fa attraverso l’invocazione getDefaultToolkit().GetScreenSize ritorna un oggetto di tipo Dimension formato da una coppia d’interi in duplice precisione che rappresentano la larghezza e la lunghezza dello schermo in pixiel. Tramite i metodi getWidth() e getHeight() si accede rispettivamente alla larghezza e alla lunghezza. Per aggiungere un qualsiasi componente alla finestra bisogna prima recuperare il contenitore al suo interno tramite getContentPane() e poi aggiungere a questo i componenti con il metodo add. Mi sono poi creato un pannello, da inserire nel contenitore della finestra, MyPanel in cui mettere i due pannelli contenenti la crocetta e il pulsante. MyPanel eredita da JPanel e implementa le interfacce WindowListener e ActionListener che servono rispettivamente per renderlo “ascoltatore” delle azioni del pulsante Quit e dei pulsanti della finestra. MyPanel in fase di costruzione richiama il costruttore di JPanel() e crea al suo interno due pannelli, uno di tipo JPanel in cui sarà inserito il pulsante, e l’altro di tipo MyPanel1 in cui disegnare la crocetta. Per dividere il pannello in due parti; Centro e Sud, si usa il BorderLayout che viene settato come Layout con il metodo setLayout(new BorderLayout) e poi quando i due pannelli vengono aggiunti a quello principale tramite il metodo add si specifica la zona dove devono essere posizionati, ad esempio add(p, BorderLayout.CENTER) dove p è un pannello o un qualsiasi altro componente. Poi viene creato il pulsante Quit che è un JButton tramite il costruttore JButton(string s) dove s è la stringa che si vedrà stampata sul pulsante. Per fare in modo che il pannello diventi l’ascoltatore del pulsante si ricorre al metodo addActionListener(Component c) da invocare sul pulsante e che rende il componente c l’ascoltatore delle azioni del pulsante. Si passa quindi ad “aggiungere il pulsante al pannello situato nella zona sud e poi si aggiungono i due pannelli a quello principale. La classe MyPanel1 eredita da JPanel e al suo interno definisce i metodi per disegnare la crocetta, infatti, quando viene creato un nuovo oggetto di tipo MyPanel1, questo invoca automaticamente il metodo paintComponent che specifica gli elementi da disegnare all’interno del pannello ad esempio il colore del background, quello delle linee che disegna tramite il metodo drawLine(int x, int y, int x1, int y1) che disegna una linea dal punto di coordinate(x, y) a quello di coordinate(x1, y1).Per centrare la crocetta nel pannello uso metodo screenSize() da invocare sul pannello e ritorna un oggetto di tipo Dimension contenente le dimensioni del pannello stesso. Come ho già detto la classe MyPanel implementa le interfacce WindowListener e ActionListener e questo fa sì che il pannello diventi l’ascoltatore delle azioni del pulsante e della finestra, infatti, quando il pulsante viene premuto, si genera un evento di tipo ActionEvent che viene inviato all’ascoltatore che lo gestisce in questo caso chiudendo l’applicazione. Allo stesso modo quando viene premuto il pulsante di chiusura della finestra si genera un WindowEvent e sta sempre all’ascoltatore gestirlo in modo opportuno. Implementazione: Implementazione della finestra: import java.awt.*; import javax.swing.*; public class MyFrame extends JFrame{ private Dimension d; private int dim=500; public MyFrame(){ super("Esercitazione"); d=new Dimension(Toolkit.getDefaultToolkit().getScreenSize()); setBounds((int)(d.getWidth()/2-dim/2),(int)(d.getHeight()/2-dim/2),dim,dim); } } Implementazione del pannello MyPanel: import java.awt.*; import javax.swing.*; import java.awt.event.*; public class MyPanel extends JPanel implements ActionListener, WindowListener { public MyPanel(){ super(); setLayout(new BorderLayout()); JPanel p=new JPanel(); JButton b=new JButton("Quit"); b.addActionListener(this); p.add(b); MyPanel1 p1=new MyPanel1(); add(p1,BorderLayout.CENTER); add(p,BorderLayout.SOUTH); } public void actionPerformed(ActionEvent e){ System.exit(0); } public void windowClosing(WindowEvent e) { System.exit(0); } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e){} } Implementazione del pannello MyPanel1: import java.awt.*; import javax.swing.*; public class MyPanel1 extends JPanel{ public void paintComponent(Graphics g){ Dimension d=new Dimension(getSize()); super.paintComponent(g); g.setColor(Color.black); g.drawLine((int)(d.getWidth()/2),(int)(d.getHeight()/2),(int)(d.getWidth()/2+5),(int)(d.getHeight()/2)); g.drawLine((int)(d.getWidth()/2),(int)(d.getHeight()/2),(int)(d.getWidth()/2),(int)(d.getHeight()/2+5)); } } Casi d’uso: import java.awt.*; import javax.swing.*; public class Esercizio { public static void main(String[] v){ MyFrame f = new MyFrame(); Container c=f.getContentPane(); / recupero il contenitore all’interno della finestra / MyPanel p=new MyPanel(); c.add(p); f.show(); / per mostrare la finestra sullo schermo / } } Secondo Problema Descrizione del problema: Si vuole aggiungere alla finestra la possibilità di muovere la crocetta di un numero di pixiel uguale al valore riportato in un area di testo situata nella parte nord della finestra. La crocetta deve muoversi in conseguenza della pressione dei tasti, U(up) D(down) L(left) R(right), situati nella zona sud. Analisi: Si riutilizza la finestra del primo problema ma a questa vanno aggiunti i quattro pulsanti nella zona sud, un’area di testo nella zona nord e una scritta vicino all’area di testo. Premendo uno dei quattro tasti la crocetta deve muoversi nella direzione indicata dal tasto di tanti pixiel quanto è il valore nell’area di testo in quel momento, valore che può essere cambiato in ogni istante dall’utente che, premendo il tasto enter, rende effettivo il cambiamento. Progetto: Il Frame usato è lo stesso dell’esercizio precedente gli unici componenti che cambiano sono il MyPanel e il MyPanel1. Il MyPanel questa volta è diviso in tre parti una centrale, una sud e una nord. In quella nord ci sono un JLabel e un JTextField: il primo consiste in un componente non reattivo in grado di visualizzare una scritta o un immagine, il secondo invece è un componente attivo formato da una casella in cui può essere immessa una stringa e che genera un evento quando viene schiacciato il tasto enter. Nel costruttore del MyPanel vengono quindi creati tre pannelli, che verranno posizionati tramite il BorderLayout, uno che contiene il JLabel e il JTextField di tipo JPanel, un altro, contenente la crocetta, di tipo MyPanel1 e l’ultimo, contenente i pulsanti, di tipo MyPanel2.Il MyPanel1 è quasi uguale a quello usato nell’esercizio precedente ma ha in più i metodi pubblici incx(int n) e incy(int n) che spostano la posizione della crocetta rispettivamente in orizzontale e in verticale di n pixiel, controllando che non esca dal pannello, e poi invocano il metodo reapaint() sul pannello in modo che si ridisegni ogni volta. Gli spostamenti vengono ogni volta memorizzati in due variabili private della classe in modo che la volta successiva la crocetta si trovi nella posizione in cui era stata lasciata prima. Il pannello sito a nord della finestra contenente il JLabel e il JTextField viene definito all’interno del MyPanel e invoca il costruttore JLabel(string s), che crea un JLabel che visualizza la stringa s, e il costruttore JTextField(string s, int c) che crea una casella di testo di c spazi in cui è scritta la stringa s. All’interno della casella di testo può essere inserita una qualsiasi stringa e quando viene schiacciato il tasto enter si genera un ActionEvent che viene mandato all’ascoltatore. All’interno del MyPanel ho creato una classe privata MyPanel2 che deriva sempre dal JPanel e al suo interno contiene i quattro pulsanti direzionali U, D, L, R e il pulsante Quit. Il MyPanel2 in fase di costruzione ha il compito di creare questi cinque pulsanti e assegnare i loro ascoltatori. Il MyPanel2 inoltre implementa l’interfaccia ActionListener, al posto del MyPanel, infatti è l’ascoltatore di tutti e cinque i pulsanti e del JTextField: quando arriva un ActionEvent il MyPanel2 gli chiede la sua sorgente tramite il metodo getSource(), se è il pulsante Quit invoca il metodo exit(0) sulla classe System, se è il JTextField recupera il valore scritto nella casella di testo tramite il metodo getText() e prova a convertirlo in un intero invocando valueOf() sulla classe Wrapper Integer e di seguito intValue(). In questo modo cerca di convertire la stringa presente nella casella di testo in un intero e, se ci riesce la salva in una variabile privata i della classe altrimenti solleva una NumberFormatException che viene catturata riportando il valore della casella di testo e della variabile i a uno. Nel caso in cui il pulsante premuto è uno dei quattro tasti direzionali viene invocato il metodo incx(int n) o incy(int n), dove n è il valore che è memorizzato nella variabile i, sul pannello MyPanel1. Implementazione: Il MyFrame è identico a quello dell’esercizio precedente. Implementazione di MyPanel: import java.awt.*; import javax.swing.*; import java.awt.event.*; public class MyPanel extends JPanel implements WindowListener { private JButton up,down,left,right,quit; private MyPanel1 p1; private JTextField f; private int i=1; public MyPanel(){ super(); setLayout(new BorderLayout()); Panel p3=new Panel(); JLabel l=new JLabel("Griglia:"); f=new JTextField("1",5); p3.add(l); p3.add(f); MyPanel2 p=new MyPanel2(); f.addActionListener(p); p1=new MyPanel1(); add(p1,BorderLayout.CENTER); add(p,BorderLayout.SOUTH); add(p3,BorderLayout.NORTH); } public void windowClosing(WindowEvent e){ System.exit(0); } public void windowActivated(WindowEvent e){} public void windowClosed(WindowEvent e){} public void windowDeactivated(WindowEvent e){} public void windowDeiconified(WindowEvent e){} public void windowIconified(WindowEvent e){} public void windowOpened(WindowEvent e){} private class MyPanel2 extends JPanel implements ActionListener{ public MyPanel2(){ super(); quit=new JButton("Quit"); up=new JButton("U"); down=new JButton("D"); left=new JButton("L"); right=new JButton("R"); quit.addActionListener(this); up.addActionListener(this); down.addActionListener(this); left.addActionListener(this); right.addActionListener(this); add(up); add(down); add(left); add(right); add(quit); } public void actionPerformed(ActionEvent e){ Object pulsantePremuto=e.getSource(); if(pulsantePremuto==up)(p1.incy(-i)); if(pulsantePremuto==down)(p1.incy(+i)); if(pulsantePremuto==left)(p1.incx(-i)); if(pulsantePremuto==right)(p1.incx(+i)); if(pulsantePremuto==quit)(System.exit(0)); if(pulsantePremuto==f){ try{ (i=Integer.valueOf(f.getText()).intValue()); } catch(NumberFormatException e1){ i=1; f.setText("1"); } } } } } Implementazione di MyPanel1: import java.awt.event.*; import java.awt.*; import javax.swing.*; public class MyPanel1 extends JPanel{ private int x=0; private int y=0; private int a,b; public void paintComponent(Graphics g){ setBackground(Color.white); Dimension d=new Dimension(getSize()); a=(int)(d.getWidth()/2); b=(int)(d.getHeight()/2); super.paintComponent(g); g.setColor(Color.black); g.drawLine((a-5+x),(b+y),(a+x+5),(b+y)); g.drawLine((a+x),(b+y-5),(a+x),(b+y+5)); } public void incx(int n){ x+=n; if(x>a)(x=a); if(x<-a)(x=-a); repaint(); } public void incy(int n){ y+=n; if(y>b)(y=b); if(y<-b)(y=-b); repaint(); } } Casi d’uso: Anche il main rimane identico a quello dell’esercizio precedente: import java.awt.*; import javax.swing.*; public class Esercizio { public static void main(String[] v){ MyFrame f = new MyFrame(); Container c=f.getContentPane(); MyPanel p=new MyPanel(); c.add(p); f.show(); } } Terzo Problema Descrizione del problema: Si vuole aggiungere alla finestra la possibilità di spostare la crocetta dove si clicka col mouse e, premendo un tasto, la possibilità di disegnare delle linee. Analisi: Partendo dalla finestra precedente bisogna rendere la finestra sensibile al click del mouse e fare in modo che la crocetta si sposti a seconda di dove si clicka. Inoltre bisogna aggiungere un pulsante nella zona nord con la scritta "Start" che se viene premuto abilita il disegno di linee e la scritta viene cambiata in "Stop". Le linee vengono disegnate dalla posizione corrente della crocetta al punto in cui viene spostata dopo che si è clickato col mouse. Progetto: Anche questa volta gli unici componenti che cambiano sono il MyPanel e il MyPanel1 e anche il MyPanel è quasi identico a quello precedente. Le differenze tra il MyPanel precedente e quello attuale sono: in quello attuale in fase di costruzione viene aggiunto un pulsante start con la scritta “Start” al pannello situato nella zona nord e vi è un booleano privato st che dice se start è premuto o no. L’ascoltatore di start è il MyPanel2 che, quando riceve l’ActionEvent da parte di start va a controllare il booleano st e a seconda di st cambia la scritta “Start” in “Stop” o viceversa, modifica il valore di st e invoca il metodo setStart(boolean b) sul MyPanel1 passandogli come parametro st. Nel MyPanel1 invece le differenze sono maggiori infatti per disegnare delle linee nel pannello ho bisogno di un array di oggetti di tipo Line che aumenti di uno ogni volta che si clicka col mouse e per questo uso la classe Vector. Un Vector è un contenitore di oggetti e viene inizializzato tramite il costruttore Vector(int initialCapacity, int capacityIncrement) dove initialCapacity rappresenta la capienza iniziale e capacityIncrement la quantità di cui si vuole aumentare la capienza ogni volta che il numero di oggetti contenuti supera la capienza del Vector. La classe Line rappresenta una linea ed è formata da due oggetti privati di tipo Point. Ogni oggetto di tipo Point è definito da due interi, che rappresentano le coordinate del punto, cui si può accedere tramite i metodi getX() e getY(), che ritornano rispettivamente il valore dell’ascissa e dell’ordinata con la precisione di un double. Per accedere ai punti di ogni interni, la classe Line fornisce i metodi getStartPoint() e getEndPoint() che ritornano il punto di partenza e di arrivo della linea. Nel MyPanel1 c’è un booleano privato che può essere modificato invocando il metodo pubblico setStart(boolean b) che assegna alla variabile interna il valore di b. Quindi in fase di costruzione il MyPanel1, oltre a fare tutto ciò che faceva prima, crea un oggetto di tipo Vector con capienza iniziale 0 e incremento di capienza 1, inizializza una variabile booleana (draw) a false e crea un oggetto privato di tipo Point che servirà a memorizzare la posizione attuale della crocetta (currPos). Il MyPanel1 diventa inoltre l’ascoltatore degli eventi lanciati dalle azioni del mouse infatti implementa l’interfaccia MouseListener: ogni volta che si genera un evento di tipo MouseEvent che invochi il metodo mouseClicked(MouseEvent e) si risale al punto in cui si è clickato con il metodo getPoint() da invocare sull’oggetto e, si memorizza il punto in una variabile temporanea e poi si va a controllare il valore di draw e a seconda del valore si memorizza il punto in currPos o si crea un nuovo oggetto di tipo Line da memorizzare nel Vector con il metodo add e si memorizza in currPos la nuova posizione. Alla fine si chiede al pannello di ridisegnarsi. Nella fase di disegno il pannello va a controllare la capienza del Vector e se è 0 non fa niente altrimenti disegna tutte le linee memorizzate. Implementazione: Il MyFrame resta identico. Impelmentazione di MyPanel: import java.awt.*; import javax.swing.*; import java.awt.event.*; public class MyPanel extends JPanel implements WindowListener { private JButton up,down,left,right,quit,start; private MyPanel1 p1; private Panel p3; private JTextField f; private int i=1; private boolean st=false; public MyPanel(){ super(); setLayout(new BorderLayout()); p3=new Panel(); JLabel l=new JLabel("Griglia"); f=new JTextField("1",5); start=new JButton("Start"); p3.add(l); p3.add(f); p3.add(start); MyPanel2 p=new MyPanel2(); start.addActionListener(p); f.addActionListener(p); p1=new MyPanel1(); add(p1,BorderLayout.CENTER); add(p,BorderLayout.SOUTH); add(p3,BorderLayout.NORTH); } public void windowClosing(WindowEvent e){ System.exit(0); } public void windowActivated(WindowEvent e){} public void windowClosed(WindowEvent e){} public void windowDeactivated(WindowEvent e){} public void windowDeiconified(WindowEvent e){} public void windowIconified(WindowEvent e){} public void windowOpened(WindowEvent e){} private class MyPanel2 extends JPanel implements ActionListener{ public MyPanel2(){ super(); quit=new JButton("Quit"); up=new JButton("U"); down=new JButton("D"); left=new JButton("L"); right=new JButton("R"); quit.addActionListener(this); up.addActionListener(this); down.addActionListener(this); left.addActionListener(this); right.addActionListener(this); add(up); add(down); add(left); add(right); add(quit); } public void actionPerformed(ActionEvent e){ Object pulsantePremuto=e.getSource(); if(pulsantePremuto==up)(p1.incy(-i)); if(pulsantePremuto==down)(p1.incy(+i)); if(pulsantePremuto==left)(p1.incx(-i)); if(pulsantePremuto==right)(p1.incx(+i)); if(pulsantePremuto==quit)(System.exit(0)); if(pulsantePremuto==start){ if(!st){ start.setText("Stop"); st=true; } else{ start.setText("Start"); st=false; } p1.setStart(st); } if(pulsantePremuto==f){ try{ (i=Integer.valueOf(f.getText()).intValue()); } catch(NumberFormatException e1){ i=1; f.setText("1"); } } } } } Implementazione di MyPanel1: import java.awt.event.*; import java.awt.*; import javax.swing.*; import java.util.*; public class MyPanel1 extends JPanel implements MouseListener{ private boolean draw=false; private Vector v=new Vector(0,1); private Point currPos; private int x=0; private int y=0; private int a,b; public void paintComponent(Graphics g){ addMouseListener(this); setBackground(Color.white); Dimension d=new Dimension(getSize()); a=(int)(d.getWidth()/2); b=(int)(d.getHeight()/2); super.paintComponent(g); currPos=new Point(a+x,b+y); g.setColor(Color.black); g.drawLine((a-5+x),(b+y),(a+5+x),(b+y)); g.drawLine((a+x),(b-5+y),(a+x),(b+5+y)); if(v.capacity()!=0){ for(int i=0; i<v.capacity(); i++){ Line l=(Line)v.elementAt(i); Point p1=l.getStartPoint(); Point p2=l.getEndPoint(); g.drawLine((int)p1.getX(),(int)p1.getY(),(int)p2.getX(),(int)p2.getY()); } } } public void incx(int n){ x+=n; if(x>a)(x=a); if(x<-a)(x=-a); repaint(); } public void incy(int n){ y+=n; if(y>b)(y=b); if(y<-b)(y=-b); repaint(); } public void mouseClicked(MouseEvent e){ Point p=e.getPoint(); x=(int)(p.getX()-a); y=(int)(p.getY()-b); if(draw) v.add(new Line(currPos,p)); repaint(); } public void mouseEntered(MouseEvent e){} public void mouseExited(MouseEvent e){} public void mousePressed(MouseEvent e){} public void mouseReleased(MouseEvent e){} public void setStart(boolean b1){ draw=b1; } } Implementazione di Line: import java.awt.*; public class Line{ private Point x,y; public Line(Point n,Point m){ x=n; y=m; } public Point getStartPoint(){ return x; } public Point getEndPoint(){ return y; } } Casi d’uso: Il main rimane sempre identico: import java.awt.*; import javax.swing.*; public class Esercizio { public static void main(String[] v){ MyFrame f = new MyFrame(); Container c=f.getContentPane(); MyPanel p=new MyPanel(); c.add(p); f.show(); } } Concetti e tecniche acquisiti: Con questa esercitazione ho imparato l’uso di alcuni componenti facenti parte dei package grafici di java, la gestione degli eventi e delle eccezioni che vengono generati durante l’esecuzione di un’applicazione e l’utilizzo della classe Vector per la creazione di array che possono variare durante l’esecuzione.