PCE defines executable objects. Executable objects (called code objects) can be compared to lambda functions in Lisp. They are used in the contexts given below.
<-
selection'
returns a chain object
containing all graphicals currently selected on the device. The
implementation is:
..., send(Dev?graphicals, for_all, message(@arg1, free)). ...
The method `chain->
for_all'
will execute the argument message
object in a loop for all elements in the chain. It will bind @arg1
to the currently processed element of the chain.
Two major groups of code objects are distinguished: procedures and functions. The first only returns status information similar to the send-operation. The latter returns a value or failure status information similar to the get-operation.
Procedures perform an action when executed. Similar to Prolog, the
execution of a procedure either returns successful or failed completion.
Procedures are normally executed by the object or method they are handed
to. For example, the procedure associated to a button
object is executed when the button is pressed. The procedure handed to
the
`chain->
for_all'
method we have seen above is executed by this method for each element of
the chain.
Procedures can also be executed explicitly using the method
`code->
forward'.
This binds the argument-forwarding objects @arg1,
..., and then executes the procedure and restores the old bindings of
argument-forwarding objects. For example:
1 ?- new(@m, message(@prolog, format, 'Hello ~w~n', @arg1)). 2 ?- send(@m, forward, world). Hello world
The online manual may be used to get an overview of the other available code objects. See section 3.3.4.
Functions are code objects which ---when executed--- evaluate to a value. They are commonly used as arguments to other code objects or as arguments to any method. A function is automatically evaluated iff:
The most important function objects are:
The objects @arg1, ... are the message forwarding arguments. Assume the following message.
?- new(@m, message(@pce, format, 'Hello %s\n', @arg1)).
When executed, @arg1 (a function)
is first evaluated. The method `code->
forward:
argument, ...' binds these var
objects locally:
?- send(@m, execute), send(@m, forward, world), send(@m, execute). @default world @default
The objects @receiver and @event are functions referring to the receiver of the currently executing method and the current user-event. Var objects are rarely created explicitly by application programmers.
BOX <name> at X, Y
A naive approach and common beginners mistake is to write:
..., new(F, file(Output)), send(F, open, write), send(Dev?graphicals, for_all, if(message(@arg1, instance_of, box), message(F, append, string('BOX %s at %d,%d\n', @arg1?name, @arg1?x, @arg1?y)))), send(F, close), ...
This example will yield a warning:
[PCE error: get: No implementation for: @default/constant <-x in: get(@default/constant, x)] PCE: 4 fail: get(@default/constant, x) ?
The reason for this is that the interface will translate
string(...) into an instance of class string,
instead of the execution of the ->
for_all
method's body creating different strings for each box displayed on the
device. The correct solution is to use:
..., new(F, file(Output)), send(F, open, write), send(Dev?graphicals, for_all, if(message(@arg1, instance_of, box), message(F, append, create(string, 'BOX %s at %d,%d\n', @arg1?name, @arg1?x, @arg1?y)))), send(F, close), ...
The construct `create(...)' will create an instance of class
create. `message->
execute'
will first evaluate all functions in the message and thus create an
instance of class
string.
A common problem is to find objects, notably some specific graphical object on a window. If we want to find all instances of class box within a graphical device, we can use the call below, collecting all boxes on the device in a new chain.
... get(Dev?graphicals, find_all. message(@arg1, instance_of, box), Boxes), ...
Code are most commonly used to specify the internal behaviour of dialog windows, such that the call-back predicate can concentrate on the real function. We have seen an example of this in section 2.8.
Below there is another example. Data is assumed to be an XPCE string object containing a PostScript® description of a graphical object as can be obtained using
..., get(Graphical, postscript, PostScriptString), ...,
In this example both the internal dynamics of the dialog window (the label of the text-entry fields changes if the user switches from file to printer) and grabbing the arguments from the various dialog items is written using XPCE executable objects. Prolog will only be called to do the real work: printing the data to the requested destination.
Note that XPCE/Prolog does not require you to use XPCE executable objects this way. It is also possible to call Prolog from both the menu and the buttons, passing the dialog window as argument and write all behaviour in Prolog. We leave this as an exercise to the user.
postscript(Data) :- new(D, dialog('Print destination')), send(D, append, new(T, menu(destination, marked))), send_list(T, append, [printer, file]), send(T, layout, horizontal), send(D, append, new(A, text_item(printer_name, 'PostScript'))), send(T, message, if(T?selection == printer, message(A, label, ?(A, label_name, printer_name)), message(A, label, ?(A, label_name, file_name)))), send(D, append, button(ok, and(message(@prolog, print_postscript, T?selection, A?selection, Data), message(D, destroy)))), send(D, append, button(cancel, message(D, destroy))), send(D, default_button, ok), send(D, open). print_postscript(printer, Address, Data) :- !, new(F, file), send(F, open, write), send(F, append, Data), send(F, close), get(F, name, TmpFile), get(string('lpr -P%s %s', Address, TmpFile), value, Command), unix(shell(Command)), send(F, remove). print_postscript(file, Address, Data) :- new(F, file(Address)), send(F, open, write), send(F, append, Data), send(F, close).
Figure 15 : Print destination dialog using code objects for internal behaviour |