bind
command
this
info which command
code
command
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 unfortunate 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]"
}
}
::foo
, but in the after
command, it is reported
as an empty string, meaning the global context.
Button commands work similarly:
namespace foo {
catch {destroy .x}
toplevel .x
button .x.b -text pushme -command {
::tycho::inform "button context: [info context]"
destroy .x
}
pack .x.b
}
bind
commandBindings 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:
%q
%Q
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
}
bar
inside namespace foo
derived from
an Itk top-level window class. In the constructor, we pack
into the window a canvas widget.
We bind the key a
to simply report the expansion for
%q
, which will be ::foo::.y
.
The key b
uses this expansion to delete the object.
Notice that the window specified for the bindings is
itk_component(hull)
, which will be the top-level window.
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]
y
is visible to Tk in any scope because Tk
has only a flat name space. The Itk object .y
, however, is
not visible in the global scope.
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.
this
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
}
this
is reported as ::foo::y
, which is the
fully qualified command name associated with the object y
.
The context inside the constructor is ::foo::bar
, a namespace
associated with the class.
The command $this x
or ::foo::y x
can be executed
at the global
scope, so the after
directive works.
Notice that it would not have worked to say:
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.
info which command
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]
}
x
, the second informs
us of its fully qualified name :foo::x
and the third executes the fully
qualified name as a command at the global scope.
The following, by contrast, yields an error:
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
}
z
here is a procedure, not a method.
A procedure does not need an instance of an object to be invoked.
Indeed, the command that we schedule using after
is ::foo::bar::z
,
which does not make any reference to any particular instance of the class bar
.
Thus, info which
should not be used to access methods, but can be used
to access procedures.
code
command
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]
}
[code x]
directive returns the list
@scope ::foo x
, which can be executed as a command at the global
scope. It can also be used in class definitions.
For example:
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
}
after
is
@scope ::foo::bar z
.