% % sccsid("@(#)doc 1.9 94/09/13"). % sccscr("@(#) Copyright 1993 ECRC GmbH "). % Predicates for the Prolog interface to Tcl/Tk - ProTcl ------------------------------------------------------ ProTcl is a Prolog interface to the Tcl/Tl toolkit. It is a lightweight interface in that it does not try to map Tcl/Tk functions directly onto Prolog predicates. Instead, the Tcl/Tk commands are accessible from Prolog using the tcl_eval/1,2 predicate which accepts any Tcl expression and passes it to the Tcl interpreter. ProTcl also allows to call Prolog from a Tcl command or script using the 'prolog' command and to get back the value of variables (Eclipse only). It is possible to specify callback predicates in Prolog using the Tcl 'prolog_event' command. The Tcl/Tk interface is in the library tk, use :- use_module(library(tk)) to load it. The following predicates become available: tk_demo Starts the Tcl/Tk demo program. tcl_test Starts the Tcl test suite; not all test programs can be executed, because they need additional commands not defined here. tk_test Starts the Tk test program suite, if available tk_file(File, Options) Implements the Tcl/Tk command wish -f File [other options] [Args] Options is nil or a list of options from [geometry(G), display(D), name(N), argv(Args), sync, nodisplay] This command is to be used when there are no further Prolog goals to be directly executed. After Tk reads and executes the file, it enters its main loop that waits for events and serves them. This predicate exits after all Tk windows have been destroyed, i.e. using the Tcl command 'exit'. It is not possible to use it for directly interpreting Tcl, use tk/1 for that purpose (if available). tk(Options) Implements the Tcl/Tk command wish [options] Options is the same as in tk_file/2, except for: nodisplay - starts just Tcl, without Tk This predicate is used for interactive work with Tk. It succeeds after creating the top-level Tk window. Then it is possible to execute Tcl/Tk commands in it. This predicate changes the Prolog top-level loop so that while waiting for the user query it also checks if there are any X events pending and if so, it executes them. When a Prolog program is active, it has to poll the events itself, e.g. using tk_do_one_event/1. tk_main_loop Starts the Tk main loop which polls the events and serves them. It succeeds after all windows have been destroyed. tk_do_one_event(Mask) This is an interface to the Tk function Tk_DoOneEvent(). It waits for a single event, invokes the handler(s) for it and succeeds. If Mask is not zero, it is taken as a bitfields of the following flags and the event processing is restricted to the specified flags: TK_DONT_WAIT 16'1 don't wait for the events, process only those that are ready TK_X_EVENTS 16'2 process X events TK_FILE_EVENTS 16'4 process Tk's file events TK_TIMER_EVENTS 16'8 process timer events TK_IDLE_EVENTS 16'10 process Tk_DoWhenIdle callbacks TK_ALL_EVENTS 16'1e process all event types Thus e.g. 3 can be used to poll X events. tk_num_main_windows(N) Returns the number of currently used windows. One of its main uses is to recognize in Prolog that all windows have been deleted and the application is thus finished. tcl_eval(Cmd) Execute the Tcl/Tk command Cmd. Cmd can be an (Eclipse) string or an atom and it is assumed to contain a valid Tcl/Tk command or a series of commands separated by newlines or semicolons. tcl_eval(Cmd, Result) The same as tcl_eval/1, but the returned Tcl value is unified with Result. The Tcl command 'exit' is different from Tcl/Tk because it exits only Tcl but the Prolog process is still running. Use exit_prolog to exit back to Unix. There is also a Tcl command 'prolog' to call Prolog goals from Tcl: prolog {pred arg1 arg2 ...} module The module argument is optional. The ECLiPSe version supports variable, atom, number and flat list arguments, the SICStus one only atoms. Examples: prolog true # call true/0 prolog {write a} # call write(a) prolog {p a b c} mod1 # call p(a, b, c) in mod1 prolog {p {a b c}} # call p([a, b, c]) In the Eclipse version, arguments whose name start with an uppercase character are interpreted as Prolog variables: prolog {p X} # call p(X) Variables are not recognized inside lists. To prevent a tcl argument to be interpreted as a Prolog list or a variable, it is necessary to enclose it into double braces, because for tcl X, "X" and {X} are all the same value: prolog {p {{a b c}}} # call p("a b c") prolog {p {{X}}} # call p("X") prolog {p {X}} # call p(X) prolog {p X} # call p(X) After the 'prolog' command is executed, the values of the Prolog variables can be accessed in the Tcl array $var indexed by the variable name, e.g: [eclipse 2]: wish. % prolog {append {a b} {c d} X} /usr/local/eclipse/lib/lists.pl compiled traceable 7012 bytes in 0.03 seconds % puts $var(X) a b c d The standard application initialization is performed at the beginning, but .wishrc is not sourced. In Eclipse, it is possible to use the predicate tk_load/1 in the tk/tkext library to load a Tcl/Tk extension that defines its own Tcl_AppInit(). For other Prologs, the same effect can be probably achieved by changing the load_foreign_files call in foreign.pl. --------------------------------------------------------------------- ECLiPSe only: wish A wish-like interface, it reads commands from the input stream and executes them. If a tk window and an interpreter already exist, it is used, otherwise a new one is started. tclsh The same without display, only Tcl commands are available. ECLiPSe has the means to execute Prolog event handlers in a more flexible way. The command prolog_event [args] can be used as a callback procedure for Tk events and in this case Tk does not process them, but it returns the event data to Prolog which can then call the appropriate Prolog predicate to handle it. The main difference to the 'prolog' command being a callback procedure is that the Prolog predicate is executed at the same level as the main program, whereas with 'prolog' the callback is executed from inside Tk, which may cause problems if it is deeply nested. The following predicates can be used to process these Prolog events: tk_next_event(List) Wait for the next Prolog event. List is a list of arguments of the 'prolog_event' command. If no Prolog event occurs by the time the application exits, List is 0. tk_next_event(Mask, List) Wait for the next Prolog event, but handle only those Tk events that correspond to Mask (see tk_do_one_event/1 above). tk_get_event(List) Return the next Prolog event, if there is any. This predicate does not block for events, it returns 0 if no Prolog event is currently in the queue. All Tk events encountered in the queue are also processed. tk_get_event(Mask, List) Return the next Prolog event, but handle only those Tk events currently in the queue that correspond to Mask. tk_do_one_event(Mask, List) Similar to tk_do_one_event/1, but if a Prolog event is encountered, List is bound to the command data, otherwise it is 0. Prolog events which are not processed explicitly by tk_next_event/1,2, tk_get_event/1,2 or tk_do_one_event/2 raise the event 333. The handler for this event receives in the second argument the list of prolog_event arguments. Example: We want to process in Prolog the X and Y coordinates of the cursor when the user presses the first button over the main window: [eclipse 2]: tk([]), tcl_eval('bind . <1> "prolog_event %x %y"'). yes. [eclipse 3]: tk_next_event(E). % now clicking in the window E = [41, 24] yes. [eclipse 4]: 3. tk_next_event(E). % moved the mouse and clicked again E = [153, 167] yes. Note also that Tk events are processed when the Prolog toplevel is waiting for some input, but not when the debugger is waiting for input (in Eclipse). A very simple loop that would process Tk events in Prolog (like e.g. in XWIP) looks like this: % A basic loop that processes Tk events in Prolog tk_loop :- tk_next_event(L), (L = 0 -> true ; handle_event(L), tk_loop ). handle_event(L) :- printf("event: %w\n%b", [L]). :- use_module(library(tk)). :- tk([]). :- tcl_eval('bind . {prolog_event enter %x %y}'). :- tcl_eval('bind . {prolog_event leave %X %Y}'). :- tcl_eval('button .b -text Quit -command exit; place .b -in .'). :- tk_loop. Typically, an application might want to poll the events only at certain places instead of blocking for them; this is then done using tk_get_event/1,2. Loading Tcl/Tk Extensions ------------------------- Extensions which are defined in Tcl/tk can be normally source'd. Extensions that define their own Tcl_AppInit function can use the predicate tk_load/1 from the tk/tkext library. The argument of this predicate is a string containing all .o files, libraries and other parameters that are to be used when loading and linking the Tcl/Tk toolkit. Typically, it will include a .o file which defines the Tcl_AppInit function and a library that contains all functions defined in the extension. All pathnames are relative to the current directory. For example, the tree widget can be loaded as follows (assuming the current directory is tree-3.6/treesh; the GNU pathnames vary from site to site): :- lib('tk/tkext'). :- tk_load("tkAppInit.o ../dir/Dir.o -ltktree -lOS -lg++ -lgcc -L../tree -L../OS -lnsl -L/usr/local/gnu/lib/gcc-lib/sparc-sun-sunos4.1.1/2.5.7 -L/usr/local/gnu/lib"). There must not be any main() in the .o files. This very example needs some of the GNU C++ libraries to be included because it is written in C++. The simplest way to figure out the correct parameters is to compile the application with the verbose option and to include the library arguments passed to the linker.