Untitled Document - General

Go to the first, previous, next, last section, table of contents.

# General

## 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 section Widgets, and more general functions for making them appear, lowering them, querying about them in section Control.

## 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 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).

## 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 section 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 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).

## Return Values

### 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.

### 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.

### Control Function Return Values

The control funcions (see section 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.

## Argument Lists

### 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 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 scrollbar.

    (.scrollbar :set a1 a2 a3 a4)

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

    (.scale :set a1 )

only one numeric argument should be supplied, in order to position the scale.

### 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 Widgets.

### 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.

## 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 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 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 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 bind.

    (bind "Entry" "<Control-KeyPress>" '(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.

## 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 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 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`.

## 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 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.

Go to the first, previous, next, last section, table of contents.