Scripts and Utilities -- Tcl/Tk lecture



What is Tcl?

Tcl (Tool Control Language)is an interpreted scripting language that performs many of the same functions as Perl or Python. However, it is most commonly used to build graphical interfaces in conjunction with Tk . One example use of Tcl is to build a graphical interface that displays the output of a C command, and/or controls the parameters to the C command.

What is Tk?

Tk is a graphical toolkit that gives one access to many of the graphics primitives of the underlying windowing system, as well as providing access to many types of widgets, such as buttons, scrollbars, menus, and text boxes.

What is wish?

wish is an interpreter that one can use that 1) interprets Tcl commands, and 2) interprets Tk commands. wish provides access to the combined functionality of Tcl/Tk. The man page on wish is sort of slim. I also recommend you read the man page for Tcl to get a better idea of the syntax.

Getting Started With Tcl/Tk

To try out Tcl/Tk, type wish at the Unix prompt. You will see the following prompt:

%
You may now start typing Tcl or Tk commands.

Learning about Tcl/Tk

Tcl is a full-featured programming language and Tk is a full-featured graphics library, so it is not possible to cover all the functionality of Tcl/Tk in one lecture. Instead, this lecture will:

  1. show how you can use Tcl/Tk to quickly build graphical interfaces to C and shell commands,

  2. show how you can create a Tcl/Tk applet that works with Netscape, and

  3. give you a taste of some of the more advanced types of graphical applications you can create with Tcl/Tk.

Building Graphical Interfaces with Tcl/Tk

An Interface to Ping

The first example we will cover is that of building a graphical interface for the ping command. Here are two links to Brent Welch's introductory tutorial to Tcl/Tk that covers the ping program, text and listing These tutorials were published in Unixworld. Unixworld. In this section we will talk about some of the commands used by the ping program in more detail. The program listing is also part of the links page.

pack: The pack command specifies how you want Tk to lay out the objects in a window or frame. Although pack has a great many parameters, the only ones you typically need to concern yourself with are the -side and -fill options:

winfo: The winfo command can be used to find out information about Tk windows. In line 21 of the modified ping2 program, the winfo command is used to determine the name of the application so that pinghelper's send command can access the application. In general, winfo name window returns a window's name (its name within the parent as opposed to its full path name) and winfo name . returns the name of the application.

yview: The yview command when sent to a text widget adjusts the view in the text widget's window so that the desired lines of text will be shown. The -pickplace end option used in line 27 of the ping2 program tells the .log text widget to position the window at the end of the widget's text buffer.

An Interface to a Timer

In this section we will create a timer that counts in increments of 50 milliseconds. To see what the completed interface looks like, type:

timer
Try resizing the window. Note how the start and stop buttons expand themselves to fill the increased space. To quit the program, type either Ctrl-c or Ctrl-q.

This interface has three primary components:

  1. The commands that construct the interface's appearance (i.e., the buttons and the text widget showing the computed time),

  2. The commands that compute the time, and

  3. The commands that handle the Ctrl-c and Ctrl-q keys.

Building the interface. The code that builds the interface is shown below:

label .counter -text 0.00 -relief raised -width 10
button .start -text Start -command {
    if $stopped {
	set stopped 0
	tick
    }
}
button .stop -text Stop -command {set stopped 1}
pack .counter -side bottom -fill both 
pack .start -side left -fill both -expand yes
pack .stop -side right -fill both -expand yes

The label command creates a label widget called .counter that displays the computed time. The two button commands create buttons labeled Start and Stop respectively and assign each of the buttons a command that should be executed when the buttons are pressed.

The first pack command tells Tk to place the .counter widget at the bottom of the window and to stretch the widget to accommodate any extra space that is assigned to it. The next two pack commands tell Tk to place the .start and .stop widgets on the left and right sides of the window. By default, these widgets are placed above the .counter widget, since the .counter widget expressed a preference to be placed at the bottom of the window. Both buttons express a preference to be stretched to accommodate any additional space.

The -expand option is subtle. Some widgets will stretch themselves properly if only the -fill option is provided. Others will stretch themselves properly only if both the -fill and -expand options are provided. Label widgets expand properly when only the fill parameter is specified but button widgets expand properly only when both the fill and expand parameters are specified. Basically one has to experiment in order to determine which parameters should be specified.

