Chapter 6. Scripting with SpecTcl

SpecTcl incorporates an extended version of the Tcl/Tk scripting language created originally by John Ousterhout. Maintenance and development of that language migrated through Sun, Scriptics and Ajuba before settling into a community based Tcl-Core team mediated development model in 2000. The Tk extension to Tcl, also incorporated into Tcl provides the ability to easily create graphical user interfaces.

https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html is one good Tcl tutorial. http://www.tkdocs.com/tutorial/ provides a good Tk tutorial.

Very likely you're still reading this manual because you're a bit too impatient to wade through all those tutorials. The remainder of this chapter will:

Note that several research groups have already produced quite a lot of scripting on top of SpecTcl, both to provide user interfaces and to extend SpecTcl's features. Before starting fresh, you might save yourself a lot of work by checking into what's out there.

NoteHere Be Dragons
 

Note that the NSCL supports SpecTcl and the Tcl scripts released along with it. We cannot provide support for your scripts and add ons other than to help you understand enough about SpecTcl to move forward with your development work.

6.1. A quick introduction to Tcl/Tk

This section will introduce you to the basic syntax and form of Tcl/Tk scripts. Tcl is an extremely regular language. Its regularity makes for a very shallow learning curve. This shallow learning curve is one of the reasons it was adopted as the command language for SpecTcl.

NoteSpecTcl is really NSCLSpecTcl
 

