TkForth
An interface to the Tk toolkit for ProForth for Windows

Robert M. Boyce and Peter J. Knaggs

Department of Computing and Information Systems,
University of Paisley, High Street,
Paisley, Scotland. PA1 2BE

pjk@paisley.ac.uk


Abstract

This paper was written and compiled by Robert M. Boyce and Dr Peter J. Knaggs based upon a project undertaken as part of an honours degree at the University of Paisley [Boy96]. The Tk toolkit is a freeware user interface widget toolkit based upon the Tcl scripting language developed by Dr John K. Ousterhout. Originally developed under the X11 windows system, the Tk toolkit has recently been ported to the 32 bit Microsoft Windows platform. In this paper we look at an interface between this port of Tcl/Tk and MPE's ProForth for Windows. The interface was to be designed in such a way that a Forth programmer could utilise the Tk toolkit to build user interfaces to Forth programs, in place of the cumbersome and difficult to learn win32 API (Application Programmer Interface) currently provided by ProForth for Windows.


1. Tcl/Tk Background

Tcl (Tool command language, pronounced tickle) provides the application programmer with a powerful yet elegant scripting language. It was designed to be easily incorporated into applications based on the C programming language, although interfaces for other languages, such as Ada, now exist.

With the development of the Tk toolkit, a powerful Motif like widget set based upon the Tcl scripting language, a new role emerged for Tcl. When combined with Tk, Tcl provides the application developer and indeed the end user with a simple, powerful Graphical User Interface (GUI) designer. The nature of Tcl/Tk allows the GUI to be built dynamically, and also allows for the end product to have several user definable interfaces. The parsing of an external script file at run time to determine the look and feel of the interface being the most common use. This has many applications, eg, multi-lingual applications and different interfaces for novice and expert users. Tcl/Tk also allows a series of small applications to be tied together to make one super application, with Tk providing an interface between these applications. A typical example is a word processor, composed of separate text editor, spell checkers, grammar checker etc. with the end user being provided with a common, user-configurable, interface. If the user frequently uses a sequence of commands he could bind all these commands to a button/keypress/mouse click which he himself defined in the interface script.

Recently Tcl and Tk have been ported to other operating systems and environments, from the original incarnation on a UNIX X-Windows environment. These other systems include Macintosh's system 7 and Microsoft's Windows (32 bit versions only).

2. Tcl, an Overview

Tcl is an embeddable scripting language, which has much in common with most other scripting languages in that it is founded upon the principle that in an ideal world we would have one universal scripting language which controls all aspects of an application. Scripting languages in general increase the speed with which applications are developed as they raise the level at which the application is developed. In the current climate of interactive applications, a command language is typically employed to control the high level functions, and to give the user a degree of flexibility in the way they use the application. Scripting languages tend to be targeted to specific application areas.

A possible solution to this is a universal scripting language. In the case of Tcl the language interpreter is provided as a C library, as C is by far the most common development language in use at this time. This scripting language would provide basic programming constructs such as variables and procedures. A key point, however, is that the scripting language must be easily extensible. Tcl was designed to be used with a two language approach to application development. Where the scripting language would provide the high level control procedures and a conventional language such as C would provide the functionality. This is desirable because a scripting language cannot achieve the performance of conventional languages. Conventional languages however are not as easy to program as scripting languages nor are they easily interchangeable. Figure 1 shows the basic structure of a common Tcl application.


Figure 1: Structure of Tcl Applications [Ous94d]

3. Tk, an Overview

The Tk toolkit is a Motif-like widget toolkit based upon the Tcl scripting language. Tk has a simple syntax. For example, the command:
button .hello -text " Hello World"
creates a button called .hello with the text Hello World on its face (Figure 2). This is clearly an improvement over the standard Win32 API provided by ProForth, which is complex and requires a great deal of specialist knowledge on the part of the programmer. For example, the command:
10 10 100 20 BS_PUSHBUTTON Z" Hello World" "BUTTON" CONTROL
creates a similar button to the above Tk code, in actual fact due to the myriad of defaults supplied to the Tk button, it is more pleasing to the eye.


Figure 2: Tk button widget example