Computing the Time . The following code computes the time:

set seconds 0
set hundredths 0
set stopped 1

proc tick {} {
    global seconds hundredths stopped
    if $stopped return
    after 50 tick
    set hundredths [expr $hundredths+5]
    if {$hundredths >= 100} {
	set hundredths 0
	set seconds [expr $seconds+1]
    }
    .counter config -text [format "%d.%02d" $seconds $hundredths]
}

The global command specifies that the seconds, hundredths, and stopped variables declared above the procedure tick should be used, rather than declaring local copies of these variables.

The after command delays execution of a procedure for a specified period of time or schedules a procedure to be executed at a later time. If only an integer argument is provided, the command causes the procedure to sleep for the specified number of milliseconds. If both an integer argument and a script are provided, then the script is scheduled to be executed the specified number of milliseconds in the future and the command returns immediately. In this instance, the after command schedules the tick procedure to be executed again in 50 milliseconds and then proceeds with the execution of the current incarnation of the tick procedure.

The line of code that reads:

set hundredths [expr $hundredths+5]
adds 5 to the hundredths variable. In Tcl, one specifies arithmetic by preceding the arithmetic expression with the expr command. Similarly, the next three lines use arithmetic to ensure that the hundredths and seconds variables are computed correctly.

The final line:

.counter config -text [format "%d.%02d" $seconds $hundredths]
updates the text of the label widget so that it reflects the newly computed time. The format command is similar to C's sprintf command and produces a formatted string of text.

Binding Control Keys to the Destroy Command . The commands that bind the control keys to commands that destroy the timer application are shown below:

bind . <Control-c> {destroy .}
bind . <Control-q> {destroy .}
focus .

The bind command associates Tcl scripts and commands with X events. In particular, whenever the specified X event occurs in the specified window or widget (in this case the specified object is the top-level window), bind arranges for the associated command or script to be executed. Hence, the first bind causes the destroy command to be executed whenever the Control-c event occurs in the top-level window (the destroy command is given the top-level window and hence destroys the top-level window). The second bind performs the same function for the Control-q event.

The focus command ensures that all events that occur in the top-level window are directed to the top-level window. Sometimes one wants events that occur in one window to be sent to another window (e.g., sometimes when a mouse cursor is outside a text entry widget, we still want all key events to be sent to the text entry widget until a carriage return is received), and the focus command allows such an action to be specified.

The entire source code for timer can be found here.

Creating Tcl/Tk Applets that Work with Netscape

A plug-in for Tcl/Tk applets that can be downloaded from Scriptics. Some plugin info.

A Tcl/Tk applet requires no additional work beyond writing the Tcl/Tk script. For example, the timer script described in the previous section could be embedded below. The only difference between this applet (which can be found in timer_applet.tcl) and the script described above is that the lines invoking the /bin/sh and wish shells have been deleted from the applet script.

To embed a Tcl/Tk applet in an html document, use the embed tag and ensure that the applet file has a .tcl suffix or the browser probably will not recognize it. For example, the timer applet could be embedded in this document using the commands:

<center>
<embed src=timer_applet.tcl width=120 height=50>
</center>
I have NOT embedded it because until you get the plug-in working it causes the browser to generate errors every time that part of thepage comes into view. You would not want that, trust me.

More Advanced Tcl/Tk Applications

Tcl/Tk can be used to create far more powerful applications than the simple ones described in these notes. If you want to look at some more advanced applications and their code, check out the line-drawing and text-moving applications in /lymon/homes/cs300/Tcl/ or the Scriptics Resources.

Finding out more about Tcl/Tk

Shyam Pather has written an excellent introductory tutorial to Tcl. Rather than try to regurgitate it in the lecture notes, I will just point you to the appropriate link, Tcl/Tk tutorial.

There are also two excellent Tcl/Tk texts that I would recommend:

  1. Tcl and the Tk Toolkit by John Ousterhout, and

  2. Practical Programming in Tcl and Tk, by Brent Welch.
One final way of learning about Tcl/Tk is to look at the code that other people have written, such as the demo code pointed to in the previous section. You can often adapt someone else's code to your own use. Don't reinvent the wheel.