Throughout I've been calling this program SpecTcl. It's actual name is NSCLSpecTcl to avoid confusion within the Tcl communit with the SpecTcl interface builder ( http://www.tcl.tk/software/spectcl/index.tml ). I'll continue to use the word SpecTcl as within the nuclear physics community SpecTcl is NSCLSpecTcl.

Once the language has been introduced, we'll take a quick tour through Tk describing its philosopohy and a few of the more common widgets. Again there will be liberal reference pointers.

6.1.1. Tcl

The Tcl scripting language has very simple syntax. There are, in fact only 12 syntax rules for Tcl. These are commonly referred to as the Dodekalogue. They are described here: http://www.tcl.tk/man/tcl/TclCmd/Tcl.htm

In fact you can do significant work by knowing only some of these (rephrased and reordered somewhat) words:

  1. A Tcl command consists of a list of words. Unless quoting or a contiunation marker is used, a semicolon or newline ends a Tcl command.

    The Tcl line continuation marker is \ as the last character of the line.

  2. Tcl commands have two phases of evaluation. First the Tcl interpreter performs indicated substitutions on the command. The first word of the resulting command determines the operation. The remaining words are passed to the command.

    Note that commands return a result. This is key to the concept of substitution.

  3. The $ indicates that what follows is the name of a variable and that the $ and variable name will be substituted with the value of the variable.

  4. In a command text within square brackets [ ] is considered a command. That command is evaluated and the result of the command substituted for it.

  5. If the first character of a word is a { the word extends until the next }. Substituion is turned off within brackes.

    If the first character of a word is a " the word extends until the next ". Unlike quoting with braces, substitution is performed within "".

A couple of points not mentioned in the syntax that are core Tcl principles. First everything is a command. Many programming languages have looping and conditional constructs built in. Tcl provides commands that implement these. Second everything has a string representation (sometimes this is simplified to the acronym EIAS - Everything Is A String). This defines how type conversions operate in Tcl as well as providing some regularity in the management of dicts.

Variables. The syntax hinted at the fact that Tcl has variables. It also has arrays. Tcl arrays are, however indexed by strings rather than integers. Internally hashing is used so that indexing is a constant time operation.

Tcl has two additional data types that are supported via commands; These are lists and dicts. A list is an ordered set of words (A command is a well formatted Tcl list). A dict is a set of key value pairs.

Tcl has support for object oriented programming via several extensiona and, as of Tcl8.6 a native package called TclOO.

The set command creates Tcl variables. As noted, variables are accessed using $ substitution. The set command also returns the value of its variable, and does not require the variable be given a new value:


set a {this is a string}
set c $a
set d "[set c] with more stuff"
set first("Fox") Ron
                    

In the example above, a has the value this is a string as does c. d, however has the value this is a string with more stuff. Finally first is an array and the element indexed by the string Fox is Ron.

Note that in Tcl variables are created the first time they are set. There are no commands for declaring variables (well that's a bit of a lie in some circumstances but ignore that for now).

Data manipulation commands include:

The string and dict commands are examples of what Tclers call command ensembles. These are commands that have a second word that is a subcommand that determines what the command does.

Flow control. As already described, Tcl implements flow control via commands rather than as language structures. Many flow control structures require a condition. In order to ensure that the variables in conditions are evaluated by the command rather than the interpreter's first stage of evaluation, it's important to quote these conditions in braces. After introducing the flow control commands I'll give an example that shows why this is.

Note also that the bodies of flow control commands are scripts but, again {} quotation is needed for all scripts that require more than one word since each body is a single word of the command. This may sound puzzling but actually looks quite natural as we'll see in the examples.

Tcl provides the following flow control commands:

  • if provides the usual conditional. The if command can have several branches via the elseif and else keywords.

  • switch Executes a single script from amongst several depending on way a value matches simple expressions.

  • for provides for a loop that iterates in a manner very similar to the C/C++ for construct. In many cases you may be used to using a for loop the foreach loop may be a more natural choice.

  • while provides for loops that are top tested. A common Tcl programming exercise is to implement a bottom tested loop in pure Tcl (if you want to give that a try, see the uplevel command).

  • http://www.tcl.tk/man/tcl8.5/TclCmd/foreach.htmforeach iterates over elements in a list.

We have enough mechanics now that exmples can be relevant to SpecTcl (though some may be toy examples).


set raw0Description [lindex [spectrum -list raw.00] 0]
set type [lindex $raw0Description 2]
if {$type == "1"}  {
   puts "1-d spectrum"
} elseif {$type == "2"} {
   puts "2-d spectrum"
} else {
   puts "Some other spectrum type: $type"
}
                    

The first two lines of this example extract the spectrum type code from the description of the spectrum named raw.00. The puts command outputs text, by default to standard output, so the if bodies output mesages indicating if the spectrum is a 1D, 2D or otherwise. Note he use of variable substitution in a quoted string in the else branch of the if.

The same outcome can be expressed as a switch. Replace the if with:


switch -exact $type {
    1 {
        puts "1-d spectrum"
    }
    2 {
        puts "2-d spectrum"
    }
    default {
        puts "Some other spectrum type: $type"
    }
}
                    

Here's an example of a for loop note that the expr command evaluates arbitrary arithmetical expressions (among other things): and incr increments a variable.


for {set i 0} {$i < 10} {incr i} {
    puts "$i Squared is [expr {$i*$i}]}
}
                    

Similarly the while loop:


set sq 0
set i  0
while {$sq < 81} {
    set sq [expr {$i*$i}]
    puts "$i squared is $sq"
    incr i
}
                    

I said that the condition clauses for all flow control command should be braced. The example above is a great example why this is true. Suppose I'd written the while command as:


set sq 0
while $sq < 81 {
    ...
}
                    

Tcl would have done its round of substitution before turning control over to the while command. This would mean that the while command would get rewritten to read:


while 0 < 81 {
    ...
}
                    

resulting in an infinite loop.


foreach spectrum [spectrum -list] {
    set name [lindex $spectrum 1]
    set type [lindex $spectrum 2]
    puts "$name  has a type of $type"
}
                    

foreach is equivalent to the following] awkward for loop:


set specs [spectrum -list]
for {set i 0} {$i < [llength $specs]} {incr i} {
    set spectrum [lindex $specs $i]
    set name [lindex $spectrum 1]
    set type [lindex $spectrum 2]
    puts "$name  has a type of $type"
}
                    

Whenever you find yourself using lindex inside a for loop ask yourself if a foreach loop might be simpler.

The last Tcl command I want to describe is the proc command. proc, short for procedure creates new Tcl commands. Once created these commands look and operate just like any other built in Tcl command.

Here's a simple example:


proc spectrumTypes spectra {
    foreach spectrum $spectra {
        set name [lindex $spectrum 1]
        set type [lindex $spectrum 2]
        puts "$name  has a type of $type"
    }
}
spectrumTypes [spectrum -list raw*]

                    

The example first creates a new command spectrumTypes with one parameter, a list of spectrum descriptions. The proc outputs the names and types of each spectrum in the list.

The last line of the example uses this proc to output the name and types of all spectra whose names start with raw.

For a full list of Tcl (8.6) commands with links to their reference documentation see: http://www.tcl.tk/man/tcl8.6/TclCmd/contents.htm

6.1.2. Tk

The Tk extension to Tcl provides the ability for Tcl scripts to easily create graphical user interfaces. GUI software in C/C++ is quite a chore in most user interface frameworks that ranges from nightmarish (Motif/Xt e.g.) to a pain (Qt e.g.).

Perhaps the best way to describe the value of Tk is to show the following script. You don't need SpecTcl to run this. You can use the wish command to start a Tcl interpreter with Tk built in and paste the script below into the command line:


ttk::button .b -text Exit -command exit
pack .b
                    

This script should pop up an X window with a button labeled Exit. If you click on that button wish should exit.

Simple as this script is, it illustrates several important points about Tk:

  • Tk provides a set of commands (like ttk::button) that create fairly high level components, or widgets as they're called, from which user interfaces can be composed.

  • Widgets have names. Their names reflect their position in a tree of parent/child widgets. The name .b means this widget is a child of the main window (.). Periods are used as path separators when specifying a widget's name.

  • Widgets have options. Collectively the current values of all of a widget's options are called that widget's configuration. -text and -command are two of the options supported by the ttk::button widget.

  • Actions can be attached to events handled by the widget. For example, obviously a button can be pressed (clicked). Options are provided for most common widget events that allow you to specify scripts to execute in response to those events. In the example above, -command specifies a script (the command exit) to be executed when .b is clicked.

  • In addition to widget commands, Tk provides several methods to layout widgets. These are called. geometry managers. Tk provides three of them: pack, used in the example above, grid, which generally provides better layout control than pack and place which gives exact placement control along with all the joys and pains that entails (you are responsible for re-layout in response to resize events for example).

    For a given container, you must stick with one geometry manager.

  • Once the user interface is laid out, and the interpreter has no more commands to execute (runs off the bottom of the script), it enters an event loop. The event loop catches events that occur on the user interface and dispatches them to the appropriate widgets. Widgets then, if appropriate, dispatch events to event handling scripts.

    For more advanced applications the Tk bind command allows you to specify handlers for arbitrary events on widgets that may not ordinarily handle them. The evetn

Tk provides two widget sets. The classic widgets, those Tk originally came from, have an appearance that mimics Motif widgets. They look the same regardless of the platform on which they are run. They look a bit clunky by modern standarsd.

In 2007, Joe English provided a new group of widgets originally called Tile but now called TTk which stands for themable Tk widgets. These widgets have commands that start with ttk::. ttk widgets determine the environment in which they are running and adapt a display appearance that meets the user interface guidelines of the underlying display system. They do this automatically.