XPCE/Prolog Course Notes Edition 2
XPCE/Prolog Course Notes Edition 2
XPCE/Prolog
Course Notes
Jan Wielemaker
[Link]@[Link]
This document provides background reading material and exercises for a course in pro-
gramming XPCE/Prolog.
This is edition 2 of the XPCE training-course notes. It adds sections on types, custom-dialog de-
sign and interprocess communication to edition 1. Please E-mail comments and suggestions to
[Link]@[Link].
Typeset using LaTeX. LaTeX files created using perl preprocessors from a LaTeX extended nota-
tion for PCE documentation and raw XPCE/Prolog code. Diagrams are [Link] included PostScript
generated by PceDraw, the XPCE/Prolog drawing tool.
An electronic version of this manual is available using anonymous ftp to [Link] ([Link]),
directory /pub/xpce/doc/course.
Premission is granted to make and distribute verbatim copies of this manual provided this permission
notice is preserved on all copies.
Contents
1 Introduction 4
1.1 Organisation of the PCE documentation . . . . . . . . . . . . . . . . . . . . . . . . 5
3 Programming Techniques 12
3.1 Control Structure of Graphical Applications . . . . . . . . . . . . . . . . . . . . . . 12
3.1.1 Event Driven Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.1.2 Asking Questions: Modal windows . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Executable Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.1 Procedures and Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 Creating PCE Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.1 The Prolog Front End . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.2 Called methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.5 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.5.1 Types are not (only) classes . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.5.2 Programming conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2
5.4.2 Handling reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.6 Custom Dialog-Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
6 Graphics 27
6.1 Graphical Devices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.2 Making Graphicals Sensitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6.2.1 Adding popup menus to graphicals . . . . . . . . . . . . . . . . . . . . . . . 28
6.2.2 Using gestures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.3 Graphs: Using Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.4 Reading the Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
7 Multiprocess Applications 32
7.1 XPCE building blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.2 Calling non-interactive data-processing applications . . . . . . . . . . . . . . . . . . 33
7.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.3.1 Calling simple Unix output-only utilities . . . . . . . . . . . . . . . . . . . 33
7.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7.5 Front-end for terminal-based interactive applications . . . . . . . . . . . . . . . . . 35
7.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3
Chapter 1
Introduction
This document provides course-notes and exercises for programming in XPCE/Prolog. Some basic
knowledge of Prolog is assumed. XPCE provides the following subsystems.
XPCE is both a very large library and a programming environment in its own right. XPCE has
similar problems as other big systems such as SmallTalk, CommonLisp, GNU-Emacs and the Unix
Tool Kit. All of these environments are hard to master, just because they provide a virtually enless
collection of built-in behaviour together with an elegant mechanism to combine behaviour. Novice
users have little problems typing ‘ls’ to get a listing of the current directory. Putting all files modified
after STAMP onto a tape is harder.1
This course only deals with the basic building blocks of the XPCE environment. In addition to the
building blocks we will learn the ‘glue’ of XPCE: XPCE’s executable objects that allow for elegant
connections between GUI components and the application. Handling the manual and debugging tools
is another subject in this course.
Besides knowing the theory, practice and looking at examples are obligatory ingredients for learn-
ing a language.
1
tar cf /dev/rst0 ‘find . -newer STAMP -type f -print‘
4
1.1 Organisation of the PCE documentation
Currently, the following documents are available for learning XPCE. In addition tothese documents,
the demo programs, libraries and the sources of the online manual may be examined as a source of
examples.
• Programming in PCE/Prolog
[Wielemaker & Anjewierden, 1992b] Explains the basics of programming the PCE/Prolog en-
vironment. It should be the first document to read.
5
Chapter 2
X = @791307/point
Created an instance of class ‘point’ at location (5,6). In general, The first argument of new/2 provides
or is unified to the object reference, which is a Prolog term of the functor @/1. The second argument
is a ground Prolog term. The function describes the class from which an instance is to be created,
while the arguments provide initialisation arguments.
New/2 may also be used to create objects with a predefined object reference:
Yes
Created an instance of class size with width 100 and height 5. We call @s a ‘named’ or ‘global’ object
reference.
?- send(@791307, x, 7).
Yes
Send Manipulates objects. In the example above, the X-coordinate of the point is changed to 7. The
first argument of send is the object-reference. The second is the selector and the remaining arguments
(up to 10) provide arguments to the operation.
D = 9
6
Figure 2.1: Screendump for the ‘Hello World’ window
Get requests the object to return (compute) a value. In this case this is the (rounded) distance to the
origin of the coordinate system. The arguments to get/[3-13] are the same as for send/[2-12],
but get/[3-13] requires one additional argument for the return value.
Finally, the following call destroys the created object.
?- free(@791307).
Yes
P = @682375
First created a picture (=graphics) window labeled ‘Hello World’, displays a text (graphical object
representing text) on the picture at location (20,20) from the top-left corner of the window and finally
opens this window on the display:
2.3 Architecture
XPCE is a C-library written in an object-oriented fashion.1 This library is completely dynamically
typed: each data element is passed the same way and the actual type may be queried at runtime if
necessary. Also written in C is a meta-layer that describes the functions implementing the methods
1
It was developed before C++ was in focus. We are considering C++ but due to the totally different viewpoints taken by
XPCE (symbolic, dynamically typed) and C++ (strong static typing) it is unclear whether any significant advantage is to be
expected.
7
and the C-structures realising the storage. The meta-layer is written in the same formalism as the rest
of the system and describes itself. C-functions allow for invoking methods (functions) in this library
through the meta-layer. This sub-system is known as the message passing system or PCE virtual
machine. This machine implements the 4 basic operations of XPCE: new(), send(), get() and free().
The ‘host’ language (Prolog in this document) drives the PCE virtual machine instructions through
the Prolog to C interface.
PCE predefines class ‘host’ and the instance @prolog. Messages send to this object will call
predicates in the Prolog environment:
makes Prolog call PCE send() through its foreign interface. In turn, the message to the @prolog is
mapped on the predicate format. Note however that both systems have their own data representation.
Try
PCE has nothing that resembles a logical variable and thus will complain that ‘A’ is an illegal argu-
ment. Neither the following will work as Prolog lists have no counterpart in XPCE:2
Figure 2.2 summarises the data and control flow in the XPCE/Prolog system.
File/Demo Programs starts a demo-starter which also provides access to the sources of the demo’s.
Browsers/Class Hierarchy examines the inheritance hierarchy of PCE classes. Most classes are
right below class object. Useful sub-trees are program object (PCE’s class en executable world),
recogniser (event handling) and visual (anything that can appear on the screen.
2
The empty list [] is an atom, and thus maybe passed!
3
Quintus: user help/0. To use manpce/1, load the library(pce manual).
8
hostSend()
@prolog
hostGet()
P
message(@prolog, ...)
r Control
o flow
l ?(@prolog, ...)
o
g
send/[2-12]
new/2
PCE
get/[3-13] Virtual Machine
Prolog/PCE
Interface
9
Browsers/Class Browser is the most commonly used tool. It displays attributes of a class. Various
searching options allow for finding candidate methods or classes. The Scope option needs
explanation. By default it is set to ‘own’, making the tool search only for things (re)defined on
this class. Using scope ‘Super’ will make the browser show inherited functionality too. Using
scope ‘sub’ is for searching the entire class hierarchy below the current class.4
Browsers/Global Objects visualises all predefined (named) objects: @pce, @prolog, @display,
@arg1, @event, etc.
Browsers/Search Provides a WAIS search facility for the entire hypercard system.
Browsers/Group OverView provides an overview of functionally related methods. Typing in the
window searches.
Tools/Visual Hierarchy provides an overview of the consists of hierarchy of GUI components.
Tools/Inspector to analyse the structure of objects.
History allows you to go back to previously visited cards.
Manpce/1 accepts a classname, in which case it switches the ClassBrowser to this class. It also
accepts a term of the form ‘class ← selector’ or ‘class → selector’, in which case it
will pop up the documentation card of the indicated card. Try
?- manpce((display ->inform)).
2.5 Exercises
Exercise 1
Start the PCE manual tools and find answers to the following questions:
(a) What is the super-class of class ‘device’?
(b) Find the description of class ‘tree’. Which different layouts are provided by class tree?
(c) Which attributes describe the appearance of a box?
(d) Which classes (re)define the →report method?
Exercise 2
Examine the structure of the inheritance path visualisation as shown in the top-right window of
the ClassBrowser using the VisualHierachy tool.
Exercise 3
Class device defines compound (‘nested’) graphical objects. Define a predicate make text box(?Ref,
+Width, +Height, +String), which creates a box with a centered text object. Test your predicate
using:
?- send(new(P, picture), open),
make text box(B, 100, 50, ’Hi There’),
send(P, display, B, point(50, 20)).
4
The current implementation does not show delegated behaviour.
10
Exercise 4
Class tree and class node define primitives for creating a hierarchy of graphical objects. The
method ‘directory ← directories’ allow you to query the Unix directory structure.
Write a predicate show directory hierarchy(+DirName) with displays the Unix directory hier-
archy from the given root.
Some methods and classes you might want to use are: class picture, class tree, class node,
class directory and the methods ‘node → son’, ‘directory ← directories’ and
‘directory ← directory’.
11
Chapter 3
Programming Techniques
Yes
This query creates a button object and opens the button on your screen. The button is instructed to
call the Prolog predicate format/1 with the given argument when it is pressed. The query itself just
returns (to the Prolog toplevel).
Using this formalism, the overall structure of your application will be:
initialise :-
initialise_database,
create_GUI_components.
call_back_when_GUI_xyz_is_operated(Context ...) :-
change_application_database(Context ...),
update_and_create_GUI_components.
This type of control structure is suitable for applications that define operations on a database repre-
sented using the Prolog database or some external persistent database (see also chapter 8.1. Unfor-
tunately ‘good’ style Prolog programming often manipulates data represented on the Prolog runtime
12
stacks because destructive operations on the Prolog database are dangerous and loose two important
features of Prolog: logical variables and backtracking.
The event-driven approach is commonly used by applications that present and/or edit some exter-
nal (persistent) database. As long as name conflicts in the Prolog program and the global PCE name
spaces (classes and named object references (e.g. @prolog, @main window)) are avoided, mul-
tiple applications may run simultaneously in the same XPCE/Prolog process. For example the PCE
manual runs together with the various PCE demo applications.
application :-
initialise(Heap),
create_GUI_components(Heap),
process_events_until_computation_may_proceed,
proceed(Heap),
...
The ‘process events until computation may proceed’ is generally realised using the methods ‘frame ← confirm’
combined with ‘frame → return’. Suppose we have a diagnose system that presents a graphics
representation of the system to be diagnosed. The application wants to the the user’s hypothesis for
the faulty component. The window has implemented a selection mechanism, an ok button and a label
to ask the question. The relevant program fragment could read:1
....
send(Label, format, ’Select hypothesis and press "OK"’),
send(OkButton, message,
message(Frame, return, Diagram?selection)),
get(Frame, confirm, Hypothesis),
....
13
?- new(D, dialog(’Hello’)),
send(D, append,
button(hello, message(@prolog, format, ’Hello˜n’, []))),
send(D, append,
button(quit, message(D, destroy))),
send(D, open).
...
send(Chain, sort, ?(@arg1?name, compare, @arg1?name)),
...
‘?’ is the class-name of a PCE ‘obtainer’. When executed, an obtainer evaluates to the result
of a PCE get-operation: @arg1?name2 evaluates to the return value of ‘get(@arg1, name,
Name)’.
• Implement a method
A method represents the mapping of a ‘selector’ to an ‘action’. The action is normally repre-
sented using a PCE executable object. Define methods is discussed further in section 3.3.
14
? Evaluates the result of get-operation
progn Sequence returning value
when Conditional function
var Variable (returning ← value)
*, +, -, / Arithmetic operations
1. It is the receiver of a get- or send-operation and the method is not defined on class function
or a relevant subclass. These classes define very few methods for general object manipulation,
which normally start with an ’ ’ (underscore).
2. It is passed as an argument to a method and the argument’s type-test does not allow for functions.
This is true for most arguments except for behaviour that requires a function.
:- pce_end_class.
These two predicates manipulate Prolog’s macro processing and Prolog’s operator table, which changes
the basic syntax.
An (additional) instance variable for the class is defined using
3
Note that object can define individual methods and variables.
4
The XPCE/CommonLisp interface defines a dedicated syntax for defining XPCE classes and methods where the imple-
mentation is realised by PCE executable objects. See [Anjewierden, 1992]
15
initialise Called when an instance of the class is created
unlink Called when an instance is destroyed
event Called when an event arrives on the (graphical) object
geometry Called when the size/position of a (graphical) object is changed
Where ‘Name’ is the name of the instance variable, ‘Type’ defines the type and ‘Access’ the implicit
side-effect-free methods: {none,get,send,both}.
The definition of a method is very similar to the definition of an ordinary Prolog clause:
Defines a send method for the current class. ‘Receiver’, ‘Arg1’, ... are Prolog variables that are
at runtime bound to the corresponding XPCE objects. The body of the method is executed as any
normal Prolog clause body. The definition of a get method is very similar:
The body is supposed to bind ‘ReturnValue’ to the return value of the method or fail.
3.3.3 Examples
Defining a two-dimensional matrix
The first example defines a class two-dimensional matrix of fixed size. We want to be able to say:
?- new(@matrix, matrix(3,3)).
?- send(@matrix, element, 2, 2, x).
?- send(@matrix, element, 1, 2, o).
We will implement the two-dimensional matrix as a subclass of the one dimensional vector:
16
"Create matrix fom width and and height"::
send(M, send_super, initialise),
send(M, slot, width, Width),
send(M, slot, height, Height),
Size is Width * Height,
send(M, fill, @nil, 1, Size).
17
element(T, X:int, Y:int, Value:char_array) :->
"Set text of cell at X-Y"::
xy_name(X, Y, XYName),
get(T, member, XYName, Text),
send(Text, string, Value).
xy_name(X, Y, Name) :-
get(string(’%d,%d’, X, Y), value, Name).
:- pce_end_class.
3.4 Exercises
Exercise 5
Extend class matrix with a get-method ←element. See what happens if you define the return-
type incorrect (for example as ‘int’).
Exercise 6
We would like to visualise a matrix using a text table object, such that changes to the matrix are
reflected on the text table and modifications of the text table are reflected in the matrix.
XPCE does not provide a built-in model/controller architecture such as SmallTalk. There are
various ways to relate objects to each other: object-level attributes (see ‘object ←
→ attribute’),
instance-variables and finally ‘hyper-links’. See class hyper.
Try to realise the mapping using hyper objects. See class hyper and ‘object → send hyper’.
What are the (dis)advantages of all these techniques?
Exercise 7
Advanced usage! Suppose the matrix is used in heavy computations and modified often. This
would imply passing through the mechanism described above many times. The display is
not updated for each intermediate state (unless you explicitly request this using ‘graphical
→flush’).
To avoid part of the computation as well, you can use the mechanism described by ‘graphical → request com
and ‘graphical → compute’.
Try to implement this interface.
3.5 Types
XPCE is an untyped language like Prolog. This allows the system to define container classes such as
class chain, vector, hash table etc. in a comfortable fashion. Unlike Prolog however, XPCE allows
you to declare types. Types are used for four purposes:
• Documentation
Given the name, argument-names and argument-types for each method generally suffices to get
a strong clue on what it does.
18
• Run-time trapping of errors
The earlier errors are trapped, the easier it is to locate the errornous statement in your source!
• Run-time conversion
Each type defines a validation method and a conversion method. If the first fails, the second is
tried to convert the input value to the requested type. For example if the expected argument is
of type ‘int’ and you provide the string “554” it will gracefully convert “554” to the integer 554
and pass this value to the implementation of the method.
3.6 Exercises
Exercise 8
If an argument is specified as type ‘bool’, what values are acceptable as input.
Exercise 9
Advanced usage: A very common usage for conversion is a class with uniquely named reusable
objects. Try to define such a class. You will have to define the methods →initialise,
←lookup and ←convert.
19
Chapter 4
This chapter provides a brief overview of the tracer. Besides the tracer, the VisualHierachy and In-
spector tools are very useful tools for locating problems with your application. See section 2.4.
On each port, the user can specify a trace- point or a break- point. A trace-point will cause a
message printed when the port is ‘passed’. A break-point will do the same and start the interactive
tracer (similar to a spy- point in Prolog.
The Prolog predicates (no)tracepce/1 and (no)breakpce/11 provide a friendly interface for setting
trace- and break-points on methods. Example:
?- tracepce((point->x)).
Trace variable: point <->x
20
P = @892203/point
Both tracepce/1 and breakpce/1 accept a specification of the form <Class> ->, <- or
-<Selector>.
?- tracepce((box->device)).
If—for example—you finally discovered that the object is sent the message →device: @nil, you
can trap the tracer using:
4.2 Exercises
Exercise 10
Consider PceDraw. Use the XPCE tracer to find out what happens to an object if the Cut
operation is activated.
21
Chapter 5
A common use is to query or inform the user during an ongoing task. The task is blocked until the
user presses some button. For example, the user selected ‘Quit’ and you want to give the user the
opportunity to save before doing so or cancel the quit all the way.
Model dialog windows are the answer to the problem. In XPCE, Windows by themselves are not
modal, but any window may be operated in a modal fashion using the method ‘frame ← confirm’.
This method behaves as ‘frame → open’ and next starts an event-dispatching subloop until
‘frame → return’ is activated. So, our quit operation will look like this:
quit :-
new(D, dialog(’Quit my lovely application?’)),
( application_is_modified
-> send(D, append,
button(save_and_quit,
message(D, return, save_and_quit)))
; true
),
send(D, append, button(quit, message(D, return, quit))),
send(D, append, button(cancel, message(D, return, cancel))),
22
5.2 Entering Values
Operations often require multiple values and dialog windows are a common way to specify the values
of an operation. Actually executing the operation is normally associated with a button. Below is an
example of a dialog for this type of operations.
create_window :-
new(D, dialog(’Create a new window’)),
send(D, append, new(Label, text_item(label, ’Untitled’))),
send(D, append, new(Class, menu(class, choice))),
send_list(Class, append,
[ class(window),
class(picture),
class(dialog),
class(browser),
class(view)
]),
send(D, append,
new(Width, slider(width, 100, 1000, 400))),
send(D, append,
new(Height, slider(height, 100, 1000, 200))),
send(D, append,
button(create,
message(@prolog, create_window,
Class?selection,
Label?selection,
Width?selection,
Height?selection))),
send(D, append,
button(cancel, message(D, destroy))),
send(D, open).
Note that a menu accepts the method →append: menu item. Class menu item defines a type-
conversion converting any value to a new menu item using the value as ‘menu item ← → value’.
The visible labels of the menu items are generated automatically (see ‘menu item ←default label’).
The action associated with the button is a message invoking the Prolog predicate create window/4.
Four obtainers collect the values to be passed.
23
5.3 Editing Attributes of Existing Entities
All dialog items that may be used to edit attributes (i.e. represent some value) define a ←
→default
←
value and the methods →restore and →apply. →default specifies an XPCE function object or
plain value. On →restore, the ←selection is restored to the ←default or the result of evalu-
ating the ←default function. →apply will execute ←message with the current ←selection
if the ←selection has been changed by the user.
Class dialog defines the methods →restore and →apply, which will invoke these methods
on all items in the dialog that understand them. This schema is intended to define attribute editors
comfortably.
Below we define a dialog-window to edit some appearance values of a box object.
edit_box(Box) :-
new(D, dialog(string(’Edit box %N’, Box))),
send(D, append,
text_item(name, Box?name, message(Box, name, @arg1))),
send(D, append,
new(S, slider(pen, 0, 10, Box?pen, message(Box, pen, @arg1)))),
send(S, width, 50),
send(D, append, button(apply)),
send(D, append, button(restore)),
send(D, append, button(cancel, message(D, destroy))),
send(D, default_button, apply),
send(D, open).
my_move(Gr, Point) :-
( valid_position(Gr, Point)
-> send(Gr, move, Point)
; send(Gr, report, warning,
’Cannot move %N to %N’, Gr, Point)
).
The first argument of →report is the type of report. The reporting mechanism uses this information
to decide what to do with the report. The types are: status, warning, error, inform, progress and done.
24
5.4.2 Handling reports
A default report handling mechanism is defined that will try to report in a label object called reporter
in a dialog window in the frame from where the command was issued. If no such label can be found
important (error, inform) reports are reported using a confirmer on the display. Less serious (warning)
are reported using the →alert mechanism (→bell, →flash) and others are ignored.
Most applications therefore can just handle all reports by ensuring the frame has a dialog window
with a label called reporter in it. In specific cases, redefining →report or ←report to can
improve error reporting.
5.5 Exercises
Exercise 11
Define a dialog based application that allows you to inspect the properties of Prolog predicates.
Use predicate property/2 and source file/2. The dialog should have appropriate
fields for the file the predicate is defined in, whether it is dynamic or not, multiple, built-in, etc.
Use a browser to display all available predicates. Double-clicking on an predicate in the browser
should show the predicate in the dialog.
item → restore
Normally simply invokes →selection with ←default.
25
item → apply: Force:[bool]
If ←modified returns @on or Force equals @on, forward the ←selection over @arg1
of the ←message.
The XPCE library (‘@pce ← home’/prolog/library) contain various examples of custom dialog
items. See for example pce font [Link] (consists of three cycle-menus for specifying a PCE font),
pce style [Link] (enter a style (font, colour, underline, etc.) for an editor-fragment). The dialog editor
contains various examples as well.
5.7 Exercises
Exercise 12
Study the pce font [Link] file. Use the font item to control the font of a simple text object.
Analyse the structure of the application using the Visual Hierarchy tool.
26
Chapter 6
Graphics
In this chapter we will build a little direct-manipulation graphical editor. The application consists of
a graphical editor that allows the use to define entities and their interrelations involved.
recenter(TB) :->
"Center text in box"::
get(TB, member, box, Box),
get(TB, member, text, Text),
send(Text, center, Box?center).
27
geometry(TB, X:[int], Y:[int], W:[int], H:[int]) :->
"Handle geometry-changes"::
get(TB, member, box, Box),
send(Box, set, 0, 0, W, H),
send(TB, recenter),
send(TB, send_super, geometry, X, Y).
:- pce_end_class.
:- use_module(library(pce_prompter)).
make_graph_editor(E) :-
new(E, picture(’Graph Editor’)),
send(E, popup, new(P, popup(options))),
send_list(P, append,
[ menu_item(add_new_box,
28
message(@prolog, add_new_box,
E, E?focus_event?position)),
menu_item(clear,
and(message(@display, confirm,
’Clear entire drawing?’),
message(E, clear)))
]),
send(E, open).
add_new_box(E, Pos) :-
prompter(’Name of box’,
[ name:name = Name
]),
send(E, display, text_box(Name), Pos).
:- pce_extend_class(text_box).
:- pce_global(@text_box_recogniser,
make_text_box_recogniser).
make_text_box_recogniser(R) :-
new(R, handler_group(new(resize_gesture),
new(move_gesture),
popup_gesture(new(P, popup(options))))),
TB = @arg1,
send_list(P, append,
[ menu_item(rename,
message(TB, rename),
end_group := @on),
menu_item(delete,
message(TB, free))
]).
1
In this example we define the class text box in small parts that can be loaded on top of each other. We use :-
pce extend class(+Class). to continue the class-definition.
29
event(TB, Ev:event) :->
( send(TB, send_super, event, Ev)
-> true
; send(@text_box_recogniser, event, Ev)
).
rename(TB) :->
"Prompt for new name"::
prompter(’Rename text box’,
[ name:name = Name/TB?string
]),
send(TB, string, Name).
:- pce_end_class.
:- pce_extend_class(text_box).
:- pce_global(@link_recogniser, new(connect_gesture)).
:- pce_end_class.
30
6.4 Reading the Diagram
XPCE has been designed to be able to create drawings that can be interpreted. This design is ex-
emplified by the use of connections. If the drawing had been built from just lines, boxes and text it
would have been difficult to convert the drawing into a Prolog representation of a graph. Connections
explicitly relate graphical object instead of positions.
Below is a Prolog fragment that generates a Prolog fact-list from the graph.
assert_graph(E, Predicate) :-
functor(Term, Predicate, 2),
retractall(Term),
send(E?graphicals, for_all,
if(message(@arg1, instance_of, connection),
message(@prolog, assert_link,
Predicate,
@arg1?from?string?value,
@arg1?to?string?value))).
6.5 Exercises
Exercise 13
Extend the graph-editor’s frame with a dialog window that allows for saving the graph to the
Prolog database.
Exercise 14
The example in section 6.3 uses a generic connect gesture and a generic connection. Make
a refinement of class connect gesture that creates instances of a class arc connection. Define
class arc connection as a subclass of class connection that has a popup attached which allows
the user to delete the connection.
Exercise 15
Write a predicate to create a graph from a list of Prolog facts defining the graph. Add a possi-
bility to load a graph to the dialog window.
Exercise 16
Experiment with the ‘graphical → layout’ method to create some layout for the graph-
elements created in the above exercise
31
Chapter 7
Multiprocess Applications
In this chapter we will discuss the usage of XPCE-4 for applications spread over multiple Unix pro-
cesses, possibly running on multiple systems. We distinguish the following designs:
• Class process
Class process defines the interaction between XPCE and both synchronously and asynchronously
running Unix processes. Communication can be provided using Unix pipes as well as using a
Unix pseudo-tty.
The process controlled this way can only be started as a child process of XPCE/Prolog. Class
socket discussed below allows for non-child-process communication.
• Class socket
The XPCE class socket makes Unix sockets available to the XPCE programmer. It supports
both Unix-domain sockets (sockets that make an end-point in the Unix file-structure and can
only be used to communicate between processes on the same machine) and inet domain sockets
(sockets that make an internet end-point and can be used to communicate with other processes
running anywhere on the internet).
32
• Class display and class display manager
XPCE can communicate with multiple X11-displays simultaneously.1 Thus, cooperative ap-
plications (CSCW) may be implemented as client-server applications as well as using a single
XPCE/Prolog application managing windows for multiple users.
The proper choice from the above depends on the characteristics of the application:
7.3 Examples
The examples below generally take trivial standard Unix applications. Many of the manipulations
could be established inside Prolog or XPCE.
1
Provided the application is purely event-driven
33
• List of arguments to it
pce_string_to_list(S, L) :-
pce_string_to_list(S, 0, L).
pce_string_to_list(S, I, [C|T]) :-
get(S, character, I, C), !,
NI is I + 1,
pce_string_to_list(S, NI, T).
pce_string_to_list(_, _, []).
This version of the call will block and not dispatch X11 events during the execution of call unix/3.
Below we use another definition that processes normal user events during the execution. We borrow
the definition of pce string to list/2 from the previous example.
The call ‘process → wait’ runs the XPCE event-loop until all data from the process has
been handled.
34
7.4 Exercises
Exercise 17
What is the function of ‘process → record separator’ and why is it set to @nil in
the example above?
Exercise 18
Write a predicate call unix(+Utility, +Args, +Input, -Output) that pipes the data from the Prolog
string Input through the command and unifies Output with a Prolog string representing the
output of the process. Use call unix/3 above as a starting point.
1 /* Part of XPCE
2 This example code is in the public domain
3 */
4 :- module(ftp,
5 [ ftp/0, % just start it
6 ftp/1, % ... and connect as ‘ftp’
7 ftp/2 % ... and connect as user
8 ]).
9 :- use_module(library(pce)).
10 :- require([ atomic_list_concat/2
11 , ignore/1
12 , maplist/3
13 , reverse/2
14 , send_list/3
15 ]).
16 ftp :-
17 new(_, ftp_frame).
18 ftp(Address) :-
19 new(_, ftp_frame(Address)).
20 ftp(Address, Login) :-
21 new(_, ftp_frame(Address, Login)).
Like most tools, the tool as a whole is represented by a subclass of class frame. The major
advantage of this approach is that all visual parts are in a natural way ‘part’ of the tool and each of
them can easily find the overall tool using ←frame, which is defined on most visual classes.
35
Figure 7.1: Screendump for the ‘FTP tool’
36
28 send(new(P, picture), below, DT),
29 send(new(DB, dialog), below, P),
30 fill_top_dialog(DT),
31 fill_picture(P),
32 fill_bottom_dialog(DB),
33 send(F, open),
34 ( Address == @default
35 -> true
36 ; send(F, connect, Address, Login)
37 ).
fill top dialog/1 creates the top menu-bar. There is only one item in this incomplete tool. The
→pen and →gap of the dialog window are zeroed to make the menu-bar appear nicely above the
application window. A label is put right to the menu-bar. A ‘reporter’ label is used by the general
→report mechanism and will display all errors and warnings produced by the tool.
38 fill_top_dialog(D) :-
39 get(D, frame, Tool),
40 send(D, pen, 0),
41 send(D, gap, size(0,0)),
42 send(D, append, new(MB, menu_bar)),
43 send(D, append, graphical(width := 20), right), % add space
44 send(D, append, label(reporter), right),
45 send(MB, append, new(File, popup(file))),
46 send_list(File, append,
47 [ menu_item(quit,
48 message(Tool, destroy))
49 ]).
The application window is a graphics window itself (picture) consists of a tree holding ‘ftp node’
objects. We turned this into a class because much of the basic functionality of the tool (expanding,
collapsing, viewing, etc. are naturally defined as operations on the nodes of the tree.
50 fill_picture(P) :-
51 send(P, display, new(T, tree(ftp_node(directory, /)))),
52 send(T, node_handler, @ftp_node_recogniser).
53 fill_bottom_dialog(D) :-
54 get(D, frame, Tool),
55 send(D, append, button(abort, message(Tool, abort))).
The →unlink method is called when the frame (= tool) is destroyed. It ensures that the ftp-
connection is terminated before removing the frame. →unlink *cannot* stop the unlinking process:
it must succeed and it must call →send super: unlink.
56 unlink(F) :->
57 "Make sure to kill the process"::
58 ignore(send(F, disconnect)),
59 send(F, send_super, unlink).
37
Part of the tool may be found using the ←member method. It is good programming practice to make
methods for finding commonly used parts so that the structure of the tool can remain hidden for the
outside world.
The ftp-process is connected using a ‘hyper’ link. Alternatives are the use of attributes or instance-
variables. The advantage of using hyper-links is that they are bi-directional, safe with regard to de-
struction of either end-point and the semantics of destructions may be defined on the link rather than
on the each of the connected objects. See ‘hyper →unlink to’ ‘hyper →unlink from’.
Connect simply creates an ‘ftp process’ object, which automatically will login on the remote machine.
After the connection is established, it will obtain the root-directory using the pwd command. See the
method ‘ftp process →pwd’ below. The message argument is the message that should be called when
the ftp’s pwd command completes. See description on ftp process class below for the communication
details.
81 disconnect(F) :->
82 "Close possible open connection"::
83 ( get(F, ftp, Process)
84 -> send(Process, kill),
85 send(F, ftp, @nil)
86 ; true
87 ).
88 abort(F) :->
89 "Send Control-C to the ftp process"::
90 get(F, ftp, Process),
91 send(F, report, status, ’Sending SIGINT to ftp’),
38
92 send(Process, kill, int).
93 :- pce_end_class.
Node in the ftp-directory/file hierarhy. Directory nodes are printed bold, file nodes in normal
roman font. This class defines handling of a selection (inverted item), attaching a popup menu and the
basic operations (expand, collapse, view, etc.).
94 :- pce_global(@ftp_node_recogniser, make_ftp_node_recogniser).
95 make_ftp_node_recogniser(R) :-
96 Node = @receiver,
97 new(S1, click_gesture(left, ’’, single,
98 and(message(Node?device, for_all,
99 message(@arg1, inverted, @off)),
100 message(Node, inverted, @on)))),
101 new(S2, click_gesture(left, s, single,
102 message(Node, inverted,
103 Node?inverted?negate))),
104 new(E1, click_gesture(left, ’’, double,
105 and(message(Node, expand),
106 message(Node, inverted, @off)))),
107 PopNode = @arg1,
108 new(P, popup_gesture(new(Pop, popup(options)))),
109 send_list(Pop, append,
110 [ menu_item(expand,
111 message(PopNode, expand),
112 condition := PopNode?type == directory),
113 menu_item(collapse,
114 message(PopNode, collapse),
115 condition := not(message(PopNode?sons, empty)))
116 ]),
117 new(R, handler_group(S1, S2, E1, P)).
Deduce the path of the node by appending the components upto the root.
39
133 path(N, Path:name) :<-
134 "Get path-name"::
135 node_path(N, L0),
136 reverse(L0, L1),
137 insert_separator(L1, /, L2),
138 atomic_list_concat(L2, Path).
139 node_path(N, [Me|Above]) :-
140 get(N?image?string, value, Me),
141 ( get(N?parents, head, Parent)
142 -> node_path(Parent, Above)
143 ; Above = []
144 ).
145 insert_separator([], _, []).
146 insert_separator([H], _, [H]) :- !.
147 insert_separator([H|T0], C, [H, C | T]) :-
148 insert_separator(T0, C, T).
Expand means: If it is a directory, cd the ftp-server to the directory of this node and issue an ‘ls’
command. Parse the output line-by line using the given message.
If the node is a file, ‘view’ it: get the file in a local tmp file and start an XPCE view on it.
The method →son is redefined to make sure the new node is visible. The method ‘window →normalise’
ensures the visibility of a graphical, set of graphicals or area of the window.
40
The class ftp process defines the interaction with the ftp process. It is written such that it can
easily be connected in another setting.
Communication is completely asynchronous. This approach is desirable as FTP servers sometimes
have very long response-times and there is no reason to block the interface in the mean-while.
In general, when an ftp-command is issued, it will:
1. Wait for the ftp-process to get to the prompt. It dispatches events (keeping other applications
happy) while waiting.
2. Set the ‘state’ variable indicating the command and the ‘action message’ indicating what to do
with the output and finally send the command to the server.
Subsequent data from the ftp-process will be handled by the →input method, which parses the data
according to ←state and invokes the ←action message on some patterns.
Initialise the ftp process. Noteworthy are the →record separator, which will combine and/or
break the data-records as received from the process into lines. It knows about the two prompts and
will consider those to be a separate record. Finally it preperes the login command.
Password stuff. As a little hack, the password entry-field is unreadable by using a font that produces
no readable output. This is the simplest solution. A more elegant solution would be to use two text-
entry-fields: one that is visible and one that is not visible. The visible one could pass all editing
commands to the invisible one to do the real work. XPCE is not designed for this kind of things ...
41
197 getpass(User, Passwd) :-
198 new(D, dialog(’Enter Password’)),
199 send(D, append, new(T, text_item(User, ’’))),
200 send(T, value_font,
201 font(screen, roman, 2,
202 ’-*-terminal-medium-r-normal-*-2-*-*-*-*-*-iso8859-*’)),
203 send(D, append, button(ok, message(D, return, T?selection))),
204 send(D, append, button(cancel, message(D, return, @nil))),
205 send(D, default_button, ok),
206 get(D, confirm_centered, RVal),
207 send(D, destroy),
208 Passwd = RVal.
209 email(EMail) :-
210 get(@pce, user, User),
211 get(@pce, hostname, Host),
212 new(EMail, string(’%s@%s’, User, Host)).
This is a case where we have to help the reporting system a bit. By default, reports for non-visual
objects are forwarded to the visual object that handles the current event. On call-backs from the
process input however, there is no current event and messages will go to the terminal. The method
←report to ensures they are forwarded to the associated tool. See also ‘ftp frame →ftp’.
Handling input from the process (ftp) is the central and most tricky bit of this application. The
method →input will scan through the patterns for one matching the input (which is broken in lines).
When a match is found, the action part is translated into a message:
Thus, action(1:name) will invoked ‘ftp process →action’ with the first register of the regular ex-
pression converted to a name.
42
229 ’[-l].* \\(\\sd+\\) [A-Z][a-z][a-z].* \\([ˆ \n]+\\)$’,
230 action(file, 2:name, 1:int)).
231 pattern(ls,
232 ’d.* \\([ˆ \n]+\\)$’,
233 action(directory, 1:name)).
234 pattern(ls,
235 ’ˆtotal \\sd+$\\|ˆ\\sd+ bytes received\\|remote: -l’,
236 succeed).
237 pattern(pwd,
238 ’257[ˆ"]*"\\([ˆ"]+\\)’,
239 action(1:name)).
240 pattern(login,
241 ’ˆPassword:’,
242 give_pass).
243 pattern(view,
244 ’226 Transfer complete.’,
245 action).
246 pattern(_,
247 ’\\sd+.*\\.$’,
248 report(status, 0:string)).
249 pattern(_,
250 ’2[35]0-\\(.*\\)’,
251 message(1:string)).
252 pattern(_State,
253 ’.*’,
254 message(0:string)).
255 % report(warning, ’Unrecognised (%s): %s’, State, 0:string)).
The method →action is called from →input to have data handled by the caller. Doing
will, when the pwd-state pattern mathes, call ‘ftp process →action: PWD’, which in turn will
execute the given message using the PWD argument.
43
272 ).
If we get back to the prompt, all input is handled and we will clear the action message.
Informative messages from the ftp process are processed by this method. It pops up a view for dis-
playing the messages. Note once more the usage of a hyper, which ensures the user can discard the
view without introducing inconsistencies.
The method →wait for prompt runs the main XPCE event-loop using ‘display →dispatch’
until the ftp-process gets in the ‘prompt’ ←state.
The basic commands. Given all the infra-structure above, these are quite simple now!
44
308 pwd(P, Msg:[code]) :->
309 send(P, wait_for_prompt),
310 send(P, slot, state, pwd),
311 send(P, slot, action_message, Msg),
312 send(P, format, ’pwd\n’).
7.6 Exercises
Exercise 19
Add a facility to download a file to your current directory.
45
Chapter 8
There are various alternatives for representing application data in XPCE/Prolog. The most obvious
choice is to use Prolog. There are some cases where XPCE is an alternative worth considering:1
• chain
A chain is a single-linked list. Class chain defines various set-oriented operations and iteration
primitives.
1
Using XPCE for storage of application-data has been used in three Esprit-funded projects for the development of
knowledge acquisition tools: ‘Shelley’ (storage only), The ‘Common Kads WorkBench’ (using XPCE for OO control
structure) and ‘KEW’ (Common Lips, Using XPCE for storage only). In the ‘Games’ project XPCE user-defined classes
are only used for specialising the UI library. CLOS is used for storing application data.
Representing ‘knowledge’ in XPCE to be used for reasoning in Prolog or Lisp is difficult due to the lack of ‘natural’
access to the knowledge base. Representing text and drawings in XPCE works well.
46
• vector
A vector is a dynamically expanding one-dimensional array. Multi-dimensional arrays may be
represented as vectors holding vectors or using a large one-dimensional array and redefine the
access methods. See also section 3.3.3.
• hash table
A hash-table maps an arbitrary key on an arbitrary value. Class hash table defines various
methods for finding and iteration over the associations stored in the table. Class chain table
may be considered to associate a single key with multiple values.
For the representation of frames and relations, XPCE offers the following building-blocks:
• sheet
A sheet is a dynamic attribute-value set. Cf. a property list in Lisp.
• hyper
A hyper is a relation between two objects. The classes hyper and objects provide methods to
define the semantics of hypers and to manipulate and exploit them in various ways.
A hyper is a simple and safe way to represent a relation between two objects than cannot rely
on their mutual existence.
47
get(P, hypered, father, M).
:- pce_end_class.
48
new(_, hyper(M, Child, daughter, father))
).
:- pce_end_class.
:- pce_end_class.
8.3 Exercises
Exercise 20
Load the file [Link] containing the example above and enter a simple database by hand.
Inspect the data-representation using the inspector and online manual tools.
Exercise 21
Design and implement a simple graphical editor for entering a database. What visualisation will
you choose? What UI technique will you use for marriage and born children?
Exercise 22
The current implementation does not define an object that reflects the entire database. Define
and maintain a hash-table that maps names onto person entries. You can redefine destruction of
an object by redefining the →unlink method.
49
Bibliography
50