7. Creating Graphical User Interfaces in the Jess Language
Jess, being just a set of Java classes, is easily incorporated as a
library into graphical applications written in Java. It is also
possible, though, to write graphical applications in the Jess
language itself. The details of this are outlined in this chapter.
7.1. Handling Java AWT events
It should now be obvious that you can easily construct GUI objects from
Jess. For example, here is a Button:
(defglobal ?*b* = (new java.awt.Button "Hello"))
What should not be obvious is how, from Jess, you can arrange to have something
happen whan the button is pressed. For this, I have provided a full set
of EventListener classes:
-
jess.awt.ActionListener
-
jess.awt.AdjustmentListener
-
jess.awt.ComponentListener
-
jess.awt.ContainerListener
-
jess.awt.FocusListener
-
jess.awt.ItemListener
-
jess.awt.KeyListener
-
jess.awt.MouseListener
-
jess.awt.MouseMotionListener
-
jess.awt.TextListener
-
jess.awt.WindowListener
Each of these classes implements one of the Listener interfaces
from the java.awt.event package in Java 1.1 and later. Each implementation
packages up any event notifications it receives and forwards them to a
Jess function, which is supplied as a constructor argument
to the Listener object.
An example should clarify matters. Let's say that when the Hello
button is pressed, you would like the string Hello, World! to
be printed to standard output (how original!). What you need to do is:
-
Define a deffunction which prints the message. The deffunction
will be called with one argument: the event object that would be passed
to actionPerformed(). (If this is gibberish to you, pick up a
book on Java AWT programming.)
-
Create a jess.awt.ActionListener object, telling it about
this deffunction, and also which Jess engine it belongs
to. You simply use Jess' new command to do this.
-
Tell the Button about the ActionListener using the addActionListener
method of java.awt.Button.
Here's a complete program in Jess:
;; Create the widgets
(defglobal ?*f* = (new java.awt.Frame "Button Demo"))
(defglobal ?*b* = (new java.awt.Button "Hello"))
;; Define the deffunction
(deffunction say-hello "Unconditionally print a message" (?evt)
(printout t "Hello, World!" crlf))
;; Connect the deffunction to the button
(?*b* addActionListener
(new jess.awt.ActionListener say-hello (engine)))
;; Assemble and display the GUI
(?*f* add ?*b*)
(?*f* pack)
(set ?*f* visible TRUE)
The Jess engine function returns the jess.Rete object
in which it is executed, as an external address. You'll have to quit
this program using ^C. To fix this, you can add a
WindowListener which handles WINDOW_CLOSING
events to the above program:
;; If the event is a WINDOW_CLOSING event, exit the program
(deffunction frame-handler (?evt)
(if (= (?evt getID) (get-member ?evt WINDOW_CLOSING)) then
(call (get ?evt source) dispose)
(exit)))
;; Connect this deffunction to the frame
(?*f* addWindowListener
(new jess.awt.WindowListener frame-handler (engine)))
Now when you close the window Jess will exit. Notice how we can examine
the ?evt parameter for event information.
We have used the "raw" AWT widgets here, but this same technique works
fine with Swing as well (the new GUI toolkit for Java 1.2).
(defglobal ?*f* = (new javax.swing.JFrame "Button Demo"))
(defglobal ?*b* = (new javax.swing.JButton "Hello"))
(defglobal ?*p* = (get ?*f* "contentPane"))
(deffunction say-hello (?evt)
(printout t "Hello, World!" crlf))
(call ?*b* addActionListener
(new jess.awt.ActionListener say-hello (engine)))
(call ?*p* add ?*b*)
(call ?*f* pack)
(set ?*f* visible TRUE)
(deffunction frame-handler (?evt)
(if (= (?evt getID) (get-member ?evt WINDOW_CLOSING)) then
(call (get ?evt source) dispose)
(exit)))
(?*f* addWindowListener
(new jess.awt.WindowListener frame-handler (engine)))
See the demo examples/frame.clp for a slightly more complex
example of how you can build an entire Java graphical interface from within
Jess.
7.2. Screen Painting and Graphics
As you may know, the most common method of drawing pictures in Java is
to subclass java.awt.Canvas, overriding the
void paint(Graphics g) method to call the methods of the
java.awt.Grpahics argument to do the drawing. Well, Jess
can't help you to subclass a Java class (at least not yet!), but it
does provide an adaptor class, much like the event adaptors described
above, that will help you draw pictures. The class is named
jess.awt.Canvas, and it is a subclass of
java.awt.Canvas. As such it can be used as a normal Java GUI
component. When you construct an instance of this
class, you pass in the name of a Jess function and a reference to the
Rete engine. Whenever
paint() is called to render the jess.awt.Canvas,
the jess.awt.Canvas in turn will call the
given function. The function will be passed two arguments: the
jess.awt.Canvas instance itelf, and the
java.awt.Graphics argument to paint(). In this way,
Jess code can draw pictures using Java calls. An example looks like
this:
;; A painting deffunction. This function draws a red 'X' between the
;; four corners of the Canvas on a blue field.
(deffunction painter (?canvas ?graph)
(bind ?x (get-member (call ?canvas getSize) width))
(bind ?y (get-member (call ?canvas getSize) height))
(?graph setColor (get-member java.awt.Color blue))
(?graph fillRect 0 0 ?x ?y)
(?graph setColor (get-member java.awt.Color red))
(?graph drawLine 0 0 ?x ?y)
(?graph drawLine ?x 0 0 ?y))
;; Create a canvas and install the paint routine.
(bind ?c (new jess.awt.Canvas painter (engine)))
A simple but complete program built on this example is in the file
examples/draw.clp in the Jess distribution.