UnixWorld Online: Tutorial November 1994

  • Tcl Basics
  • A Tcl/Tk Front End to ping
  • GUI Made Simple
  • Improving our Front End
  • Where to Get Tcl/Tk
  • An Introduction to Tcl and Tk

    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.

    Tcl Basics

    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.

    A Tcl/Tk Front End to ping

    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 (see Figure 1). 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.

    GUI Made Simple

    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.

    Improving our Front End

    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 (see Figure 2).

    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.

    Where to Get Tcl/Tk

    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.

    Copyright © 1995, 1996 The McGraw-Hill Companies, Inc. All Rights Reserved.
    Edited by Becca Thomas / Online Editor / UnixWorld Online / editor@unixworld.com

    [Go Home] [Go to Content] [Search Editorial] [Register]

    Last Modified: Tuesday, 24-Sep-96 07:48:14 PDT