10.41.4.4 Event Functions

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.


Send feedback on this subject.