One of the trickiest parts about using Itcl is to properly manage namespaces and scope. A key problem that arises repeatedly is how to construct a command that is bound to an event (like a key press), bound to a button, executed in the background using "after," or executed in some scope different from where the command is defined. In general, the problem is that a command has a namespace context. There are two ways in Itcl that this namespace context can be represented:
::namespace::commandname {@scope ::namespace commandname}Either of these can be executed as a command. (One of the unfortunately features of Itcl is that there are several ways to accomplish just about everything related to namespaces and scope, and the subtle differences between them are not always clear.)
Within an Itcl object, the variable "this" has the first of these two forms, as a fully qualified command name. The "info which" command can be used to obtain the first form for any command ("this" only works for an object). The "code" or the "scope" commands can be used to obtain the second form.
In Itcl, all commands that are executed as a consequence of a keyboard
or window event are executed at the global scope (in namespace "::").
Also, commands executed in the background using "after" are executed
at the global scope. To verify this, we can use the
"info context" command,
which returns a null string at the global scope.
For example:
namespace foo {
::tycho::inform "Inside context: [info context]"
after idle {
::tycho::inform "after context: [info context]"
}
}
namespace foo {
catch {destroy .x}
toplevel .x
button .x.b -text pushme -command {
::tycho::inform "button context: [info context]"
destroy .x
}
pack .x.b
}
Bindings to key or mouse events, like the button presses above, execute at the global scope. Itcl has provided two particularly useful additions to the special symbols that the bind command understands:
These can be used as in the following example:
namespace foo {
catch {delete class bar}
class bar {
inherit ::itk::Toplevel
constructor {args} {
itk_component add f {
canvas $itk_interior.f
} {}
pack $itk_component(f)
bind $itk_component(hull) <a> \
{::tycho::inform "expansion of %%q: %q"}
bind $itk_component(hull) <b> \
{delete object %q}
}
}
bar .y
}
The "%Q" directive is similar, except that the full expanded name of an Itk megawidget will be reported.
The "winfo command windowname"
command returns the fully-qualified command name of
an object given a Tk top-level window name.
For example, if the canvas window from the above example is still
present, try:
::tycho::inform [winfo command .y]
The "winfo megawidget windowname" command is similar, except that it returns the name of an Itk megawidget rather than a top-level window object. The specified window name can be any window inside the megawidget.
The variable "this" inside a class context has as its value
the fully qualified command name associated with the object
(yes, it could have been used instead of %q in the above example).
Consider the following:
namespace foo {
catch {delete class bar}
class bar {
constructor {args} {
::tycho::inform "Constructing object: $this"
::tycho::inform "Current context: [info context]"
after idle "$this x"
}
method x {} {::tycho::inform {works}}
}
bar y
}
after idle {$this x}because the "$this" will not be evaluated until the "after" command triggers, and this will happen at the global scope where no variable "this" is defined.
If in a binding or delayed invocation using "after" you wish to access methods in a widget contained in the calling widget, use syntax like that in the following example:
after idle "$this component name method arg1 arg2 ... "Here, "name" is the name of the Itk component, "method" is the name of its method, and the argument list is the arguments to pass to the method.
The "info which" command
takes one argument, which is the name of a command in the
current scope, and returns a fully qualified command with namespace
information. Thus, for example,
namespace foo {
proc x {} {::tycho::inform {works}}
::tycho::inform [info which x]
after idle [info which x]
}
namespace foo {
proc x {} {::tycho::inform {works}}
after idle "x"
}
namespace foo {
catch {delete class bar}
class bar {
constructor {args} {
::tycho::inform "Scheduling command: [info which z]"
after idle [info which z]
}
proc z {} {::tycho::inform {works}}
}
bar y
}
The "code" command returns a scoped reference in the second form
described above, beginning with "@scope".
For example:
namespace foo {
proc x {} {::tycho::inform {works}}
::tycho::inform [code x]
after idle [code x]
}
namespace foo {
catch {delete class bar}
class bar {
constructor {args} {
::tycho::inform "Scheduling command: [code z]"
after idle [code z]
}
proc z {} {::tycho::inform {works}}
}
bar y
}