[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1. General

1.1 Introduction  
1.2 Getting Started  
1.3 Common Features of Widgets  
1.4 Return Values  
1.5 Argument Lists  
1.6 Lisp Functions Invoked from Graphics  
1.7 Linked Variables  
1.8 tkconnect  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.1 Introduction

GCL-TK is a windowing interface for GNU Common Lisp. It provides the functionality of the TK widget set, which in turn implements a widget set which has the look and feel of Motif.

The interface allows the user to draw graphics, get input from menus, make regions mouse sensitive, and bind lisp commands to regions. It communicates over a socket with a `gcltksrv' process, which speaks to the display via the TK library. The displaying process may run on a machine which is closer to the display, and so involves less communication. It also may remain active even though the lisp is involved in a separate user computation. The display server can, however, interrupt the lisp at will, to inquire about variables and run commands.

The user may also interface with existing TCL/TK programs, binding some buttons, or tracking some objects.

The size of the program is moderate. In its current form it adds only about 45K bytes to the lisp image, and the `gcltksrv' program uses shared libraries, and is on the order of 150Kbytes on a sparc.

This chapter describes some of the common features of the command structure of widgets, and of control functions. The actual functions for construction of windows are discussed in 2. Widgets, and more general functions for making them appear, lowering them, querying about them in 3. Control.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.2 Getting Started

Once GCL has been properly installed you should be able to do the following simple example:

 
(in-package "TK")
(tkconnect)
(button '.hello :text "Hello World" :command '(print "hi"))
==>.HELLO
(pack '.hello)
We first switched to the "TK" package, so that functions like button and pack would be found. After doing the tkconnect, a window should appear on your screen, see See section 1.8 tkconnect. The invocation of the function button creates a new function called .hello which is a widget function. It is then made visible in the window by using the pack function.

You may now click on the little window, and you should see the command executed in your lisp. Thus "hi" should be printed in the lisp window. This will happen whether or not you have a job running in the lisp, that is lisp will be interrupted and your command will run, and then return the control to your program.

The function button is called a widget constructor, and the function .hello is called a widget. If you have managed to accomplish the above, then GCL is probably installed correctly, and you can graduate to the next section! If you dont like reading but prefer to look at demos and code, then you should look in the demos directory, where you will find a number of examples. A monitor for the garbage collector (mkgcmonitor), a demonstration of canvas widgets (mkitems), a sample listbox with scrolling (mklistbox).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.3 Common Features of Widgets

A widget is a lisp symbol which has a function binding. The first argument is always a keyword and is called the option. The argument pattern for the remaining arguments depends on the option. The most common option is :configure in which case the remaining arguments are alternating keyword/value pairs, with the same keywords being permitted as at the creation of the widget.

A widget is created by means of a widget constructor, of which there are currently 15, each of them appearing as the title of a section in 2. Widgets. They live in the "TK" package, and for the moment we will assume we have switched to this package. Thus for example button is such a widget constructor function. Of course this is lisp, and you can make your own widget constructors, but when you do so it is a good idea to follow the standard argument patterns that are outlined in this section.

 
(button '.hello)
==> .HELLO
creates a widget whose name is .hello. There is a parent child hierarchy among widgets which is implicit in the name used for the widget. This is much like the pathname structure on a Unix or Dos file system, except that '.' is used as the separator rather than a / or \. For this reason the widget instances are sometimes referred to as pathnames. A child of the parent widget .hello might be called .hello.joe, and a child of this last might be .hello.joe.bar. The parent of everyone is called . . Multiple top level windows are created using the toplevel command (see section 2.15 toplevel).

The widget constructor functions take keyword and value pairs, which allow you to specify attributes at the time of creation:

 
(button '.hello :text "Hello World" :width 20)
==>.HELLO
indicating that we want the text in the button window to be Hello World and the width of the window to be 20 characters wide. Other types of windows allow specification in centimeters 2c, or in inches (2i) or in millimeters 2m or in pixels 2. But text windows usually have their dimensions specified as multiples of a character width and height. This latter concept is called a grid.

Once the window has been created, if you want to change the text you do NOT do:
 
(button '.hello :text "Bye World" :width 20)
This would be in error, because the window .hello already exists. You would either have to first call

 
(destroy '.hello)

But usually you just want to change an attribute. .hello is actually a function, as we mentioned earlier, and it is this function that you use:

 
(.hello :configure :text "Bye World")

This would simply change the text, and not change where the window had been placed on the screen (if it had), or how it had been packed into the window hierarchy. Here the argument :configure is called an option, and it specifies which types of keywords can follow it. For example

 
(.hello :flash)
is also valid, but in this case the :text keyword is not permitted after flash. If it were, then it would mean something else besides what it means in the above. For example one might have defined

 
(.hello :flash :text "PUSH ME")
so here the same keyword :text would mean something else, eg to flash a subliminal message on the screen.

We often refer to calls to the widget functions as messages. One reason for this is that they actually turn into messages to the graphics process `gcltksrv'. To actually see these messages you can do
 
(debugging t).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.4 Return Values


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.4.1 Widget Constructor Return Values

On successful completion, the widget constructor functions return the symbol passed in as the first argument. It will now have a functional binding. It is an error to pass in a symbol which already corresponds to a widget, without first calling the destroy command. On failure, an error is signalled.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.4.2 Widget Return Values

The widget functions themselves, do not normally return any value. Indeed the lisp process does not wait for them to return, but merely dispatches the commands, such as to change the text in themselves. Sometimes however you either wish to wait, in order to synchronize, or you wish to see if your command fails or succeeds. You request values by passing the keyword :return and a value indicating the type.

 
(.hello :configure :text "Bye World" :return 'string)
==> "" 
==> T
the empty string is returned as first value, and the second value T indicates that the new text value was successfully set. LISP will not continue until the tkclsrv process indicates back that the function call has succeeded. While waiting of course LISP will continue to process other graphics events which arrive, since otherwise a deadlock would arise: the user for instance might click on a mouse, just after we had decided to wait for a return value from the .hello function. More generally a user program may be running in GCL and be interrupted to receive and act on communications from the `gcltksrv' process. If an error occurred then the second return value of the lisp function will be NIL. In this case the first value, the string is usually an informative message about the type of error.

A special variable tk::*break-on-errors* which if not nil, requests that that LISP signal an error when a message is received indicating a function failed. Whenever a command fails, whether a return value was requested or not, `gcltksrv' returns a message indicating failure. The default is to not go into the debugger. When debugging your windows it may be convenient however to set this variable to T to track down incorrect messages.

The `gcltksrv' process always returns strings as values. If :return type is specified, then conversion to type is accomplished by calling

 
(coerce-result return-string type)

Here type must be a symbol with a coercion-functions property. The builtin return types which may be requested are:

T
in which case the string passed back from the `gcltksrv' process, will be read by the lisp reader.
number
the string is converted to a number using the current *read-base*
list-strings

 
(coerce-result "a b {c d} e" 'list-strings)
==> ("a" "b" "c d" "e")

boolean
(coerce-result "1" 'boolean) ==> T (coerce-result "0" 'boolean) ==> NIL

The above symbols are in the TK or LISP package. It would be possible to add new types just as the :return t is done:

 
(setf (get 't 'coercion-functions)
      (cons #'(lambda (x) (our-read-from-string x 0))
	    #'(lambda (x) (format nil "~s" x))))

The coercion-functions property of a symbol, is a cons whose car is the coercion form from a string to some possibly different lisp object, and whose cdr is a function which builds a string to send to the graphics server. Often the two functions are inverse functions one of the other up to equal.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.4.3 Control Function Return Values

The control funcions (see section 3. Control) do not return a value or wait unless requested to do so, using the :return keyword. The types and method of specification are the same as for the Widget Functions in the previous section.

 
(winfo :width '.hello :return 'number)
==> 120
indicates that the .hello button is actually 120 pixels wide.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5 Argument Lists


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5.1 Widget Functions

The rule is that the first argument for a widget function is a keyword, called the option. The pattern of the remaining arguments depends completely on the option argument. Thus

 
(.hello option ?arg1? ?arg2? ...)

One option which is permitted for every widget function is :configure. The argument pattern following it is the same keyword/value pair list which is used in widget creation. For a button widget, the other valid options are :deactivate, :flash, and :invoke. To find these, since .hello was constructed with the button constructor, you should see See section 2.1 button. The argument pattern for other options depends completely on the option and the widget function. For example if .scrollbar is a scroll bar window, then the option :set must be followed by 4 numeric arguments, which indicate how the scrollbar should be displayed, see See section 2.6 scrollbar.

 
(.scrollbar :set a1 a2 a3 a4)

If on the other hand .scale is a scale (see section 2.3 scale), then we have

 
(.scale :set a1 )
only one numeric argument should be supplied, in order to position the scale.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5.2 Widget Constructor Argument Lists

These are

 
(widget-constructor pathname :keyword1 value1 :keyword2 value2 ...)

to create the widget whose name is pathname. The possible keywords allowed are specified in the corresponding section of See section 2. Widgets.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5.3 Concatenation Using `:' in Argument List

What has been said so far about arguments is not quite true. A special string concatenation construction is allowed in argument lists for widgets, widget constructors and control functions.

First we introduce the function tk-conc which takes an arbitrary number of arguments, which may be symbols, strings or numbers, and concatenates these into a string. The print names of symbols are converted to lower case, and package names are ignored.

 
(tk-conc "a" 1 :b 'cd "e") ==> "a1bcde"

One could use tk-conc to construct arguments for widget functions. But even though tk-conc has been made quite efficient, it still would involve the creation of a string. The : construct avoids this. In a call to a widget function, a widget constructor, or a control function you may remove the call to tk-conc and place : in between each of its arguments. Those functions are able to understand this and treat the extra arguments as if they were glued together in one string, but without the extra cost of actually forming that string.

 
(tk-conc a b c .. w) <==> a : b : c : ... w
(setq i 10)
(.hello :configure :text i : " pies")
(.hello :configure :text (tk-conc i  " pies"))
(.hello :configure :text (format nil "~a pies" i))

The last three examples would all result in the text string being "10 pies", but the first method is the most efficient. That call will be made with no string or cons creation. The GC Monitor example, is written in such a way that there is no creation of cons or string types during normal operation. This is particularly useful in that case, since one is trying to monitor usage of conses by other programs, not its own usage.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6 Lisp Functions Invoked from Graphics

It is possible to make certain areas of a window mouse sensitive, or to run commands on reception of certain events such as keystrokes, while the focus is in a certain window. This is done by having a lisp function invoked or some lisp form evaluated. We shall refer to such a lisp function or form as a command.

For example

 
(button '.button :text "Hello" :command '(print "hi"))
(button '.jim :text "Call Jim" :command 'call-jim)

In the first case when the window .button is clicked on, the word "hi" will be printed in the lisp to standard output. In the second case call-jim will be funcalled with no arguments.

A command must be one of the following three types. What happens depends on which type it is:

`function'
If the value satisfies functionp then it will be called with a number of arguments which is dependent on the way it was bound, to graphics.
`string'
If the command is a string, then it is passed directly to TCL/TK for evaluation on that side. Lisp will not be required for the evaluation when the command is invoked.
`lisp form'
Any other lisp object is regarded as a lisp form to be eval'd, and this will be done when the command is invoked.

The following keywords accept as their value a command:

 
   :command
   :yscroll    :yscrollcommand
   :xscroll    :xscrollcommand
   :scrollcommand
   :bind

and in addition bind takes a command as its third argument, see See section 3.2 bind.

Below we give three different examples using the 3 possibilities for a command: functionp, string, and lisp form. They all accomplish exactly the same thing. For given a frame .frame we could construct a listbox in it as:

 
(listbox '.frame.listbox :yscroll 'joe)

Then whenever the listbox view position changes, or text is inserted, so that something changes, the function joe will be invoked with 4 arguments giving the totalsize of the text, maximum number of units the window can display, the index of the top unit, and finally the index of the bottom unit. What these arguments are is specific to the widget listbox and is documented See section 2.2 listbox.

joe might be used to do anything, but a common usage is to have joe alter the position of some other window, such as a scroll bar window. Indeed if .scrollbar is a scrollbar then the function

 
(defun joe (a b c d)
  (.scrollbar :set a b c d))

would look after sizing the scrollbar appropriately for the percentage of the window visible, and positioning it.

A second method of accomplishing this identical, using a string (the second type of command),

 
(listbox '.frame.listbox :yscroll ".scrollbar set")

and this will not involve a call back to lisp. It uses the fact that the TK graphics side understands the window name .scrollbar and that it takes the option set. Note that it does not get the : before the keyword in this case.

In the case of a command which is a lisp form but is not installed via bind or :bind, then the form will be installed as

 
#'(lambda (&rest *arglist*) lisp-form)

where the lisp-form might wish to access the elements of the special variable *arglist*. Most often this list will be empty, but for example if the command was setup for .scale which is a scale, then the command will be supplied one argument which is the new numeric value which is the scale position. A third way of accomplishing the scrollbar setting using a lisp form is:

 
(listbox '.frame.listbox :yscroll '(apply '.scrollbar :set *arglist*))

The bind command and :bind keyword, have an additional wrinkle, see See section 3.2 bind. These are associated to an event in a particular window, and the lisp function or form to be evaled must have access to that information. For example the x y position, the window name, the key pressed, etc. This is done via percent symbols which are specified, see See section 3.2 bind.

 
(bind "Entry" "" '(emacs-move  %W %A ))

will cause the function emacs-move to be be invoked whenever a control key is pressed (unless there are more key specific or window specific bindings of said key). It will be invoked with two arguments, the first %W indicating the window in which it was invoked, and the second being a string which is the ascii keysym which was pressed at the same time as the control key.

These percent constructs are only permitted in commands which are invoked via bind or :bind. The lisp form which is passed as the command, is searched for the percent constructs, and then a function

 
#'(lambda (%W %A) (emacs-move %W %A))

will be invoked with two arguments, which will be supplied by the TK graphics server, at the time the command is invoked. The *arglist* construct is not available for these commands.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.7 Linked Variables

It is possible to link lisp variables to TK variables. In general when the TK variable is changed, by for instance clicking on a radiobutton, the linked lisp variable will be changed. Conversely changing the lisp variable will be noticed by the TK graphics side, if one does the assignment in lisp using setk instead of setq.

 
(button '.hello :textvariable '*message* :text "hi there")
(pack '.hello)

This causes linking of the global variable *message* in lisp to a corresponding variable in TK. Moreover the message that is in the button .hello will be whatever the value of this global variable is (so long as the TK side is notified of the change!).

Thus if one does

 
(setk *message* "good bye")

then the button will change to have good bye as its text. The lisp macro setk expands into

 
(prog1 (setf *message* "good bye") (notice-text-variables))

which does the assignment, and then goes thru the linked variables checking for those that have changed, and updating the TK side should there be any. Thus if you have a more complex program which might have done the assignment of your global variable, you may include the call to notice-text-variables at the end, to assure that the graphics side knows about the changes.

A variable which is linked using the keyword :textvariable is always a variable containing a string.

However it is possible to have other types of variables.

 
(checkbutton '.checkbutton1 :text "A button" :variable '(boolean *joe*))
(checkbutton '.checkbutton2 :text "A button" :variable '*joe*)
(checkbutton '.checkbutton3 :text "Debugging" :variable '(t *debug*)
              :onvalue 100 :offvalue -1)

The first two examples are the same in that the default variable type for a checkbutton is boolean. Notice that the specification of a variable type is by (type variable). The types which are permissible are those which have coercion-fucntions, See section 1.4 Return Values. In the first example a variable *joe* will be linked, and its default initial value will be set to nil, since the default initial state of the check button is off, and the default off value is nil. Actually on the TK side, the corresponding boolean values are "1" and "0", but the boolean type makes these become t and nil.

In the third example the variable *debug* may have any lisp value (here type is t). The initial value will be made to be -1, since the checkbutton is off. Clicking on .checkbutton3 will result in the value of *debug* being changed to 100, and the light in the button will be toggled to on, See section 2.7 checkbutton. You may set the variable to be another value besides 100.

You may also call

 
(link-text-variable '*joe* 'boolean)

to cause the linking of a variable named *joe*. This is done automatically whenever the variable is specified after one of the keys

 
:variable   :textvariable.

Just as one must be cautious about using global variables in lisp, one must be cautious in making such linked variables. In particular note that the TK side, uses variables for various purposes. If you make a checkbutton with pathname .a.b.c then unless you specify a :variable option, the variable c will become associated to the TK value of the checkbutton. We do NOT link this variable by default, feeling that one might inadvertently alter global variables, and that they would not typically use the lisp convention of being of the form *c*. You must specify the :variable option, or call link-variable.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.8 tkconnect

 
tkconnect &key host display can-rsh gcltksrv

This function provides a connection to a graphics server process, which in turn connects to possibly several graphics display screens. The graphics server process, called `gcltksrv' may or may not run on the same machine as the lisp to which it is attached. display indicates the name of the default display to connect to, and this in turn defaults to the value of the environment variable DISPLAY.

When tkconnect is invoked, a socket is opened and it waits for a graphics process to connect to it. If the host argument is not supplied, then a process will be spawned which will connect back to the lisp process. The name of the command for invoking the process is the value of the `gcltksrv' argument, which defaults to the value of the environment variable GCL_TK_SERVER. If that variable is not set, then the lisp *lib-directory* is searched for an entry `gcl-tk/gcltksrv'.

If host is supplied, then a command to run on the remote machine will be printed on standard output. If can-rsh is not nil, then the command will not be printed, but rather an attempt will be made to rsh to the machine, and to run the command.

Thus

 
(tkconnect)

would start the process on the local machine, and use for display the value of the environment variable DISPLAY.

 
(tkconnect :host "max.ma.utexas.edu" :can-rsh t)

would cause an attempt to rsh to max and to run the command there, to connect back to the appropriate port on the localhost.

You may indicate that different toplevel windows be on different displays, by using the :display argument when creating the window, See section 2.15 toplevel.

Clearly you must have a copy of the program `gcltksrv' and TK libraries installed on the machine where you wish to run the server.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Camm Maguire on July, 15 2003 using texi2html