[Home] [Credit Search] [Category Browser] [Staff Roll Call] | The LINUX.COM Article Archive |
Originally Published: Thursday, 21 December 2000 | Author: Mark Stone |
Published to: featured_articles/Featured Articles | Page: 1/1 - [Std View] |
Tk: The Forgotten Language (part 2)
In the first installment of our series on Tcl and its graphical extension, Tk, we introduced you to the concepts and some of the basic syntax required to code a small application. It's likely that you were surprised by the simplicity and flexibility that the Tcl/Tk approach offered, as you saw just how easy it was to rapidly develop a prototype. Today, we'll finish where we left off - coding the backend and completing our network configuration tool.
|
Tcl is a full featured scripting language with all of the control structures and data structures one would expect. We have relatively little control flow to worry about in this script; mainly the script is a front end to bash shell commands. Let's look at the whole startNet procedure, and then walk through it:
proc startNet {netHost} {
}
Our goal here is simple: we need correct settings for IP number, netmask, and gateway for each of three connection locations. Once we have those values, we'll insert them into the ifconfig and route commands to bring up an ethernet connection.
A word about Tcl variable notation and variable assignments. In Tcl you refer to a variable directly simply by using the variable name. You refer to the value assigned to that variable by preceding the variable name with a "$". The command to assign a value to a variable is the set command, which has the format "set variable value". You can send the value of a variable to standard output using the puts command. In other words:
set foo bar
puts $foo
would print the string "bar" to standard output.
Tcl has a very sophisticated set of commands for manipulating lists, so lists are the fundamental data structure in Tcl. Once you become adept at handling lists, you'll find you seldom need to use arrays.
Finally, note that command execution can be nested within a Tcl command line, using brackets for delineation; commands nested within the innermost brackets are executed first.
Now let's look at what we've done. Our first command is the switch command:
switch $netHost {
Switch is a convenience that's equivalent to a series of "if... then... elseif" statements. The switch command says to check a value, $netHost in our case, and to execute a command sequence flagged by a string match to that value. In other words, if the value of netHost is "osdn", execute the command sequence following the string "osdn", etc.
The command sequence, in our case, is just an instance of the set command. We're going to assign a value to the netList variable. As the variable name implies, the value assigned to netList is a list, in our case a three element list consisting of an IP number, an netmask value, and a gateway address. We'll get this list by another procedure call.
In other words, when we execute the command:
set netList [osdnNet]
We are calling a procedure named osdnNet, passing no parameters to it, and asking it to return a list that will be assigned to netList.
The three procedures osdnNet, dslNet, and vaNet couldn't be simpler. Here they are:
proc osdnNet {} {
proc dslNet {} {
proc vaNet {} {
}
The lappend command stands for list append. It takes a list and elements to the end of the list. If the list does not already exist, then it creates a list with the indicated elements. So the value of nList will look something like this:
{192.168.254.200 255.255.255.0 192.168.254.}
Technically, this use of procedures violates good coding practice. You should only use a procedure for code that will be invoked more than once in a program, or that could be called from more than one place in the program. The proper way to set this up would be to store this information in a file rather than in a procedure, and then read from the file.
Proper, yes. But this approach was quick and easy, and is still simple to read, update and modify. It would be trivial to add a fourth network location to this script.
Once our list has been constructed, the actual remaining program exeuction is almost anticlimactic:
set ipNum [lindex $netList 0]
set netM [lindex $netList 1]
set gateW [lindex $netList 2]
exec ifconfig eth0 $ipNum up netmask $netM
exec route add default gw $gateW eth0
Here we take the values from our list and pass them on to ifconfig, which brings up the ethernet interface with the correct IP number and netmask, and pass them to route, which adds eth0 as a route with the proper gateway assignment. Note that the commands are set up this way for purely cosmetic reasons. It would be just as effective, but less readable, to simply use the following two commands:
exec ifconfig eth0 [lindex $netList 0] up netmask [lindex $netList 1]
exec route add default [lindex $netList 2] eth0
The lindex command is used to extract one element from a list. List numbering begins with 0, hence "lindex $netList 0" returns the first element of netList.
The exec (stands for execute) command is what makes Tcl a real system administrator's friend. I've actually never learned how to do bash shell scripting, because it's just as easy for me to set up all the control structures in Tcl and then call any needed shell commands with exec.
When making a dial-up connection, we call the startPpp procedure. This is an even simpler procedure. I need a few different variations on my wvdial.conf file for these reasons: sometimes I'm calling an 800 number to connect; sometimes I'm calling a number in my local 650 area code. I may also be calling from a hotel, in which case I need to precede the phone number with a "9." You'd think I'd only need the 800 number for hotel connections, but in my experience when calling from hotels with older phone systems, I've had better luck making the connection to the 650 number; I have no idea why.
I have four different wvdial.conf files for these four scenarios. All that startPpp has to do is copy the correct file into the correct place, and then call wvdial. Here's the procedure:
proc startPpp {dialFrom} {
}
What's attractive about Tk is the simplicity with which this kind of graphical programming can be done. This entire script involves only 64 lines of code:
#!/usr/bin/wish
proc osdnNet {} {
proc dslNet {} {
proc vaNet {} {
proc startNet {netHost} {
proc startPpp {dialFrom} {
wm title . "Network Configuration"
frame .netchoices -relief groove -borderwidth 3
label .netchoices.title -text "Network Hosts:"
radiobutton .netchoices.osdn -text "OSDN (Acton)" -variable netHost -value "osdn"
radiobutton .netchoices.dsl -text "DSL (from home)" -variable netHost -value "dsl"
radiobutton .netchoices.va -text "VA (Fremont)" -variable netHost -value "va"
button .netchoices.sub -text "Start eth0" -command {
pack .netchoices .pppchoices -side top -fill x
pack .netchoices.title .netchoices.osdn .netchoices.dsl .netchoices.va .netchoices.sub -side top -padx 10 -pady 2
pack .pppchoices.title .pppchoices.h800 .pppchoices.d800 .pppchoices.h650 .pppchoices.d650 .pppchoices.sub -side top -padx 10 -pady 2
Tcl/Tk has never achieved the popularity, or at least the visibility, of other scripting languages like Perl. I'm not sure why that is. It's an easy language to learn, has all the power and sophistication one could want, and it's also cross-platform: many Tcl/Tk scripts will run without modification on all flavors of Unix, all 32-bit flavors of Windows, and MacOS.
Tcl/Tk is getting to be one of the older scripting languages now, but it's still a good one. Anyone interested in graphical applications should think about dusting off this jewel and seeing if they can make it shine.