Tcl Scripting


Tcl Scripting - News and articles on software development with Tcl/Tk

Author: Brian Oakley

Table of contents:

  1. You only need curly braces…
  2. Using named arguments
  3. Introduction to named fonts
  4. Introduction to Toplevel Windows
  5. Introduction to bindtags
  6. Intelligent text widget autoscroll
  7. An example is worth 1000 words
  8. Toolbars and Grayscale Images
  9. The difference between eq and ==
  10. Dynamic forms and variables
  11. Writing Tk Code in the Right Order
  12. Getting started with tcltest
  13. Web Services and the Google API

  14. Acknowledgement

separator

You only need curly braces…

Unlike some languages which require curly braces to delimit blocks of code, in Tcl curly braces are always optional. While this is a fundamental feature of how the Tcl parser works [1], few if any books and tutorials actually spell it out. My way to describe this is with the following rule:

You only need curly braces when you need curly braces

What usually happens is that a new programmer is taught “here’s how to do if: if {condition} {body}“, or “here’s how to create a proc: proc name {args} {body}“. While it is true that those are both the best way to create conditions or procs, it remains true that the curly braces are entirely optional.

This brings me to another simple statement I use to describe Tcl quoting:

Quoting is a tool, not a rule

So what does all that mean?

It means that you should use curly braces whenever you need them to get the quoting behavior you desire. There is nothing in the Tcl language specification that requires curly braces in any situation. We teach their use according to best practices but they are just that - best practices. Whenever you find a situation where curly braces don’t give you the proper behavior, switch to something else. It’s that simple.

A practical example

There’s hardly a week that goes by that someone doesn’t post to comp.lang.tcl some variation of the following question:

From: Tcl Newbie
Subject: Help!!!
Newsgroups: comp.lang.tcl

When I press the button it always has the last value of $i. 
How do I get it to have the value of the button??????

  for {set i 0} {$i < 10} {incr i} {
    button .b$i -command {doSomething $i}
  }

Knowing what you now know about quoting, doesn’t the answer just jump out at you?

In this case we need $i to be substituted inside the loop rather than when the button is clicked. Thus, we don’t want to use curly braces because they don’t give us the quoting behavior we desire. The answer to the above problem is simple: use some other form of quoting than curly braces.

If you can remember that “quoting is a tool, not a rule” and “you only need curly braces when you need curly braces” you’re well on your way to never having a quoting problem again.

So finally, the code block which gives the desired behavior might look like:

  for {set i 0} {$i < 10} {incr i} {
    button .b$i -command "doSomething $i"
  }

separator

Using named arguments

The other day I wrote a proc that took three integers as arguments. The arguments represented a date. The easy way to write the proc is to just define it with three arguments. Consider the following example:

set_date 3 12 2006

Am I setting the date to March 12 or December 3? It’s impossible to know without looking at the definition of the proc. Of course, most of the time when this proc is called it will be called with variables so it would look like this:

set_date $month $day $year

Problem solved. That is, until you are tasked with calling this procedure from a new file and you never can remember if it’s “month day year” or “day month year” or maybe “year month day”. If you are familiar with the code it might be easy to remember but if you are a new team member trying to come up to speed on the application it may not be so obvious. Or imagine the scenario where standards changed over time and part of the code base uses “month day year” and part of it uses “day month year”. It happens.

A simple solution to the problem is to use named arguments. Named arguments are argument pairs that are made up of a name (beginning with “-”) followed by a value. Tk makes use of this technique for most of the tk commands (e.g. label .l -borderwidth 1 -relief raised …) and it has proven to be a very effective way to work.

Using named arguments, the above code might look like the following. Notice how the intent of the code is now crystal clear and the programmer doesn’t have to remember the order of the arguments:

set_date -month $month -day $day -year $year

No built in support?

The problem is, neither tcl nor tk have built-in routines for handling named arguments. The proc command has support for variable arguments and default arguments, but no support for named arguments.

Fortunately, over time library routines have been developed for parsing argument lists so we don’t have to contiually reinvent the wheel. For example, tcllib has the cmdline package, the Simple Development Library has SimpleOption, and the tcl’ers wiki page titled “Named Arguments” discusses other techniques. If you don’t care about error checking you can even get by with something as simple as “array set option $args”.

Given a choice, I prefer to keep program dependencies to a minimum. Maybe I’m inflicted with “Not Invented Here” syndrome, but I generally try to keep dependencies to a minimum. While some of the option parsing libraries support shortest unique abbreviations, type enforcement, aliases (e.g. treating -bd and -borderwidth as the same thing), etc., considerably more often then not I simply don’t need those features.

My requirements are usually quite simple. I want to support optional, named arguments, and I need the ability to say control when option processing should stop in case a procedure needs to pass in data that looks like an option but isn’t. To that end, I use a solution that makes use of an array to store options and values, and while and switch to parse the arguments.

Example: setting a date

Let’s consider an example where we need to write a proc named ‘set_date’ for setting the date of an object. The user should be able to specify the month, day and year. In addition, we want a shorthand that says the date is in GMT but we also want to be able to explicitly set the timezone. The synopsis might look something like this:

set_date ?-month month? ?-day day? ?-year year? ?-gmt? ?-timezone|-tz tz?

Step 1: getting started

The rest of the code in this article assumes that a varible named ‘args’ is a list that contains the arguments to be parsed. This means we need to define args as the last (and in this case, only) argument in the proc definition:

proc set_date {args} {
    ...
}

Step 2: establish defaults

I see this step almost as much of a documentation step then anything else. It establishes defaults for the options, but equally importantly, it enumerates the accepted options in a concise manner. For this reason I define the defaults before parsing even though, strictly speaking, I don’t need to do this until later.

The defaults will be stored in an array named ‘default’. I’ll use ‘array set’ if the default values are static or I’ll set each value individually if the defaults are dynamic. For example, in this case the value for -gmt and -timezone is static, but the default day, month and year must be computed from todays date:

array set default {-gmt 0 -timezone ""}
set now [clock seconds]
set default(-month) [get_month $now]
set default(-day)   [get_day $now]
set default(-year)  [get_year $now]

If you want to copy and paste the above code you’ll need to provide your own definitions for get_month, get_day and get_year, or replace the proc calls with static data.

Step 3: parse the arguments

To parse the arguments we’ll use a while loop that looks for values that begin with a “-”. When an argument is encountered that doesn’t begin with “-” or there are no more arguments the loop will terminate. Each time an argument is found the corresponding opt element should be set. This may mean pulling another argument from the list, doing some computations, doing validation, etc.

Even though this example doesn’t support additional arguments other than the named arguments we’ll go ahead and support the argument “–” to mean “stop processing arguments”.

while {[string match "-*" [lindex $args 0]]} {
  set name [lindex $args 0]
  set args [lrange $args 1 end]
  switch -exact -- $name {
    -- {
      # stop processing any more arguments
      break
    }
    -month -
    -day -
    -year {
      set value [lindex $args 0]
      set args [lrange $args 1 end]
      if {![string is integer -strict $value]} {
        return -code error "$name: invalid integer \"$value\""
      }
      set option($name) $value
    }
    -gmt {
      if {[info exists option(-timezone)]} {
        return -code error "-gmt cannot be specified if you set the timezone"
      }
      set option(-gmt) 1
    }
    -timezone -
    -tz {
      if {[info exists option(-gmt)] && $option(-gmt)} {
        return -code error "timezone cannot be specified with -gmt 1"
      }
      set value [lindex $args 0]
      set args [lrange $args 1 end]
      set option(-timezone) $value
    }
  }
}

Checking for required arguments and applying the defaults

If you want to require the caller to supply certain arguments this is the place to do it. After parsing, our array now holds all of the options specified by the user so we can quickly determine if any required arguments were omitted. For this example we have no required arguments so there’s nothing to do but to copy the default values from any missing arguments:

foreach name [array names default] {
    if {![info exists option($name)]} {set option($name) $default($name)}
}

We could choose to not apply the defaults here, but that means any place we need to reference an argument we have to first check whether it was supplied or not. I prefer to fill in the option array with all user values and all defaults so all argument values are defined.

Finishing up

Once we’ve done all the parsing and applied all the defaults, there’s nothing left to do. However, there may be data left in $args. This might be good if you’re trying to implement an interface that accepts additional arguments after the optional arguments (e.g. ‘puts -nonewline “string”’). It might be bad if your procedure does not accept any other arguments. In this case we’ll throw an error if there are any additional arguments that haven’t been processed:

if {[llength $args] > 0} {
    return -code error "unknown arguments: $args"
}

A note about performance

