Q-Tk ==== This module provides a basic interface between Q and Tcl/Tk. The operations of this module allow you to execute arbitrary commands in the Tcl interpreter, set and retrieve variable values in the interpreter, and invoke Q callbacks from Tcl/Tk. A recent version of Tcl/Tk is required (8.0 or later should do). You can get this from http://www.tcl.tk. Both releases in source form and binary releases for Windows and various Unix systems are provided there. The "stub" script is in tk.q, tk.c contains the actual C code for the module. A description of the available functions can be found in tk.q. A very basic example can be found in tk_test.q; a slightly more advanced example of a tiny but complete Q-Tk application is in tk_examp.q. For a really substantial Q-Tk program take a look at the graphed script in the Q-Graph package, available from the Q homepage. *** IMPORTANT *** When starting a new interpreter, the Tcl/Tk initialization code looks for some initialization files which it executes before anything else happens. Usually these files will be found without any further ado, but if that does not happen automatically, you must set the TCL_LIBRARY and TK_LIBRARY environment variables to point to the Tcl and Tk library directories on your system. For instance, using Tcl/Tk 8.4 under Unix, you might set these variables as follows (assuming Tcl/Tk is installed under /usr): (sh:) export TCL_LIBRARY=/usr/lib/tcl8.4; export TK_LIBRARY=/usr/lib/tk8.4 (csh:) setenv TCL_LIBRARY /usr/lib/tcl8.4; setenv TK_LIBRARY /usr/lib/tk8.4 Under Windows, with Tcl/Tk installed under c:/tcl (these settings are also taken care of automagically by the Qpad application): set TCL_LIBRARY=c:/tcl/lib/tcl8.4 set TK_LIBRARY=c:/tcl/lib/tk8.4 Similar initializations may be required when using optional Tcl/Tk extension packages such as Tix (http://tix.mne.com). USAGE ===== You can submit a command to the Tcl/Tk interpreter with `tk CMD' where CMD is a string. This also starts a new instance of the Tcl/Tk interpreter if it is not already running. To stop the Tcl/Tk interpreter, you can use the `tk_quit' function. Interpreters are local to the thread in which they are started. Thus in a multithreaded script you can have multiple interpreters running in different threads. (For this to work, your Tcl/Tk version must have been built with thread support. Otherwise only one interpreter in the main thread is allowed.) Simple dialogs can be created directly using Tk's `tk_messageBox' and `tk_dialog' functions. For instance: tk "tk_dialog .warning \"Warning\" \"Are you sure?\" warning 0 Yes No Cancel" Other kinds of common dialogs are available; see the Tcl/Tk manual for information. For more elaborate applications you probably have to explicitly create some widgets and provide a main loop which takes care of events and callbacks. For this purpose, the Tcl command `q' can be used to return a "callback message" from the Tcl interpreter to the calling Q script. The Q-Tk module maintains such messages in a private queue. You can access callback messages in FIFO fashion, using the `tk_reads' function which returns the oldest message as a string. There are also two convenience functions, tk_read = val tk_reads, and tk_readq = valq tk_reads, which are useful when the callback messages denote Q expressions which are to be evaluated immediately or returned as a quoted expression, respectively. All these functions, as well as the `tk_check' and `tk_ready' functions discussed below, also process pending events in the Tcl/Tk interpreter s.t. the display is updated when Tk commands or user actions change the state of the application. The Tcl/Tk interpreter keeps running until either `tk_quit' is called, or the main window is destroyed or closed. You can check whether the interpreter is currently running using the `tk_ready' function. A basic main loop, which repeatedly evaluates callback messages and processes events until the interpreter is exited, may look as follows: main_loop = tk_read || main_loop if tk_ready; = () otherwise; This is appropriate if the whole state of the application is kept in the Tcl interpreter. Otherwise you might wish to invoke the loop with some initial STATE value on which the callback functions operate. In this case you can change the definition of main_loop to something like the following: main_loop STATE = main_loop (tk_read STATE) if tk_ready; = STATE otherwise; In any case, the loop terminates as soon as the Tcl/Tk interpreter is exited, which can happen, e.g., in response to a callback which invokes the `tk_quit' function, Tcl code which destroys the main window (`destroy .'), or when the user closes the main window from the window manager. Note that the `tk_read' functions are blocking, i.e., they wait until a callback message becomes available. Sometimes you will also want to check beforehand whether there are any messages, which can be done with the `tk_check' function. This is useful, e.g., to implement any kind of background processing when the application is currently idle: main_loop STATE = STATE if not tk_ready; = main_loop (tk_read STATE) if tk_check; = main_loop (idle STATE) otherwise; Alternatively, you can also do background processing using the Tcl interpreter's own facilities, i.e., the Tcl `after' command. Q-Tk also allows your script to set and retrieve variable values in the Tcl interpreter with the tk_set and tk_get functions. This is useful, e.g., to change the variables associated with entry and button widgets, and to retrieve the current values from the application. Note that all values are passed as strings, since this is really the only data type Tcl/Tk knows. TIPS AND TRICKS ==== === ====== (1) Errors in Tcl/Tk commands can be handled by giving an appropriate definition of the `tk_error' function, which is invoked with an error message as its single argument. For instance, the following implementation of `tk_error' prints the error message and halts evaluation: tk_error MSG = writes ("! Tk Error: "++MSG++"\n") || halt; If no definition for this function is provided, then errors cause a literal `tk_error MSG' expression to be returned as the result of the `tk' function. You can then check for such results to take an appropriate action. (2) The Tcl/Tk interpreter, when started, displays a default main window, which is required by most Tk applications. If this is not desired (e.g., if only the basic Tcl commands are needed), you can hide this window using a `tk "wm withdraw ."' command. To redisplay the window when it is needed, use the `tk "wm deiconify ."' command. It is also common practice to use `wm withdraw' and `wm deiconify' while creating the widgets of an application, in order to reduce "flickering". (3) The `tk' function can become rather tedious when coding larger Tk applications. Usually, you will prefer to put the commands making up your application into a separate Tcl/Tk script, and then invoke this script from your Q script using the Tcl `source' command, e.g.: ==> tk "source myapp.tcl" This is also the method to use for running existing Tk applications, e.g., if you create the interface using some interface builder like vtcl (http://vtcl.sourceforge.net). (4) The Tcl `package' command allows you to load additional extensions into the Tcl/Tk interpreter at runtime. For instance, if you want to work with the extra widgets provided by the Tix extension (see http://tix.mne.com), you can load the corresponding package as follows: ==> tk "package require Tix" (5) The Tcl `exit' procedure, just as in tclsh or wish, causes exit from the the current process. Since the Tcl/Tk interpreter hosted by the Q-Tk module runs as part of the Q interpreter process, and not as a separate child process, `tk "exit"' will exit from the Q interpreter and take you back to the shell. If you'd like `exit' to only exit the Tcl/Tk interpreter, without exiting Q, you can redefine the `exit' procedure, e.g., as follows: ==> tk "proc exit { {returnCode 0} } { q tk_quit }" (Of course, the `tk_quit' call will only take effect next time you use `tk_read', but presumably that will be done by your application's main loop.) If you want to do something with the exit code provided by `exit', you will have to provide an appropriate callback function, e.g.: ==> tk "proc exit { {returnCode 0} } { q quit_cb $returnCode }" An implementation of `quit_cb' might then look as follows: quit_cb 0 = writes "Application exited normally.\n" || tk_quit; quit_cb N = writes ("Application exited with exit code "++str N++".\n") || tk_quit otherwise; (6) If you need dialogs beyond the standard kinds of message boxes and common dialogs, you will have to do these yourself using a secondary toplevel. The dialog toplevel is just like the main window but will only be shown when the application needs it. You can construct both non-modal and modal dialogs this way, the latter can be implemented using Tk's `grab' command. An example can be found in the Q-Graph package on the Q homepage. (7) The latest version of this module also supports synchronous callbacks from the Tcl interpreter via the special `qval' command. This is the recommended method when the main loop of the application is actually executed in the Tcl interpreter, as is the case for some Tcl extensions like Gnocl (http://www.dr-baum.net/gnocl/). The arguments of `qval' take the same form as with the `q' command described above. They should form a valid Q expression which is parsed and evaluated in the Q interpreter. Such callbacks may also return a string value to the Tcl interpreter. There also is a second new Tcl command, `qtrace', which works like `qval' but also prints messages as the evaluation of the callback starts and end, which is _very_ useful for debugging purposes. ;-) The `qtrace' command also accepts a Tcl boolean value (any of 0, 1, true, false, yes, no, on, off are recognized) which causes it to switch on or off the automatic tracing of all calls to the qval command. Well, I hope this suffices to get you started. Please also see tk_examp.q for a tiny, but complete example Tk application. Furthermore, the tix.q example illustrates some of the additional bits of Tk code discussed above, including the method to run a Tix script with Q-Tk. Enjoy! :) January 23 2006 Albert Graef ag@muwiinfa.geschichte.uni-mainz.de, Dr.Graef@t-online.de http://www.musikwissenschaft.uni-mainz.de/~ag