Programming Languages
What is Tcl/Tk?
Tcl/Tk is actually two products originally developed at the University of California Berkeley, now at Sun by John Ousterhout: the Tool Command Language and the Tool Kit. They are a versatile scripting language and a graphical interface development tool, respectively.
Itcl is an extension of Tcl/Tk that allows the developer to make scripts more object oriented using C++ style syntax. Tix is a set of widgets that extend the base set to Tk widgets.
Tcl has been widely used as a scripting language. In most cases, Tcl is used in combination with the Tk ("Tool Kit") library, a set of commands and procedures that make it relatively easy to program graphical user interfaces in Tcl.
Tips
Saving and loading TCL array variables
It's quite common to have a lot of information stored in TCL array variables, and I tend to try and keep as much as possible in arrays (as opposed to little individual variables). This makes it easier to pass the array name as an argument, etc. You can save the data in a flat text file, and parse it in line by line, but this is a waste of time. Instead, make use of the inherent power of TCL to source files.
To begin, lets say we have a bunch of data in an array variable "MyVar" which is visible in the current scope. We'll start by saving this data into a file called MyVar.dat. The trick to write this data into the file in a format the source can read and parse.
set f [ open MyVar.dat w ] |
Try this! Look at the file that's created, and verify that it looks the same as if you had typed the code in yourself (except that it will all be on one line, which TCL doesn't mind at all.
Parsing a file
Say you have a procedure that you use to parse strings, and you want to apply this procedure to all the data in a file. Here's a simple scrap that can do this (The parsing procedure is called "ParseLine" in this case).
set f [ open $filename ] |
This will parse one line at a time. If you want to parse the whole file in one go, try this.
set f [ open $filename ] |
You can do this a little simpler using read.
set f [ open $filename ] |
This reads the file until the end of the line. If you are reading a binary file, you'll want to set binary translation on input (and output if you have any) using the TCL command fconfigure
Configuring Tk widgets from TCL
The Tk widget commands (button, list, menu, etc.) create new instances of their respective widgets.
% button .but1 |
The state of these instances can be modified before, during, or after, their creation as described below.
The options database before the instance in created:
option add *but2.text "Push me!" |
The option database can also be used to set defaults for all instances of a particular class of widget:
option add *Button.text "Depress me!" |
Options to the widget command when the instance is created:
button .but5 -text "Hit me!" |
Options to the widget instance's `configure' command once it has been created:
.but5 configure -text "Hit me gently!" |
The options database is a powerful way to set default values for an application or configure it for user or task preferences.
'unlisting' a list
This is a really handy operation. Say you've got a list called $list and you'd like to assign the first element of the list to $a, the second element to $b and the third element to $c. The rest, you'd like to just ignore. I used to have a custom TCL proc that did this, but then Will Webb showed me this little gem, which is clean, correct, and faster than anything:
foreach {a b c} $list { break } |
If you look above to the foreach usage, you can see that this is going to step through $list and assign the first three elements to $a, $b, $c. Then, it's going to assign the next three elements to $a, $b, $c. But wait! There's a break in the foreach body! Which means it's just going to go through ONE TIME and then quit. After the loop exits, you'll have the d esired result. Try it! This is really handy for parsing some kinds of strings. Say for example that you've got the current time in a string like "12:31:13", which is 12 hours, 31 minutes and 13 seconds. You'd like to have hours, minutes and seconds in separate variables so you can fiddle with them. You know you would.
foreach {hours minutes seconds} [split $time :] {break} |
Uniqing a list
If you have a list that has duplicate elements, and you want to remove those, here is a simple trick. Note: list order will NOT be retained!
foreach l $list { |
What basically happens here is we make an array entry for each element in the list. Since there can only be one entry for any given key in an array, we end up with an array that has one entry for each unique item in the list. "array names a" returns a list of all the keys, which is our unique list.
Counting word frequency in a list
Say you want something like the above, but you want to know how many times each item appeared in the original list. We can make a few modifications to the above to handle that.
foreach l $list { |
This does the same thing as the uniqeing scrap, but every time it sees an item it's previously seen in the list, it increments a counter. At the end, a "frequency list" is made. Here's an example:
input { This is a list with a multiple word } |
Depending on how you wish to use this data, the values in the array "a" may be more useful than the output list.
Regular Expressions
Regular expressions are one of the most important components of TCL, especially for web based programs, like CGI scripts and Database processing scripts. TCL regular expression commands are broken up into two categories: find (regexp) and find-and-replace (regsub).
regexp
The basic syntax for regexp is
regexp ?switches? exp string ?matchVar? ?subMatchVar sub-MatchVar ...? |
exp is the regular expression, string is the string you are looking for a match in. matchVar will contain the first substring that matches the regular expression. The submatchVars will contain the match for each parenthetical match, respectively. Look at the TCL Regexp Manpage for more complete information.
regsub
regsub is similar to regexp. Instead of just finding the sub-string, it allows you to replace it with something else. This is useful for commenting things out, or getting rid of HTML tags, or changing text in a document, etc. The syntax for regsub is
regsub ?switches? exp string subSpec varName |
exp is the regular expression, string is the string you are looking for a match in. subSpec is what you want to replace exp with, and varName is the name of the variable you want the result to go into.
Something to note is that TCL regexp and regsub are greedy, meaning it will always find the longest substring that matches the regular expression. This can be a bad thing! For example, say you are looking for html elements (things which begin with "<" and end with ">". try
regexp {<.*>} $string |
but this will match from the beginning of the first HTML tag to the end of the last one! If you are like me, and start every document with , and end with then it will match the whole document! If you were trying to replace the HTML tags with something else, you are out of luck!
Used for Expression Description
Removing HTML tags regsub -all {<([^<])*>} $s {} s Find and replace with nulls, all strings which begin with a <, end with a > and do NOT have a < in the middle.
Replacing ' with '' regsub -all ' $s '' s Replace all occurrences of ' with ''. This is referred to as "quoting out" the ', which interferes with proper insertion into a SQL database.
Finding IP addresses regexp [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ Finds strings in the form of number.number.number.number
Parse href tags regsub -all {} $s \1 s Find all strings that begin with "", and don't have a "<" in the middle. Replace these with the stuff between ""
Parse out non-numeric chars regsub -all {([^0-9])+} $string {} string Replace all characters which are not in [0-9] with nothing (effectively removing everything but numbers).
Sockets
Sockets are used in all kinds of network programming, to allow communication between two computers, or between two programs on the same computer. The socket model is used extensively in TCP/IP communication. Typically, some program, a server, listens to a particular port number. When some program, a client, initiates a connection, the server "answers" and starts reading information from the client, and writing more information when necessary, much like reading from standard input and writing to standard output, or reading/writing from a file. In fact, socket communications in TCL are NO MORE DIFFICULT than reading from a file or reading from a command pipeline, and operation almost exactly the same.
Opening a socket as a server
set trigger 0 |
The first command opens a socket to port 5000 (usually only ports below 1000 are reserved for system, i.e. root, use). When a client connects, the argument to -server is invoked, in this case the procedure "openchan". The parameters of the file handle for the connection, the address and the port of the client are automatically passed in. The second command is a "vwait" which will suspend execution of the program until the variable $var changes.
I'll show the openchan procedure later in this document. Suffice it to say that this procedure should be made to set up the channel parameters or maybe initialize some variables. Also, it should set up some method of receiving/parsing data from the client, and returning the results.
Opening a socket as a client
For simple testing, I often use the very norm al UNIX "telnet" command. If I have a server waiting on port 5000, for example, on the same machine that I want to run the client on, I just say
telnet localhost 5000 |
If I was using the server shown above, this is what the results would look like.
Client side:
{pickles:~} telnet localhost 5000 |
Server side:
{pickles} ./server.tcl |
This is great for simple testing, but for production use or extended debugging you'll probably want to make your own client. Of course, you don't HAVE to use TCL, as the telnet example shows. But it is pretty convenient, so why not?
Here is a very simple client, that simply opens a connection to the server and merely listens.
set s [ socket localhost 5000 ] |
Web Programming
This is one of the main focuses of my programming these days. Until a few months ago I was using Apache web server, with TCL as my CGI language. Recently I've started using AOLServer, which includes native support for TCL, which makes it much more efficient. It also allows for TCL code to be embedded into HTML pages. I won't get into that much here. I'll try to include code for both here.
Cookies
Cookies are a way for you, the web programmer, to store little bits of information on the users computer. When the user views your CGI, you have the opportunity to send these cookies, and later you can retrieve them. Since the web is generally a "stateless" programming paradigm, whenever a user clicks something, or enters a page, you have little or no information on where he was, or what he was doing, before. If he comes back tomorrow, you have no way of identifying him, etc. Cookies change all that by letting you set "persistant" data in his web browser that you can use to "remember" things about him. For a detailed explanations of cookies and their attributes, see the Netscape cookies page.
I have attached some procedures you can use to form/set/get cookies using either typical CGIs scripts, or AOLServer. Both files have the same procedures in them, although the actual guts of the procedures are different in the two cases (although semantically very similar).
Keep TCL procedures separate from the main TCL/Tk GUI.
This allows the running program to re-source a modified procedure and thus incorporate code changes without having to be stopped and restarted.
Include an interpreter GUI as a menu pull down.
This allows you to examine and modify variables in a running program, even create and test new procedures. Such a GUI is available in /usr/local/src/tcl/lib/interp_gui.tcl
Removing leading 0's from numbers can be done with the scan command:
set now [fmtclock [getclock] "%y %j %T %m %d %H %M %S"] |
A second way is with the force_decimal procedure which returns the converted value, whereas scan returns the number of elements converted. scan must therefore always be on a separate line. Compare set aa [expr abs([force_decimal $a])] with
scan $a %f aa |
Also that force_decimal leaves the format type unchanged, whereas scan will convert an integer to float if %f is used.
A third way with trimleft:
set var [ string trimleft $var "0"] |
Implement top levels from within procs because this allows you to invoke them from different places, for example, from a button-down menu and from a toolbar button. The top level can be invoked then wherever needed. Also, test whether the top level already exists before creating it to avoid an error. And destroy it automatically if deleted with
wm protocol .my_top_level WM_DELETE_WINDOW { |
Have you included the line: #!/usr/bin/wish -f as the very first line of your script ?
If you haven't, the shell does not know whom to pass on your commands.
Have you made your script executable ? This is done via:
chmod u+x your_filename |
Check your spelling and punctuation.
Widget options are indicated by a dash ( - ) and if the line you are typing gets too long you must end it with a back slash ( \ ) so that the shell knows it has to continue reading from the next line. Any widgets you define must be placed below the main widget, the period ( . ). Thus writing pack .mywidget is OK but pack mywidget will return an error.
The Basic Syntax of Tcl
The general syntax of Tcl is described more fully in `man Tcl'. I'll summaries it briefly here.
Commands separated by newlines or semi-colons are broken into words separated by white space.
Words can be stuck together to form larger words with doubles quotes (`"'), curly brackets (`{' and `}'), and square brackets (`[' and ']').
Variables in words are substituted for their values, except between curly brackets. Curly brackets are also used to protect variable names from adjacent text. Some examples:
set foo hello # hello |
Variables between square brackets are substituted for their values, and the resulting string executed. The whole command is substituted by the result. Some examples:
set foo hello # hello |
The `eval' command treats a string as a command, much like square brackets, except the string is first split into words.
set foo hello # hello |
Generating symbols
New symbols are often required at runtime in Tcl/Tk applications. A new variable may be required whose name is different from all existing variables. Or a name for new instance of a Tk widget (window, button, whatever) different form all exist names may be required.
A function similar to the one shown below can be useful for this task. You supply a suitable prefix, and it tacks on a unique number for this execution of the program.
set next_symbol_num 1 |
So, for example, creating arbitrary numbers of new widgets is now easy.
% gen_named_sym foo_ |
Quick and easy dialog boxes
Most applications have need for a simple dialogue window to ensure the user is informed of something, or to ask the user to make a simple choice. There is a Tcl/Tk library function which makes this very easy. The `tk_dialog' function is described fully in `man dialog'.An example is given below.
tk_dialog .panic "Panic?" "Should we panic yet?" \ |
It is easy to make use of the result of `tk_dialog'.
proc panic_if_desired {} { |
|
|||