There is some extra work being done in the above code that doesn’t need to be done. For example, we do [lindex $args 0] twice, once in the while condition, and once at the top of the loop to assign the variable ‘name’. We can set the variable name inside the loop condition but I think that makes the code harder to read. Since these commands take only microseconds to execute I think the tradeoff for readability is worth the overhead. If this were a time critical routine I probably wouldn’t use named arguments to begin with, but the vast majority of the procs I write are not time-critical.

When to use named arguments

I dont’ recommend using named arguments for every proc. Single-use procs or procs that take just one or two fixed arguments don’t need the overhead of named arguments. Named arguments are best for procedures that take a variable number of arguments, or that have arguments that may be ambiguous when reading code that calls the procedure. Named arguments should not be used for time-critical functions. As a general rule, then, I recommend named arguments for ‘public’ procedures – procedures that may be used outside their immediate context, such as in public APIs.

Conclusion

Named arguments can be used to make your code more readable. This can translate into “more maintainable”, and “more maintainable” is almost always desireable.

There are several canned solutions for argument parsing, and if you don’t mind having external dependencies or already depend on a library that includes argument processing functions you should use an existing solutoin. However, if you don’t want to depend on an external library it’s easy to do simple named argument parsing that likely covers 90+% of the normal cases.

separator

Introduction to named fonts

If you’ve ever written a commercial app, or any app that you share with others, you’ve probably been asked “how can I change the fonts?” Maybe you have a finely crafted set of fonts custom tailored to your design or maybe you rely on the defaults provided by tk, but no matter what you provide some user will want to change it.

One of the more unique features of Tk is support for something called named fonts, part of the standard tk font command [1]. This article briefly describes how and why you should use named fonts. In addition, example code is given for adding the ability to dynamically change the fonts at runtime with a simple binding.

What is a named font?

What is a named font? A named font can be thought of as an alias for a complete font description. You create a named font with the font create command. Once created, you can use the named font anywhere you would normally use a standard font specification. That’s all there is to it.

The following example illustrates how to create and use a named font:

font create customFont -family Helvetica -size 14 -slant italic
label .label -text "A label with a named font" -font customFont
pack .label

Changing the font at runtime

The advantage to using named fonts is that when the font is changed, all widgets that use the font automatically get updated. To see the effect, start an intereactive Wish shell and enter the code from the above examples.

The main window should look something like this:

namedfonts-1

Changing the font is as simple as calling the font configure command, giving it the name of the font and any new attributes:

font configure customFont -slant roman -weight bold

If you run the above command in your interactive shell the window should now look like this:

namedfonts-2

Growing and shrinking fonts

Once you start using named fonts it becomes easy to provide a mechanism to let the user grow or shrink the fonts. For example, you might want to bind Control-plus to increase the font and Control-minus to decrease it. Or, you may want a menubar View menu item for “Bigger Fonts” and “Smaller Fonts”. The following example shows one way to accomplish that task:

proc bump_fonts {incr} {
  set size [font configure customFont -size]
  catch {
    incr size $incr
    # keep font sizes sane...
    if {$size >== 8 && $size <== 32} {
      font configure customFont -size $size
    }
  }
}
# Control-plus actually requires the user to press Control-Shift-equals;
# using Control-equal is a convenience to make it a bit easier on the user
bind all <Control-plus> [list bump_fonts +2]
bind all <Control-equal> [list bump_fonts +2]
bind all <Control-minus> [list bump_fonts -2]

Of course, you can have multiple named fonts within an application and bump_fonts could iterate over each font to change them all at the same time. For example, my apps often have several fonts such as normalFont, boldFont, italicFont, etc.

Using a named font as the default font

You can use named fonts by supplying the name of the font for every widget that has a font. This can be tedious. An arguably better solution is to use the built-in option database features of Tk. The following example shows how to use a named font for all widgets:

option add *font customFont

Putting it all together

The following example creates an application with a single text widget and a menubar, and uses three different named fonts:

proc main {} {
  font create defaultFont -family Helvetica -size 12
  font create boldFont -family Helvetica -size 12 -weight bold
  font create italicFont -family Helvetica -size 12 -slant italic

  option add *font defaultFont

  menu .mb
  menu .mb.file
  menu .mb.view
  .mb.file add command -label "Exit" -command exit
  .mb.view add command -label "Bigger Fonts" \
      -accelerator "Control-plus" -command [list bump_fonts +2]
  .mb.view add command -label "Smaller Fonts" \
      -accelerator "Control-minus" -command [list bump_fonts -2]
  .mb add cascade -label File -menu .mb.file
  .mb add cascade -label View -menu .mb.view
  . configure -menu .mb

  text .t -wrap word -width 20 -height 5
  pack .t
  .t tag configure normal -font defaultFont
  .t tag configure bold -font boldFont
  .t tag configure italics -font italicFont

  bind all <Control-plus>  [list bump_fonts +2]
  bind all <Control-equal> [list bump_fonts +2]
  bind all <Control-minus> [list bump_fonts -2]

  .t insert end "Normal\n" "normal" "Bold\n" bold "Italics\n" italics
}

proc bump_fonts {incr} {
  foreach font {defaultFont boldFont italicFont} {
    catch {
      set size [font configure $font -size]
      incr size $incr
      if {$size >= 8 && $size <= 32} {
        font configure $font -size $size
      }
    }
  }
}

main

namedfonts-3

Limitations

One limitation of named fonts is that you can’t directly copy a font. You can use the output of tk’s font actual command to get the attributes of a font and assign them to another font. Doing so, however, doesn’t yield perfect results. For example, most unix systems have a bitmapped font alias named “6x13”. As of tk 8.5 there doesn’t seem to be any way to create a named font that is exactly that font. For example, with the following code it seems like the two labels should be identical but they are not:

eval font create myFont [font actual "6x13"]
label .l1 -text "ABCabc123 this is 6x13" -font 6x13 -anchor w
label .l2 -text "ABCabc123 this is myFont" -font myFont -anchor w
pack .l1 .l2 -side top -fill x

If you try this on a non-X11 system the widgets may look the same; on most flavors of unix they will look slightly different:

namedfonts-4

Summary

As long as you don’t need to use one of the X11 bitmapped fonts, named fonts provide a lot of flexibility. By creating a handful of named fonts at startup it becomes easy to build in the ability for the user to reconfigure fonts on the fly without having to manually re-apply font definitions to every widget. In addition, if you need to make font adjustments for specific platforms you can do so all in one place without having to track down dozens of font references in your code.

Get in the habit of always using named fonts and you’ll have fewer font related problems as your code matures.

separator

Introduction to Toplevel Windows

Once in a while on comp.lang.tcl we get a question like the following, often from someone coming from the world of Visual Basic:

From: Tk Newbie
Subject: how do I create floating windows?
Newsgroups: comp.lang.tcl

Help! How do I create windows?

The not-so-obvious answer is to create a toplevel widget. Some toolkits call these “windows”, “panels”, “forms” or “dialogs”. In Tk they are called toplevel widgets. Toplevel widgets are created with the toplevel [1] command. Tk gives you one toplevel widget (named “.”) automatically; when this toplevel is destroyed your application will exit.

For many applications the default toplevel is the only toplevel you’ll ever need. For some, though, it is necessary to create and manage additional toplevels. File selection dialog boxes, message boxes, help windows, “About…” windows, search and replace dialogs are all examples of toplevel windows.

Window Managers and Toplevel Windows

Since toplevel windows — including “.” — aren’t connected to any other windows, they are managed by the window manager. If you are coming from the Windows or Macintosh world you may not be familiar with the concept of a window manager. It is simply the part of the OS that gives windows a consistent window border, buttons for minimizing and maximizing, etc. On unix users have a choice from dozens of window managers with hundreds of different looks to them. Windows and Mac users typically have no choice, they simply get what the OS gives them (though there can be variations, such as the ability to get either a Win2k or WinXP style “look” when running WinXP).

The window manager is something that cannot be fully controlled from within a Tk application. You can’t, for example, request that a custom button be placed on the titlebar. However, Tk gives us a way to provide hints to the window manager via the wm [2] command. Unfortunately many of these hints are just that, and window managers are free to ignore these hints.

Give your window a title

One thing Tk can control is the title that is displayed on the title bar. Tk will give every window a default title based on the name of the widget, but that is rarely sufficient if you wish to create a polished application. To create a title use the “wm title” command. For example, if you want to create an “About” window you might want to start by giving it a decent title:

toplevel .about
wm title .about "About My App"

The above code would give you a window that looks something like the following image, though the colors and look of the buttons depends on your specific window manager. The following screenshot was taking on a Macintosh:

toplevel-1-mac

toplevel window with a title (MacOSX)

Hint: Even though the window “.” is created automatically and is given a default title, you should get in the habit of always giving it a better title.

Preparing for the worst

