Comp210 Lecture # 40    Spring 2003

Event-driven Graphical User Interfaces (GUIs) and Strategy Designs

Graphical user interfaces are the "windowed" systems we are all used to. They work differently than the algebraic, functional programs we've been writing. GUIs are event-driven, which means that the things that happen are not because we ran a particular function, but rather in reponse to events, which could be in the form of keystrokes, mouse clicks, network inputs, screen repaint request, etc. This means that things are quite different than what we've been doing. In particular, there is no set sequence of function calls because one doesn't know the order in which the events will occur.

Basically, when the user creates an event, such as clicking on a button, the system calls a particular function, called an "event listener" or "event observer". That function then goes off and does whatever is supposed to be done when that button is clicked. The function that the button calls is said to "listen" for the event or to be an "observer" of the event.

But here's the problem: If we are running an immutable system, what can the event listener actually accomplish? For instance, suppose the listener goes off an creates a wonderfully complex fractal. But the event to repaint (redraw) the screen has its own listener that actually draws the fractal onto the screen. But if the button's listener doesn't mutate anything, then the fractal that is drawn onto the screen is still un-grown. Bummer.

Thus, event-driven systems fundamentally must be mutable to work.

Today, we will explore a GUI system to see how mutation, abstraction and polymorphism work hand-in-hand.

First, some terms:

Glossary (not complete)

Frame: A window that, when displayed, has a title bar and usually has resizing controls on it. Frames can hold GUI components such as panels, buttons, text fields, radio buttons, canvases, etc.

Panel: A GUI component that defines a region inside of a frame. A panel can hold other GUI components, including other panels. Can you say Composite Design Pattern?

Button: A GUI component that appears as a clickable button on the screen. Buttons have a text caption and/or a image icon on them. Buttons can have (multiple) event listeners that are all called when the button is clicked. The button often passes information about the button itself and the event that occurred to the event listener. Buttons are very useful for creating an event that causes something to happen.

Radio Button: A group of radio buttons acts like the old-style push station-selection buttons in the car radios from when I was your age. That is, if one radio button is selected by clicking the mouse on it, then all the other radio buttons are automatically deselected. Thus only one radio button can be selected at a time. Event listeners are (should) be attached to each radio button, so that when that button is selected, the attached event listener(s) are called. Radio buttons are very good for selecting an input that will be used when another event takes place.

Label: A GUI component used to display textual information on the screen. Labels are often but not always limited to a single line of text.

Text Field: A GUI component used to input a single line of textual information. Text fields usually create an event when the user types in a Return/Enter. This event can be used to process the contents of the text field.

Canvas: A GUI component that is used to display line and shaded area graphics.

Repaint/redraw: All frames and GUI components can be asked to repaint or redraw themselves. Note that this is a request for repainting, not a command. The operating system will redraw the screen when its good and ready to do so. When the operating system finally redraws the system, each component will call its paint event listener which will create any custom drawings desired on the screen.

Timer: A timer is an object that will periodically create an event. Thus, at regular intervals, all event listeners that the timer has are called. Timers are very useful for creating regularly occuring processes, such as animations. In an animation, the timer's event listener calls the repaint request on an particular component, such as the frame or a canvas. When that component is actually repainted, it will call its paint event listener, which is the function that will actually redraw the animated entity one the screen.

 

Radio Buttons: The Good and The Evil....

There is a right way and a wrong way to use a radio button.

Suppose we have a process whose inputs are determined by which radio button is selected. There are two possible ways that this could be handled:

Compare and contrast the two above techniques by asking yourself these questions:

  1. What happens if we add another radio button?
  2. What happens if there is more than one process that uses the information provided by the radio buttons? Answer question #1 in this light.
  3. What if we want to rearrange the order of the radio buttons?
  4. In which case will the radio-button dependent process run faster?
  5. How can the notion of "decoupling" be used to describe the two possibilities and what are the advantages it incurs?

Description of Demo Program

In the demo provided, radio buttons are used to select which growing function is used to grow a fractal tree by setting an abstract growing function to a concrete function.

A button then grows the tree with that growing function and also requests that the canvas be repainted. The button simply executes the visitor to grow the fractal providing the abstract growing function. By polymorphism, the fractal then grows by whatever concrete growing function the radio buttons set it to be. Since our fractal-growing code is for immutable systems, we sinply set the fractal is set back to being its own grown self.

When the canvas is repainted, it calls its paint listener (supplied when the canvas was first created) to actually draw the fractal onto it using the drawing visitor. The only change to the tree fractal code was to use the new GUI system was to use a different function to draw a Line

There is a button that adds the current growing functions to a list of growing functions. This is done by setting the list back to its cons'd self. The last radio button creates a growing function that will grow the fractal as per all the growing functions in the list. Since the number and order of the growing functions in the list of growing functions depends on what growing functions were selected and what order they were added to the list of growing functions, the results produced by that radio button are determined at run time. The demo thus demonstrates true dynamic behavior modification.

In the parlance of Design Patterns, the "Grow Tree" button calls a function that uses an abstract growing strategy. A strategy is the abstract way a process does a sub-process. Here, the growing visitor, doesn't care how the fractal's base case grows, just that it does. so it uses an abstract growing strategy to accomplish this. The radio buttons are setting the abstract strategy to a concrete strategy value (remember that functions are first class values), hence providing the final behavior of the system. You can find out more about the Strategy Design Pattern here: http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/StrategyPattern.htm

Another way to think about what's going on is that the abstract data (growFn and growFns) represent a state of the system and the radio buttons are changing the state of the system. The "Grow" button behavior depends on the state of the system and hence the fractal grows differently because the system is in another state. While this is very similar to the State Design Pattern, it is not the same thing -- its the Strategy Design Pattern.

Question: What's the difference between the State and Strategy Design Patterns?

Finally there is a button that resets the list of growing functions by setting the list to the empty list.

 

In order to run the following code, the language level must be set to "Pretty Big (includes MrEd. and Advanced)".

(Note that the documentation for DrScheme's GUI system is not part of a normal install and needs to be downloaded.)

Download all 3 files into a common directory. Load and run guitest.scm.

guitest.scm : contains all the demo-specific code. Loads the other two files and defines the abstract data structures. Creates the gui.

fractaltree2.scm : The fractal tree code from lab with only one minor modification to the drawLine function to use a different solid line drawing utility..

gui-lib.scm : a library of simplified but not complete set of functions that can be used to set up a GUI. There is nothing sacred about this code. It is simply there to encapsulate much of the unnecessary details of GUI creation. Please feel free to improve it -- I'll be happy to take any credit you don't want!

 

 

©2003 Stephen Wong