Graphical User Interfaces

The Model-View-Controller Architecture

See http://www.mathcs.sjsu.edu/faculty/pearce/patterns/mvc/mvc.html

AWT Components

A Java GUI is a container such as a frame, dialog box, or applet, that contains user interface components such as buttons, text boxes, lists, menus, and sliders. A container can also contain other containers.

The Event Delegation Model

Activating a control-- pushing a button, adjusting a scroll bar, typing in a text box, selecting an item from a list –fires a semantic event. The control is the source of the event. Semantic events are actually sequences of low-level events. For example, the adjusting-the-scroll-bar event is a sequence of mouse events: moving the mouse over the scroll bar's thumb tab, holding down the left mouse button, moving the mouse up or down, then releasing the left mouse button. Semantic and low-level events are represented by objects in Java:

AWTEvent
   semantic events
      ActionEvent (button press, etc.)
      AdjustmentEvent (fired by adjustable components)
      ItemEvent (fired by selectable components)
      TextEvent (fired by text components)
   ComponentEvent (i.e., low-level events)
      WindowEvent
      FocusEvent
      InputEvent
         MouseEvent
         KeyEvent

A listener is an object that contains one or more event handlers. An event handler is a method that gets called when a component fires a certain kind of event.

EventListener
   ActionListener
   AdjustmentListener

   ComponentListener

   ContainerListener

   FocusListener

   ItemListener

   KeyListener

   MouseListener

   MouseMotionListener

   TextListener

   WindowListener

These are all interfaces:

interface ActionListener extends EventListener
{
    public void actionPerformed(ActionEvent e);
}

 

Example: Device Monitor and Control

A device control monitor is a typical program. A desk top control panel allows an engineer to monitor and modify the internal state of a device. For example, a control panel might display the temperature of an associated nuclear reactor. Buttons on the control panel let the operator increment and decrement the reactor's temperature by fixed, small amounts.

Following the model-view-controller architecture, we divide the application into two parts. The view-controller is the control panel. The model is a nuclear reactor .

The Application

Here are some of the imports we'll need:

import java.awt.*;
import java.awt.event.*;
import pearce.java.util.*;

main() creates the reactor and a view of the reactor, then shows view:

public class App
{
   public static void main(String[] args)
   {
      Reactor r = new Reactor();
      View v = new View(r);
      v.show();
   }
}

Applications with graphical user interfaces (GUIs) are event-driven. This means the application is normally idle. The view and its components are listening for low-level events from the Java virtual machine. These, and their corresponding semantic events, are passed to programmer-defined listeners. A series of programmer-defined methods are called. These respond to the user-input. Then the application enters its idle state again. Here's a simple state diagram:

The Model

The reactor is very simple. Later, incTemp(), decTemp(), and getTemp() will be re implemented so that they talk to the real reactor through a serial port or a network connection. An object that presents an interface to a remote object is called a proxy.

class Reactor
{
   public Reactor() { temp = 9000; }
   public Reactor(double t) { temp = t; }

   public void incTemp() { temp += DELTA1; }
   public void decTemp() { temp -= DELTA2; }
   public double getTemp() { return temp; }
   public boolean isCritical() { return getTemp() > CRITICAL; }

   private static double DELTA1 = 30; // standard increment
   private static double DELTA2 = 10; // standard decrement
   private static double CRITICAL = 10000; // too hot!
   private double temp; // core temperature
}

The View

View extends MainFrame, which encapsulates the basic low-level event handlers. For its part, the view creates an increment button and a decrement button. IncButtonListener and DecButtonListener (inner classes) objects are created and are registered with the increment and decrement buttons, respectively. When called, the handler method of each listener calls myReactor.incTemp() or myReactor.decTemp(), respectively, where myReactor is the view's reference to its associated reactor.

The thermometer is a member variable of type Label. An update() function writes myReactor.getTemp() into the label.

class View extends MainFrame
{
   private Reactor myReactor;
   private Label therm; // thermometer

   public View(Reactor r)
   {
      super();
      myReactor = r;

      setTitle("Reactor Control Panel");

      Button incButton = new Button("INC");
      add(incButton);
      incButton.addActionListener(new IncButtonListener());

      therm = new Label();
      add(therm);
      update();

      Button decButton = new Button("dec");
      add(decButton);
      decButton.addActionListener(new DecButtonListener());
   }

   // update thermometer
   private void update()
   {
      char bell = (char)7; // 7 = ASCII code for '\a'
      String temp
         = "Core temperature = " + myReactor.getTemp() + " degrees";
      therm.setAlignment(Label.CENTER);
      therm.setText(temp);
      if (myReactor.isCritical())
         System.out.println("Warning, reactor is too hot!" + bell);
   }

   // Button Listeners

   class IncButtonListener implements ActionListener
   {
      public void actionPerformed(ActionEvent ae)
      {
         myReactor.incTemp();
         update();
      }
   }

   class DecButtonListener implements ActionListener
   {
      public void actionPerformed(ActionEvent ae)
      {
         myReactor.decTemp();
         update();
      }
   }

} // View

Running the Program

Compile and run the program. The following window should appear on your desk top:

class MainFrame

MainFrame, the view's base class, is not a pre-defined class. It extends Frame, which is a pre-defined class, by creating and registering a window listener that terminates the program when the window closing event is received.

As it turns out, the WindowListener interface dictates that implementers implement seven methods!

interface WindowListener extends EventListener
{
   public void windowOpened(WindowEvent e);
   public void windowClosing(WindowEvent e);
   public void windowClosed(WindowEvent e);
   public void windowIconified(WindowEvent e);
   public void windowDeiconified(WindowEvent e);
   public void windowActivated(WindowEvent e);
   public abstract void windowDeactivated(WindowEvent e);
}

This is a potential hassle, because we are only interested in the window closing event. Anticipating this laziness, Java provides adapter classes for listener interfaces with more than one handler method. The adapters implement the listener interfaces by implementing each method as a no op. We can extend an adapter and redefine only the handlers we care about.

package pearce.java.util;

import java.awt.*;
import java.awt.event.*;

public class MainFrame extends Frame
{
   public MainFrame()
   {
   
      setSize(600, 300);
      setLocation(100, 100);
      setLayout(new FlowLayout());
      setBackground(Color.white);
   
      addWindowListener(new MyWindowListener());

   }

   class MyWindowListener extends WindowAdapter
   {
      public void windowClosing(WindowEvent we)
      {
         dispose(); // release system resources
         System.exit(0);
      }
   }

} // MainFrame

Layout Managers

Everything is pretty intuitive except for the command:

setLayout(new FlowLayout());

A FlowLayout object is an example of a layout manager. Each time Container.Add() is called, the layout manager determines where the added component will be placed in the container (i.e., the frame). There are several pre-defined layout managers that are easy to use, but do a lousy job. A flow layout manager merely places the components in rows, from left to right. Each time a row is filled up, a new row is started.

Problem

Implement the stack calculator again, this time with a GUI. Users will press a push button to push a number typed into a text control onto the stack. The top of the stack will be displayed in a label control (like the reactor's thermometer). The GUI also has pop, add, mul, div, and sub buttons.