snit(n) Snit snit(n)
NAME
snit - Snit's Not Incr Tcl
SYNOPSIS
package require Tcl 8.4
package require snit ??00.93??
$$object method args...
$$object configure ?option? ?value? ...
$$object configurelist optionlist
$$object cget option
$$object destroy
$$object info type
$$object info vars ?pattern?
$$object info typevars ?pattern?
$$object info options ?pattern?
varname name
typevarname name
codename name
from argvName option ?defvalue?
variable name
typevariable name
install compName using objType objName args...
installhull using widgetType args...
installhull name
mymethod name ?args...?
snit::::type name definition
typevariable name ?value?
typemethod name arglist body
option namespec ?defaultValue?
variable name ?value?
method name arglist body
constructor arglist body
destructor body
onconfigure name arglist body
oncget name body
proc name args body
delegate method name to comp
delegate method name to comp as target
delegate method ** to comp
delegate method ** to comp except exceptions
delegate option namespec to comp
delegate option namespec to comp as target
delegate option ** to comp
delegate option ** to comp except exceptions
expose comp
expose comp as method
snit::::widget name definition
widgetclass name
hulltype type
snit::::widgetadaptor name definition
snit::::typemethod type name arglist body
snit::::method type name arglist body
$$type typemethod args...
$$type create name ?option value ...?
$$type info typevars ?pattern?
$$type info instances ?pattern?
$$type destroy
DESCRIPTION
Snit is yet another pure Tcl object and megawidget system. It's unique
among Tcl object systems (so far as I know) in that it's a system based
not on inheritance but on delegation. Object systems based on inheri-
tance only allow you to inherit from classes defined using the same
system, and that's a shame. In Tcl, an object is anything that acts
like an object; it shouldn't matter how the object was implemented. I
designed Snit to help me build applications out of the materials at
hand; thus, Snit is designed to be able to incorporate and build on any
object, whether it's a hand-coded object, a Tk widget, an Incr Tcl
object, a BWidget or almost anything else.
This man page is intended to be a reference only; see the accompanying
snitfaq for a gentler, more tutorial introduction to Snit concepts.
REFERENCE
The Instance Command
A Snit type or widget's create type method creates objects of the type;
each object has a unique name which is also a Tcl command. This com-
mand is used to access the object's methods and data, and has this
form:
$$object method args...
The method can be any of the standard instance methods defined
in the next section, or any instance method defined in the type
definition. The subsequent args depend on the specific method
chosen.
Standard Instance Methods
In addition to any delegated or locally-defined instance methods in the
type's definition, all Snit objects will have at least the following
methods:
$$object configure ?option? ?value? ...
Assigns new values to one or more options. If called with one
argument, an option name, returns a list describing the option,
as Tk widgets do; if called with no arguments, returns a list of
lists describing all options, as Tk widgets do.
Warning: This information will be available for delegated
options only if the component to which they are delegated has a
configure method that returns this same kind of information.
$$object configurelist optionlist
Like configure, but takes one argument, a list of options and
their values. It's mostly useful in the type constructor, but
can be used anywhere.
$$object cget option
Returns the option's value.
$$object destroy
Destroys the object, calling the destructor and freeing all
related memory.
Note: The destroy method isn't defined for snit::::widget or
snit::::widgetadaptor objects; instances of these are destroyed by
calling the Tk destroy command, just as a normal widget is.
$$object info type
Returns the instance's type.
$$object info vars ?pattern?
Returns a list of the object's instance variables (excluding
Snit internal variables). The names are fully qualified.
If pattern is given, it's used as a string match pattern; only
names which match the pattern are returned.
$$object info typevars ?pattern?
Returns a list of the object's type's type variables (excluding
Snit internal variables). The names are fully qualified.
If pattern is given, it's used as a string match pattern; only
names which match the pattern are returned.
$$object info options ?pattern?
Returns a list of the object's option names. This always
includes local options and explicitly delegated options. If
unknown options are delegated as well, and if the component to
which they are delegated responds to $$object configure like Tk
widgets do, then the result will include all possible unknown
options which could be delegated to the component.
If pattern is given, it's used as a string match pattern; only
names which match the pattern are returned.
Note that the return value might be different for different
instances of the same type, if component object types can vary
from one instance to another.
Commands for use in Object Code
Snit defines the following commands for use in your object code: that
is, for use in type methods, instance methods, constructors, destruc-
tors, onconfigure handlers, oncget handlers, and procs. They do not
reside in the ::snit:: namespace; instead, they are created with the
type, and can be used without qualification.
varname name
Given an instance variable name, returns the fully qualified
name. Use this if you're passing the variable to some other
object, e.g., as a -textvariable to a Tk label widget.
typevarname name
Given an type variable name, returns the fully qualified name.
Use this if you're passing the variable to some other object,
e.g., as a -textvariable to a Tk label widget.
codename name
Given the name of a proc (but not a type or instance method),
returns the fully-qualified command name, suitable for passing
as a callback.
from argvName option ?defvalue?
The from command plucks an option value from a list of options
and their values, such as is passed into a type's constructor.
argvName must be the name of a variable containing such a list;
option is the name of the specific option.
from looks for option in the option list. If it is found, it
and its value are removed from the list, and the value is
returned. If option doesn't appear in the list, then the def-
value is returned. If the option is a normal (undelegated)
option, and defvalue is not specified, then the option's default
value as specified in the type definition will be returned
instead.
variable name
Normally, instance variables are defined in the type definition
along with the options, methods, and so forth; such instance
variables are automatically visible in all instance-specific
code. However, instance code (e.g., method bodies) can declare
such variables explicitly using the variable command, if
desired; or, instance code can use the variable command to
declare instance variables that don't appear in the type defini-
tion.
It's generally best to define all instance variables in the type
definition, and omit declaring them in methods and so forth.
Note that this is not the same as the standard Tcl ::::variable
command.
typevariable name
Normally, type variables are defined in the type definition,
along with the instance variables; such type variables are auto-
matically visible in all of the type's code. However, type
methods, instance methods and so forth can use typevariable to
declare type variables explicitly, if desired; or, they can use
typevariable to declare type variables that don't appear in the
type definition.
It's generally best to declare all type variables in the type
definition, and omit declaring them in methods, type methods,
and so forth.
install compName using objType objName args...
Creates a new object and installs it as a component, as
described under Components and Delegation. If this is a
snit::::type, then the following two commands are equivalent:
install myComp using myObjType $self.myComp options...
set myComp [myObjType $self.myComp options...]
Note that whichever method is used, compName must still be
declared in the type definition using variable, or must be ref-
erenced in at least one delegate statement. If this is a
snit::::widget or snit::::widgetadaptor, and if options have been
delegated to component compName, then those options will receive
default values from the Tk option database. Note that it
doesn't matter whether the component to be installed is a widget
or not. See The Tk Option Database for more information.
installhull using widgetType args...
installhull name
The constructor of a snit::::widgetadaptor must create a widget to
be the object's hull component; the widget is installed as the
hull component using this command. Note that the installed wid-
get's name must be $$win. This command has two forms. The first
form specifies the widgetType and the args... (that is, the
hardcoded option list) to use in creating the hull. Given this
form, installhull creates the hull widget, and initializes any
options delegated to the hull from the Tk option database. In
the second form, the hull widget has already been created; note
that its name must be "$win". In this case, the Tk option data-
base is not queried for any options delegated to the hull. See
The Tk Option Database for more information about snit::::wid-
getadaptors and the option database. The longer form is pre-
ferred; however, the shorter form allows the programmer to adapt
a widget created elsewhere, which is sometimes useful. For
example, it can be used to adapt a "page" widget created by a
BWidgets tabbed notebook or pages manager widget.
The command which creates the hull widget usually just passes
its result to installhull as follows:
installhull [frame $win options....]
mymethod name ?args...?
The mymethod command is used for formatting callback commands to
be passed to other objects. It returns a command that when
called will invoke method name with the specified arguments,
plus of course any arguments added by the caller. In other
words, both of the following commands will cause my object's
dosomething method to be called when $$button is pressed:
$button configure -command [list $self dosomething myargument]
$button configure -command [mymethod dosomething myargument]
The chief distinction between the two is that the latter form
will not break if the creator of my object renames its object
command.
Type and Widget Definitions
Snit provides the following commands for defining new types:
snit::::type name definition
Defines a new abstract data type called name. If name is not a
fully qualified command name, it is assumed to be a name in the
namespace in which the snit::::type command appears (usually the
global namespace). It returns the fully qualified type name.
The type name is then a command which is used to create objects
of the new type, along with other activities.
The snit::::type definition block is a script which may contain
the following definitions:
typevariable name ?value?
Defines a type variable with the specified name, and
optionally the specified value. Type variables are
shared by all instances of the type. This definition can
be used to define array variables, but cannot initialize
their elements.
typemethod name arglist body
Defines a type method with the specified name, argument
list, and body. The variable type is automatically
defined in the body to the type's fully-qualified name.
The arglist is a normal Tcl argument list and may contain
default arguments and the args argument; however, it may
not contain the argument names type, self, selfns, or
win.
Type variables defined in the type definition are auto-
matically visible in the body of every type method.
option namespec ?defaultValue?
Defines an option for instances of this type, and option-
ally gives it an initial value. (The option's value
defaults to the empty string if no initial value is spec-
ified.)
An option defined in this way is said to be locally
defined.
The namespec is a list defining the option's name,
resource name, and class name, e.g.:
option {-font font Font} {Courier 12}
The option name must begin with a hyphen, and must not
contain any upper case letters. The resource name and
class name are optional; if not specified, the resource
name defaults to the option name, minus the hyphen, and
the class name defaults to the resource name with the
first letter capitalized. Thus, the following statement
is equivalent to the previous example:
option -font {Courier 12}
See The Tk Option Database for more information about
resource and class names. Options are normally set and
retrieved using the standard configure and cget instance
methods.
variable name ?value?
Defines an instance variable, a private variable associ-
ated with each instance of this type, and optionally its
initial value. This definition can be used to define
array instance variables, but cannot initialize their
elements.
Note that the delegate statement implicitly defines an
instance variable for the named component.
method name arglist body
Defines an instance method, a subcommand of each instance
of this type, with the specified name, argument list and
body. The arglist is a standard Tcl argument list, and
may contain default values and the argument names. The
arglist is a normal Tcl argument list and may contain
default arguments and the args argument. In addition,
the method is implicitly passed the following arguments
as well: type, which contains the fully-qualified type
name; self, which contains the current instance command
name; selfns, which contains the name of the instance's
private namespace; and win, which contains the original
instance name. Consequently, the arglist may not contain
the argument names type, self, selfns, or win.
An instance method defined in this way is said to be
locally defined.
Type and instance variables defined in the type defini-
tion are automatically visible in all instance methods.
If the type has locally defined options, the options
array is also visible.
constructor arglist body
The constructor definition specifies a body of code to be
executed when a new instance is created.
The arglist is a normal Tcl argument list and may contain
default arguments and the args argument. As with meth-
ods, the arguments type, self, selfns, and win, are
defined implicitly.
If the constructor is not defined, it defaults to this:
constructor {args} {
$self configurelist $args
}
For standard Tk widget behavior (or to achieve the behav-
ior of previous versions of snit) the argument list
should be the single name args, as shown.
destructor body
The destructor is used to code any actions which must
take place when an instance of the type is destroyed:
typically, the destruction of anything created in the
constructor.
As with arguments, the parameters type, self, selfns, and
win, are defined implicitly.
onconfigure name arglist body
Every locally-defined option has an onconfigure handler
which is called when the option is set to a new value by
the configure or configurelist instance method.
The arglist may contain exactly one argument name. As
with methods, the arguments type, self, selfns, and win,
are defined implicitly.
If no explicit onconfigure handler is defined for an
option, the handler is defined as follows:
onconfigure name {value} {
set options(name) $value
}
If an explicit onconfigure handler is defined, the
options array will be updated with the new value only if
the handler so updates it.
oncget name body
Every locally-defined option has an oncget handler which
is called when the option's value is retrieved. Although
there is no explicit argument list, the arguments type,
self, selfns, and win, are defined implicitly, just as
they are for methods.
The variables type, self, selfns, and win are defined as
usual in the handler's body. Whatever the handler
returns will be the return value of the call to the cget
instance method.
If no explicit oncget handler is defined for an option,
the handler is defined as follows:
oncget name {
return $options(name)
}
proc name args body
Defines a new Tcl procedure in the type's namespace. The
new proc differs from a normal Tcl proc in that all type
variables defined in the type definition are automati-
cally visible.
Although they are not implicitly defined for procs, the
argument names type, self, selfns, and win should be
avoided.
delegate method name to comp
delegate method name to comp as target
delegate method ** to comp
delegate method ** to comp except exceptions
Delegates one or more instance methods to a component of
the object. When a method name is explicitly stated, it
will automatically be delegated to the named component as
though the method were defined as follows:
method name {args...} {
$comp mymethod args...
}
If desired, the delegated method may target a method with
a different name by using the as clause; the target may
also include arguments add to the beginning of the argu-
ment list.
The form "delegate method *" delegates all unknown method
names to the specified component. The except clause can
be used to specify a list of exceptions, i.e., method
names that will not be so delegated.
A method cannot be both locally defined and delegated.
delegate option namespec to comp
delegate option namespec to comp as target
delegate option ** to comp
delegate option ** to comp except exceptions
Defines a delegated option; the namespec is defined as
for the option statement. When the configure, config-
urelist, or cget instance method is used to set or
retrieve the option's value, the equivalent configure or
cget command will be applied to the component as though
these onconfigure and oncget handlers were defined, where
name is the option name from the namespec:
onconfigure name {value} {
$comp configure name $value
}
oncget name {
return [$comp cget name]
}
If the as clause is specified, then the target option
name is used in place of name.
The form "delegate option *" delegates all unknown method
names to the specified comp. The except clause can be
used to specify a list of exceptions, i.e., option names
that will not be so delegated.
Warning: options can only be delegated to a component if
it supports the configure and cget instance methods.
Note that an option cannot be both locally defined and
delegated.
expose comp
expose comp as method
Exposes component comp as a method of the type. In the
first form, the method name is comp; in the second form,
the method name is method.
This differs from delegation in that it maps an instance
method to the component itself instead of to one of the
component's methods.
Calling the new instance method is just like calling the
component, except that if the method is called with no
arguments it returns the component.
snit::::widget name definition
This command defines a Snit megawidget type with the specified
name. The definition is defined identically to that for
snit::::type. A snit::::widget differs from a snit::::type in these
ways:
]o Every snit::::widget instance has an automatically-created
component called hull, which is normally a Tk frame wid-
get. Other widgets created as part of the megawidget
will be created within this widget.
The hull component is initially created with the
requested widget name; then Snit does some magic, renam-
ing the hull component and installing its own instance
command in its place. The hull component's new name is
saved in an instance variable called hull.
]o The name of an instance must be valid Tk window name, and
the parent window must exist.
A snit::::widget definition can include any of statements allowed in a
snit::::type definition, and may also include these as well:
widgetclass name
Sets the snit::::widget's widget class to name, overriding
the default. See The Tk Option Database for more infor-
mation.
hulltype type
Determined the kind of widget used as the snit::::widget's
hull. The type may be frame (the default) or toplevel.
snit::::widgetadaptor name definition
This command defines a Snit megawidget type with the specified
name. It differs from snit::::widget in that the instance's hull
component is not created automatically, but is created in the
constructor and installed using the installhull command. Once
the hull is installed, its instance command is renamed and
replaced as with normal snit::::widgets. The original command is
again accessible in the instance variable hull.
Note that in general it is not possible to change the widget
class of a snit::::widgetadaptor's hull widget. See The Tk Option
Database for information on how snit::::widgetadaptors interact
with the option database.
snit::::typemethod type name arglist body
Defines a new typemethod (or redefines an existing typemethod)
for a previously existing type.
snit::::method type name arglist body
Defines a new instance method (or redefines an existing instance
method) for a previously existing type. Note that delegated
instance methods can't be redefined.
The Type Command
A type or widget definition creates a type command, which is used to
create instances of the type. The type command this form.
$$type typemethod args...
The typemethod can be any of the standard type methods defined
in the next section, or any type method defined in the type def-
inition. The subsequent args depend on the specific typemethod
chosen.
Standard Type Methods
In addition to any typemethods in the type's definition, all types and
widgets will have at least the following method:
$$type create name ?option value ...?
Creates a new instance of the type, giving it the specified name
and calling the type's constructor.
For snit::::types, if name is not a fully-qualified command name,
it is assumed to be a name in the namespace in which the call to
snit::::type appears. The method returns the fully-qualified
instance name.
For snit::::widgets and snit::::widgetadaptors, name must be a valid
widget name; the method returns the widget name.
So long as name does not conflict with any defined type method
name, the create keyword may be omitted.
If the name includes the string %%AUTO%%, it will be replaced with
the string $$type$$counter where $$type is the type name and
$$counter is a counter that increments each time %%AUTO%% is used
for this type.
By default, any arguments following the name will be a list of
option names and their values; however, a type's constructor can
specify a different argument list.
$$type info typevars ?pattern?
Returns a list of the type's type variables (excluding Snit
internal variables); all variable names are fully-qualified.
If pattern is given, it's used as a string match pattern; only
names which match the pattern are returned.
$$type info instances ?pattern?
Returns a list of the type's instances. For snit::::types, it
will be a list of fully-qualified instance names; for snit::::wid-
gets, it will be a list of Tk widget names.
If pattern is given, it's used as a string match pattern; only
names which match the pattern are returned.
$$type destroy
Destroys the type's instances, the type's namespace, and the
type command itself. This method is defined only for
snit::::types; snit::::widgets use the Tk destroy command instead.
Components and Delegation
When an object includes other objects, as when a toolbar contains but-
tons or a GUI object contains an object that references a database, the
included object is called a component. The standard way to handle com-
ponent objects owned by a Snit object is to assign their names to a
instance variable. In the following example, a dog object has a tail
object:
snit::type dog {
variable mytail
constructor {args} {
set mytail [tail %AUTO% -partof $self]
$self configurelist $args
}
method wag {} {
$mytail wag
}
}
snit::type tail {
option -length 5
option -partof
method wag {} { return "Wag, wag, wag."}
}
Because the tail object's name is stored in an instance variable, it's
easily accessible in any method.
As of Snit 0.84, the install command provides an alternate way to cre-
ate and install the component:
snit::type dog {
variable mytail
constructor {args} {
install mytail using tail %AUTO% -partof $self
$self configurelist $args
}
method wag {} {
$mytail wag
}
}
For snit::::types, the two methods are equivalent; for snit::::widgets and
snit::::widgetadaptors, the install command properly initializes dele-
gated options by querying the Tk option database.
In the above examples, the dog object's wag method simply calls the
tail component's wag method. In O circles, this is called delegation.
Snit provides an easier way to do this, as shown:
snit::type dog {
delegate method wag to mytail
constructor {args} {
set mytail [tail %AUTO% -partof $self]
$self configurelist $args
}
}
The delegate statement in the type definition implicitly defines the
instance variable mytail to hold the component's name; it also defines
the dog object's wag method, delegating it to the tail component.
If desired, all otherwise unknown methods can be delegated to a spe-
cific component:
snit::type dog {
delegate method * to mytail
constructor {args} {
set mytail [tail %AUTO% -partof $self]
$self configurelist $args
}
method bark { return "Bark, bark, bark!" }
}
In this case, a dog object will handle its own bark method; but wag
will be passed along to mytail. Any other method, being recognized by
neither dog nor tail, will simply raise an error.
Option delegation is similar to method delegation, except for the
interactions with the Tk option database; this is described in the next
section.
The Tk Option Database
This section describes how Snit interacts with the Tk option database,
and assumes the reader has a working knowledge of the option database
and its uses. The book Practical Programming in Tcl and Tk by Welch et
al has a good introduction to the option database, as does Effective
Tcl/Tk Programming.
Snit is implemented so that most of the time it will simply do the
right thing with respect to the option database, provided that the wid-
get developer does the right thing by Snit. The body of this section
goes into great deal about what Snit requires. The following is a
brief statement of the requirements, for reference.
]o If the widget's default widget class is not what is desired, set
it explicitly using widgetclass in the widget definition.
]o When defining or delegating options, specify the resource and
class names explicitly when necessary.
]o Use installhull using to install the hull for snit::::widgetadap-
tors.
]o Use install to install all other components.
The interaction of Tk widgets with the option database is a complex
thing; the interaction of Snit with the option database is even more
so, and repays attention to detail.
Setting the widget class: Every Tk widget has a widget class. For Tk
widgets, the widget class name is the just the widget type name with an
initial capital letter, e.g., the widget class for button widgets is
"Button".
Similarly, the widget class of a snit::::widget defaults to the unquali-
fied type name with the first letter capitalized. For example, the
widget class of
is "ScrolledText". The widget class can also be set explicitly using
the widgetclass statement within the snit::::widget definition.
Note that only frame and toplevel widgets allow the user to change the
widget class name, which is why they are the allowable hull types for
normal snit::::widgets.
The widget class of a snit::::widgetadaptor is just the widget class of
its hull widget; this cannot be changed unless the hull widget is a
frame or toplevel, in which case it will usually make more sense to use
snit::::widget rather than snit::::widgetadaptor.
Setting option resource names and classes: In Tk, every option has
three names: the option name, the resource name, and the class name.
The option name begins with a hyphen is all lowercase; it's used when
creating widgets, and with the configure and cget commands.
The resource and class names are used to initialize option default val-
ues by querying the Tk option database. The resource name is usually
just the option name minus the hyphen, but may contain uppercase let-
ters at word boundaries; the class name is usually just the resource
name with an initial capital, but not always. For example, here are
the option, resource, and class names for several text widget options:
-background background Background
-borderwidth borderWidth BorderWidth
-insertborderwidth insertBorderWidth BorderWidth
-padx padX Pad
As is easily seen, sometimes the resource and class names can be
inferred from the option name, but not always.
Snit options also have a resource name and a class name. By default,
these names follow the rule given above: the resource name is the
option name without the hyphen, and the class name is the resource name
with an initial capital. This is true for both locally-defined options
and explicitly delegated options:
snit::widget mywidget {
option -background
delegate option -borderwidth to hull
delegate option * to text
# ...
}
In this case, the widget class name is "Mywidget". The widget has the
following options: -background, which is locally defined, -borderwidth,
which is explicitly delegated; all other widgets are delegated to a
component called "text", which is probably a Tk text widget. If so,
mywidget has all the same options as a text widget. The option,
resource, and class names are as follows:
-background background Background
-borderwidth borderwidth Borderwidth
-padx padX Pad
Note that the locally defined option, "-background", happens to have
the same three names as the standard Tk "-background" option; and
"-pad", which is delegated implicitly to the "text" component has the
same three names for mywidget as it does for the text widget. "-bor-
derwidth", on the other hand, has different resource and class names
than usual, because the internal word "width" isn't capitalized. For
consistency, it should be; this is done as shown:
snit::widget mywidget {
option -background
delegate option {-borderwidth borderWidth} to hull
delegate option * to text
# ...
}
The class name will default to "BorderWidth", as expected.
Suppose, however, that mywidget also delegated "-padx" and "-pady" to
the hull. In this case, both the resource name and the class name must
be specified explicitly:
snit::widget mywidget {
option -background
delegate option {-borderwidth borderWidth} to hull
delegate option {-padx padX Pad} to hull
delegate option {-pady padY Pad} to hull
delegate option * to text
# ...
}
Querying the option database: If you set your widgetclass and option
names as described above, Snit will query the option database when each
instance is created, and will generally do the right thing when it
comes to querying the option database. The remainder of this section
goes into the gory details.
Initializing locally defined options:
When an instance of a snit::widget is created, its locally defined
options are initialized as follows: each option's resource and class
names are used to query the Tk option database. If the result is non-
empty, it is used as the option's default; otherwise, the default hard-
coded in the type definition is used. In either case, the default can
be overridden by the caller. For example,
option add *Mywidget.texture pebbled
snit::widget mywidget {
option -texture smooth
# ...
}
mywidget .mywidget -texture greasy
Here, "-texture" would normally default to "smooth", but because of the
entry added to the option database it defaults to "pebbled". However,
the caller has explicitly overridden the default, and so the new widget
will be "greasy".
Initializing options delegated to the hull:
A snit::::widget's hull is a widget, and given that its class has been
set it is expected to query the option database for itself. The only
exception concerns options that are delegated to it with a different
name. Consider the following code:
option add *Mywidget.borderWidth 5
option add *Mywidget.relief sunken
option add *Mywidget.hullbackground red
option add *Mywidget.background green
snit::widget mywidget {
delegate option -borderwidth to hull
delegate option -hullbackground to hull as -background
delegate option * to hull
# ...
}
mywidget .mywidget
set A [.mywidget cget -relief]
set B [.mywidget cget -hullbackground]
set C [.mywidget cget -background]
set D [.mywidget cget -borderwidth]
The question is, what are the values of variables A, B, C and D?
The value of A is "sunken". The hull is a Tk frame which has been
given the widget class "Mywidget"; it will automatically query the
option database and pick up this value. Since the -relief option is
implicitly delegated to the hull, Snit takes no action.
The value of B is "red". The hull will automatically pick up the value
"green" for its -background option, just as it picked up the -relief
value. However, Snit knows that -hullbackground is mapped to the
hull's -background option; hence, it queries the option database for
-hullbackground and gets "red" and updates the hull accordingly.
The value of C is also "red", because -background is implicitly dele-
gated to the hull; thus, retrieving it is the same as retrieving -hull-
background. Note that this case is unusual; in practice, -background
would probably be explicitly delegated to some other component.
The value of D is "5", but not for the reason you think. Note that as
it is defined above, the resource name for -borderwidth defaults to
"borderwidth", whereas the option database entry is "borderWidth". As
with -relief, the hull picks up its own "-borderwidth" option before
Snit does anything. Because the option is delegated under its own
name, Snit assumes that the correct thing has happened, and doesn't
worry about it any further.
For snit::::widgetadaptors, the case is somewhat altered. Widget adap-
tors retain the widget class of their hull, and the hull is not created
automatically by Snit. Instead, the snit::::widgetadaptor must call
installhull in its constructor. The normal way to do this is as fol-
lows:
snit::widgetadaptor mywidget {
# ...
constructor {args} {
# ...
installhull using text -foreground white
#
}
#...
}
In this case, the installhull command will create the hull using a com-
mand like this:
set hull [text $win -foreground white]
The hull is a text widget, so its widget class is "Text". Just as with
snit::::widget hulls, Snit assumes that it will pick up all of its normal
option values automatically; options delegated from a different name
are initialized from the option database in the same way.
Initializing options delegated to other components:
Non-hull components are matched against the option database in two
ways. First, a component widget remains a widget still, and therefore
is initialized from the option database in the usual way. Second, the
option database is queried for all options delegated to the component,
and the component is initialized accordingly--provided that the install
command is used to create it.
Before option database support was added to Snit, the usual way to cre-
ate a component was to simply create it in the constructor and assign
its command name to the component variable:
snit::widget mywidget {
delegate option -background to myComp
constructor {args} {
set myComp [text $win.text -foreground black]
}
}
The drawback of this method is that Snit has no opportunity to initial-
ize the component properly. Hence, the following approach is now used:
snit::widget mywidget {
delegate option -background to myComp
constructor {args} {
install myComp using text $win.text -foreground black
}
}
The install command does the following:
]o Builds a list of the options explicitly included in the install
command -- in this case, -foreground.
]o Queries the option database for all options delegated explicitly
to the named component.
]o Creates the component using the specified command, after insert-
ing into it a list of options and values read from the option
database. Thus, the explicitly include options (-foreground)
will override anything read from the option database.
]o If the widget definition implicitly delegated options to the
component using "delegate option *", then Snit calls the newly
created component's configure method to receive a list of all of
the component's options. From this Snit builds a list of
options implicitly delegated to the component which were not
explicitly included in the install command. For all such
options, Snit queries the option database and configures the
component accordingly.
Non-widget components: The option database is never queried for
snit::::types, since it can only be queried given a Tk widget name. How-
ever, snit::::widgets can have non-widget components. And if options are
delegated to those components, and if the install command is used to
install those components, then they will be initialized from the option
database just as widget components are.
CAVEATS
Please understand that while Snit is already very stable, it is still
early days in Snit's development, and not be too critical. If you have
problems, find bugs, or new ideas you are hereby cordially invited to
submit a report of your problem, bug, or idea at the SourceForge track-
ers for tcllib, which can be found at http://source-
forge.net/projects/tcllib/. The relevant category is snit.
One particular area to watch is the interaction of Snit with other
megawidget packages. Some widgets in BWidgets for example place their
own binding not on a separate bind-tag, but on the widget
itself. When used as the hull of a snit::::widgetadaptor this causes them
to be called before Snit, removing the widget command. A previous ver-
sion of Snit was tripped by this and threw errors because it tried to
operate on and with an already deleted widget command. Snit is now able
to deal with this, despite the fact that the ultimate cause is at least
bad behaviour of Bwidget, possibly even a bug. This however does not
preclude that there might be other issues lurking.
So, if you use a snit::::widgetadaptor to adapt somebody else's megawid-
get, you need to be very careful about making sure the bindtags are
done properly. There's no way for Snit to take into account all the
possible weird things other megawidget frameworks might do wrong.
KNOWN BUGS
]o Error stack traces returned by Snit are extremely ugly and typi-
cally contain far too much information about Snit internals.
]o Also see the SourceForge Trackers at http://source-
forge.net/projects/tcllib/, category snit.
HISTORY
During the course of developing Notebook (See http://www.wjdu-
quette.com/notebook), my Tcl-based personal notebook application, I
found I was writing it as a collection of objects. I wasn't using any
particular object-oriented framework; I was just writing objects in
pure Tcl following the guidelines in my Guide to Object Commands (See
http://www.wjduquette.com/tcl/objects.html), along with a few other
tricks I'd picked up since. And it was working very well. But on the
other hand, it was getting tiresome. Writing objects in pure Tcl is
straightforward, once you figure it out, but there's a fair amount of
boilerplate code to write for each one, especially if you're trying to
create megawidgets or create objects with options, like Tk widgets
have.
So that was one thing--tedium is a powerful motivator. But the other
thing I noticed is that I wasn't using inheritance at all, and I wasn't
missing it. Instead, I was using delegation: objects that created
other objects and delegated methods to them.
And I said to myself, "This is getting tedious...there has got to be a
better way." And one afternoon, on a whim, I started working on Snit,
an object system that works the way Tcl works. Snit doesn't support
inheritance, but it's great at delegation, and it makes creating megaw-
idgets easy.
I should add, I'm not particularly down on Incr Tcl. But "Snit's Not
Incr Tcl" occurred to me while I was casting about for a name, and I
guess there was a certainly inevitability about it.
If you have any comments or suggestions (or bug reports!) don't hesi-
tate to send me e-mail at will@wjduquette.com. In addition, there's
now a Snit mailing list; you can find out more about it at the Snit
home page, see http://www.wjduquette.com/snit.
CREDITS
Snit has been designed and implemented from the very beginning by
William H. Duquette. However, much credit belongs to the following
people for using Snit and providing me with valuable feedback: Rolf
Ade, Colin McCormack, Jose Nazario, Jeff Godfrey, Maurice Diamanti,
Egon Pasztor, David S. Cargo, Tom Krehbiel, Michael Cleverly, Andreas
Kupries, Marty Backe, Andy Goth, Jeff Hobbs, and Brian Griffin.
KEYWORDS
BWidget, C], Incr Tcl, adaptors, class, mega widget, object, object
oriented, widget, widget adaptors
COPYRIGHT
Copyright (c) 2003-2004, by William H. Duquette
snit 0.93 snit(n)
|