Tk provides a programmable widget set composed of the following widgets:
button, checkbutton, radiobutton, menubutton, menu, canvas, label, entry, message, listbox, text, scrollbar, scale, frame, toplevel
Like most widget toolkits Tk uses geometry managers to control the display of its widgets. Geometry managers are a set of algorithms which control the display of on screen objects. The main Tk geometry manager is the packer and it is used as follows:
pack <widget list> <options>
It is important to note that the widgets are not displayed until managed by a geometry manager. Geometry managers also have parameters which alter their function, such as padding, stack order, and anchoring. For a more detailed description of geometry managers see [Wel95].

As with all windowing systems Tk requires an event loop to process commands, the event loop for Tk is called Tk_Mainloop, and is provided in the Tk41 Dynamic Link Library (DLL). It is possible however to process all existing events by flushing the event queue with TclWinFlushEvents, it is this function that is used to allow TkForth to build GUI's interactively.

Given the programmability, power and the elegance of the ProForth system it would provide a powerful Windows development tool if the complexity of developing the GUI could be overcome. Given the ease with which Tk Applications can be developed, and it's interactive nature, it would seem like an ideal solution to providing the ProForth programmer with an ability to develop complex interfaces quickly.

4. System Design

The TkForth interface was designed to mimic as much as possible the standard Tcl and C interface, as developed by Ousterhout. The system was developed in order to facilitate two main models of use. These two models would both allow the Forth programmer to develop Windows based software without the need to learn the complex and varied syntax of the Windows 32 API. Also the developer requires only a minimal understanding of windows programming as most of the work is done by Tk and not by the developer as is usual in conventional Windows 32 API programming. As part of this project we have developed a simple, consistent, easy to learn syntax developed for this project, with only minimal changes from the Tcl syntax. Another important aspect of the Tk toolkit is the ease with which applications can communicate with each other. This would allow an application written in TkForth to communicate with an application written in C or Ada or any other language, providing it also contained a Tcl interpreter.

Unlike most current widget toolkits, neither model is object-oriented as Tk is not strongly object-oriented. The widget structure may present a superficial resemblance to object-oriented methods but there is no official class structure and no inheritance amongst widget types [Ous90]. This was a conscious decision by the developers of Tk.

4.1. Model 1

The first model of use is based on the original model proposed by Ousterhout (Figure 1). Allowing the Forth programmer to write the main body of his application in Forth, with the interface written as a Tcl script, which can be evaluated at run-time by the application. This allows the end-user to customise the application with only a rudimentary knowledge of Tcl/Tk as opposed to the specialist knowledge and skill required to program exclusively in Forth. This model works by first writing the functionality of the system in Forth. The words which are to called from Tcl are then defined as callbacks and the addresses are passed to the Tcl Interpreter. The interface and high level functionality is then controlled by an external Tcl file which uses these callbacks as Tcl commands. This requires the Tcl file to be evaluated by the application after the Forth callbacks are implemented. This model is not as readily debugged interactively, as it would be necessary to write procedures to generate Tcl scripts on the fly. This is however not difficult task. [We95] gives a number of such scripts.

4.2. Model 2

The second model is aimed at the Forth programmer who wishes to code exclusively in Forth. The interface is simple and easy to use, and works on a need-to-know basis. The developer is only required to learn a small subset of the toolkit in order to produce fairly complex results. To create a button, for example, the developer uses the :button word, followed by the name of the button and then a list of optional parameters. These parameters can be used in any order, and indeed it not necessary to include any of them at all, as there are defaults for all.

Example:

:button Hello
  text>" hello World"
  callback>  cb-greeting
;
This creates a Tk button with the text "Hello World" on the face. The Forth word cb-greeting is bound to this button through a callback structure. The Forth word greeting must be defined before the callback> parameter is supplied to the button. The Forth programmer can then use the Hello word to refer to the button. The parameters supplied at creation can be altered through the use of the configure command, as follows:
Hello configure text>" Hello Universe" ;
The callback word is created from a common word through the use of the >callback word ( word-address <Forth_Name> <Tcl_Name> -- ), as follows:
' greeting >callback cb-greeting Tcl-greeting
Given the predefined word greeting.

The configure command can also be followed by any number of optional parameters, providing the widget in question supports that parameter. It was decided that this model should be interactive. Thus allowing the developer to build/modify the application from the ProForth debug window.

