Tcl/Tk

These are the notes I created as I learned how to program with Tcl/Tk 8.3. This page will read like a tutorial, but you should be able to find any specific help you need in the Table of Contents.


Introduction

Getting Started

Variables and Expressions

Program Flow - Selection

Program Flow - Iteration

Text String Handling

Procedures

Arrays

Math Functions

Introduction

 TCL is essentially a language for the internet. It is a scripting language that allows you to do everything from CGI scripting to creating applets (or tclets as they are known) to writing full fledged GUI applications. More and more internet related jobs are requesting experience with TCL, it is still nowhere near as necessary as experience with Perl however. I did a search on an Australia/New Zealand job site and got about 30 responses for TCL, as opposed to about 200 for Perl and 800 for JAVA. It is always a good thing to know just the basics in a language though (so at least you can sound intelligent). 

This page doesn't contain all there is to know about TCL, but it gives you a reasonable understanding of the language. You should be able to learn all this material in about a day. This page actually only contains information of TCL, not Tk which is the toolkit extension that comes with TCL. The Tk is very useful for creating applications and tclets, and information on it should be available elsewhere on the Internet.

The best books on the subject are:

Tcl and the Tk Toolkit

Author John Ousterhout

Addison Wesley, 1994

Web Development with Tcl/Tk 8.1

Author :Steve Holzner

Wiley Computer Publishing, 1999

Getting Started

I am using Windows (of course) but this advice should apply if you are using a Mac or UNIX. 

All the downloads you will need can be made from http://scriptics.com

You need to download and install the file. You will want to use the wish program. This starts up the TCL interpreter.

You can enter code straight into the interpreter, but you will usually want to create scripts and feed these into the interpreter. On windows you can create them on Notepad, and save the files as .tcl. Once you have installed TCL, files saved with the .tcl extension will have their own icons.

You then go to wish, and after the %, enter source pathname. So if you saved the script on your a: drive with the name first.tcl, the command would be :

% source a:first.tcl

The download comes with its own help files, so you can use them for more advanced help.

You will also need to download a plug-in for your browser if you want to run tclets (the TCL equivilent of applets). These are available from http://scriptics.com as well. They come for both Netscape and Internet Explorer, although I couldn't find one for IE 5.0.

Variables and Expressions

The basic command for creating a variable is this :

set variablename variablevalue

So this creates a variable called num1 and gives it the value 200:

set num1 200

You can also store a string in the variable:

set string1 Hello

or if there is more than one word :

set string2 "Hello World"

Actually, everything is stored as a string in Tcl. Even if you enter a number into a variable, such as 200, it will be stored as a string of text "200". This doesn't matter though, because Tcl treats variables according to their context, so the interpreter will automatically interpret the same variable as a string or a number depending on the situation in which it is used. This lack of explicit typing is fairly standard for a scripting language.

We can now make statements like this :

puts $string1

The $ tells the interpreter that you are referring to a variable (If you actually need to use $ in a string, simply use \$ instead).

You can also make statements like this :

puts "The string says $string1"

To change the value of a variable just use the set statement again

Using numbers is easy. They can be positive or negative, and they can be floating-point or integer. They can then be subjected to all the main mathematical operations.

set num 100

expr $num - 40

This will return 60.

You can also use the following : +, -, *, %, <, >, <=, >=, ==, !=, &&, ||

Most are self explanatory, of the ones that aren't, here are their meanings :

% : the mod operator - it returns the remainder in division.

== and != : the Boolean equal to and not equal to (same as in C)

&&, || : the logical and and or operators.

There are also many built in math functions, click here for a full list

The example of subtraction above isn't what you always need. You will usually want to change the value of num. This is how you would do that :

set num [expr $num - 40]

set takes two arguments - the variable name and the new value. Because of this we need to group together the expression that creates the new value in [ ].

Here is another example :

puts "The number is : [expr $num * 100]"

You can also use append to change the value in a variable :

set S1 "Hello"

append S1 " World"

puts $S1

Another very useful of changing the value of an integer is the incr command :

set num1 100

incr num1

Or we can state by how much we want to increment it :

incr num1 3

You can also use negative numbers.

Finally, to delete a variable use unset :

unset num1

Program Flow - Selection

This is the basic shape of the if command :

set num 100

if {$num > 50} then {

puts "Greater than 50"

}