Most window managers will put a button on the titlebar the user can use to destroy the window. For normal windows we can’t control whether that button appears or not. We can, however, control what it does. Because Tk was originally developed on a unix system, the solution is somewhat “unixy”. Unix window managers have the notion of protocols which are sent to an application as a result of some event happening on the window. For the button that destroys the window this protocol is WM_DELETE_WINDOW. This is by far the most common protocol, but others are supported to a lesser extent on some windowing systems.

Tk lets us intercept this protocol and do whatever we want. If we don’t specify anything, Tk will simply honor the request to destroy the window. Using the “wm protocol” command, however, we can request that a proc be called, and this proc can decide whether or not to destroy the window.

A common use of this is to give the user an opportunity to save their work, since many users prefer to click on the button on the titlebar rather than select “Quit” or “Exit” from the menubar. One way to do that is with code that looks something like the following:

wm protocol . WM_DELETE_WINDOW [list deleteWindow .]
proc deleteWindow {w} {
  # If no save is needed, destroy the window
  if {![saveNeeded $w]} {
    destroy $w
    return
  }

  # Prompt the user to save changes
  set response [tk_messageBox \
    -type yesnocancel -default yes -icon question \
    -title "Save Changes?" -message "Do you wish to save your changes?"]
    
  if {$response eq "yes"} {
    # Attempt to save; if we succeed, destroy the window
    if {[saveChanges $w]} {destroy $w}

  } elseif {$response eq "no"} {
    # User doesn't want to save; just destroy the window
    destroy $w

  } else {
    # User pressed cancel; don't save and don't destroy
    # the window
  }
}
# You wouldn't use these definitions in the real world, but you
# can use them to test out the above code:
proc saveNeeded {w} {return 1}
proc saveChanges {w} {return 1}

If you wanted to use the above code, you would of course have to supply your own definitions for the saveNeeded and saveChanges procedures. The important things to notice is that if no save is needed the window is destroyed, and if a save is needed but fails or the user cancels, the window is not destroyed. When I develop applications I often will have the “File->Exit” menu item call this same procedure so the same thing happens whether the user quits normally or via the window manager control.

It is important to note that this code only works in response to the specific window manager protocol. If the user quits the application by destroying the process (e.g. by issuing the unix kill command, or killing it from an administrative console) this code will not be called. It is a fact of life that processes can be killed without notification. If this were not so, malicious code could be written that could overtake the screen and not be killed even by an administrator.

For additional information about other protocols that might be supported, read up on ‘wm protocol’ on the wm man page…

Transient Windows

Many window managers have the notion of “transient” windows – windows that only appear for a brief amount of time. Typical examples include message dialogs, find-and-replace dialogs, etc. Again with the wm command, we can inform the window manager that a window is transient or not. This may or may not have any effect. Most window managers will treat transient windows differently from other windows; for example, transient windows may not get the same set of buttons on the title bar as regular windows.

Unfortunately, different window managers may do slightly different things for transient windows so it’s best to test this out on any platforms on which you wish to deploy. The general rule, though, is to always mark short-lived windows as transient.

Here is a brief example of a transient window. Notice the slightly different appearance of the titlebar and the resize grip in the bottom right of the windows which is unique to the MacOSX platform; your platform may do something entirely different:

toplevel .transient
wm transient .transient .
wm title . "Normal Window"
wm title .transient "Transient Window"

transient-1-mac

normal and transient toplevels (MacOSX)

If you type that code in by hand you might not see the transient effect. The transient attribute needs to be set before the window is actually drawn on the screen. In some cases it is sufficient to withdraw then deiconify the window, and in some cases it’s not. If the above code is put in a file and run you will see the effect. If you type it in interactively then the window will be drawn when you press <Return> after the toplevel command but before the window is marked as transient. If you are creating transient windows in an application just be sure that you call “wm transient” before the next update.

Event Bindings

Toplevel widgets are a little bit different than other widgets with respect to bindings. Tk has a notion of bindtags, and toplevel widgets play a special role with bindtags. If you are unfamiliar with bindtags you might want to read “Introduction to Bindtags”…

What makes toplevels special? Every widget gets a default list of bindtags, and one of the elements of that list is the name of the toplevel to which the widget belongs. Why is that important? It allows us to have bindings that only work within a specific window.

For example, message dialog boxes typically have an OK button, and pressing <Return> when the window is visible will do the same thing as if you clicked on the OK button. This result can be achieved even if there are other widgets in the window, by binding to the toplevel widget. In doing so, no matter which widget inside the toplevel has the focus, the binding will still fire.

toplevel .about
label .about.text -text "My App is Cool"
pack .about.text -side top -fill both -expand 1
button .about.ok -text OK -command about_ok
pack .about.ok -side bottom -expand 1
bind .about <Return> about_ok
proc about_ok {} {destroy .about}

Truly global bindings

When first starting out with Tk, it’s easy to get into the habit of binding to “.” whenever you want a global binding. For example, to have <F1> bring up a help window you might be tempted to do it like this:

bind . <F1> show_help

From what we just learned, however, that binding will only ever work if focus is in the main window. You might only have one window in which case the point is moot. If you get in the habit of doing this, though, you might find yourself one day scratching your head and wondering why <F1> doesn’t work when you have a second window open.

The lesson is simple: if you If you want a truly global event, bind to “all” rather than “.”. And if you want a binding that is global within the scope of one window only, bind to the toplevel window instead of to “all”.

Creating toplevel widgets on demand

There are a couple of different ways to manage the creation of toplevel widgets. Once is to create them ahead of time and make them appear (wm deiconify) or disappear (wm withdraw) as needed. Another approach is to create them on demand on an as-needed basis. This second approach is arguably a bit more efficient with respect to memory, though the memory overhead of a toplevel is pretty darn small.

Creating the toplevel widgets on demand is hardly any different than creating them once up front. Write a procedure to create the toplevel, then simply check to see if the toplevel exists or not at the start of the procedure. Once created but no longer needed, it can either be hidden and reused, or destroyed and recreated depending whatever makes the most sense for this particular window.

proc about {} {
  if {![winfo exists .about]} {
    <code to create .about window>
  }
  wm deiconify .about
}

As you can see, the first time “about” is called it will create the window. If I have a button that dismisses the window I will typically have it just hide the window rather than destroy it. If the state of the application depends on the window being recreated, I can destroy it at will and the next time it needs to be displayed it will again be created.

Plain (Overrideredirected) Windows

Once in a great while it is useful to create a window that is completely devoid of all window manager decorations — no titlebar, no buttons, no border. Tk gives us the ability to do that by setting the overrideredirect flag on the window with the “wm overrideredirect” command. Be forewarned: when you do this you likely will lose all ability to move, resize or destroy the window with the mouse. You will be responsible for making bindings that let the user manage the window.

What are these windows good for? You can use them to create a kiosk-type application that takes over the whole screen, for example. These windows are also commonly used as mouse-over tooltips, for splash screens, and other instances where you the user doesn’t need to directly manipulate the window.

Summary

Toplevel windows are simple to create and are a useful solution to many problems. Unlike most widgets, however, the look and feel of a toplevel is partially controlled by the window manager. Fortunately Tk gives us several mechanisms to interface with the window manager so that we can create floating windows that behave in a way that is consistent with other applications on the system, whether that system is some variant of Unix, Windows or the Mac OS.

separator

Introduction to bindtags

