.gif)
Tcl with its X Window toolkit adds command scripts and user
interfaces to both standalone tools and applications
By Brent
Welch
The Tcl (Tool Command Language) has gained a lot of recognition
as a new scripting language and as an embeddable interpreter that is
easy to add to your applications. As a scripting language, Tcl
(pronounced ``tickle'') fills the niche also occupied by various
Unix shell languages such as the Bourne shell, the C shell, and
Perl. By using Tcl or these other shell languages, you can glue
together existing Unix applications into a tool that is customized
for your particular needs. What helps to distinguish Tcl are
extensions not found in other shells, for example, an easy-to-use X
window system toolkit.
The core Tcl language is quite simple. There are a number of
predefined commands that provide basic programming capabilities such
as variables, control flow, procedures, and error handling. There
are also a set of predefined commands that provide access to Unix
facilities, such as accessing the file system and running other Unix
programs. There is just a small amount of syntax you need to
know.
Adding a Tcl interpreter to your application provides even more
flexibility to your users and the system integrators who use your
application. Users can write short Tcl scripts that customize your
application to their liking. System integrators can exercise
sophisticated control over your application, for example, by
bundling it with a set of Tcl-aware applications that all work
together in concert.
Perhaps the real power of Tcl comes from the large and growing
number of extensions that are available for it. A Tcl extension is a
set of Tcl commands that are implemented in C and packaged into a
library. The programmer can create a supershell that combines
several extensions together to create a platform for rapidly
constructing applications by scripting them in Tcl.
The most important extension is Tk, a toolkit for the X window
system. Using a Tcl/Tk shell (called wish ), programmers
can create a graphical user interface at the same time they glue
together their customized tool. Tk provides a much higher-level
interface when compared to other toolkits such as Motif or the
Windowing Korn Shell. It takes just a few Tcl commands to define
user interface widgets and compose them into a working
interface.
The syntax for a Tcl command is simple. The first word is the
name of a command. The command name and the remaining arguments are
separated by spaces. The command is terminated by a newline or a
semicolon. The arguments are passed to the command implementation
without interpretation, except for the substitutions described
below.
The basic structure is augmented with two mechanisms: grouping,
used to get arguments that contain whitespace; and substitution, a
macro-like facility used with variables and nested command
invocations. There are two ways to group things. Grouping with curly
braces, { } , prevents substitutions from occurring on
the things inside them, while grouping with double quotes, "
" , allows substitutions. Finally, there are also some
backslash sequences that can be used to quote special characters and
to obtain characters by specifying their character code.
The first line in Listing
1A defines a Tcl variable, name , as having the
value Brent Welch . The curly braces used to group the
value into one argument are discarded before the set
command is invoked. The second command outputs a string to the
standard output. In this case double quotes are used so that
$name will be substituted with the value of variable
name . The substitution and grouping occurs before
puts is invoked. Thus the basic function of the Tcl
interpreter is to parse a command to do groupings, then
substitutions, and finally invoke the command with the resulting
arguments.
The interpreter also does command substitution to allow for
nested commands. For example, suppose we want to get the current
date. We can do this by running the Unix date command,
as shown in Listing
1B.
The Tcl interpreter treats the string between the matching square
brackets as another Tcl command. It postpones processing of the
current command, invokes the nested command, and then replaces the
square brackets and everything between them with the value returned
by the nested command. In this case, the Tcl exec
command returns whatever the program has written to its standard
output, which is something like Mon Aug 8 17:01:53 PDT
1994 .
There is an implicit grouping done too, so there is no need to
group the results of the date command even though it
contains spaces. In other words, it is not necessary to
quote a nested command, as in set d "[exec
date]" .
A rule of thumb to help you in more complex cases is to remember
that grouping decisions are made before substitutions. Grouping of
characters into arguments is determined by whitespace, curly braces,
and double quotes. Substitutions are triggered by the dollar sign
for variable substitutions and square brackets for command
substitutions.
For our first extended example we will write a user interface to
the Unix ping command, which sends a network packet to
a host and waits for an answer. It reports the time taken or a
failure if the host does not respond. Our first version of the
script (see Listing
2A) will be a bit limited, but we will address those limitations
in the second example.
The first line of the script causes it to be interpreted by
wish , the Tcl/Tk shell. Of course, on your system
wish might be installed in an alternate location. The
-f is required for historical reasons. The
wish shell was originally designed just to test the Tk
toolkit. Due to limitations of the Unix exec() system
call, this line must be fewer than 32 characters in length,
including the #! and the -f , so be careful
if you install a private copy of wish in some deeply
nested directory. You will get confusing ``Command not found''
errors when running the script.
This script creates several Tk widgets, one each on lines 3, 6,
7, 10, 11, 12, and 15. The example uses buttons , which
have an associated Tcl command; a label , which is a
read-only text widget; an entry , which is a one-line
text-entry widget; frames , which are container widgets;
and a text widget, which is a general-purpose multiline
text widget.
If you examine Tcl commands that create the widgets you will see
a common form. The command name indicates what kind of widget is
being created. The first argument is the name of the widget. The
remaining arguments are pairs of attributes and values for the
widgets. Tk widgets have a lot of attributes so that you can control
most of their appearance and behavior, but in most cases you only
need to specify a few attributes and can leave the rest of the
attributes in their default settings.
Tk uses a naming convention for its widgets that reflects their
position in the hierarchy of windows. The main window of an
application is named simply ``.'', and other widgets have a path
name of components separated by periods. This pattern is analogous
to the way Unix files are named, where ``/'' is the name of the root
directory, and files have path names with components separated by
slashes.
In this example there are three widgets that are children of the
main window: .buttons , .f , and
.log . The pack commands on lines 4, 10,
and 16 arrange these widgets in a vertical stack inside the main
window by specifying the -side top packing
parameters.
There are two buttons that are children of the
.buttons frame: .buttons.quit and
.buttons.ping . These are arranged in a horizontal stack
by the pack command on line 8 by specifying -side
right . A similar thing is done on lines 10 through 13 with
the .f frame that is used to hold the label,
.f.l , and the entry, .f.host . Line 10
contains two Tcl commands just to remind you that the semicolon
character functions as a command terminator.
I have found horizontal and vertical stacking of widgets within
frames to be the most trouble-free way of using the Tk packer. There
are other features of the packer that I have to skip for this
introduction.
The interface consists of the Quit and Ping buttons, the entry
widget used to name the host, and the text widget used to log the
output.
The way that the Quit button functions is fairly straightforward.
When the user clicks on it, the associated Tcl command,
exit , is invoked, which causes the program,
wish , to terminate. Note that there is nothing at the
end of the script to tell it to keep going, either. This action is
implicit in the behavior of wish , which reads a Tcl
script to configure the interface and then goes into an event loop
processing window events.
The Ping button invokes the Ping command, as shown on lines 18
through 24. Let's digress and talk about Tcl syntax again. Lines 18
through 24 form a single Tcl command because of the way grouping
with curly braces works. It groups characters, including newlines
and semicolons, until a matching brace is found. Grouping with
double quotes works the same way, except that there is no
nesting.
The Tcl proc command (line 18) takes three
arguments: the procedure name, an argument list, and a command body.
In this case the argument list is empty, so we use curly braces to
group no characters to form the empty list. The curly braces around
the procedure body are placed carefully. The opening brace at the
end of line 18 causes the interpreter to keep scanning characters
until it gets to the matching brace, in spite of the embedded
newlines. These characters together form the third argument to
proc . At this time they are uninterpreted because of
the curly braces used to group them.
The commands within Ping are interpreted later when the Ping
command is invoked. At that time the newlines in the command body
serve as command terminators and break the body up into several
commands. Finally, note that hostname is a local
variable inside Ping and that it is not defined elsewhere, even
after Ping has been invoked.
The Ping command does three things. First, it gets the current
value of the entry widget with the .f.host get command.
The name of the widget is a Tcl command that operates on the widget.
The .log command is used to insert the results of Ping
into the text widget. The Unix ping command is run
under the protection of the Tcl catch command in case
it raises an error. Tcl exec will raise an error if the
command is not found or if the command writes to standard error.
The first argument to catch is a Tcl command, and
the second is the name of a variable in which to place the result
(or error message). The catch command returns 1 if an
error was raised, or 0, but we are ignoring the return value in this
case. I recommend using braces to group the Tcl command argument to
catch because it will call the full Tcl evaluator on
that argument, at which time any substitutions will occur. If you
use double quotes, you will get two rounds of substitutions, which
can cause unexpected results.
There are some details not covered here, like the exact meanings
of all the attributes for the widgets and the details of using text
and entry widgets. There are Unix manual pages for each of the Tcl
and Tk commands that describe all the details.
The main problem with this example is that we'd like to run
ping in its mode that continuously queries the host,
and add a Stop button. To do so, we will need to split the example
into two scripts that communicate using the Tk send
command. One script will be the control panel, the other will be a
helper script that runs ping and sends the output to
the control panel for logging. Listing
2B shows the helper script.
Line 3 tells the window manager to unmap the window. The main
window isn't needed by the helper, but we need to use
wish to use the send command.
Lines 4 and 5 access the command-line arguments. The
lindex command is used to index into a list. The
argv variable is the list of command-line arguments for
the script. The first argument is the name of the host to
ping , and the second argument is the name of the Tcl
interpreter with which to communicate.
Line 6 opens a pipeline for reading. That is, the Unix command
ping -s $hostname is forked (with
$hostname suitably replaced), and the output of that
program is available to the script by reading from an I/O stream.
Line 7 uses the gets command to do the reading. The
gets command puts the next line of input into a
variable, line , and returns the number of characters
read, not counting the newline that is discarded.
Line 8 uses the send command to invoke a Tcl
command, namely Insert $line , in another interpreter.
In this case we use the list command to properly
construct the command for us, taking into account any special
characters that might be in the line. The $line is
substituted before list is invoked and well before the
command gets sent. The list command formats its
arguments in such a way that they will survive reparsing by the
remote interpreter. Thus the send command gets two
arguments, the name of the other interpreter, and a safely packaged
Tcl command.
We can leave the control panel script alone, except for changing
the Ping procedure and adding two more procedures. The
Insert procedure inserts into the log, and the
Stop procedure stops the helper script. The changes to
Ping and the two new procedures are shown in Listing
2C.
On line 21 the helper script is run. It is passed two arguments:
the host name and the result of a command that returns the name of
the Tcl interpreter. This name identifies the interpreter to others
so that they can send it Tcl commands. The helper script is run in
the background, and exec returns its process ID.
You can dynamically change any Tk widget attribute that you could
specify when you created the widget. On lines 22 and 33 the behavior
and appearance of .button.ping is changed. The button
is alternately a Ping button and a Stop button.
The Ping and Stop procedures need to communicate the process ID
of the helper script, done with the global variable
pid . By default, variables inside procedures are local.
The global command makes them visible in the global
scope.
The Insert procedure is there for convenience. We
could have arranged for pinghelper to send all the
commands inside Insert , but that would be awkward. As
the script evolves, the Insert procedure will probably
prove useful in other situations, too.
Tcl and Tk, and a host of extensions, are available freely over
the Internet. The current archive site for Tcl is ftp://www.sunlabs.com/pub/tcl .
Tcl and Tk are distributed under a University of California at
Berkeley copyright that allows for use in commercial products with
no license fee. There is a newsgroup, comp.lang.tcl ,
for discussion of Tcl and its applications. Tcl and the Tk toolkit
were created by Professor John Ousterhout while he was at U.C.
Berkeley. I recommend his book, Tcl and the Tk Toolkit,
published by Addison-Wesley. |