Note that the expression is inside curly brackets, unlike most other languages where it is in round brackets. Also note that immediately after the that you must have the {, you can't put it on the line below.

You can also add an else clause :

if {$num > 200} then {

puts "Greater than 200"

} else {

puts "Less than 200"

}

For more than two options you need to add one or more elseif clauses :

if {$num > 200} then {

puts "Greater than 200"

} elseif {$num > 80} then {

puts "Greater than 80"

} else {

puts "Less than 200"

}

Once it finds a clause that meets a condition, it executes it's body, and leaves the if-then-else clause - so only one option is ever executed even if more than one condition is true.

Like most languages, TCL also has a switch command :

switch $num {

    20 {puts "Number is twenty"}

    50 {puts "Number if 50"}

    100 {puts "Number is 100"}

    default {puts "I don't know what the number is"}

}

Program Flow - Iteration

For loops are no doubt familiar to you from virtually every other language, so here is an implementation of one in TCL:

for {set index 1} {$index < 10} {incr index} {

    puts "Number is $index"

}

Once again, note the use of curly brackets, and also be sure to remember the $ in {$index < 10} : this is very easy to forget.

You can also create nested for loops :

for {set index 1} {$index < 10} {incr index} {

    for {set inner 1} {$inner < 5} {incr inner} {

    puts "Outer umber is $index, inner number is $inner"

}}

Just remember the end brackets for both loops.

There is also a while loop :

set index 1

while {$index < 10} {

puts "The value is $index"

incr index

}

This loop is very simple, and needs no expalining.

You can use continue to jump to the next iteration of the loop, and the break command to get out of a loop : this is familiar to anyone who has programmed in C. For instance you could have an expression in a loop like this :

if {$index == 100} then {break}

If you use break in a nested loop, you only break out of the immediate loop, not any outer loops.

Text String Handling

This is a very important topic in TCL, since, as you have seen, everything is handled as a string.

Firstly, there are some commands that can be added to puts, a useful one is this :

puts -nonewline "This will not be followed by a new line."

There is also a format command for controlling screen output. Suppose we have a variable like this :

set num 98.365423

We can control the precision with which this is printed out like this :

format "Two decimal points : %1.2f" $num

Note : this prints out all numbers to the left of the decimal point automatically, it is only numbers trailing the decimal point that are controlled.

You can also do this with strings :

set name "Dane"

format "First two letters %.2s" $name

To display a number in decimal, use this structure :

set int 40

format "Decimal : %d" $int

A lot of string operations are done with the string command. Here is an example :

string toupper "change this to capitals"

There are a whole lot of these commands - here are a few more (you can either enter strings or variables containing strings) :

string length stringname

string tolower stringname

string match pattern stringname : this returns 1 if it matches, otherwise 0.

string compare stringname1 stringname2 :Returns -1 if stringname1 is less, 1 if it is greater, or 0 if they are equal.

string index stringname number : returns the character at possition number in stringname. 0 is the first character.

string last stringname1 stringname2 : Searches stringname2 for characters that match the characters in stringname1. If found, it returns the index of the last match within stringname2, or it returns -1 if there is no match.

string trim stringname [characters] : trims any leading or trailing characters from stringname that are contained in the set that follows. This can be made to trim only leading or only trailing by changing it to trimleft or trimright.

You can also use append to join one string to the end of another :

set s1 "Hello "

set s2 "World"

append s1 $s2

You can also parse a string (break it up into its component parts). The basic structure of the command is this:

scan string format varName [varName...]

The string argument is the string to be operated on. The format option lets you chose the format - %. Each varName argument is the name of a variable to hold the values scanned by the string. The possible formats are - %d (decimal integer) %o (octal integer) %x (hexadecimal integer) %c (single character) %s (string) %e or %f or %g.

Here is an example :

scan "split this line up" "%s %s %s %s" word1 word2 word3 word4

This line returns the value of 4 which is the number of variables assigned. Now if we write :

puts "$word2"

the interpreter will return this

This ability to split a string up is particularly useful when dealing with user input.

We can also use the switch command to implement pattern matching. switch has options so that the pattern matched doesn't have to be exact. Here is a switch statement that tests a variable called input. If the value equals "display" it prints something out, and if it equals "exit" it exits the program. However, through the -glob command within switch we can create a situation whereby the input string can be in either capitals or lower case letters :

set input "DIspLay"

switch -glob $input {

[dD][iI][sS][pP][lL][aA][yY] {puts "This is the display"}

[eE][xX][iI][tT] {exit}

default {puts "I don't understand"}

}

Just as long as each letter of input corresponds to what is in the [] for that position, it will work. Note that there shouldn't be gaps between the sets of brackets.

We can also introduce wildcards :

switch -glob $input {

[dD]* {puts "This is the display"}

[eE][xX][iI][tT] {exit}

default {puts "I don't understand"}

}

exit still needs to be typed in full, but anything starting with d will count as display. Note that there is no gap between the brackets and the wildcard character.

There is also a far more complex pattern matching option called -regexp. This is just a small introduction to what it can do.

Here is an example that looks for any uppercase characters in a string :

switch -regexp $input {

[A-Z] {puts "It contains an upper case character"}

}

Things can get quite complex. For instance, you might want a variable to contain only letters and numbers :

switch -regexp $input {

[^A-Za-z0-9] {puts "It is not a valid string"}

}

The ^ means not of course, so if it contains one of these characters the string isn't valid.

There is another function called regsub that allows you to replace a portion of a string with something else - this example changes the string Hello World to Hello America :

regsub "World" "Hello World" "America" newstring

newstring is where the new string will be stored. Naturally you can use variables containing strings instead of actual strings. "World" was the portion of the string we wanted to get rid of, "Hello World" was the string we were working on, and "America" is what we want put into the string in place of "World".

If there is likely to be more than one sub string that you want replaced, simply insert an -all :

regsub -all ....

You can also use some of the other tricks we learned :

regsub {[Ww]orld} "Hello World" "America" newstring

Now it doesn't matter whether world begins with a capital or not.

Procedures

As well as all the built in procedures we have already come across, TCL lets you create your own procedures.

The general structure of a procedure is this :

proc procedurename {argument list} {procedure body}

For instance ::

proc firstprocedure {} {

puts "This is my first procedure"

}

Now, by entering firstprocedure the procedure is executed.

Note that even though this procedure takes no arguments, it still needs the empty brackets {}.

Here is another procedure that accepts an argument and prints it out :

proc printout {texttoprint} {

puts $texttoprint

}

We can now call it like this :

printout "Print out this"

It is just as easy to pass multiple arguments :

proc multiprint {text1 text2} {

puts $text1

puts $text2

}

We now call it like this :

multiprint "print this" "and this"

Procedures can also return values :

proc squarenum {num} {

return [expr $num * $num]

}

This returns the square of the number sent to it.

TCL allows for local variables within procedures. Just use set statements inside the procedure :

proc createlocal {} {

set localvar 50

puts $localvar

}

You cannot access localvar from outside createlocal.

It is still possible to create a global variable inside a procedure though :

global glvar 100

This goes against the idea of creating procedures though, so only do it if you have to.

It is also possible to give arguments default values incase the user doesn't supply one :

proc squarenum {{num 10}} {

return [expr $num * $num]

}

Note the extra set of brackets around the argument list.

Not only can you redefine your own procedures, you can redefine the built in ones :

proc exit {} {

puts "No, you can't exit"

}

Arrays

Arrays are a bit different in TCL than most other languages in the sense that they are associative : they work with key values not numeric indices. For instance, this is an array named highesttemp with  keys corresponding to month names (the array could store the highest temperature of the month) :

set highesttemp(January) 67

set highesttemp(June) 98

We can then access those values like this :

puts $highesttemp(June)

You can use whatever value you want for a key value, just as long as it's unique. Also note, that unlike most languages, the array can contain data of different types. This isn't entirely true, since everything in TCL is a string, but the effect is that anything can be put in any array.

The great advantage of associative arrays like this is that there is no limit on their size. They are expandable, an effect that requires linked lists or trees in most other languages.

There is also an easy way to display all the elements in the array :

parray highesttemp

The array command is another way to deal with arrays. It is especially useful in allowing you to loop through arrays, which is difficult without index values. Its general form is this:

array option arrayname [arg1 arg2 ...]

Their are several options:

This will loop through the array and print out the values of the key items :

foreach keyitem [array names highesttemp] {

puts $highesttemp($keyitem)

}

Looping through an array searching for a value isn't easy. Here is an example that sets up an array holding details on a person. It then searches for a particular number representing the date of birth :

set person(name) "Dane"
set person(born) 1980
set person(salary) 78000
set search [array startsearch person]
while {[array anymore person $search]} {
set arrayelement [array nextelement person $search]
if {$person($arrayelement) == 1980} then {
puts "Found the date of birth"
}
}

This is much more complex than an array search in C or Pascal.

The set search [array startsearch person] will return s-1-person. We then use the anymore option inside array to create a loop where the terminating clause occurs when all the elements have been searched. With each iteration the nextelement option moves us through the array, and we test the value of that element against the value we are searching for.

Math Functions

abs : returns absolute value

acos, asin, atan : returns arccosine etc.

cos, sin, tan : returns cosine etc.

exp : returns exponential to power of e.

fmod : returns floating point modulus.

int : converts value to integer.

log : returns log.

log10 : returns log 10.

rand : returns random floating-point number between 0 and 1.

round : rounds a number.

sqrt : returns the square root.