3. Tcl and Tk Basics

Tcl is built up from commands which act on data, and which accept a number of options which specify how each command is executed. Each command consists of the name of the command followed by one or more words separated by whitespace. Because Tcl is interpreted, it can be run interactively through its shell command, tclsh, or non-interactively as a script. When Tcl is run interactively, the system responds to each command that is entered as illustrated in the following example. You can experiment with tclsh by simply opening a terminal and entering the command tclsh.


$ tclsh
% set a 35
35
% expr 35 * $a
1225
% puts "35 * a is: [ expr 35 * $a ]"
35 * a is: 1225
% exit
$

The previous example illustrates several aspects of the Tcl language. The first line, set a 35 assigns 35 to the variable a using the set command. The second line evaluates the result of 35 times the value of a using the expr command. Note that Tcl, like Perl and Bash requires the use of the dollar operator to get the value of a variable. The open brackets around the expression [ expr 35 * $a ] instruct the interpreter to perform a command substitution on the expression, adds it to the rest of the string and uses the puts command to print the string to Tcl's default output channel, standard output.

Tcl's windowing shell, Wish, is an interpreter that reads commands from standard input or from file, and interprets them using the Tcl language, and builds graphical components from the Tk toolkit. Like the tclsh, it can be run interactively.

To invoke Wish interactively, start X on your system, open a terminal, and type wish at the command prompt. If your environment is set up properly, this will launch an empty root window and start the windowing shell in your terminal. The following example is a two-line script that is one of the simplest programs that can be created with wish:


$ wish
% button .submit -text "Click Me" -command { puts "\nHello World" }
.submit
% pack .submit

Let's break down these two lines of code:

button .submit -text "Click Me" -command { puts "\nHello World" }:

The button command enables you to create and manipulate the Tk button widget. As with all Tk widgets, the syntax is button .name [-option value] [-option value] .... The curly braces surrounding the puts command allow you to nest the text string, "Hello World", inside of the command without performing any variable substitutions. Other basic widgets include the following: label, checkbutton, radiobutton, command, separator, entry, and frame. Click the button a few times to verify that it works.

pack .submit

The pack command tells the Tk packer geometry manager to pack the window name as a slave of the master window . which is always referred to by the character .. As with the other Tk widget commands we will see, the syntax is pack .name [-option value] [-option value].

While the previous example was very simple, more advanced examples are nearly as easy to build. Have a look at the following script which creates a simple graphical front end for apachectl ( please note, this example is intended to be run as a script rather than interactively from the shell. You will need to set the permissions of the script as executable and run this script as a user with privileges to start and stop apache ):


#!/usr/bin/wish

set apachectl "/usr/local/apache/bin/apachectl"
global apachectl

proc start {} {
  global apachectl
  exec $apachectl start & 
}

proc stop {} {
  global apachectl
  exec $apachectl stop &
}

proc restart {} {
  global apachectl
  exec $apachectl restart &
}

proc screen {} {
  frame .top -borderwidth 10
  pack .top -fill x	
  button .top.start -text "Start" -command start
  button t.op.stop -text "Stop" -command stop
  button .top.restart -text "Restart" -command restart
  pack .top.start .top.stop .top.restart -side left -padx 0p -pady 0
}
screen

This script introduces a few new concepts. Let's look at some of them line by line:


set apachectl "/usr/local/apache/bin/apachectl"
global apachectl

As we saw earlier, the set command is used to assign a value to a variable. As with the previous examples, the syntax is simple: set variable_name value. In order to make the variable available to the Tcl procedures that we are creating in this program, we need to import the apachectl variable into each procedure. This is accomplished using the global command which adds a named variable to the local namespace of a given procedure. The global command accepts one or more variables as arguments and assigns the named variables to each procedure used in the program. Global is also used to export variables that are declared within a procedure's local namespace.


proc start {} {
  global apachectl
	  exec $apachectl start &
}

Procedures in Tcl are created with the proc command. The proc command takes the following form: proc name {args} {body} where name is the name of the procedure. Args are the formal arguments accepted by the procedure, and body is the main code of the procedure. Procedures are executed the same way that any other command is executed in Tcl.

The script we are currently working with consists of 4 procedures. The first 3 ( start, stop, restart ), simply import the apachectl variable into the local namespace and execute the basic apachectl commands as background processes while the 4th procedure, "screen", uses the packer to build the basic screen and call each of the functions.

Let's have a closer look at the screen procedure:

  
proc screen {} {
  frame .top -borderwidth 10
  pack .top -fill x	
  button .top.start -text "Start" -command start
  button .top.stop -text "Stop" -command stop
  button .top.restart -text "Restart" -command restart
  pack .top.start .top.stop .top.restart -side left -padx 0p -pady 0
}

The screen procedure begins by using the frame command to construct the basic frame that will contain the buttons specified further down in the procedure. As this example illustrates, slave widgets are specified by prepending them with the name of their master followed by a ".". The master must already be packed before the slave can use them, so we pack the frame .top before specifying the button command and tell it to fill along the x axis.

Last, we use the button command to create 3 buttons as slaves to .top, passing in the appropriate procedure to execute when the button is pressed, and adding a text label using the -command and -text arguments, respectively.