When you read the tcl manpage on bind [1] you will notice that the synopsis of the command doesn’t mention windows or widgets, but rather tags (also known as bindtags or binding tags.

bind tag ?sequence? ?+??script?

Bindings are associated with tags, and tags are associated with widgets. This bit of indirection makes for a very powerful and flexible way to associate bindings with a specific widget or any logical grouping of widgets.

For example, most default bindings are associated with a tag named after the widget class so that all widgets of the same type share the same behavior. In addition, there are also bindtags associated with the logical grouping of all widgets inside a toplevel (e.g. “.”) and all widgets (bindtag of “all”).

Why is this important? In many cases you can go a long way with Tk without ever needing to know about bindtags. You can associate bindings with widgets, and since the default bindtags for a widget include a tag that is the same as the widget name it all just works. The importance of bindtags comes into play when you want to either apply the same binding to a logical group of widgets, or have your bindings override or work in conjuction with existing bindings, or even if you want to completely disable the default bindings altogether.

You may not ever need to do any of those things. If you do, however, you need to know a little bit about how bindtags work

Looking under the hood

You can use the bindtags command to set or view the bindtags of any widget. For example, here is what you get when you view the bindtags for a few different widgets:

$ wish
% frame .f
.f
% bindtags .f
.f Frame . all
% canvas .c
.c
% bindtags .c
.c Canvas . all
% button .b
.b
% bindtags .b
.b Button . all

Notice that the default bindtags are ordered from most specific (the widget itself) to least specific (all widgets). This order is important because it is the order in which the bindings are processed when an event is received by a widget.

To confirm the relationship between the bindtags and the order that they are processed, let’s create a new toplevel and a frame within that toplevel to see how it affects the bindtags. We’ll assign the frame a custom class* as well, to illustrate the effect that has on the bindtags:

  • The Frame and Toplevel widgets are the only widgets as of tcl 8.4 which can accept a ‑class argument
$ wish
% toplevel .t
.t
% frame .t.x -class "XFrame"
.t.x
% bindtags .t.x
.t.x XFrame .t all

Notice the difference between the bindtags for this frame and the frame in the first example. In this example the bindtag “XFrame” takes the place of “Frame” for the class and “.t” takes the place of “.” in the bindtag representing the toplevel widget.

Experimenting with bindings

Because frames don’t typically have any bindings associated with them they make a good place to experiment with bindings since we don’t have to worry that default behaviors will get in our way.

First we’ll need a frame that we can click on; to make the frame easy to see we’ll give it an explicit size and borderwidth, though they have no bearing on the behavior of the bindings. Once we have the frame we need to assign bindings to each of the default bindtags:

% frame .f -width 50 -height 50 -borderwidth 1 -relief raised
.f
% bindtags .f
.f Frame . all
% pack .f
% bind .      <ButtonPress-1> {puts "bindtag: ."}
% bind all    <ButtonPress-1> {puts "bindtag: all"}
% bind Frame  <ButtonPress-1> {puts "bindtag: Frame"}
% bind .f     <ButtonPress-1> {puts "bindtag: .f"}

After entering the above code in a wish console, click on the frame and you will see the following output:

bindtag: .f
bindtag: Frame
bindtag: .
bindtag: all

As you can see, the the bindings fire in the order of the bindtags for the widget. If we change the order of the bindtags, we change the order that the event is processed:

% bindtags .f [list all . Frame .f]

Again, after entering the above code in the wish console, click on the frame and you will see the following output. Notice that the order in which the bindings are handled exactly mirror the relative order of the bindtags:

bindtag: all
bindtag: .
bindtag: Frame
bindtag: .f

To illustrate that bindtags can be any arbitrary string, let’s add a custom bindtag after the class bindtag:

% bindtags .f [list .f Frame MyTag . all]
% bind MyTag <ButtonPress-1> {puts "bindtag: MyTag"}

The output from the above should look like this:

bindtag: .f
bindtag: Frame
bindtag: MyTag
bindtag: .
bindtag: all

Default bindings

As I stated earlier, bindtags are used for the default behavior of all widgets. While it may seem that default bindings for a widget are somehow “built in” to the widget itself, they are simply attached to one of the default bindtags. Mostly these bindings are associated with a tag named after the widget class, though a few are associated with the tag “all”.

To see the bindings for a bindtag we can use the bind command. Like many tk commands, it can be used both for setting values and well as for introspection. The following example shows what events are bound to the tag Button, which is one of the default bindtags for button widgets:

% bind Button
<ButtonRelease-1> <Button-1> <Leave> <Enter> <Key-space>

Using the same technique we can see what bindings are associated with the tag “all”:

% bind all
<<PrevWindow>> <Key-Tab> <Key-F10> <Alt-Key>

Practical uses

Most of the time I use bindtags it is to solve one of two problems: to change the behavior of a widget, or to change the order in which events are handled.

Changing the behavior of a widget

Suppose, for example, you are creating a text editor and want to let the user choose bindings that emulates another well-known editor that they may already be familiar with. You may want to give them the choice to have the text widget work more like emacs, more like vi, or more like bbedit. Additionally, you may want to give them the ability to create all their own keyboard shortcuts.

In such a scenario you could associate vi-like bindings with a tag named “Vi”, emacs-like bindings to a tag named “Emacs”, and let the user specify bindings for a tag named “Custom”. When the user wants to switch to a different set you can simply change the bindtags (e.g. “.editor Text . all”, “.editor Vi . all”, “.editor Emacs . all”, “.editor Custom . all”, etc.) Although you could write code to completely change all the bindings to the widget itself, it’s easier to maintain complete sets of bindings and just swap one bindtag for another.

Changing the order of binding execution

Another situation where bindtags are useful is to make sure that an event is handled in a specific order. For example, by default bindings on widgets occur before the class bindings since, as we’ve seen, the class bindtag follows the widget-specific bindtag.

A classic example is where you want to create a binding on a mouse click for a listbox, and in that binding you want to grab the selection*. Since the selection is set by the class binding and the class binding is executed after the widget specific binding, when you query the listbox for the current selection you’ll always get whatever was selected before the click.

Hopefully the solution is now clear: you can add a bindtag after the class binding (e.g. “.listbox Listbox PostListbox . all”) and add your binding to that tag. Your binding is then guaranteed to be executed after the class binding, and after the selection has been set.

  • In modern versions of Tk you can bind to <<ListboxSelect>> if all you want is the selection, and avoid the bindtag issue altogether. The technique can apply to any widget, however. For example, you may want to add a binding to <Any-KeyPress> for an entry widget. Unless you arrange for that binding to fire after the class binding you’ll discover that when your binding fires the entry widget has not yet been updated to include the character the user just typed.

Summary

Bindtags are a remarkably flexible mechanism for associating bindings with widgets. As you can see, a widget can have multiple bindtags, and the order determines the order in which bindings on those bindtags are called when a widget receives an event. The default ordering of bindtags is the widget name, the widget class, the name of the toplevel in which the widget exists, and the special tag “all”.

You can write very large GUI applications with Tk and never need to manipulate the bindtags. Once in a while, however, you may find a situation where you need to do something a little out of the ordinary, and knowing how to manipulate the bindtags may save you a lot of headaches.

For more on bindtags, read the man page and see the Tcler’s Wiki page on bindtags…

separator

Intelligent text widget autoscroll

At work we use a simple chat program to keep in touch since we don’t all work in a central office. It’s a handy little app that is just a few hundred lines long. The program has one little quirk that I find annoying, which is that when a new message comes in the text automatically scrolls to the bottom so one can see the new text. This is what I want 99% of the time but it’s that last 1% that was driving me crazy, and it’s that last 1% that can sometimes make the difference between an OK application and one that is first class.

This isn’t unique to a chat program; there are many types of applications that may do some sort of automatic scrolling. For example, interactive consoles, viewing the log of a running process, etc. If you have this sort of program and have added some simple autoscrolling you might have this same problem and not realize it if you haven’t yet tested the program in a live environment.

The problem is this: once in a while I want to scroll back to see some text that came earlier. When I do, if someone sends a message the window will scroll to the bottom even if I have clicked on the scrollbar. If there’s a conversation going on it becomes impossible to see data that has scrolled out of view – I scroll a little, someone sends a message and BAM. I scroll a little, another message comes in, BAM. And so on.

A simple solution would be to add a checkbutton that lets the user turn the autoscroll on or off. A belt-and-suspenders solution might be to put a checkbutton on the toolbar and add a user preference to a menu:

checkbutton .toolbar.scroll -text "Auto-scroll?" -variable prefs(autoscroll)
...
.menubar.prefsMenu add checkbutton -label "Auto-scroll" -variable prefs(autoscroll)
...
proc new_message {message} {
    global prefs

    # insert the text
    .message insert end $message

    # if the user wants autoscroll, do it
    if {$prefs(autoscroll)} {.t see end}
}

That is effective but not very user friendly. If I want to scroll back through the text I must uncheck the checkbutton, do my scrolling, then remember to re-check the checkbutton to turn the scrolling back on. We can do better.

Thinking about the problem for a couple of minutes it is clear that about the only time I don’t want the autoscrolling behavior is if I’ve manually scrolled the window. The solution, then, must lie in knowing when I’ve scrolled or not. If the code can determine when I’ve scrolled or not it can decide whether or not to do the autoscrolling.

How can the code decide if I’ve scrolled back? As it turns out, the text widget can tell us what portion of the text is visible by using the built-in introspection of the yview subcommand. Without any additional arguments, rather than changing what is in view yview will report the visible portion of the text widget as two fractions which describe what portion of the text is presently visible.

Armed with this knowledge, before we insert any additional text we can determine if the user has scrolled or not. Simply stated, if the bottom of the text was visible before we insert any new text it should be visible after we insert any new text. The solution thus becomes:

proc new_message {message} {
    # get the state of the widget before we add the new message
    set yview [.t yview]
    set y1 [lindex $yview 1]

    # insert the text
    .t insert end $message

    # autoscroll only if the user hasn't manually scrolled
    if {$y1 == 1} {
        .t see end
    }
}

As you can see, it doesn’t take much effort to change a good solution (allowing the user to turn autoscroll on or off) into a great solution (intelligently turning it on or off). With tk, either solution is trivial to accomplish.

separator

An example is worth 1000 words

One of the tricks I use when I first start writing a chunk of code is to package the code up in a namespace, and I (almost) always include a working example as part of the implementation. I have found that this can be both a timesaver during development and also a year or two later when I’ve forgotten how the code works and want to use it again in a new project.

This past weekend I was cleaning out some of my directories and came across some old code I hadn’t touched in a couple of years. One file was called “wrap.tcl”, which I vaguely recall was used to add a binding to text widgets that would intelligently wrap the current paragraph to a particular column width. This code wasn’t in a library of a released project but rather in a personal “hacks” directory that I tend to leave in my wake. Was this working code? Did it do what I was guessing it did? How do I use it?

The first thing I did was to open the file with emacs and look at the end of the file. There I found the following block of code:

proc ::wrap::example {} {
    catch {destroy .t}
    pack [text .t -wrap none]
    bind .t  <Control-space> [list ::wrap::wrap %W insert]
    .t insert end "To cause the text in this
paragraph to wrap, place the cursor anywhere in 
the paragraph then press control-space.

* notice how it also will
intelligently wrap bulleted paragraphs
* yada yada
yada"
}

I instantly knew the basic feature of this chunk of code and also had a way to immediately test it out. Of course, if I had written sufficient API documentation maybe I wouldn’t need this crutch. However, we all know that in the real world documentation is just about the last thing that gets created, and the last thing to get updated when the code changes. This is perhaps doubly true for code that isn’t part of an official project but instead is just something that gets created out of idle curiosity or to solve an immediate need.

Because I had taken the time a couple years ago to write this short example, in just one minute, if that, I was able to determine:

  1. what the code did
  2. how to use the code
  3. whether the code actually worked

That’s a lot of knowledge to be gained about the code in a file, from just a baker’s dozen lines of code.

Including a working example right in the implementation itself doesn’t make the documentation problem go away but it does make it easier to keep the example in sync with the code and provide at least a modicum of factual documentation.

Practical advice

If you’re just starting out as a Tcl programmer, I recommend learning about namespaces so that you can encapsulate your functionality in reusable units. Once you get past that hurdle, adding an example proc to your files as a matter of course can help make your code more easily reusable over the long run.

separator

Toolbars and Grayscale Images

I was reading the Microsoft UI guidelines the other day to get some specifics about toolbar look & feel. Even though Microsoft isn’t particularly consistent even within their own apps, they at least publish guidelines as a starting point. What I learned was that toolbar buttons, in addition to being flat when the mouse is not over them, should also be “neutral or grayscale” (look for the section named “Hot-Tracked Appearance”). Of course, what Microsoft says and what Microsoft does can somtimes be two different things. A quick survey of my windows desktop shows that a few apps behave this way, and many don’t.

Tk button widgets have built-in support (via -relief and -overrelief) for changing the relief when the cursor hovers over them, so that requirement is easy. Even without built in support we could accomplish the same thing with the use of <Enter> and <Leave> bindings. But what about the notion of changing the images to grayscale? Obviously we could have two versions of every icon — one grayscale and one full color — and swap them in and out, but that is inefficient.

What you may not know is that the Tk photo image has an option with which you can change the color palette of an image on the fly. This is done, not surprisingly, through the -palette option of each image. If given a single number it designates a number of shades of gray to use. Thus, to convert an image to grayscale we can configure the palette to be, say “256” meaning 256 shades of gray. This is all documented in the photo man page.

What is not documented is how to revert an image that has been converted to grayscale back to its original colors. For that we need to read between the lines and do a little experimentation. If you create an image and then use the cget method to get the value of the -palette option you’ll see that by default it is the empty string. It is therefore reasonable to assume that setting the -palette option to the empty string will return the image to its original form, and so it does.

A simple example

To illustrate the technique, first we need a couple of color images that we want to use in a toolbar. We want to be able to reference the images with symbolic names rather than “image1”, “image2”, etc but it’s generally considered to be bad practice to hard-code image names. There’s a possibility that any name we pick could clash with a command name or an image created by a library procedure. The best choice is to let the image command pick a name which we’ll store in an array. That guarantees us a unique image name yet lets us reference it with a symbolic name.

set image(file_open) [image create photo -data {
  R0lGODlhEgASAPIAAAAAAICAAMDAwPj8APj8+AAAAAAAAAAAACH5BAEAAAIALAAAAAASABIA
  AAM7KLrc/jAKQCUDC2N7t6JeA2YDMYDoA5hsWYZk28LfjN4b4AJB7/ue1elHDOl4RKAImQzQ
  cDeOdEp1JAAAO///
}]

set image(file_save) [image create photo -data {
  R0lGODlhEgASAPEAAAAAAICAAMDAwAAAACH5BAEAAAIALAAAAAASABIAAAI3lI+pywYPY0Qg
  AHbvqVpBamHhNnqlwIkdeoJrZUlPcML0jde0DOnxxHrtJA6frLhC8YiNpnNRAAA7////
}]

Now that we have our images, let’s create a simple toolbar that uses these images. We’ll use the built‑in “‑relief” and “‑overrelief” options to cause the button to become raised or go flat when the mouse hovers over them.

frame .toolbar
pack .toolbar -side top -fill x

button .toolbar.open -image $image(file_open) \
    -relief flat -overrelief raised -borderwidth 1
button .toolbar.save -image $image(file_save) \
    -relief flat -overrelief raised -borderwidth 1

pack .toolbar.open .toolbar.save -side left

If you combine the above code into a single file, or copy and paste it into a running wish process you should see a toolbar with two simple icons

toolbar_color

toolbar with colored buttons

Starting with the code from the previous example, we can give ourselves a new version of the toolbar that has grayscale images:

$image(file_open) configure -palette 256
$image(file_save) configure -palette 256

toolbar_gray

toolbar with grayscale buttons

This is a step in the right direction but we need to restore the color when the cursor hovers over the button. To do that we’ll reset the palette to the empty string to get the default color palette. Since we need to switch the colors as the cursor moves, let’s put the code into a procedure.

# Usage: changeColor <color> button ?button ...?
# If <color> is "grayscale" the images will be converted to grayscale,
# otherwise they will be in full color
proc changeColor {color args} {
  set palette [expr {$color eq "grayscale" ? "256" : ""}]
  foreach widget $args {
    set image [$widget cget -image]
    $image configure -palette $palette
  }
}

Now, we must modify the toolbar to call this procedure when the mouse hovers over the icons. Also, because the images were initially created in color we need to convert them to grayscale to reflect the initial state of not having the mouse cursor hovering over them:

bind .toolbar.open <Enter> [list changeColor fullcolor %W]
bind .toolbar.open <Leave> [list changeColor grayscale %W]
bind .toolbar.save <Enter> [list changeColor fullcolor %W]
bind .toolbar.save <Leave> [list changeColor grayscale %W]

changeColor grayscale .toolbar.open .toolbar.save That’s all there is to it. If you put all that code in a file and execute it you should have a toolbar where each button is raised and colored or flat and gray depending on whether or not the mouse cursor is hovering over the button.

The complete example

set image(file_open) [image create photo -data {
  R0lGODlhEgASAPIAAAAAAICAAMDAwPj8APj8+AAAAAAAAAAAACH5BAEAAAIALAAAAAASABIA
  AAM7KLrc/jAKQCUDC2N7t6JeA2YDMYDoA5hsWYZk28LfjN4b4AJB7/ue1elHDOl4RKAImQzQ
  cDeOdEp1JAAAO///
}]

set image(file_save) [image create photo -data {
  R0lGODlhEgASAPEAAAAAAICAAMDAwAAAACH5BAEAAAIALAAAAAASABIAAAI3lI+pywYPY0Qg
  AHbvqVpBamHhNnqlwIkdeoJrZUlPcML0jde0DOnxxHrtJA6frLhC8YiNpnNRAAA7////
}]

# Usage: changeColor <color> button ?button ...?
# If <color> is "grayscale" the images will be converted to grayscale,
# otherwise they will be in full color
proc changeColor {color args} {
  set palette [expr {$color eq "grayscale" ? "256" : ""}]
  foreach widget $args {
    set image [$widget cget -image]
    $image configure -palette $palette
  }
}

frame .toolbar
pack .toolbar -side top -fill x

button .toolbar.open -image $image(file_open) \
    -relief flat -overrelief raised -borderwidth 1
button .toolbar.save -image $image(file_save) \
    -relief flat -overrelief raised -borderwidth 1

pack .toolbar.open .toolbar.save -side left

bind .toolbar.open <Enter> [list changeColor fullcolor %W]
bind .toolbar.open <Leave> [list changeColor grayscale %W]
bind .toolbar.save <Enter> [list changeColor fullcolor %W]
bind .toolbar.save <Leave> [list changeColor grayscale %W]

changeColor grayscale .toolbar.open .toolbar.save

Summary

This article uses a toolbar and buttons to illustrate a simple technique for changing the colors of an image. Should you use this technique for the toolbars in the apps you create? That’s a design decision I can’t answer. If you want this sort of behavior it comes at virtually no cost; Tk has been designed in a way that makes this simple to implement. Even if you don’t want this look for your toolbars, the technique can be applied any time you have a color image that you want to display in grayscale, such as to imply that it is disabled or unavailable, etc. One of the great things about Tk is that it doesn’t try to inforce any sort of policy, but gives us the tools to make very rich applications.

separator

The difference between eq and ==

Starting with Tcl 8.4, expr (and its cousins if, while and for) includes the operators “eq” and “ne”. The documentation, though accurate, doesn’t present much of a distinction between those and their counterparts “==” and “!=”.

From the expr man page:

  • == != Boolean equal and not equal. Each operator produces a zero/one result. Valid for all operand types.
  • eq ne Boolean string equal and string not equal. Each operator produces a zero/one result. The operand types are interpreted only as strings.

So what’s the difference? The definition for eq and ne mention that the operand types are interpreted “only as strings” but how do == and != treat the operands? One must closely read the man page to learn that Tcl will attempt to do perform a numerical interpretation of the operands if at all possible, falling back to strings if either operand can’t be interpreted as a number.

Thus, with == and != Tcl will do a numerical comparison if possible, and a string comparison otherwise. With eq and ne Tcl will always perform a string comparison. The difference can be significant, as illustrated in the example below:

set v1 "01"
set v2 "1"
if {$v1 == $v2} {puts "equal"} else {puts "not equal"}
if {$v1 eq $v2} {puts "equal"} else {puts "not equal"}

When you run the above example you should see “equal” followed by “not equal”. In this case, the choice of operators is critical as the result between the two are exact opposites.

Practical Advice

  • If you know you want to do string comparisons, prior to Tcl 8.4 you either had to use == or != and rely on both operands not looking like numbers, or resort to using a string function such as “string equal”, “string compare”, “string match” or “regexp”. The careful programmer would always use one of those functions, especially if the comparison was done on external data not under direct control of the program.

  • Starting with 8.4 we can now use the eq and ne operators any time we want to coerce the comparison to be done on the string representation of the operands. This guarantees that a numerical comparison won’t happen in an edge case while making the program arguably easier to read then when using a string function to do the comparison.

separator

Dynamic forms and variables

Last week on comp.lang.tcl I was helping someone who claimed to have problems with passing value from an entry widget to a procedure. Initially it seemed to be a problem with scoping, but it turns out his real problem was in needing to create a form with a variable number of input fields. He was struggling with what to use as the target of the -textvariable option and how to reference that in a proc.

Dynamically creating widgets based on a runtime specification — for example, a list of field names from a database — is something Tk does extremely well. I’ve been doing it so long that the fact that the solution may be non-obvious never occurs to me. The truth is, though, if you’re just starting out with Tcl and Tk the solution probably isn’t obvious.

However, just because it may not be obvious doesn’t mean that it’s not very easy to do. Although one can create variables on the fly with ease, the trick is to instead use a global or namespace variable.

The person asking the question in comp.lang.tcl had code that looked roughly like the following code. Where he got stuck was in figuring out what to do for ???, and how to ultimately pass the data entered by the user into the validate procedure.

foreach field $listOfFields {
  frame .frame$field
  label .frame$field.label ...
  entry .frame$field.entry ... -textvariable ???
  bind .frame$field.entry <Return> {validate ???}
  ...
}
proc validate {string} {...}

Using the same value (e.g. -textvariable field) for every iteration of the loop can’t work because all entry widgets would end up sharing a single variable. If you use something like $field in the above code you get unique variables for each entry widget (whatever string is stored in the field variable) but then you still have the problem of what to send to the validate command.

There is a simple solution, which is to use an array to contain the data for all the fields. You can index the array with the field name, and pass the field name around to other procs. In the specific example coded above, because the validate proc couldn’t be rewritten we need to introduce a small helper proc.

The following code shows a complete working example.

set listOfFields [list "Field 1" "Field 2" "Field 3"]
foreach field $listOfFields {
  set f ".frame-$field"
  frame $f
  label $f.label -text "$field:"
  entry $f.entry -textvariable data($field)
  pack $f.label -side left
  pack $f.entry -side left -fill x -expand 1
  bind $f.entry <Return> [list validate_field $field]
  pack $f -side top -fill x
}
proc validate_field {field} {
  variable data
  validate $data($field)
}
proc validate {string} {puts "validate called with string '$string'"}

As you can see, by using arrays we avoid all the unpleasantness of dynamic variable names and the odd quoting that sometimes requires.

Built-in data validation

The entry widget has options for doing input validation which obviates the need to bind on <Return>. This article is more about the technique of using arrays rather than dynamic variable names so I stuck to the coding style of the original example. If you are doing forms that need to do input validation I encourage you to check out the entry widget options “-validate”, “-validatecommand” and “-invalidcommand”.

separator

Writing Tk Code in the Right Order

A few days ago I was working with some code someone else had written, trying to remove a few of the most annoying bugs. One thing that I found most frustrating was that if I made the window smaller, the scrollbar disappeared. The following code illustrates the problem. Copy the code into a file then execute it with just about any version of wish:

frame .f -borderwidth 1 -relief sunken
text .t -wrap word -yscrollcommand [list .vsb set] \
    -borderwidth 0 -background white
scrollbar .vsb -command [list .t yview] \
    -borderwidth 1

pack .t -in .f -side left -fill both -expand 1
pack .vsb -in .f -side right -fill y -expand 0
pack .f -side top -fill both -expand 1

Make the window smaller and you should see the scrollbar disappear. When packing the text widget we told pack to let the text widget expand and contract to take up the available space, yet before it expands or contracts the scrollbar disappears. Why is that?

This is a case where the order of the code is important. Simply switching the order in which the text widget and scrollbar are packed makes the problem vanish:

frame .f -borderwidth 1 -relief sunken
text .t -wrap word -yscrollcommand [list .vsb set] \
    -borderwidth 0 -background white
scrollbar .vsb -command [list .t yview] \
    -borderwidth 1

pack .vsb -in .f -side right -fill y -expand 0
pack .t -in .f -side left -fill both -expand 1
pack .f -side top -fill both -expand 1

The reason for this is hidden in the pack man page under the section labeled “The Pack Algorithm”. First, it says:

“The packer arranges the slaves for a master by scanning the packing list in order…”

The key phrase is “in order”. The packing list is simply the list of widgets that have been packed, in the order that they were packed. The first clue to this behavior, then, is that the order in which they are packed is significant in some way.

The significance of the order is described later in the same section:

“Once a given slave has been packed, the area of its parcel is subtracted from the cavity, leaving a smaller rectangular cavity for the next slave…If the cavity should become too small to meet the needs of a slave then the slave will be given whatever space is left in the cavity. If the cavity shrinks to zero size, then all remaining slaves on the packing list will be unmapped from the screen”

Thus, when we shrink the window the packer first tries to allocate space for the text widget. The text widget has a preferred size (even though we didn’t explicitly give it one) and the packer tries to honor that. If we shrink the window down small enough the text widget will take up all the available space. Because the remaining cavity is too small for the next widget in the packing list — the scrollbar — no space will be allocated for it and the scrollbar vanishes from view.

Creation order can be important too

While we’re on the subject of ordering code, it’s important to notice the effect of ordering when it comes to creating widgets, too.

I typically try to keep the pathnames to widgets as short as possible, expecially for “significant” widgets – widgets I’m likely to reference in other parts of the code. I don’t want to have to reference something like “.mainframe.subframe.innerblock.maintext” when I could use “.text” instead. Tk supports this style of coding because I can pack or grid a widget in a widget other than its immediate parent. The sequence looks something like this:

frame .f ...
text .t ...
pack .t -in .f ...

You can see this style in practice in the code posted earlier in this article. If you choose to use this style, you must again be aware of the order in which things are done. In addition to a packing order (or a grid order…) widgets have the notion of a “stacking order”. By default this is the order in which a widget is created. If you create widget “.foo” and then widget “.bar”, “.bar” will be above .foo in the stacking orde and will be above .foo visually.

The practical ramifications are important. Here’s the earlier example, rewritten so that the text widget is created before the frame in which it is packed:

text .t -wrap word -yscrollcommand [list .vsb set] \
    -borderwidth 0 -background white
scrollbar .vsb -command [list .t yview] \
    -borderwidth 1
frame .f -borderwidth 1 -relief sunken

pack .vsb -in .f -side right -fill y -expand 0
pack .t -in .f -side left -fill both -expand 1
pack .f -side top -fill both -expand 1

When you run the above code you shouldn’t be able to see the text widget. That is because it is behind the frame since the frame was created after the text widget and thus is higher in the stacking order.

There are two ways to solve this problem. One is to simply create the widgets in order from lowest to highest as I did in the first versions of the example. By doing so, issues of stacking order become non-issues.

The second solution is to use the commands “raise” and “lower” which changes the relative stacking order between widgets. We can add a “raise” command at the end of the code to raise the text widget above the containing frame. We need to do the same thing for the scrollbar since it, too, was created before the containing frame:

text .t -wrap word -yscrollcommand [list .vsb set] \
    -borderwidth 0 -background white
scrollbar .vsb -command [list .t yview] \
    -borderwidth 1
frame .f -borderwidth 1 -relief sunken

pack .vsb -in .f -side right -fill y -expand 0
pack .t -in .f -side left -fill both -expand 1
pack .f -side top -fill both -expand 1

raise .t .f
raise .vsb .f

Final Thoughs

You could go years and never run into the sort of problems described in this article. Tk was designed to work best when you use the most obvious coding style. It’s a testament to Tk’s designer that it works so well out of the box, without you having to know about packing order, stacking order, the packing algorithm, etc. However, if you find that Tk doesn’t quite work the way you expect there is almost always a documented reason, and a way to tweak Tk to get the behavior you desire.

separator

Getting started with tcltest

When you install Tcl from the offical sources, included with the distribution is a test harness named tcltest. Tcltest makes it remarkably easy to write automated regression tests. This article briefly describes how to get started with tcltest, and includes links to resources for more information. For additional help, see the tcltest page on the Tcler’s wiki.

Why tcltest?

If you don’t test your software rigorsly, how do you know that it works? It’s easy to write code that solves an immediate problem, but in doing so we often write the code with blinders on. “We’ll always call this proc with three arguments”. “This value will always be an integer”. “I’ll rewrite this when I have more time”. And so on. Then, a new programmer is added to the team who doesn’t know about these assumptions; she calls a proc with a floating point number rather than an integer and everything breaks. It happens. A lot.

One solution to this problem is to think about testing up front. Many people believe that tests should be written before a single line of executable code is written. To be sure, that is an excellent way to develop reusable library components. I won’t go so far as to say it is the way, but it is certainly a good way. Even if you don’t subscribe to the test driven development model, tests can be an extremely important tool for developing quality code.

No matter when you write the tests, though, it can be a full time job. In fact, many people believe it must be a full time job; that is, programmers shouldn’t be writing their own tests, that’s the job for QA. The reality is, though, programmers should test their own code. In an ideal word a QA tester will come behind and give us better coverage but we are our own first line of defense. Obviously, every bug we catch early on is a bug our customers will never see.

As Tcl programmers we are lucky to have an extremely powerful testing harness that comes free with the product. It makes writing tests so simple there is almost no excuse not to write tests every time we create a new proc.

Do you have tcltest?

Many distributions of Tcl and Tk come with tcltest included. However, it is not guaranteed that you will have it available. You can determine whether you have it available or not by doing the following simple test in an interactive tcl shell:

$ tclsh
% package require tcltest
2.2.8

If instead of a version number like “2.2.8” you see “can’t find package tcltest”, that means you don’t have tcltest installed where the interpreter can find it. If you wish to follow along with the examples in this article you need to first download and install tcltest. For help, see my article titled Installing tcltest.

A simple example

Tcltest is remarkably simple to use. As a first example, consider the following code fragment which defines a test for the built-in command ‘string toupper’:

package require tcltest 2
namespace import tcltest::*
test example-1.1 {an example test} -body {
  string toupper "hello world"
} -result "HELLO WORLD"
cleanupTests

This code defines a single test (created by the test command) with the following attributes:

  • The test is named example-1.1
  • The test has a description of “an example test”
  • the test code is {string toupper “hello world”}
  • the expected result is the string “HELLO WORLD”

If you copy that code into a file and run it with a tcl interpreter, you should see output similar to the following (assuming you have tcltest 2.x properly installed):

$ tclsh test.tcl
test.tcl:       Total   1       Passed  1       Skipped 0       Failed  0

As you can see, it is extremely easy to create a simple test which executes an arbitrary block of code and then compares it to an expected result. If the result was not what we expect an error will be printed. For example, if you change the expected output from “HELLO WORLD” to “Hello World” you’ll get the following output:

$ tclsh test.tcl
==== example-1.1 an example test FAILED
==== Contents of test case:

  string toupper "hello world"

---- Result was:
HELLO WORLD
---- Result should have been (exact matching):
Hello World
==== example-1.1 FAILED

test.tcl:       Total   1       Passed  0       Skipped 0       Failed  1

In this case we not only got a summary that told us one test failed, we got a very useful block of output that describes what failed and why.

A more complex example

As the first example shows, it is quite simple to create basic tests. Pick a name, write a block of code, and define the expected outcome. However, tcltest is considerably more powerful than that first example shows. Here is a somewhat more complex example:

test example-1.2 {a more complex example} -constraints {
  unix
} -setup {
  set f [open /tmp/testdata w]
  puts $f "hello, world"
  close $f
} -body {
  set f [open /tmp/testdata r]
  set data [read $f 5]
  close $f
  set data
} -cleanup {
  file delete -force /tmp/testdata
} -result "hello"
cleanupTests

The above test has the following attributes:

  • The test name is “example-1.2”
  • The test will only run on unix systems; it will be skipped on mac an windows platforms
  • Before the test is run it will execute a block of code to create a test data file
  • The test itself will read the test data file and return what it read
  • The test will delete the test data file when it is finished
  • The test will pass if the data “hello” was read from the file

As you can see, it is possible to have additional code execute both before and after the actual test in order to set up the context for the test. It is also possible to have tests that are unique to certain platforms. With version 2.2.8 of tcltest there are nearly 30 different constraints covering a large range of variances between platforms and environments.

At this point it’s worth noting that the above test is a bit contrived. Tcltest includes convenience procedures for creating test data files so it’s not necessary to hard-code references to /tmp, for example. For a complete list of commands included in tcltest, consult the man page.

In total, a test may be made up of nine sections, not counting the name and description:

  • -constraints

    a list of constraints that must be met for the test to run

  • -setup

    A block of code to be executed before the test is run

  • -body

    The actual test script to run

  • -match

    The method of comparing the actual to expected results. The examples in this article have used exact matching but it is possible to also do regexp and glob matching for greater flexibility.

  • -result

    The expected result of the test script

  • -output

    Data that is expected to be written to stdout when the test runs.

  • -errorOutput

    Similar to -output, this defines that that is expected to be written to stderr when the test runs.

  • -returnCodes

    The expected return codes of the test script

Style matters

The format of the test command is familiar to anybody who has ever used tk. Most options are preceeded by an option name such as -setup, -body, etc. This is still Tcl code, though, so every command needs to span a single logical line. And, because this is just pure Tcl code, curly braces are not required.

Tests are rather verbose since they almost always will include several lines of executable code in addition to all the other metadata. You might be tempted to just continue options on a line until you run out of room, add a backslash, and continue on to the next line. This is certainly possible since Tcl imposes no formatting restrictions on us. However, over time a common style has been adopted by most users of tcltest that removes almost all need for backslashes.

If you’ll notice in the examples above, each option begins on a line, followed by a curly brace, then the data for that option is on the following line. The closing curly brace begins the next line, and immediately following is another option. In this way your test scripts will alternate between lines of option names and option values, making the code easy to read and easy to maintain.

Again, this style isn’t required, strictly speaking. Neither is the use of curly braces. However, experience has shown that this particular style makes for test files that are easy to read and easy to maintain. Do yourself a favor and stick to this style.

Building test suites

Typically, a single test script will contain many tests. Exactly how you organize the tests is up you. It often makes sense to have a single test file for each functional group you are testing. Within each file you use a test naming convention that emphasizes the logical groupings. For example, you may have a test file named “io.test” in which you name the tests “io-1”, “io-2”, etc. You might have another test file named “translations.test” that have tests named “translation-1”, “translation-2”, etc.

The tcltest man page has an entire section describing how to create test suites so I will point you there rather than repeat roughly the same information. Suffice it to say that the developers of tcltest have written it with the intention of creating large hierarchies of tests.

Summary

  • This article only scratches the surface of what tcltest can do. It is a very mature and robust testing harness that can be used in a variety of situations. Obviously it can be used to test libraries of tcl code, but because of the power of Tcl as a glue language you can use tcltest as a harness for testing external processes as well. You can also use it to test GUI applications written in Tk since Tk has such rich introspection capabilities.

  • If you don’t yet use tcltest or any other testing harness in your development environment, you owe it to yourself and your customers to spend an afternoon learning how to use tcltest. You can literally master it in a single afternoon and it can give you an edge in creating robust, maintainable code.

separator

Web Services and the Google API

On August 8, 2006, Gerald Lester announced on comp.lang.tcl his web services library. This library, Web Services for Tcl, provides commands to both easily create web services and to access web services from a client.

This article gives a brief introduction to the client side of Gerald’s library by presenting an example that accesses the Google SOAP Search API with just a couple dozen lines of code.

Installing necessary software

Before you can run the examples in this article you will need to download and install some software and obtain a license key for the Google web service.

Unfortunatey, the Tcl community does not yet have a definitive way to download and install packages so you’ll have to perform a couple of manual steps. It is easy though; the matter boils down to downloading the software and copying it to a directory where your particular installation of Tcl will find it.

  • Downloading the Web Services library and dependencies

    To access any web service using the techniques presented in this article you will need Gerald Lester’s library. You can download it here:

    http://members.cox.net/~gerald.lester/WebServicesForTcl.html

    Once downloaded, copy the folder to a location where tcl can find it. Start up a tcl shell and print the value of the variable ::auto_path. Put the WebServices folder in one of the directories that is printed out.

    You must also make sure you have all of the other dependencies installed on your system. These dependencies are listed on the same page where you find the web services library. If you are running a recent version of ActiveTcl you’ve likely already got all the dependencies so you’ll only need to download and install the web services library.

    One final note: the web services library and the example in this article make use of a new feature in tcl 8.5 named dict. Even though dict is a new feature in 8.5, a backport for 8.4 is available as a loadable extension here [] if you don’t yet have an early release of tcl 8.5 installed on your machine.

  • Obtaining a Google API License

    To gain access to the Google search API you must register with Google and get a license key. If you already have a gmail account it’s just a matter of asking for the key. If you don’t have an account you’ll have to set one up, a process which takes just a couple of minutes. Start the process by going to http://code.google.com/apis/soapsearch/ . Note that you do not have to download their developers kit, you simply need a license key to perform the search.

First steps

We’re going to create a script named “googleapi.tcl”. Since we are needing to use the web services library we must add a statement to load the library at startup. This first version of the script will look like this:

package require WS::Client
package require dict

proc main {} {
    puts "The library is properly installed"
}

main

(The main() procedure isn’t strictly necessary but I find it makes the code easier to write and maintain. For more on this, read the article Your Second Tcl Program. )

If you save the above code in a file named googleapi.tcl and run it with the command tclsh googleapi.tcl you should see the output “The library is properly installed”.

Fetching and Parsing the Web Service WSDL

Now that we know we can access the web services library, the next step is to make sure we can access and parse the WSDL. The WSDL for the Google SOAP search API is at http://api.google.com/GoogleSearch.wsdl. If you click the link you’ll get back an XML document that describes the service in detail.

Before we can call a service at this address we must first fetch and parse this document with ::WS::GetAndParseWSDL. Modify the main() procedure to look like this:

proc main {} {
    ::WS::Client::GetAndParseWsdl "http://api.google.com/GoogleSearch.wsdl"
    puts "WSDL successfully fetched and parsed"
}

If you again run your script, this time you should see “WSDL successfully fetched and parsed”.

This step is necessary as it initializes the web services library to know about the operations and arguments supported by the given web service. This step is not unique to the google API — no matter what web service you want to access, it all begins with fetching and parsing the WSDL.

Making the Call

The web services library can be used to make both synchronous and asynchronous calls to the web service. The simplest case is to make a synchronous call that returns a dict. We do this by calling the function WS::Client::DoCall.

Assuming the call succeeds, we will get back a dict object that contains the data returned by the web service. A dict can also be treated as a set of nested lists, so you aren’t yet up to speed on dicts you can use ordinary tcl list commands to pull the data apart.

In the case of the Google API, we must pass in a number of arguments as defined by the WSDL (and on the human readable description at []). The argument list itself is a dict, but can also be thought of as a list of keys and values.

To print out the raw data, our main() procedure now looks like this:

proc main {} {

    ::WS::Client::GetAndParseWsdl "http://api.google.com/GoogleSearch.wsdl"

    dict set args key "<your google license key here>"
    dict set args q {site:tclscripting.com font}
    dict set args start 0 
    dict set args maxResults 10
    dict set args filter true 
    dict set args restrict {} 
    dict set args safeSearch false
    dict set args lr {} 
    dict set args ie latin1 
    dict set args oe latin1

    set result [::WS::Client::DoCall GoogleSearchService doGoogleSearch $args]
    puts $result
}

You should see output that looks something like this:

return {searchComments {} resultElements {item {{relatedInformationPresent true summary {} title {Introduction to named <b>fonts</b>} URL http://www.tclscripting.com/articles/jun06/article1.html snippet {This article briefly describes how and why you should use named <b>fonts</b>. <b>...</b> <br> The advantage to using named <b>fonts</b> is that when the <b>font</b> is changed, <b>...</b>} cachedSize 15k hostName {} directoryCategory {specialEncoding {} fullViewableName {}} directoryTitle {}} {relatedInformationPresent true summary {} title {Your second Tcl/Tk application} URL http://www.tclscripting.com/articles/sep06/article1.html snippet {package man page, http://www.tcl.tk/man/tcl8.4/TkCmd/<b>font</b>.htm; sqlite <br> http://www.sqlite.org; tcllib http://tcllib.sourceforge.net <b>...</b>} cachedSize 14k hostName {} directoryCategory {specialEncoding {} fullViewableName {}} directoryTitle {}}}} searchQuery {site:tclscripting.com font} estimateIsExact true searchTime 0.014507 startIndex 1 searchTips {} documentFiltering false estimatedTotalResultsCount 2 endIndex 2 directoryCategories {}}

Making sense of the results

Unlike dealing with block of raw XML, dealing with the output returned by ::WS::Client::DoCall is quite simple. The result is a dict, which is to say it is a nested list of keys and values. dicts are a new feature of Tcl 8.5, with a backport available as a loadable extension for tcl 8.4. While the data can be manipulated with traditional list commands, using dict commands makes parsing the output quite simple.

As a simple example, the following code prints out the title and URL of each page returned by the query:

foreach item [dict get $result return resultElements item] {
    puts [dict get $item title]
    puts [dict get $item URL]
    puts ""
}

Putting it all together

As you can see, using the Web Services for Tcl library along with the new dict feature of tcl 8.5, accessing web services is remarkably simple. In our example we are able to formulate a request, make the request, and parse the results, all with just a couple dozen lines of code.

Of course, this just scratches the surface of what we can do. The web services library provides even more features such as the ability to make asynchronous calls, saving and loading pre-parsed WSDL documents, and being able to send and receive additional headers in the SOAP envelope.

The complete script looks like the following:

package require WS::Client
package require dict

proc main {} {
    ::WS::Client::GetAndParseWsdl http://api.google.com/GoogleSearch.wsdl

    dict set args key "<your google license key here>"
    dict set args q {site:tclscripting.com font}
    dict set args start 0 
    dict set args maxResults 10
    dict set args filter true 
    dict set args restrict {} 
    dict set args safeSearch false
    dict set args lr {} 
    dict set args ie latin1 
    dict set args oe latin1

    set result [::WS::Client::DoCall GoogleSearchService doGoogleSearch $args]

    foreach item [dict get $result return resultElements item] {
        puts [dict get $item title]
        puts [dict get $item URL]
        puts ""
    }
}

main

I’m not sure it can be any easier to access a web service no matter what language you use. Gerald Lester has given the Tcl community a real gem.

References

  1. Web Services for Tcl

    http://members.cox.net/~gerald.lester/WebServicesForTcl.html

  2. Google SOAP Search API documentation

    http://code.google.com/apis/soapsearch/reference.html

  3. ActiveTcl

    http://www.activestate.com/Products/ActiveTcl/?tn=1

  4. Dict documentation

    http://www.tcl.tk/man/tcl8.5/TclCmd/dict.htm

  5. Dict extension for tcl/tk 8.4.x,

    http://pascal.scheffers.net/news/item.tcl?scope=public&news_item_id=100091

  6. Google API home page

    http://code.google.com/apis/soapsearch/

  7. W3C.org documentation on WSDL

    http://www.w3.org/TR/wsdl

separator

Acknowledgement

Thanks to Brian Oakley who originally posted various article on the Tcl Scripting server a while ago. Unfortunalely the server is not availabe any more. I still find these informations quite usefull, so I’d like to publish it here.

References:


comments powered by Disqus