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

More on the Guile interface to Tk

A couple of the examples in the quick tour made use of the Tk toolkit. Here I will say more about how Guile interfaces to Tk. We will also give you a recipe for reading Tk documentation, which is written assuming the Tcl interface, and writing scheme code with it.

In this chapter I do not give any sort of introduction to Tk in general. There are books on Tk, but maybe the best way to learn the Tk widgets is to go through the wtour tour of widgets that is distributed with Tk. It is more pedagogical than the main book Tcl and the Tk Toolkit.

What you need to use Tk

The Guile program is usually compiled with the Tk interface, so to use Tk you only need to include this preamble in your scheme code:

(require 'Gwish)
(use-library tcl)
(use-interface tcl)
(use-interface tclhack)

and then conclude the main program with:

(tk-main-loop)
(quit)

Note: the preamble is kind of ugly, and it will probably change.

Creating a widget

Here are some (out of context) examples of the syntax for creating Tk widgets:

(button '.quit :text "quit" :command (tcl-lambda () (quit-callback)))
(text '.a :yscrollcommand ".b set")
(scrollbar '.b :command ".a yview")
(frame '.button-bar :relief 'raised :bd 2)
(canvas '.ca :width 200 :height 300)
(label '.lab :text "this is a label")

What can we deduce from these examples of widget creation?

  1. There are new primitive scheme procedures for each type of widget; invoking this procedure creates the widget.
  2. The widget name is always followed by a quoted variable name. The variable name is the standard Tk widget hierarchical name with the dot separator.
  3. Options that modify the appearance or behaviour of the widget are given with a colon, such as :text, :command, :width ...

Scheme callbacks

A widget callback (or simply callback) is a procedure that gets invoked when something happens in a widget, for example if the mouse is clicked on a button or scrollbar.

When you first create a Tk widget, you can specify that a procedure should become the callback, usually with the :command option in the widget creation, although a widget could allow several callbacks, and those could be set by other options such as :yscrollcommand.

If a command is to invoke a scheme procedure, it is done with the tcl-lambda primitive. This is similar to a simple scheme lambda expression, but it straightens out the calling convention between Tcl and scheme.

The tcl-lambda expression also makes sure that the anonymous procedure created will not be garbage-collected away while a callback is not being executed.

Notice that tcl-lambda is not a clean implementation, and Guile will probably be fixed eventually so that you can just use lambda.

But not all callbacks invoke scheme procedures. In the creation examples just given, the widget created by

(scrollbar '.b :command ".a yview")

passes a message to the text widget .a. This message passing is done with the string ".a yview", which just maps directly to the equivalent Tcl code.

Sending a command to a widget

Once a widget has been created and its callbacks (if any) have been registered, that widget becomes a new scheme primitive procedure. This procedure can be used to give further instructions to a widget.

One example of this is to add or change configuration options to a widget after it has been created. Try this self-contained program to see an example of the config command being sent to a widget. It also shows examples of putting several buttons together in a frame, and illustrates the destroy command that kills off a Tk widget.

You can find this example in `examples/tk/two-button.scm'.

#!/packages/bin/guile -qb
; -*-scheme-*-

;; a two-button example that does some funky stuff

(require 'Gwish)
(use-library tcl)
(use-interface tcl)
(use-interface tclhack)

;; create a frame widget to house the buttons
(frame '.button-bar :relief 'raised :bd 2)
(pack '.button-bar)

;; create the quit button: its callback destroys the top level widget
(button '.button-bar.quit :text "quit" :command
  (tcl-lambda ()
    (begin
      (display "quitting now\n")
      (destroy "."))))

;; create the "change text" button: its callback changes the text
;; of the "quit" button, and destroys itself.
(button '.button-bar.change-text :text "change text" :command
  (tcl-lambda ()
    (begin
      (.button-bar.quit 'config :text "hey, it just changed")
      (destroy ".button-bar.change-text"))))

(pack '.button-bar.quit '.button-bar.change-text :side 'left)

(tk-main-loop)
(quit)

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