Graphics Programming Sheet 2, GUI Controller
by Mike Roam. Rev:2nov2004.

  1. We want to create a second window which will serve as a dialog or remote control, with buttons and fields to control our "DrawGrid". This control window will be a brand new class: here's a page with a working sample (works with safari, but seems to have trouble with firefox and ie5-osx) and here's a simple sketch:

    import javax.swing.*;import java.awt.*;import java.awt.event.*;/** DrawController is a window full of buttons and textfields.  * The buttons tell "myDrawGrid" what to draw and the fields specify where to draw it. * @author Mike Roam * @version 29 Nov 2004 */class DrawController extends JFrame {    /* fields... */    JButton happyBtn = new JButton("Press Me");    JTextField weirdFld = new JTextField("hi",10 /* fieldwidth */);     DrawGrid myDrawGrid = null; // constructor will set this up    /** Constructor has to be told which DrawGrid to boss around... */    public DrawController( DrawGrid whereToDraw ) /* constructor */ {        DrawGrid myDrawGrid = whereToDraw;         this.setSize(300,300);  this.setLocation(300, 40);  setVisible(true);     } // constructor()    /* methods.... */    /* inner classes */    /** The following class has a method that will run when the button is pressed.    * Use "addActionListener()" below to attach listeners to buttons. */    class HappyLsr extends Object implements ActionListener        {           public void actionPerformed( ActionEvent e ) {               int num = Integer.parseInt( weirdFld.getText() );               System.out.println( "The number in weirdFld was " + num );           }/* end of method "actionPerformed()" */       }/* end of class HappyLsr */   }/* end of class DrawController */

    (ps: be sure you "import java.awt.event.*;" at the top of your project.)

  2. Let's take a look into the basics of the constructor that makes buttons and fields (the code that is new compared to the constructor above looks like this).

    public DrawController( DrawGrid whereToDraw ){    myDrawGrid = whereToDraw;    this.getContentPane().setLayout( new FlowLayout() );    /* setLayout is obscure but necessary: otherwise buttons cover each other up */    this.add(weirdFld); /* put the field onto the window */    this.add(happyBtn); /* put the button onto the window */    happyBtn.addActionListener( new HappyLsr() ); /* tell button what to do */    setSize(300,300);  setLocation(300, 40);    this.setVisible(true); /* necessary, else window stays invisible */} // constructor
    1. After re-reading the above, you'll be ready to consider adding a few more polishing touches to the graphic user interface: Labels and Panels. "Labels" let you put words on the screen (usually to explain what a field is for). "Panels" let you group labels, fields, and buttons together.

      1. New Improved Constructor...(new code is marked)

        public DrawController( DrawGrid myNewDrawGrid ) {   myDrawGrid = myNewDrawGrid;   this.getContentPane().setLayout( new FlowLayout() );   JPanel surfPanel = new JPanel();   surfPanel.add( new Label( "X:" ) );   surfPanel.add( weirdFld );   surfPanel.setBackground( Color.yellow ); /* available colors:       black, blue, cyan, darkGray, gray, green, lightGray, magenta,       orange, pink, red, white, yellow */   surfPanel.setToolTipText("Draw circle/sphere");   surfPanel.setBorder( BorderFactory.createEtchedBorder( ) );   this.getContentPane().add( surfPanel ); /* put the Panel onto the window */   this.getContentPane().add( happyBtn );   happyBtn.addActionListener( new HappyLsr() );  /* tell button what to do */   setSize(300,300); setLocation(300,40); setVisible(true);} // constructor
  3. Now let's think about the listener for a moment: it comes to life when its button gets pressed, and then bosses our DrawGrid around. Possibly the listener wants to get some text from a field and convert it into numbers that the DrawGrid will use. The sample above shows how to turn text into an int, but technically isn't good enough: we have to deal with the possibility of bad text that doesn't turn into a number. We've got other issues to deal with, also: how to convert text into double, and how to know where our DrawGrid is (or what it is called) so that we can send commands to it.

    1. Improved integer converter (goes inside actionPerformed() method above):

      int myGroovyInt = 0; /* default value if the text is no good */String theirNastyText = weirdFld.getText();try { /* built-in Java word, meaning "give it a shot but watch out" */    myGroovyInt = Integer.parseInt( theirNastyText );} catch (NumberFormatException ex ) {/* "catch" is java word, meaning stop here if trouble */    System.out.println("bad num:" + theirNastyText );    System.out.println("error message:" + ex );} /* end of try */
    2. Converting text into integers is handy, but really we should revise things to convert text into a double, so that we can have numbers with decimal points. Here's a method that converts text into a double: put it somewhere handy in this class and call it whenever you need doubles:

      /** * In case of bad input, this prints errmsg, and returns 0  * without throwing exception. */double strToDouble( String theData) {    Double myBigDDoubleX1 = new Double( 0 ); /* default value */    /* note: big "D" Doubles are a built-in CLASS, with methods & new() */    /* The "new Double()" method is a constructor, giving value 0 */    try {            myBigDDoubleX1 = new Double( theData );    } catch( NumberFormatException ex ) {            System.out.println("Bad numStr '" + theData + "', err:" + ex );    }    double theNewDouble = myBigDDoubleX1.doubleValue();    return theNewDouble;} // strToDouble()
    3. How to find the DrawGrid:
      There are at least two approaches, and let's look at both:

      1. Make our DrawController class an internal class to DrawGrid.

        1. This has the advantage of letting all our drawGrid fields and methods be directly available to our DrawController buttons (and their listeners, in particular). Convenient!

        2. This has the disadvantage of letting all our drawGrid fields and methods be directly available... Dangerous and potentially buggy!

      2. OR, tell our DrawController just once (probably in DrawController's constructor) where the DrawGrid is. This is slightly more confusing, but probably better for now. Let's look in detail at what we need to do:

        1. DrawController gets a field to remember where the DrawGrid is:
          DrawGrid myDrawGrid = null; /* constructor will set it */

        2. DrawController's constructor better be told where the drawGrid is:

          DrawController (DrawGrid myNewDrawGrid ) {	myDrawGrid = myNewDrawGrid;	...}
        3. Your main applet has to declare a DrawController and make a new one:

          DrawGrid barney=new DrawGrid();/* you have this already*/DrawController myControlPanel  = new DrawController(barney);
  4. Let's also have a "color picker"...There are two main steps:

    1. Put a "chooseFgColor()" method into your DrawGrid:

      /*** Puts up a color chooser window so that people can choose a new Fg color*/public void chooseFgColor(){    JColorChooser myRainbow = new JColorChooser();    Color newColor = getCurrentFgColor();    try {        newColor = myRainbow.showDialog(this /* JFrame */, "Pick Fg Color", newColor );        setCurrentFgColor(newColor);    } catch (Exception ec) {        System.out.println("uh-oh when choosing color:" + ec);    }} // chooseFgColor()
    2. Put a button with listener into your DrawController. The listener merely has to tell your drawGrid to "chooseFgColor" with the command myDrawGrid.chooseFgColor();

      class ChooseColorLsr extends Object implements ActionListener {    public void actionPerformed( ActionEvent e ) {        myDrawGrid.chooseFgColor();    }/* end of method "actionPerformed()" */}/* end of class ChooseColorLsr */

If all of these basics are working, go on to assignment 3 in which you try to read lists ofcoordinates and then draw the resulting (2d) polygon.


last modified monday 29 nov 2004, by mjr.