This model works by building Tcl command strings in a buffer from the ProForth commands entered at the terminal (or from a file) and passing these strings for Tcl to evaluate. This string is then passed to Tcl for evaluation. This was an important design decision and was taken mainly for the following reasons:

4.3. Using the callback System

The callback system is used by first defining the Forth word to be executed upon the callback. A callback word is then defined using >callback which also defines a Tcl function to invoke the callback. A convention of using a cb- prefix for callback words and a Tcl- prefix for Tcl callback functions has been adopted. The callback function is passed as a parameter to a widget, via the callback> option. Callbacks may be defined on creation of the widget or later through the configure command. The callback system has been defined in this manner in order to ensure that it is possible to call the original word from inside other Forth words, if the word were to be defined with a special definer, this would not be possible.

In the following example, a greeting word is defined, a callback word is defined (cb-greeting) to execute this word. A Tcl function (Tcl-greeting) to invoke the callback is also defined. Finally a button widget is defined (Hello) which displays the text "Hello World" on the button and executes the Tcl function Tcl-greeting when pressed. This function invokes the cb-greeting callback, which executes the greeting word in turn.

: greeting
  ." Hello World"
;
' greeting >callback cb-greeting Tcl-greeting
:button Hello 
  text>" Hello World"
  callback> Tcl-greeting
;

4.4. Variable Passing

It is necessary for GUI systems to pass variable values from the controls to the functions in the main program. TkForth is no exception. Variable passing is achieved through the use of the Tcl_GetVar and Tcl_SetVar functions. Ousterhout recommends this method [Ous94c] as the arguments are passed as strings, thus allowing Tcl to maintain it's own internal variable space.

Three new Forth words were defined to implement variable passing TkVar, Tk@, and Tk!. These words were designed to be externally similar to the predefined Forth words Variable, @ and !. It is important to note however that these variables are stored as zero terminated strings, and they must be formatted as such before a Tk! or formatted into the desired form after a Tk@.

5. Implementation

5.1. Implementation of widget creation commands

The widget creation commands were to be created by a Forth definer defining word. This technique has the following benefits: The Widget creation word is called tk-make-object and is used as follows:
"" button ." tk-make-object :button
The "" button ." is the tk representation of the command to create a new button, whereas the :button word is the Forth command to create a new button. The :button word is then used as follows:
:button hello
This creates a Forth word hello which is used to refer to the button. This word effectively places a ".hello" string in the tcl-text buffer which is passed to Tcl to evaluate commands. Other widget defining words can be defined in the same way, such as:
"" label ." tk-make-object label:

5.2. Implementation of widget configuration options

Five classes of widget configuration options were identified, these were implemented using defining words, which allowed the functions of these to be altered centrally. The five classes identified were:
  1. absolute option - single word options such as 10, grey80 etc.

  2. quote option - primarily used for text such as "Hello World"

  3. strip-quote option - used to group together parameters at the Forth level, which will be interpreted by Tcl as being a single argument.

  4. path option - used to pass widget paths, such as .hello. This is required as the TkForth does not require a preceding "." in widget paths whereas this is necessary for Tk.

  5. command option - used when a widget command pair is required as an argument, this is primarily used for the scroll commands.
The defining words used are of the following format:
tk-make-absolute-option
And are used as follows:
"" -activebackground" tk-make-absolute-option activebackground>
The activebackground> word is then used in conjunction with a widget defining word or a widget command word (see next section) to set the active background colour.

Example:

:label MyLabel
  activebackground> grey80
;
creates a label widget with grey80 as its background colour when active.

Quote options (defined using tk-make-quote-option) are used as follows:

text>" Hello"
With everything between the quotes being passed as one argument to Tk inside quotes. The strip quote options are used in a similar manner but the arguments are passed without the quotes. Command options are used as follows:
xscrollcommand> f.scroll set
where f.scroll is a widget and set is the name of the widget command. The other configuration options are used in a similar manner.

5.3. Implementation of widget command options

