This chapter gives an approach to building graphical user interfaces GUIsthat combines a declarative base together with a selected set of procedural con-cepts including objects and threa
Trang 1Specialized Computation Models
Trang 3Graphical User Interface
Programming
“Nowadays the growth of a graphic image can be divided into two
sharply defined phases The process begins with the search for a
visual form that will interpret as clearly as possible one’s train of
thought [ ] After this, to my great relief, there dawns the second
phase, that is the making of the graphic print; for now the spirit can
take its rest while the work is taken over by the hands.”
– The Graphic Work of M.C Escher, M.C Escher (1898–1972)
This chapter shows a particularly simple and powerful way to do graphical userinterface (GUI) programming We combine the declarative model together withthe shared-state concurrent model in an approach that takes advantage of thegood properties of each model To introduce the approach, let us first summarizethe existing approaches:
• Purely procedural The user interface is constructed by a sequence of
graph-ics commands These commands can be purely imperative, as in tcl/tk,object-oriented, as in the Java AWT (Abstract Window Toolkit) package orits extension, the Swing components, or even functional, as in Haskell fud-gets The object-oriented or functional style is preferable to an imperativestyle because it is easier to structure the graphics commands
• Purely declarative The user interface is constructed by choosing from a set
of predefined possibilities This is an example of descriptive declarativeness,
as explained in Section 3.1 A well-known example is HTML (HyperTextMarkup Language), the formatting language used for Web pages
• Using an interface builder The user interface is constructed manually by
the developer, using a direct manipulation interface A well-known example
is Microsoft Visual Studio
Trang 4The procedural approach is expressive (anything at all can be done at run time)but is complex to use The declarative approach is easy to use (a few simpledeclarations suffice to create an interface) but lacks expressiveness The interfacebuilder approach is easy to use and gives immediate feedback on the interface,but it lacks expressiveness and the interface is hard to change at run time None
of these approaches is satisfactory In our view, this is because each is limited to
a single computation model
This chapter gives an approach to building graphical user interfaces (GUIs)that combines a declarative base together with a selected set of procedural con-cepts including objects and threads We provide a user interface toolkit that isboth expressive and easy to use In the context of the book, this has two goals:
• To present the ideas underlying a practical tool for GUI design that gives
the user a high level of abstraction It turns out that the combination ofdeclarative and non-declarative (i.e., procedural) techniques is particularlyappropriate for graphical user interface design
• To give a realistic example that shows the advantages of programming with
concepts instead of programming in models We start from the declarativeprogramming techniques of Chapter 3 and add state and concurrency ex-actly where it is needed This is a practical example of combining severalcomputation models
To a first approximation, our user interface specifications are just data structures,which can be calculated at run time The declarative model makes it easy tocalculate with symbolic data structures such as records and lists This meansthat we can easily define and manipulate quite sophisticated user interfaces Forexample:
• We build a context-sensitive clock widget, that changes its shape and
pre-sentation depending on an external parameter, which is the window size.Other widgets and external parameters are just as easily programmed
• We show how to generate user interfaces quickly starting from program
data It requires just a few simple data structure manipulations
The ideas in this chapter are embodied in the QTk module, which is part of theMozart system [65] QTk (“Quick Tk”) is a full-featured GUI design tool based
on the declarative approach [66, 67] QTk is implemented as a front end to thetcl/tk graphics package It has been used to build GUIs for real applications.All the examples we give can be run directly with QTk This chapter gives most
of the key ideas underlying QTkbut only shows a small fraction of the availablewidgets
Structure of the chapter
The chapter consists of four sections:
Trang 5• Section 10.1 introduces the basic concepts underlying declarative and
pro-cedural approaches and how we propose to combine them
• Section 10.2 gives an introduction to the principles of QTk and how to use
it to build user interfaces
• Section 10.3 gives four case studies to progressively illustrate different
as-pects of the approach: a simple progress monitor, a calendar widget, the
automatic generation of a user interface from a data set, and a
context-sensitive clock
• Section 10.4 says a few words about how QTkis implemented
10.1 Basic concepts
What are the relative merits of the declarative and procedural approaches to
specifying user interfaces? The trade-off is between manipulability and
expres-siveness:
• The declarative approach defines a set of possibilities for different attributes.
The developer chooses among this set and defines a data structure that
de-scribes the interface A purely declarative approach makes it easy to
formal-ly manipulate the user interface definitions, e.g., to translate raw data into
a user interface or to change representations However, the expressiveness
is limited because it is only possible to express what the designers initially
thought of
• The procedural approach gives a set of primitive operations and the ability
to write programs with them These programs construct the interface A
purely procedural approach has no limits on expressiveness, since in its
general form it defines a full-fledged programming language However, this
makes it harder to do formal manipulations on the user interface definitions,
i.e., to calculate the user interface
This trade-off is not a temporary state of affairs, to be solved by some ingenious
new approach It is a deep property of computation models As a language
becomes more expressive, its programs become less amenable to formal
manipu-lation This is illustrated by the Halting Problem.1
However, this trade-off is not as bad as it seems on first glance It is still
possible to define a model that is both manipulable and expressive We can do it
by combining the declarative and procedural approaches We use the declarative
1Assume a language as expressive as a Turing machine, i.e., it is based on a general-purpose
computer with potentially unbounded memory Then it is impossible to write a program that,
when given an input program, determines in finite time whether or not the input program will
halt.
Trang 6approach in those areas where manipulability is important but a limited siveness is sufficient We use the procedural approach for those areas whereexpressiveness is essential To be precise, for each window we define four partsdeclaratively:
expres-• The static structure of the window as a set of nested widgets, where a widget
is a primitive component of a graphical user interface
• The widget types.
• The initial states of the widgets.
• The resize behavior of the window, i.e., how the widgets change size and
relative position when the window size changes
We define two parts procedurally:
• Procedures that are executed when external events happen These dures are called actions Events are external activities that are detected by
When designing a graphical user interface, we recommend to use the ative approach as the primary approach, and to supplement it with proceduralaspects to increase expressiveness exactly where it is needed There is a recentstandard for Web design, Dynamic HTML, that also makes it possible to combinethe declarative and procedural approaches [61] It uses character strings instead
declar-of records for the declarative part It is not as tightly integrated with a gramming language as the approach of this chapter At the time this book waswritten, the performance of the declarative part was not yet adequate to supportthe design approach we recommend
pro-10.2 Using the declarative/procedural approach
As much of the interface as possible is defined declaratively as record values.
Records are a good choice for two reasons: they are very general data structures
and it is easy to calculate with them The GUI consists of a set of widgets, where
each widget is specified by a record Specifying a GUI is done not by defining anew mini-language, but by using records in the existing language Programming
a complex GUI then becomes a simple matter of doing calculations with recordsand lists Since both are strongly supported by the declarative model, thesecalculations are easy to specify and efficient
Trang 7User interface description: recordcontaining action procedures andunbound variables (for handler objects)
Window on screen
Build user interface
Actions and handler objects
(interpret description to create handler objects and event thread)
Data to be displayed
Calculate user interface description
ApplicationHuman user
Figure 10.1: Building the graphical user interface
10.2.1 Basic user interface elements
The GUI model of this chapter has five basic elements:
• Windows and widgets A window is a rectangular area of the screen that
contains a set of widgets arranged hierarchically according to a particular
layout A widget is a GUI primitive that is represented visually on the screen
and that contains an interaction protocol, which defines its interactions with
a human user A widget is specified by a record, which gives its type, initial
state, a reference to its handler, and some of the actions it can invoke (see
below) An interaction protocol defines what information is displayed by
the widget and what sequences of user commands and widget actions are
acceptable
• Events and actions An event is a well-defined discrete interaction by the
external world on the user interface An event is defined by its type, the
time at which it occurs, and possibly some additional information (such as
the mouse coordinates) Events are not seen directly by the program, but
only indirectly by means of actions An event can trigger the invocation of
an action An action is a procedure that is invoked when a particular event
occurs
• Handlers A handler is an object with which the program can control a
widget Each widget can have a corresponding handler
Trang 8Figure 10.2: Simple text entry window
10.2.2 Building the graphical user interface
Figure 10.1 shows how a graphical user interface is built It starts with the data
to be displayed This data is manipulated to create a record data structure, the
user interface description This description defines the logical structure of the
interface as a nested record The record contains embedded action proceduresand unbound variables which will become references to handler objects Therecord is passed to a procedure QTk.build, which interprets it and builds theinterface QTk.builddoes two things
• It builds a window using its underlying graphics package.
• It sets up an internal mechanism so that the application can interact with
the window For this, it creates one handler object per widget and onethread per window It registers the action procedures with the widgetsand the events they are triggered on The action procedures are executedsequentially in the thread as window events arrive
An example
The easiest way to see how this works is by means of an example Here is a simpleuser interface description defined as a record:
D=button(text:"Click this button")
The record D defines a widget of button type and the content of the textfieldgives the initial text in the button Other widgets follow the same conventions.The record label denotes the widget type, the field names denote widget param-eters, and the field contents denote either the parameters initial values or theprocedural parts of the interface (actions or handlers)
Some of the widgets are able to contain other widgets By using these, thecomplete user interface is a nested record that defines all the widgets and theirlogical organization on the screen For example, here is a simple interface fordoing text entry (see Figure 10.2):
D=td(lr(label(text:"Type your name:")
entry(handle:H))
button(text:"Ok" action:proc {$} {W close} end))
Trang 9Figure 10.3: Function for doing text entry
Figure 10.4: Windows generated with thelr and tdwidgets
The tdwidget organizes its member widgets in top-down fashion The lrwidget
is similar, but goes left to right This example has one action, proc {$} {W
close} end, and a handle, H, which we will explain later At this point, both
H and W are still unbound variables Create the window by passing D to the
QTk.build procedure:
W={QTk.build D}
This creates a window, a window objectWthat represents it, and a handler object
H Now we display the window:
{W show}
The user can type text in this window At any time, the text in the window can
be read by calling the handler H:
T={H get($)}
This is usually done when the window is closed To make sure it is done when
the window is closed, we can put it inside the action procedure
To complete this example, let us encapsulate the whole user interface in a
function called GetText Figure 10.3 shows the resulting code Calling GetText
will wait until the user types a line of text and then return the text:
{Browse {GetText "Type your name:"}}
Note that GetText does a {W wait} call to wait until the window is closed
before returning Leaving out this call will return immediately with an unbound
variable that is bound later, when the user clicks the button
Trang 10Figure 10.5: Window generated with newlineand continuecodes
In addition to the widgets themselves, there are two other aspects of a windowthat are defined declaratively: the geometric arrangement of its widgets and thebehavior of its widgets when the window is resized We describe each in turn Thegeometric arrangement of widgets is defined by means of three special widgetsthat can contain other widgets:
• Thelrandtdwidgets arrange their member widgets left-right or top-down.Figure 10.4 shows the two windows that are displayed with the followingtwo commands:
D=lr(label(text:"left")label(text:"center")label(text:"right"))W1={QTk.build D}
{W1 show}
E=td(label(text:"top")label(text:"center")label(text:"down"))W2={QTk.build E}
{W2 show}
• Theplaceholderwidget defines a rectangular area in the window that cancontain any other widget as long as the window exists The placeholder’scontent can be changed at any time during execution A placeholder may beput inside a placeholder, to any level of recursion In the following example,the window alternatively contains a label and a pushbutton:
placeholder(handle:P)
Trang 11n
s
Figure 10.6: Declarative resize behavior
• The lr and td widgets support the special codes newline, empty, and
continue, which allows to organize their member widgets in a grid
struc-ture with aligned rows and columns of the same size (see Figure 10.5) The
codenewline makes the subsequent contained widgets jump to a new row
(for lr) or column (for td) All the widgets in the new row or column are
aligned with the widgets in the previous row or column The empty
spe-cial code leaves an empty box the size of a widget The continue special
code lets the previous widget span over an additional box The following
description:
lr(button(text:"One" glue:we)
button(text:"Two" glue:we)button(text:"Three" glue:we) newlinebutton(text:"Four" glue:we)
button(text:"Five" glue:we)button(text:"Six" glue:we) newlinebutton(text:"Seven" glue:we)
button(text:"Eight" glue:we)button(text:"Nine" glue:we) newlineempty button(text:"Zero" glue:we) continue)
gives the window of Figure 10.5
10.2.4 Declarative resize behavior
When the size of a window is changed, the interface has to define how the internal
widgets rearrange themselves This is called the resize behavior The resize
behavior is dynamic, i.e., it defines a behavior over time But it is a sufficiently
restricted kind of dynamic behavior that we can define it using a descriptive
declarative model
Trang 12Figure 10.7: Window generated with the glueparameter
We define the resize behavior of a widget by an optional glue parameter, whose
value is an atom made up of any combination of the letters n, s, w, and e Theglue parameter places constraints on how the widget is placed and how it resizes
As Figure 10.6 illustrates, a widget W is always placed inside a rectangular areaand has a “natural” size defined by its contents One can choose for the widget
to occupy its natural size in either direction (horizontally or vertically) or to beexpanded to take as much space as possible in either direction For the left-right direction, the w value, when present, will attach the widget to the left side(“west”) The same for the e value (“east”) and the right side If w and e arepresent simultaneously, then the widget is expanded Otherwise, it takes up justits natural size For the top-down direction, the n and s values play the sameroles (“north” and “south”) For example, the description:
lr(label(text:"Name" glue:w) entry(glue:we) glue:nwe)
gives the window of Figure 10.7
The dynamic behavior of widgets is defined by means of action procedures andhandler objects Look again at the example of Section 10.2.2:
declare E D W in
D=td(lr(label(text:"Type your name:")
entry(handle:E))button(text:"Ok" action:toplevel#close))W={QTk.build D}
{W show}
The action toplevel#closeis part of the button; when the button is clickedthen this causes the window to be closed Generally, actions are zero-argumentprocedures, except that short-cuts are given for a few common actions such asclosing the window The handle E is an object that allows to control the textentry widget For example, here’s how to set the value of the widget’s text entryfield:
Trang 13{E set("Type here")}
Here is how to read and display the value of the field:
{Browse {E get($)}}
Actions can also be attached to events such as mouse clicks:
proc {P} {Browse ´clicked with third mouse button!´} end
{E bind(event:"<3>" action:P)}
The event "<3>" means a click of the third mouse button Attaching it to E
means that the button has to be clicked when the mouse is over E’s widget A
complete list of possible events is given in the QTk documentation in the Mozart
system
10.3 Case studies
We present four case studies that show different techniques of user interface
de-sign:
• The first is a simple progress monitor This example has no special features
except to show how simple it is to build a custom display for a particular
purpose
• The second builds a simple calendar widget It is based on an lr widget
with gridding ability It shows the flexibility of the gridding It also shows
how to use a placeholder and how state can be introduced to optimize
execution of what is originally a purely declarative calculation
• The third derives two different GUIs by transforming one data model into
two GUI specifications The user can switch between the two at any time
This shows the advantage of tightly integrating the GUI tool with the
lan-guage, since different data models can be represented with the same data
structures (e.g., records and lists) and transformed with the same
opera-tions
• The fourth defines a clock with an interface that adapts itself according to
an external condition The best view of the clock data is chosen dynamically
depending on the window size Because of the mixed declarative/procedural
approach, each view can be completely defined in just a few lines of code
The second through fourth case studies were originally written by Donatien
Gro-laux
We start by defining the simple interface that we used in Section 5.5.1 to monitor
the progress of a message-passing program The interface has a check button