Java Power Tools Handbook: Basic Tutorials

 

In this section we will build several programs that use a few of the JPT GUI components. Each program is short enough that most of its functionality is enclosed in a single class. However, we will delineate clearly which part of the code is responsible for the View, for the Model, for the Action, and for the GUI building segment of the program.

 

Simple Adders

 

Description of the Simple Integer Adder program

 

JPT Techniques

Creating and using the TextFieldView

Defining and using Actions

Installing GUI Components Part 1

Adapting the TextFieldView for use with double

 

In this program we will use TextFieldView and Actions. TextFieldView is a JPT GUI component that can be used to for input and display of typed text. Actions connect buttons with user-defined methods that are performed when the button is pressed.

 

Description of the Simple Integer Adder program

 

We build a simple program that will add the values of four integers typed in by the user. Two snapshots of the view of the entire GUI are shown below.

 

 



 

 

 

 

View

This program uses four TextFieldViews to allow the user to type in the input and one TextFieldView to display the result. This represents the View part of this program. The first snapshot shows the initial values displayed in the four input fields, as well as the result displayed in the Sum field after the Add action has been performed. The second snapshots shows illegal values typed into the four input fields.

 

Model

The internal data model consists of five integer variables - the four inputs and the result. This represents the Model part of the program.

 

Actions

The action is just a simple integer addition of the four numeric values. The program uses one ActionsPanel with two associated Actions. One Action performs the addition of four integers whose values are extracted from the four TextFieldViews. The sum is displayed in the fifth TextFieldView. The second Action resets the view state of the TextFieldViews to their original default values. This represents the Action part of the program.

 

GUI

The GUI is a DisplayCollection enclosed in a QuickJPTFrame titled Simple Adder. This display collection contains two GUI components: a DisplayCollection titled Data Fields and an actions Display titled Actions. The data fields display collection contains five annotated Displays that hold the TextFieldViews. The actions display holds ActionsPanel that contains the two Action buttons.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

The diagram shows the recursive nature of GUI design - display collections inside display collections inside displays, with the views and actions at the lowest level of the recursion.

JPT Techniques

Creating and using the TextFieldView

 

We will start by explaining the building of the output TextFieldView - the simplest of them all.

 

// sumTFV will only be used to display the result

// Last two parameters are never used

// they pertain to the modal error dialog box - not needed for output

private TextFieldView xyzTFV =

 

new TextFieldView(

 

"", // initial value displayed in the field

"No fix needed", // prompt for correction - not needed

"Output only"); // label for error dialog - not needed

 

The constructor for a TextFieldView asks for three String arguments - the initial String to display, the label for the modal error dialog panel, and the prompt for the correction of this error. We cannot have errors in a view used only for display of the result String, and so, only the first parameter is meaningful. In the snapshot above, this field is labeled as Sum: and currently displays the String "70". The label for this field and its size and placement in the GUI is done later, when we actually construct the content pane for Simple Adder.

 

The TextFieldViews for the four input fields look as follows:

 

// xTFVC will be used to input a required integer input

private TextFieldView xTFV =

 

new TextFieldView(

 

XVALUE, // initial value displayed in the TFV

"X has to be an integer:", // prompt for correcting the input

"Incorrect input"); // title for the error dialog box

 

Note that here the two remaining fields contain meaningful messages to the user, in case there is an error in processing the input.

 

The code for the add function illustrates how the data model extracts the values from the TextFieldView and places new Strings into the view. There are two modes for extracting data from a TextFieldView: the optional model, when the user may decline to provide any input and the exception has to be caught, and the mandatory model, when an error produces a modal dialog that persists until a valid input is supplied.

Independently, the programmer may supply a suggested value that is available to the user within the error dialog window. The four input fields illustrate the four possible combinations of these data extraction methods.

 

 