Widget commands are required to invoke some of the special features of the widget toolkit. The scrollbars providing a good example. One common command is the configure command which allows the programmer to alter a configuration option after the widget has been created. Widget commands are defined in TkForth using a definer word, Tk-make-command. This is used to create new words which place their Tk equivalent in the tcl-text buffer. For example:
"" configure" tk-make-command configure
creates the Forth word configure which places the text " configure " in the tcl-text buffer. The reason why the tk-make-command definer word has the above format and not merely tk-make-command configure, generating the Tcl string on the fly, is that some of the Forth words are not the letter for letter identical to the Tcl equivalent, some of the Forth commands translate into more than one Tcl command. This was decided upon to simplify the TkForth programmers learning curve, and maintain some consistency of interface. Also it is possible with the above word to alter the Tcl command with subsequent releases, should the syntax be changed while allowing TkForth programs to run with no modification, thus introducing an element of future proofing.

5.4. Sending the command string to Tcl

The above commands are used to generate a string in a buffer to allow the Forth programmer to send commands to the Tcl Interpreter. The correct formatting is done by the above words, but is still necessary to send the string to Tcl for evaluation. This is done by overloading the ; (semicolon) word. This word is used in a similar manner to the standard word. It is used to finish definitions of Tk widgets, and also at the end of packer commands. The new ; is defined as part of the Tk vocabulary, this vocabulary is invoked when a reference to a Tk widget or a geometry manager is made. The next ; is then judged to be a command to send the tcl-text buffer to Tcl for evaluation, it then restores the current vocabulary to the standard.

5.5. A shortcut for Tcl and Forth practitioners

If the Forth user has some experience with Tcl/Tk a method for sending Tcl commands directly to the Tcl interpreter has been provided. This is done by the use of the tk` word and is used as such:
Tk` button .hello -text " Hello World" `
This allows hand optimisation of the code, by the multilingual Tcl/Forth developer. Alternatively Tcl command strings can be passed directly to the interpreter through the use of the Tcl_Eval function.
"" set tk_strictMotif 1 " $>asciiz rel>abs tcl-evaluate

6. Examples

ProForth for Windows comes with a number of example files demonstrating the methods employed to program the windows interface using the 32 bit API provided by ProForth. Figure 4 illustrates one of these examples, while Figure 3 illustrates the TkForth equivalent. This example was chosen to highlight various TkForth features As can be seen from Figures 3 and 4, the TkControls are, by default, more aesthetically pleasing than the standard API controls.

Figure 3: TkControls Example Figure 4: ProForth API Controls Example

The ProForth 32-Bit API requires 98 lines of complex API calls to provide the above example. TkForth achieves similar functionality with only 52 lines. Coding the above example in C and providing a Tcl script file requires around 150 lines of source code (note that blank lines and comments are not included in these counts). This reduction in the size of the source code is made all the more remarkable when one considers the performance of the TkForth system. While not as efficient as the raw API calls, TkForth appears to perform admirably alongside the Tk/C method. This slight loss of performance when compared to raw API calls is minimal and is forgivable when one considers the reduced complexity and specialist knowledge required.

7. Summary

The project goal was to produce an interface to the Tk toolkit for ProForth for Windows. This task was achieved through study of both the systems and determining the aspects of both which would remain in the TkForth interface. The two models of use provide the Forth developer with differing options regarding the manner with which the Tk toolkit is utilised. The Forth purist will prefer the Forth only model. Practitioners familiar with the power of Tcl scripts however can incorporate quite complex user interfaces written in Tk into their applications.

8. References

[Boy96]
Boyce, R.M.: TkForth: An interface to the Tk toolkit for ProForth for Windows, Dissertation, University of Paisley, 1996.

[MPE96]
MicroProcessor Engineering Ltd: ProForth for Windows reference manual, 1996.

[Ous89]
Ousterhout, J. K.: Tcl: an embeddable command language, 1989.

[Ous90]
Ousterhout, J. K.: An X11 toolkit based on the Tcl language, 1990.

[Ous94a]
Ousterhout, J. K.: Writing Tk based applications in C, 1994.

[Ous94b]
Ousterhout, J. K.: Building user interfaces with Tcl and Tk, 1994.

[Ous94c]
Ousterhout, J. K.: Tcl/Tk Engineering Manual, 1994.

[Ous94d]
Ousterhout, J. K.: An overview of Tcl and Tk, 1994.

[Sto89]
Stoddart, W. J.: Forth++ Course Notes (part I—III), University of Teesside, 1989.

[Wel95]
Welch, B.: Practical programming in TCL and TK, 1995.