Another way for Prolog to communicate with Tcl is through the
predicate tcl_event/3
:
tcl_event(+TclInterpreter, +Command, -Events)
This is similar to tcl_eval/3
in that the command Command
is evaluated in the Tcl interpreter TclInterpreter, but the call
returns a list of events in Events rather than the result of the
Tcl evaluation. Command is again a Tcl command represented as a
Prolog term in the special Command Format described previously
(see Evaluation Functions).
This begs the questions what are these events and where does the event
list come from? The Tcl interpreters in the SICStus Prolog Tcl/Tk
library have been extended with the notion of a Prolog event queue.
(This is not available in plain standalone Tcl interpreters.) The Tcl
interpreter can put events on the event queue by executing a
prolog_event
command. Each event is a Prolog term. So a
Tcl interpreter has a method of putting Prolog terms onto a queue,
which can later be picked up by Prolog as a list as the result of a call
to tcl_event/3
. (It may be helpful to think of this as a way of
passing messages as Prolog terms from Tcl to Prolog.)
A call to tcl_event/3
blocks until there is something on the event
queue.
A second way of getting Prolog events from a Prolog event queue is
through the tk_next_event/[2,3]
predicates. These have the
form:
tk_next_event(+TclInterpreter, -Event) tk_next_event(+ListOrBitMask, +TclInterpreter, -Event)
where TclInterpreter reference to a Tcl interpreter and Event is the Prolog term at the head of the associated Prolog event queue. (The ListOrBitMask feature will be described below in the Housekeeping section when we talk about Tcl and Tk events; see Housekeeping.).
(We will meet tk_next_event/[2,3]
again later when we discuss
how it can be used to service Tk events; see Servicing Tk Events).
If the interpreter has been deleted, the empty list []
is returned.
The Tcl interpreters under the SICStus Prolog library are extended with
a command, prolog_event
, for adding events to a Prolog event queue.
The prolog_event
command has the following form:
prolog_event Terms ...
where Terms are strings that contain the printed representation of
Prolog terms. These are stored in a queue and retrieved as Prolog
terms by tcl_event/3
or tk_next_event/[2,3]
(described above).
An example of using the prolog_event
command:
test_event(Event) :- tcl_new(Interp), tcl_event(Interp, [prolog_event, dq(write(zap(42)))], Event), tcl_delete(Interp).
with the query:
| ?- test_event(Event).
will succeed, binding Event
to the list [zap(42)]
.
This is because tcl_event
converts its argument using the
special Command Format conversion (see Evaluation Functions), which yields
the Tcl command prolog_event "zap(42)"
. This command is
evaluated in the Tcl interpreter referenced by the variable
Interp
. The effect of the command is to take the string given as
argument to prolog_event
(in this case "zap(42)"
)
and to place it on the Tcl to Prolog event queue. The final action of a
tcl_event/3
call is to pick up any strings on the Prolog queue
from Tcl, add a trailing full stop and space to each string, and parse
them as Prolog terms, binding Event
to the list of
values, which in this case is the singleton list [zap(42)]
. (The
queue is a list the elements of which are terms put there through
calls to prolog_event
).
If any of the Term-s in the list of arguments to
prolog_event
is not a valid representation of a Prolog
term, an exception is raised in Prolog when it is converted
from the Tcl string to the Prolog term using read
. To
ensure that Prolog will be able to read the term correctly it is
better to always use write_canonical
and to ensure that Tcl is
not confused by special characters in the printed representation of the
Prolog term it is best to wrap the list with list
.
A safer variant that safely passes any term from Prolog via Tcl and back to Prolog is thus:
test_event(Term, Event) :- tcl_new(Interp), tcl_event(Interp, list([prolog_event, write_canonical(Term)]), Event), tcl_delete(Interp).
As an example of using the Prolog event system supplied by the tcltk
library, we will return to our 8-queens example but now approaching
from the Prolog side rather than the Tcl/Tk side:
:- use_module(library(tcltk)). setup :- tk_new([name('SICStus+Tcl/Tk - Queens')], Tcl), tcl_eval(Tcl, 'source queens.tcl', _), tk_next_event(Tcl, Event), ( Event = next -> go(Tcl), ; closedown(Tcl) ). closedown(Tcl) :- tcl_delete(Tcl). go(Tcl) :- tcl_eval(Tcl, 'clear_board', _), queens(8, Qs), show_solution(Qs, Tcl), tk_next_event(Tcl, Event), ( Event = next -> fail ; closedown(Tcl) ). go(Tcl) :- tcl_eval(Tcl, 'disable_next', _), tcl_eval(Tcl, 'clear_board', _), tk_next_event(Tcl, _Event), closedown(Tcl).
This is the top-level fragment of the Prolog side of the 8-queens
example. It has three predicates: setup/0
,
closedown/1
, and go/1
. setup/0
simply creates the
Tcl interpreter, loads the Tcl code into the interpreter using a call to
tcl_eval/3
(which also initializes the display) but then calls
tk_next_event/2
to wait for a message from the Tk side.
The Tk part that sends prolog_event
-s to Prolog looks like this:
button .next -text next -command {prolog_event next} pack .next button .stop -text stop -command {prolog_event stop} pack .stop
that is two buttons, one that sends the atom next
, the
other that sends the atom stop
. They are used to get the
next solution and to stop the program respectively.
So if the user presses the next
button in the Tk window, the
Prolog program will receive a next
atom via a
prolog_event
/tk_next_event
pair, and the program can
proceed to execute go/1
.
go/1
is a failure driven loop that generates 8-queens solutions
and displays them. First it generates a solution in Prolog and displays
it through a tcl_eval/3
call. Then it waits again for a Prolog
events via tk_next_event/2
. If the term received on the
Prolog event queue is next
, corresponding to the user pressing
the “next solution” button, then fail is executed and the next
solution found, thus driving the loop.
If the stop
button is pressed, the program does some
tidying up (clearing the display and so on) and then executes
closedown/1
, which deletes the Tcl interpreter and the
corresponding Tk windows altoegther, and the program terminates.
This example fragment show how it is possible for a Prolog program and a Tcl/Tk program to communicate via the Prolog event queue.