public void add() {

int x, y, z, w;

int sum;

 

// optional input using request

// try-catch is needed since user may cancel the input operation

// note that no suggestion is specified for this text field view

 

try {

x = xTFV.requestInt();

}

catch (CancelledException ex) {

// user input is invalid, so invalidate the sum view and return

sumTFV.setViewState("");

return;

}

// optional input using request

// try-catch is needed since user may cancel the input operation

// note that this text field view has suggestion for error recovery

 

try {

y = yTFV.requestInt();

}

catch (CancelledException ex) {

// user input is invalid, so invalidate the sum view and return

sumTFV.setViewState("");

return;

}

// mandatory input using demand

// note that no suggestion is specified for this text field view

z = zTFV.demandInt();

// mandatory input using demand

// note that this text field view has suggestion for error recovery

w = wTFV.demandInt();

// compute the sum

sum = x + y + z + w;

 

// update the sum text field view to show the result

sumTFV.setViewState(sum + "");

}

 

The functions demandInt and requestInt parse the String currently displayed in the view and attempt to convert it to an int value. If this process fails, an error dialog appears automatically. The demand command triggers the mandatory input mode, the request command triggers the optional input mode. Depending on the manner in which the data model extracts the values from the view, the error dialog box may have one of the following forms:

 

 

 


optional model - request

 

 

 


optional model - request

with suggestion

 

 


 

 


mandatory model - demand

 

 


mandatory model - demand

with suggestion

 

 


 

We see that the label of the error dialog box has been set to the specified text. The parser provides the string with the error location shown. The prompt for the corrected input has been specified by the programmer. The control buttons correspond to the input mode and the presence or absence of suggestion. The requests for x and y values uses the optional model - and we see in the code that we need to catch the CancelledException. The demands for z and w values do not allow the user to exit the error dialog until valid input has been given.

 

To set the view state, we supply the String that will be displayed in the TextFieldView:

 

// update the sum text field view to show the result

sumTFV.setViewState(sum + "");

 

We still need to see where the programmer provides the Suggestion for the user. A Suggestion is stored as one of the InputProperties:

 

// set suggestions

yTFV.getInputProperties().setSuggestion("100");

wTFV.getInputProperties().setSuggestion("200");


 

 


The snapshot shows the error dialog box for wTFV after a suggestion has been selected.

 


These input properties can be added to a TextFieldView at any time - and can be modified during the execution of the program, So for example, if the TextFieldView displays a counter, the Suggestion can be updated to always represent the next item in the order.

 

The code for the clear function illustrates again how the data model or any part of the program can display the desired string in the TextFieldView.

 

public void clear() {

// reset all view states to those used in the constructor

xTFV.setViewState(XVALUE);

yTFV.setViewState(YVALUE);

zTFV.setViewState(ZVALUE);

wTFV.setViewState(WVALUE);

sumTFV.setViewState("");

}

 

Defining and using Actions

 

The two functions, add and clear are the two actions the program can perform. In the GUI there are two buttons labeled Add and Clear that let the user select one of these actions. We will learn how the controls that trigger these actions are created.

 

We have already seen that actions are functions that the user wants to perform when some "action buttons" are pressed. There could be a list of functions to perform, but for now, we assume each button corresponds to a single function. We define the functions as we would define any methods in a class.

 

