Elementi di programmazione grafica Contenuti • Programmazione di semplici applicazioni grafiche che Disegnano semplici forme geometriche Utilizzano colori Compongono forme semplici per formare figure complesse Utilizzano “input dialogs” per ottenere input dall’utente • Sviluppo di test per validare la correttezza delle applicazioni Frames • La classe JFrame JFrame frame = new JFrame(); frame.setSize(300, 400); frame.setTitle(“Un frame vuoto"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); • import javax.swing.*; Frames File EmptyFrameViewer.java 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: 17: 18: import javax.swing.*; class EmptyFrameViewer { public static void main(String[] args) { JFrame frame = new JFrame(); final int FRAME_WIDTH = 300; final int FRAME_HEIGHT = 400; frame.setSize(FRAME_WIDTH, FRAME_HEIGHT); frame.setTitle(“Un frame vuoto"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } } Mini Test 1. Come disegnamo un frame quadrato con titolo "Hello, World!"? 2. Come scriviamo un programma che lancia due frames? Risposte 1. Modifca EmptyFrameViewer come segue: frame.setSize(300, 300); frame.setTitle("Hello, World!"); 2. Costruisci due oggetti JFrame definendone la dimensione e poi invoca setVisible(true) su ciascuno Disegno di forme geometriche • JComponent La classe che definisce contenitori generici, al al cui interno includiamo forme geometriche • Figure geometriche incluse in nuovi JComponent class ShapeComponent extends JComponent { . . . } • Un nuovo JComponent Disegno di forme geometriche • paintComponent il metodo invocato (automaticamente) tutte le volte che il componente per il quale e` definito viene ridisegnato è in questo metodo che si produce il disegno class ShapeComponent extends JComponent { public void paintComponent(Graphics g) { // Converti in Graphics2D Graphics2D g2 = (Graphics2D) g; . . . } } Disegno di forme geometriche • Graphics classe che permette di manipolare lo stato della componente grafica (posizione/colore corrente) • Graphics2D classe con metodi per la creazione e il disegno di figure geometriche • Usa un cast per ottenere un oggetto di tipo Graphics2D dal parametro di tipo Graphics Rectangle r = new Rectangle(5, 10, 20, 30); g2.draw(rect); • package java.awt File ShapeComponent.java 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: import import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; javax.swing.JPanel; javax.swing.JComponent; /** Un Component che disegna due rettangoli. */ class ShapeComponent extends JComponent { public void paintComponent(Graphics g) { // Ottieni un graphics2D Graphics2D g2 = (Graphics2D) g; Continua… File ShapeComponent.java 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: } // Costruisci un rettangolo e disegna Rectangle r = new Rectangle(5, 10, 20, 30); g2.draw(r); // Trasla il rettangolo (15 a dx, 25 in basso) r.translate(15, 25); // Disegna il rettangolo traslato g2.draw(r); } Applicazioni • ShapeViewer classe che lancia una finestra all’interno della quale vengono raffigurate Ie componenti L’applicazione è costruita interamente all’interno del metodo main della classe Disegna due rettangoli Continued… Applicazione ShapeViewer Il metodo main 1. Costruisci il frame 2. Costruisci un oggetto della classe component ShapeComponent component = new ShapeComponent(); 3. Aggiunti il component al frame frame.add(component); La chiamata è leggermente più complicata per versioni di Java precedenti alla 5: frame.getContentPane().add(component); 4. Rendi visibile il frame File ShapeViewer.java 01: import javax.swing.JFrame; 02: 03: class ShapeViewer 04: { 05: public static void main(String[] args) 06: { 07: JFrame frame = new JFrame(); 08: 09: final int FRAME_WIDTH = 300; 10: final int FRAME_HEIGHT = 400;11: 12: frame.setSize(FRAME_WIDTH, FRAME_HEIGHT); 13: frame.setTitle(“Due rettangoli"); 14: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15: Continua… File ShapeViewer.java 16: 17: 18: 19: 20: 21: } ShapeComponent component = new ShapeComponent(); frame.add(component); frame.setVisible(true); } Mini Test 3. Che succede se sostituiamo la chiamata g2.draw(box)con g.draw(box) ? Risposta 3. Il compilatore segnala un errore g : Graphics non ha un metodo draw Applets • Applets sono applicazioni eseguite all’interno di un browser • L’implementazione di un Applet utilizza il pattern seguente: public class MyApplet extends JApplet { public void paint(Graphics g) { // Ottieni un contesto grafico Graphics2D g2 = (Graphics2D) g; // Istruzioni per il disegno . . . } } Applets • La struttura è molto simile a quella di un JComponent, con due differenze: 1. Estendiamo JApplet, invece di JComponent 2. IL codice del disegno è parte del metodo paint, invece che del metodo paintComponent • Per eseguire un applet, dobbiamo creare un file HTML ed inserire il codice che lancia l’applet mediante la tag applet Applets • Un file HTML può avere diversi applets, ciascuno dei quali introdotto da una corrispondente tag (di tipo applet) • Un applet può essere lanciato mediante l’appletviewer, o all’interno di un browser in cui Java sia stato abilitato. appletviewer RectangleApplet.html File ShapeApplet.java 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; javax.swing.JApplet; /** Un applet che disegna due rettangoli. */ public class ShapeApplet extends JApplet { public void paint(Graphics g) { // Ottieni un contesto grafico Graphics2D g2 = (Graphics2D) g; Continua… File ShapeApplet.java 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: } // Costruisci un rettangolo e disegna Rectangle r = new Rectangle(5, 10, 20, 30); g2.draw(r); // Trasla il rettangolo (15 a dx, 25 in basso) r.translate(15, 25); // Disegna il rettangolo traslato g2.draw(r); } File ShapeAppletViewer.html <html> <head> <title>Due rettangoli</title> </head> <body> <p>Ecco qui il mio primo applet:</p> <applet code="RectangleApplet.class" width="300" height="400"> </applet> </body> </html> Applets Forme Geometriche • Rectangle, Ellipse2D.Double, Line2D.Double fornite dal package java.awt • Alcune di queste classi, e.g. Line2D.Double, sono interne. Per il momento ignoriamo questo aspetto, ma notiamo la dichiarazione import import java.awt.geom.Ellipse2D; // no .Double • Per disegnare una forma dobbiamo costruirla e disegnarla Ellipse2D.Double e = new Ellipse2D.Double(x, y, width, height); g2.draw(e); Una ellisse e la sua bounding box Linee • Due modi per disegnare una linea: Line2D.Double segmento = new Line2D.Double(x1, y1, x2, y2); oppure, Point2D.Double from = new Point2D.Double(x1, y1); Point2D.Double to = new Point2D.Double(x2, y2); Line2D.Double segmento = new Line2D.Double(from, to); Stringhe g2.drawString("Message", 50, 100); Domande 4. Quale è la sequenza di istruzioni per disegnare un cerchio con centro (100, 100) e raggio 25? 5. Quale è la sequenza di istruzioni per disegnare la lettera "V" con due segmenti di linea ? 6. Quale è la sequenza di istruzioni per disegnare la stringa costituita dalla lettera "V" ? Risposte 4. 5. 6. g2.draw(new Ellipse2D.Double(75, 75, 50, 50); Line2D.Double segm1 = new Line2D.Double(0, 0, 10, 30); g2.draw(segm1); Line2D.Double segm2 = new Line2D.Double(10, 30, 20, 0); g2.draw(segm2); g2.drawString("V", 0, 30); Colori • Colori standard Color.BLUE, Color.RED, … • Nuovi colori: formato rgb settando i valori delle componenti red, green, blue tra 0.0F e 1.0F Color magenta = new Color(1.0F,0.0F,1.0F), // F = float • setColor setta il colore del Graphic Contexts g2.setColor(magenta); • Usa il colore con i metodi draw e fill g2.fill(rectangle); // riempito con il colore corrente Domanda 7. Quale è la sequenza di istruzioni per disegnare un quadrato giallo su uno sfondo rosso? Risposta 7. Disegna un quadrato giallo all’interno di un quadrato rosso: g2.setColor(Color.RED); g2.fill(new Rectangle(0, 0, 200, 200)); g2.setColor(Color.YELLOW); g2.fill(new Rectangle(50, 50, 100, 100)); Forme complesse • Best practice: costruisci un classe per ciascuna forma class Car { . . . public void draw(Graphics2D g2) { // Istruzioni per il disegno . . . } } • Progetta la forma individuando le componenti base Due automobili Progetto della Forma Origine e punto di riferimento della forma File Car.java 01: 02: 03: 04: 05: 06: 07: 08: import import import import import java.awt.Graphics2D; java.awt.Rectangle; java.awt.geom.Ellipse2D; java.awt.geom.Line2D; java.awt.geom.Point2D; /** Una forma di auto posizionabile posizionabibile in un un punto qualunque di una finestra. 09: */ 10: public class Car 11: { 12: /** 13: Costruisce una auto a partire da una data origine 14: @param x la coordinata x dell’origine 15: @param y la coordinata y dell’origine 16: */ Continua… File Car.java 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: public Car(int x, int y) { xLeft = x; yTop = y; } /** Disegna l’auto. @param g2 il contesto grafico per il disegno */ public void draw(Graphics2D g2) { Rectangle scocca = new Rectangle(xLeft, yTop + 10, 60, 10); Ellipse2D.Double ruotaAnteriore = new Ellipse2D.Double(xLeft + 10, yTop + 20, 10, 10); Continua… Ellipse2D.Double ruotaPosteriore File Car.java 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: = new Ellipse2D.Double(xLeft + 40, yTop + 20, 10, 10); // estremo inferiore del parabrezza Point2D.Double p1 = new Point2D.Double(xLeft + 10, // estremo anteriore del tetto Point2D.Double p2 = new Point2D.Double(xLeft + 20, // estremo posteriore del tetto Point2D.Double p3 = new Point2D.Double(xLeft + 40, // estremo inferiore del lunotto Point2D.Double p4 = new Point2D.Double(xLeft + 50, Line2D.Double frontWindshield = new Line2D.Double(p1, p2); yTop + 10); yTop); yTop); yTop + 10); Continued… File Car.java 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: } Line2D.Double roofTop = new Line2D.Double(p2, p3); Line2D.Double rearWindshield = new Line2D.Double(p3, p4); g2.draw(scocca); g2.draw(ruotaAnteriore); g2.draw(ruotaPosteriore); g2.draw(parabrezza); g2.draw(tetto); g2.draw(lunotto); } public static int WIDTH = 60; public static int HEIGHT = 30; private int xLeft; private int yTop; Costruiamo l’applicazione JComponent contenente il disegno • Ricordiamo: istruzioni per il disegno nel metodo paintComponent • Posizione in basso a dx: int x = getWidth() – Car.WIDTH; int y = getHeight()– Car.HEIGHT; Car bottomCar = new Car(x, y); • getWidth e getHeight chiamati sull’oggetto che esegue paintComponent • Se cambia la dimensione della finestra paintComponent viene invocato e la posizione dell’auto ricalcolata Continua… File CarComponent.java 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JComponent; /** This component draws two car shapes. */ class CarComponent extends JComponent { public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; Car topCar = new Car(0, 0); Continua… File CarComponent.java 16: 17: 18: 19: 20: 21: 22: 23: 24: } int x = getWidth() - Car.WIDTH; int y = getHeight() - Car.HEIGHT; Car bottoCar = new Car(x, y); topCar.draw(g2); bottomCar.draw(g2); } • Il metodo draw della classe Car invoca i metodi del contesto grafico g2 per comporre il disegno L’applicazione CarViewer.java 01: import javax.swing.JFrame; 02: 03: public class CarViewer 04: { 05: public static void main(String[] args) 06: { 07: JFrame frame = new JFrame(); 08: 09: final int FRAME_WIDTH = 300; 10: final int FRAME_HEIGHT = 400; 11: 12: frame.setSize(FRAME_WIDTH, FRAME_HEIGHT); 13: frame.setTitle("Two cars"); 14: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Continua… File CarViewer.java 15: 16: 17: 18: 19: 20: 21: } CarComponent component = new CarComponent(); frame.add(component); frame.setVisible(true); } Domanda 8. Quale classe dobbiamo modificare per ottenere un disegno in cui le auto sono disposte una vicino all’altra? Risposta 8. CarComponent Altre forme grafiche Rectangle fasciaVerde = new Rectangle fasciaRossa = new Line2D.Double topLine = new Line2D.Double(130, Line2D.Double bottomLine = new Line2D.Double(130, Rectangle(100, 100, 30, 60); Rectangle(160, 100, 30, 60); 100, 160, 100); 160, 160, 160); Lettura da un dialog box • In una una applicazione grafica può possiamo interagire con l’utente chiedendo input mediante un JOptionPane • Il metodo showInputDialog attiva una finestra che dà un prompt e attende input • Il metodo showInputDialog restituisce la stringa fornita dall’utente String input = JOptionPane.showInputDialog("Enter x"); double x = Double.parseDouble(input); Continua… Un dialog box ColorViewer • Una applicazione che permette all’utente di comporre un colore decidendo i parametri del formato rgb • I valori vengono richiesti in seguenza mediante dialog boxes • Al termine l’applicazione attiva una finestra con un quadrado del colore composto dall’utente File ColorViewer.java 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: 17: 18: import java.awt.Color; import javax.swing.JFrame; import javax.swing.JOptionPane; class ColorViewer { public static void main(String[] args) { JFrame frame = new JFrame(); final int FRAME_WIDTH = 300; final int FRAME_HEIGHT = 400; frame.setSize(FRAME_WIDTH, FRAME_HEIGHT); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); String input; Continua… File ColorViewer.java 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: } // Chiedi all’utente I valori di red, green, blue input = JOptionPane.showInputDialog("red:"); double red = Double.parseDouble(input); input = JOptionPane.showInputDialog("green:"); double green = Double.parseDouble(input); input = JOptionPane.showInputDialog("blue:"); double blue = Double.parseDouble(input); Color coloreScelto = new Color( (float) red, (float) green, (float) blue); DemoColorComponent component = new DemoColorComponent(fillColor); frame.add(component); frame.setVisible(true); } File DemoColorComponent.java 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; javax.swing.JComponent; /** Una componente che include un quadrato colorato */ class DemoColorComponent extends JComponent { private Color fillColor; // colore di riempimento /** Costruisce la componente @param c il colore di riempimento del quadrato */ public DemoColorComponent(Color c) Continua… File DemoColorComponent.java 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: { fillColor = c; } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; // Seleziona il colore del contesto grafico g2.setColor(fillColor); // Costruisci e colora un quadrato al centro // della finestra Continua… File DemoColorComponent.java 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 43: } final int LATO = 100; Rectangle quadrato = new Rectangle( (getWidth() - LATO) / 2, (getHeight() - LATO) / 2, LATO, LATO); g2.fill(quadrato); } Output Domande 9. Sarebbe possibile chiedere i tre valori per r,g, e b in un unico dialog box? 10. Perché l’applicazione invoca showInputDialog dal metodo main main della classe ColorViewer e non dal metodo paintComponent della classe ColorComponent? Risposte 9. Certo, ma in quel caso è necessario estrarre poi le singole componenti … un utile esercizio di semplice programmazione su stringhe 10. Perchè in quel caso i dialog box verrebbero rilanciati tutte le volte che la finestra viene mossa / resized / deiconificata /…