When building an object-oriented widget hierarchy in Itcl, you will occasionally come across the following problems:
reload
method in its constructor. However, the action
taken on a file reload depends on options set in the constructors of
derived classes.
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.
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.
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.
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"