The Actions are objects that connect the name of the button with the method that will be called when button is activated. Note that the name of the action object is the same as the name of the function that will be invoked. This is not a requirement, but a convenience. Thus, the programmer only needs to select three identifiers (the name of the SimpleAction object add or clear, the corresponding name of the function to call for this action add(); or clear();, and finally a label for the action button "Add" or "Clear".

 

/*

* the add action is constructed using

* the String "Add" to name its button

* and specifying the add method to be performed when pressed

*/

private Action add =

new SimpleAction("Add") {

public void perform() { add(); }

};

/*

* the clear action is constructed using

* the String "Clear" to name its button

* and specifying the clear method to be performed when pressed

*/

private Action clear =

new SimpleAction("Clear") {

public void perform() { clear(); }

};

 

An ActionsPanel is a JPT GUI component that can contain one or more actions manifested as buttons. We first construct and array of Actions objects, then use this array as a parameter to the constructor for the ActionsPanel as follows:

 

Define the array of actions:

/*

* array of actions is constructed

*/

private Action[] actionList = {add, clear};

 

Construct the array panel:

// actions panel

/*

* actions panel is constructed

* with the actions it will contain supplied as parameter

*/

private ActionsPanel actions = new ActionsPanel(actionList);

 

The actions panel is added to the overall display when we compose the entire GUI.

Installing the GUI components Part 1

 

The main program calls the createQuickJPTFrame function that creates a window with the given title and calls a constructor for the main GUI content pane TFVsample that extends a DisplayCollection.

 

/*

* create a window

* titled "Simple Adder"

* whose contents are defined by the TFVsample constructor

*/

public static void main(String[] args) {

JPTFrame.createQuickJPTFrame("Simple Adder", new TFVsample());

}

 

The constructor places the various GUI components in their locations and adds more labels and annotations.

 

/*

* constructor for the graphical user interface of the application

* installs four input views and one output view

* installs an action panel with two action buttons - add and clear

* sets the preferred width for all text field views

* sets the suggestion for two of the four input views

* wraps and titles data fields and actions

* and adds them to the main panel

*/

 

The constructor consists of two sections: the View section and the GUI building section. We start with the View section:

 

public TFVsample() {

 

//////////////////

// View Section //

//////////////////

 

// set preferred width for the text fields

xTFV.setPreferredWidth(150);

yTFV.setPreferredWidth(150);

zTFV.setPreferredWidth(150);

wTFV.setPreferredWidth(150);

sumTFV.setPreferredWidth(132);

 

// set suggestions

yTFV.getInputProperties().setSuggestion("100");

wTFV.getInputProperties().setSuggestion("200");

 

We specified the widths of the five TextFieldViews and added the suggestions to two of the input fields.


Then comes the GUI building section. There are five statements that add each of the five TextFieldViews to the dataFields DisplayCollection:

 

/////////////////

// GUI Section //

/////////////////

// add text fields and actions panel

// to the dataFields DisplayCollection

// add xTFV to dataFields

dataFields.add(

new DisplayWrapper(

new Display(xTFV, "X:", null)));

 

The parameters for Display specify the TextFieldView object, an annotation to appear before the view, and the last parameter indicates that we do not wish to title this display. Enclosing a Display in a DisplayWrapper prevents it from unsightly stretching or shrinking caused by the Java layout managers.

 

We now wrap the data fields and actions into another Display - we title these displays but do not use annotations. Finally we add these two displays to the TFVSample display collection;

 

// build two titled displays and add them to the main panel

// wrap dataFields into a titled display

Display dataFieldsDisplay =

new Display(dataFields, null, "Data Fields");

 

// wrap actions into a titled display

Display actionsDisplay =

new Display(actions, null, "Actions");

 

// add data field display and actions display to the main panel

add(dataFieldsDisplay);

add(actionsDisplay);

 


Adapting the TextFieldView to use double

 

To modify the first program so that it would add doubles instead if ints, we only have to do a few things. The GUI now looks as follows:

 

 

The TextFieldViews do not care what they display. If in the error dialog we had used a prompt such as "Fix X value:", there would be no changes. However, we now have to modify the TextFieldView definitions to include a correct prompt:

 

private TextFieldView xTFV =

 

new TextFieldView(

 

XVALUE, // initial value displayed in the TFV

"X has to be a double:", // prompt for correcting the input

"Incorrect input"); // title for the error dialog box

 

Of course, the add method needs to change the types of the five variables to doubles and the statements used to extract values from the views change from requestInt and demandInt to requestDouble and demandDouble, respectively. For example we write:

 

// mandatory input using demand

// note that no suggestion is specified for this text field view z = zTFV.demandDouble();

 

We must also change the constants that are used to set the initial display in the four input views:

 


// constants: used to set and reset text fields

private static final String XVALUE = "10.5";

private static final String YVALUE = "2*pi";

private static final String ZVALUE = "e";

private static final String WVALUE = "5.2/2";

 

The parser recognizes pi and e as double values and can also parse any valid arithmetic expression. It even correctly converts 1/0 to Infinity and computes that the sum of two doubles 1/0 + (-1/0) results in NaN (not a number as specified in IEEE Standard).

 

Finally, because we changed the name of the class, we have to change the constructor name and use the correct class constructor TFVDoubleSample when calling the createQuickJPTFrame function in main and we must modify the title of the application window appropriately:

 

JPTFrame.createQuickJPTFrame

("Simple Double Adder", new TFVdoubleSample());

This completes the discussion of the second program. We encourage the reader to explore the behavior of both adders with different expressions. It can be used in a classroom to explain the rules of integer and double arithmetic in Java.

 


JPT Idioms

 

TextFieldView (TFV)

 

TextFieldView constructor:

 

private TextFieldView xTFV =

new TextFieldView(

XVALUE, // initial value displayed in the TFV

"X has to be a double:", // prompt for correcting the input

"Incorrect input"); // title for the error dialog box

 

xTFV the text field identifier

XVALUE String - initial value that will be displayed in the TFV

"X has ...:" String - prompt for correcting the input - in the error dialog box

"Incorrect..." String - title of the error dialog box

 

Adding a suggestion to the TFV:

 

yTFV.getInputProperties().setSuggestion("100");

 

xTFV identifier for the TFV that will get the suggestion

"100" String - the value that will be displayed in the TFV when user asks for a suggestion (by selecting Suggest button)

 

Setting the String displayed in the TFV:

 

sumTFV.setViewState(sum + "");

 

sumTFV identifier for the TFV that will get the suggestion

sum + "" String - the value that will be displayed in the TFV

 

Extracting the input from TFV using the mandatory model:

 

double z = zTFV.demandDouble();

 

double z identifier for the variable or object that will receive the input

zTFV identifier for the TFV that will supply the input

demandDouble demand followed by the basic data type of the expected input

 

Extracting the input from TFV using the optional model:

 

try {

int x = xTFV.requestInt();

}

catch (CancelledException ex) {

// code to execute if incorrect input is not fixed

return;}

 

int x identifier for the variable or object that will receive the input

xTFV identifier for the TFV that will supply the input

requestInt request followed by the basic data type of the expected input

 

Seting the preferred width for TFV:

 

xTFV.setPreferredWidth(150);

 

xTFV identifier for the TFV that has its width set

150 the preferred width in pixels

 

Adding a TFV to a Display:

 

 

dataFields.add(new Display(sumTFV, "Sum:", null)));

 

dataFields identifier of a DisplayPanel to which TFV is added

sumTFV identifier for the TFV that is being placed in the DisplayPanel

"Sum:" String - the annotation that will appear left of the TFV

null String - the label of the Display. The Display will be bounded and the label will appear on the top of the bounding box. Nothing will be visible if label is null.

 

Alternative way for adding a TFV to a Display:

 

dataFields.add(

new DisplayWrapper(

new Display(xyzTFV, "Sum:", null)));

 

By using the DisplayWrapper we assure that the TFV will retain a sensible size when the window in which it is contained changes its size.

 


ActionsPanel and Action

 

We need an ActionsPanel and one Action for each action we wish to perform:

 

Action declaration and initialization:

 

private Action add = new SimpleAction("Add") {

public void perform(){ add(); }

};

 

add identifier for the SimpleAction object

"Add" label for the action button

add(); method that will be called on button press

 

 

Constructor for a list of actions to be used to initialize ActionsPanel

 

private Actions[] actionsList = {add, clear};

 

actionsList identifier for the Actions array object

add identifier for the first SimpleAction object to be used

clear identifier for the second SimpleAction object to be used

 

ActionsPanel constructor:

 

private ActionsPanel actions = new ActionsPanel(actionsList);

 

actions identifier for the ActionsPanel object

actionsList identifier for the list of actions this panel will contain

 

Adding the actions panel directly to the GUI display collection inside its constructor:

 

add(actions);

 

actions identifier for the ActionsPanel object to add to the DisplayCollection

 

Alternative way for adding the actions panel to actionsDisplay Display:

 

Display actionsDisplay = new Display(actions, null, "Actions");

add(actionsDisplay);

 

actionsDisplay identifier for the Display to be built

actions identifier for the ActionsPanel object to add to the Display

null String - annotation of the Display (not used)

"Actions" String - title for the Display