Chitika

Showing posts with label swing. Show all posts
Showing posts with label swing. Show all posts

Saturday, June 16, 2007

Sample Calculator GUI in Java Swing

Recently, I was giving my 1st-year IT students several Swing GUI exercises on OOP with Java lab sessions. One of the exercises was to create a simplified Calculator GUI that looks like the Calculator on standard Windows OS. The objective of the exercise was to make the students familiar with the basic Swing layout managers (note: I haven't thought them GridBag, Spring, or JGoodies Forms layouts), and the basic event-handling mechanism in Swing. The exercise was also meant for the students to learn more about the OOP concept in practice.




Here's the code that I gave the students at the end of the session:








public class Calculator {

  // constant values
  public static final int NONE = -1;
  public static final int ADD = 0;
  public static final int SUBTRACT = 1;
  public static final int MULTIPLY = 2;
  public static final int DIVIDE = 3;

  // values to be stored in the calculator memory
  private double leftValue = 0.0;
  private double rightValue = 0.0;
  private int lastOperation = NONE;
  private double multiplier = 1;
  private boolean DOT = false;

  // the user hits the + button
  public double add () {
    lastOperation = ADD;
    resetDOT();
    return leftValue;
  }

  // the user hits the - button
  public double subtract () {
    lastOperation = SUBTRACT;
    resetDOT();
    return leftValue;
  }

  // the user hits the * button
  public double multiply () {
    lastOperation = MULTIPLY;
    resetDOT();
    return leftValue;
  }

  // the user hits the / button
  public double divide () {
    lastOperation = DIVIDE;
    resetDOT();
    return leftValue;
  }

  // the user hits the = button
  public double equate () {
    switch (lastOperation) {
      case NONE: break;
      case ADD: leftValue = leftValue + rightValue; break;
      case SUBTRACT: leftValue = leftValue - rightValue; break;
      case MULTIPLY: leftValue = leftValue * rightValue; break;
      case DIVIDE: leftValue = leftValue / rightValue; break;
    }
    rightValue = 0;
    resetDOT();
    return leftValue;
  }

  // the user hits the 0-9 button
  public double number (int i) {
    double j = i * multiplier;
    if (DOT) {
      multiplier = multiplier / 10;
    }
    if (lastOperation == NONE) {
      leftValue = leftValue * (DOT ? 10(leftValue < (-j: j);
      return leftValue;
    else {
      rightValue = rightValue * (DOT ? 10(rightValue < (-j: j);
      return rightValue;
    }
  }

  // the user hits the +/- button
  public double plusMinus () {
    if (lastOperation == NONE) {
      leftValue = -leftValue;
      return leftValue;
    else {
      rightValue = -rightValue;
      return rightValue;
    }
  }

  // the user hits the C button
  public double reset () {
    lastOperation = NONE;
    leftValue = rightValue = 0;
    resetDOT();
    return leftValue;
  }

  // the user hits the sqrt button
  public double sqrt () {
    resetDOT();
    if (lastOperation == NONE) {
      leftValue = Math.sqrt (leftValue);
      return leftValue;
    else {
      rightValue = Math.sqrt (rightValue);
      return rightValue;
    }
  }

  // to reset the DOT & multiplier
  private void resetDOT() {
    DOT = false;
    multiplier = 1;
  }

  // the user hits the . button
  public double dot () {
    if (!DOT) {
      DOT = true;
      multiplier = 0.1;
    }
    return lastOperation == NONE ? leftValue : rightValue;
  }
}




I divided this small application into two different classes. The Calculator class above will act as the model for the application, and is responsible to maintain the state of the Calculator and to handle the user actions. Having a minimum amount of time duration for the lab session, many of the Calculator features were not implemented. Many exceptions were not caught nor handled properly, and there's a slightly different behavior with the sqrt button (when you compare it with the Windows OS version).

The whole point of this Calendar application was not to make a copycat of Windows OS version, rather it was to illustrate to the students what they can do with OOP and Swing.

Here's the second part of the application:








import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class CalculatorGUI implements ActionListener {

  private Calculator c = new Calculator ();

  private JFrame frame = new JFrame ("Calculator");
  private JPanel[] panels = new JPanel [6];
  private JTextField textField = new JTextField();
  private JButton resetButton = new JButton(" C ");
  private JButton[] numberButtons = new JButton[10];
  private JButton divideButton = new JButton ("/");
  private JButton multiplyButton = new JButton ("*");
  private JButton subtractButton = new JButton ("-");
  private JButton plusMinusButton = new JButton ("+/-");
  private JButton dotButton = new JButton (" . ");
  private JButton addButton = new JButton ("+");
  private JButton equateButton = new JButton (" = ");
  private JButton sqrtButton = new JButton ("sqrt");

  public void buildGUI () {

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel contentPane = (JPanelframe.getContentPane();

    // initialize panels
    for (int i = 0; i < panels.length; i++) {
      panels[inew JPanel ();
    }

    // initialize button 0-9
    for (int i = 0; i < numberButtons.length; i++) {
      numberButtons[inew JButton (" " + i + " ");
      numberButtons[i].setActionCommand (String.valueOf(i));
      numberButtons[i].addActionListener (this);
    }

    // default layout = BorderLayout.CENTER
    textField.setColumns(20);
    textField.setText ("0");
    textField.setHorizontalAlignment (JTextField.RIGHT);
    panels[0].add (textField);

    // layout = FlowLayout.RIGHT
    panels[1].setLayout (new FlowLayout (FlowLayout.RIGHT));
    panels[1].add (resetButton);
    resetButton.setActionCommand ("RESET");
    resetButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[2].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[2].add (numberButtons[7]);
    panels[2].add (numberButtons[8]);
    panels[2].add (numberButtons[9]);
    panels[2].add (divideButton);
    panels[2].add (sqrtButton);
    divideButton.setActionCommand ("DIVIDE");
    divideButton.addActionListener (this);
    sqrtButton.setActionCommand ("SQRT");
    sqrtButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[3].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[3].add (numberButtons[4]);
    panels[3].add (numberButtons[5]);
    panels[3].add (numberButtons[6]);
    panels[3].add (multiplyButton);
    multiplyButton.setActionCommand ("MULTIPLY");
    multiplyButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[4].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[4].add (numberButtons[1]);
    panels[4].add (numberButtons[2]);
    panels[4].add (numberButtons[3]);
    panels[4].add (subtractButton);
    subtractButton.setActionCommand ("SUBTRACT");
    subtractButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[5].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[5].add (numberButtons[0]);
    panels[5].add (plusMinusButton);
    panels[5].add (dotButton);
    panels[5].add (addButton);
    panels[5].add (equateButton);
    plusMinusButton.setActionCommand ("PLUSMINUS");
    plusMinusButton.addActionListener (this);
    dotButton.setActionCommand ("DOT");
    dotButton.addActionListener (this);
    addButton.setActionCommand ("ADD");
    addButton.addActionListener (this);
    equateButton.setActionCommand ("EQUATE");
    equateButton.addActionListener (this);

    contentPane.setLayout (new BoxLayout (contentPane, BoxLayout.Y_AXIS));
    for (JPanel jPanel : panels) {
      contentPane.add (jPanel);
    }

    frame.pack ();
    frame.setVisible (true);
  }

  public void actionPerformed (ActionEvent e) {
    String actionCommand = e.getActionCommand();
    if (actionCommand == null || actionCommand.trim().length() <= 0) {
      return;
    }

    int number = -1;
    try {
      number = Integer.parseInt (actionCommand);
    catch (NumberFormatException e1) {
    }

    if (number >= 0) {
      // this is a number
      textField.setText ("" (c.number (number)));
    else {
      // this is not a number
      if (actionCommand.equals ("RESET")) {
        textField.setText ("" (c.reset()));
      else if (actionCommand.equals ("DIVIDE")) {
        textField.setText ("" (c.divide()));
      else if (actionCommand.equals ("SQRT")) {
        textField.setText ("" (c.sqrt()));
      else if (actionCommand.equals ("MULTIPLY")) {
        textField.setText ("" (c.multiply()));
      else if (actionCommand.equals ("SUBTRACT")) {
        textField.setText ("" (c.subtract()));
      else if (actionCommand.equals ("PLUSMINUS")) {
        textField.setText ("" (c.plusMinus()));
      else if (actionCommand.equals ("DOT")) {
        textField.setText ("" (c.dot()));
      else if (actionCommand.equals ("ADD")) {
        textField.setText ("" (c.add()));
      else if (actionCommand.equals ("EQUATE")) {
        textField.setText ("" (c.equate()));
      }
    }
  }

  public static void main(String[] args) {
    CalculatorGUI gui = new CalculatorGUI();
    gui.buildGUI ();
  }
}




I'm pretty sure that there are many solution variants out there. By focusing on the layout managers that are easy to use and understand, such as FlowLayout, BorderLayout and BoxLayout, I think this exercise has nicely served its main objective.

If you have any suggestions on how to help the students to understand the OOP + Swing concepts better, please do let me know.

Thursday, October 07, 2004

login GUI sample source code

Referring to my previous post, here's the source code that I made..
I decided to separate the implementation into:
- a Screen class which will manage the event handling & business delegation
- a Builder class which will be responsible for laying out the components
- constant classes, such as Fonts, Colors & Dimensions, for consistency, reusability & efficiency throughout any number of GUIs we may have
I used GridBagLayout extensively in this example, it's still one of my favourite layout because it can lay the components in almost any layout

Now, after exploring more and more, I'm considering JGoodies FormLayout & its contributor, FormLayout Maker for any well structured forms. And, ExplicitLayout for other layout which may never be possible to create using any layout manager ever existed.

Anyway, as the sharing goes..
I'm interested if anyone can suggest an idea on how the responsibility for the classes involved in a GUI be divided..
You can find my approach in the attached file, let me know yours.. :D

Further Reading:
Swing Second Edition
Java Swing Second Edition

Tuesday, October 05, 2004

Swing layout comparisons

An interesting comparison..

GBL <= TableLayout < HIGLayout < FormLayout
GBL <= TableLayout < HIGLayout < ExplicitLayout
HIGLayout < SpringLayout
BorderLayout < TableLayout
GridLayout < HIGLayout
GridLayout < FormLayout


Read their arguments here

I'm trying FormLayout Maker, and let's see how well it goes.. :D

Further Reading:
Swing Second Edition
Java Swing Second Edition

Monday, October 04, 2004

Swing layouts

Developing GUI layout in Swing requires a considerable amount of time, especially if you just starting to learn how to Swing. The easy layouts, such as FlowLayout, BorderLayout, BoxLayout, and GridLayout does not really take you anywhere, especially in the case of creating complex GUI.

Luckily, we have the powerful GridBagLayout, which is a bit complex for a startup, but can be very efficient for an experienced Swing GUI developer. However, after looking at JGoodies Forms, it's very interesting to explore more on what the community has to offer.

First, we have SpringLayout which were added into the J2SE 1.4 distribution. Then, we have FormLayout which is contributed by JGoodies. FormLayout is a very good layout which allows us to create form layouts in 1-2 hours max. I'm trying to explore through ExplicitLayout & TableLayout, and see if they have some added values as well.

I hope I can get a chance to explore more on many other available layouts.. :D

Links:
JGoodies Forms
ExplicitLayout
TableLayout

Further Reading:
Swing Second Edition
Java Swing Second Edition

Friday, October 01, 2004

Swing challenge

I'm currently challenging myself and a few other co-workers to implement a simple UI in Swing. The challenge is to create a simple XP-style login page, as can be found in the JGoodies website.



The challenge is to create it within a 10-coding hour limitation, and implementing it using standard Swing libraries. The final submission date is next Wednesday. I wanted to try to create the login page as best as possible without any help from third party libraries, such as JGoodies.

I wanted to compare the source codes that we have by then, and try to have a discussion over how the code should be best put. Then, I wanted to try the JGoodies on, and see the difference in the quality & productivity aspect. Since this is just a simple & fun challenge, I'm not hoping to receive many submissions. But, I'll try to create my version on it, and share it here once completed.. :D

Is there anyone willing to give this challenge a shot? :D

Further Reading:
Swing Second Edition
Java Swing Second Edition

Monday, September 27, 2004

using HTML in Swing components

I was going through the Sun tutorial on Swing this weekend. It's my second time since three years ago, when I use Swing last time. Many have changed since then, in fact the tutorial even states that there will be many enhancements in future versions, such as in JDK 5.0.

One of the few things that interest me is the ability to use HTML text formatting in Swing components. Here's an example:

button = new JButton(
  "<html><b><u>T</u>wo</b><br>lines</html>");

The text on this button will be displayed in two lines, where the first line consists of a 'T' which is bold & underlined, and 'wo' which is bold. You can even play with color, for example:

String color = null;
if (value <= 32) color = "blue";
else if (value <= 80) color = "green";
else color = "red";

StringBuffer sb = new StringBuffer ();
sb.append ("<html><font color=");
sb.append (color);
sb.append ('>');
sb.append (value);
sb.append ("</font></html>");

button.setText (sb.toString());

This will make our Swing application looks even more intuitive.

However, since HTML rendering involves many classes, it may be best to paint the HTML in a background thread prior to showing it.

Currently, the Swing components that supports HTML rendering are JButton, JLabel, JMenuItem, JMenu, JRadioButtonMenuItem, JCheckBoxMenuItem, JTabbedPane, JToolTip, JToggleButton, JCheckBox and JRadioButton.

I guess I'll be making intuitive suggestions if I were to create any Swing application in the future.. :D

Further Reading:
Swing Second Edition
Java Swing Second Edition