Using the Tcl after command in Itcl

When building an object-oriented widget hierarchy in Itcl, you will occasionally come across the following problems:

  1. Code in a base class constructor cannot properly execute until the derived class constructors have been executed. For example, the Tycho File class, as part of its file management support, calls the reload method in its constructor. However, the action taken on a file reload depends on options set in the constructors of derived classes.
  2. Code that is executed in a constructor does not work properly because it depends on the size of a widget, and a widget's size is not known until it is mapped to the screen. For example, the Tycho StatusBar class needs to know the size of the message pane in order to prevent wraparound of a message.

The common approach to these problems is to use the Tcl after command, generally with the idle argument. For example, the File constructor used to contain the line

    after idle [code evalIfExists $this reload]

The problem with this approach is that, with a few levels in the hierarchy, you start to build a spaghetti of after commands and widgets start to become fragile. We therefore added some more structured support to Tycho for these situations, with the methods whenConstructed and whenMapped. These methods are available to all descendents of the ::tycho::TWidget and ::tycho::TopLevel classes.

whenConstructed

The TWidget and TopLevel classes contain the protected method whenConstructed. This method takes a single argument, which is a command to be executed just before the call to itk_initialize returns in the most specific constructor of the widget. More than one command can be passed to this method. The accumulated commands are executed in the same scope in which they were created.

For example, the constructor of File now contains the line

    whenConstructed "$this reload"
This ensure that the widget reload takes place within the last constructor, but before the Tk event loop is reentered. If this method is called and the widget has been constructed already, then the passed command is evaluated immediately.

whenMapped

Some code depends on a widget being mapped to the screen before it can work correctly. The whenMapped method supports this requirement in a more robust way than after idle. The method takes two arguments: the name of the component (of the megawidget from which the method is being called) to wait for, and the command to execute. When the given component is mapped to the screen, any pending commands for that component are evaluated. As for whenConstructed, evaluation takes place in the context from which whenMapped was originally called.

For example, the StatusBar would wrap a status message incorrectly if it was written (with the putMessage method) before the containing window was fully mapped to the screen. The following code sequence solved the problem:

    if ![winfo ismapped [component message]] {
        whenMapped message [list $this putMessage $string]
    } else {
        ...
    }
This code defers the message display until the message component is mapped to the screen, ensuring that the message wrap-around works correctly.

Deferred redrawing

Widgets that have an expensive redraw should use whenMapped to defer their redrawing until the containing window has been fully mapped to the screen. For example, the HTML and EditDAG widgets do this. The way to do this is to modify the insertData method of the widget so that the call to the redrawing routine is deferred with whenMapped.

For example, HTML.itcl contains the code:

    whenMapped text "::tycho::safeUpdate $this"
    whenMapped text [list ::tycho::HTML::render $this $data]

In general, the call to safeUpdate is not needed, but (for reasons I don't fully understand) it works better for this widget.

The EditDAG widget has a similar call -- in this case, the call to safeUpdate is not needed:

    whenMapped slate "$this redrawGraph"

Tycho Home Page


Copyright © 1996-1998, The Regents of the University of California. All rights reserved.
Last updated: 06/08/98, comments to: johnr@eecs.berkeley.edu