1snitfaq(n) Snit's Not Incr Tcl, OO system snitfaq(n)
2
3
4
5______________________________________________________________________________
6
8 snitfaq - Snit Frequently Asked Questions
9
12 WHAT IS THIS DOCUMENT?
13 This is an atypical FAQ list, in that few of the questions are fre‐
14 quently asked. Rather, these are the questions I think a newcomer to
15 Snit should be asking. This file is not a complete reference to Snit,
16 however; that information is in the snit man page.
17
18 WHAT IS SNIT?
19 Snit is a framework for defining abstract data types and megawidgets in
20 pure Tcl. The name "Snit" stands for "Snit's Not Incr Tcl", signifying
21 that Snit takes a different approach to defining objects than does Incr
22 Tcl, the best known object framework for Tcl. Had I realized that Snit
23 would become at all popular, I'd probably have chosen something else.
24
25 The primary purpose of Snit is to be object glue--to help you compose
26 diverse objects from diverse sources into types and megawidgets with
27 clean, convenient interfaces so that you can more easily build your
28 application.
29
30 Snit isn't about theoretical purity or minimalist design; it's about
31 being able to do powerful things easily and consistently without having
32 to think about them--so that you can concentrate on building your
33 application.
34
35 Snit isn't about implementing thousands of nearly identical carefully-
36 specified lightweight thingamajigs--not as individual Snit objects.
37 Traditional Tcl methods will be much faster, and not much more compli‐
38 cated. But Snit is about implementing a clean interface to manage a
39 collection of thousands of nearly identical carefully-specified light‐
40 weight thingamajigs (e.g., think of the text widget and text tags, or
41 the canvas widget and canvas objects). Snit lets you hide the details
42 of just how those thingamajigs are stored--so that you can ignore it,
43 and concentrate on building your application.
44
45 Snit isn't a way of life, a silver bullet, or the Fountain of Youth.
46 It's just a way of managing complexity--and of managing some of the
47 complexity of managing complexity--so that you can concentrate on
48 building your application.
49
50 WHAT VERSION OF TCL DOES SNIT REQUIRE?
51 Snit 1.3 requires Tcl 8.3 or later; Snit 2.2 requires Tcl 8.5 or later.
52 See SNIT VERSIONS for the differences between Snit 1.3 and Snit 2.2.
53
54 WHERE CAN I DOWNLOAD SNIT?
55 Snit is part of Tcllib, the standard Tcl library, so you might already
56 have it. It's also available at the Snit Home Page, http://www.wjdu‐
57 quette.com/snit.
58
59 WHAT ARE SNIT'S GOALS?
60 · A Snit object should be at least as efficient as a hand-coded
61 Tcl object (see http://www.wjduquette.com/tcl/objects.html).
62
63 · The fact that Snit was used in an object's implementation should
64 be transparent (and irrelevant) to clients of that object.
65
66 · Snit should be able to encapsulate objects from other sources,
67 particularly Tk widgets.
68
69 · Snit megawidgets should be (to the extent possible) indistin‐
70 guishable in interface from Tk widgets.
71
72 · Snit should be Tclish--that is, rather than trying to emulate
73 C++, Smalltalk, or anything else, it should try to emulate Tcl
74 itself.
75
76 · It should have a simple, easy-to-use, easy-to-remember syntax.
77
78 HOW IS SNIT DIFFERENT FROM OTHER OO FRAMEWORKS?
79 Snit is unique among Tcl object systems in that it is based not on
80 inheritance but on delegation. Object systems based on inheritance
81 only allow you to inherit from classes defined using the same system,
82 and that's a shame. In Tcl, an object is anything that acts like an
83 object; it shouldn't matter how the object was implemented. I designed
84 Snit to help me build applications out of the materials at hand; thus,
85 Snit is designed to be able to incorporate and build on any object,
86 whether it's a hand-coded object, a Tk widget, an Incr Tcl object, a
87 BWidget or almost anything else.
88
89 Note that you can achieve the effect of inheritance using COMPONENTS
90 and DELEGATION--and you can inherit from anything that looks like a Tcl
91 object.
92
93 WHAT CAN I DO WITH SNIT?
94 Using Snit, a programmer can:
95
96 · Create abstract data types and Tk megawidgets.
97
98 · Define instance variables, type variables, and Tk-style options.
99
100 · Define constructors, destructors, instance methods, type meth‐
101 ods, procs.
102
103 · Assemble a type out of component types. Instance methods and
104 options can be delegated to the component types automatically.
105
107 WHICH VERSION OF SNIT SHOULD I USE?
108 The current Snit distribution includes two versions, Snit 1.3 and Snit
109 2.2. The reason that both are included is that Snit 2.2 takes advan‐
110 tage of a number of new features of Tcl 8.5 to improve run-time effi‐
111 ciency; as a side-effect, the ugliness of Snit's error messages and
112 stack traces has been reduced considerably. The cost of using Snit
113 2.2, of course, is that you must target Tcl 8.5.
114
115 Snit 1.3, on the other hand, lacks Snit 2.2's optimizations, but
116 requires only Tcl 8.3 and later.
117
118 In short, if you're targetting Tcl 8.3 or 8.4 you should use Snit 1.3.
119 If you can afford to target Tcl 8.5, you should definitely use Snit
120 2.2. If you will be targetting both, you can use Snit 1.3 exclusively,
121 or (if your code is unaffected by the minor incompatibilities between
122 the two versions) you can use Snit 1.3 for Tcl 8.4 and Snit 2.2 for Tcl
123 8.5.
124
125 HOW DO I SELECT THE VERSION OF SNIT I WANT TO USE?
126 To always use Snit 1.3 (or a later version of Snit 1.x), invoke Snit as
127 follows:
128 package require Snit 1.3
129
130 To always use Snit 2.2 (or a later version of Snit 2.x), say this
131 instead:
132 package require Snit 2.2
133
134 Note that if you request Snit 2.2 explicitly, your application will
135 halt with Tcl 8.4, since Snit 2.2 is unavailable for Tcl 8.4.
136
137 If you wish your application to always use the latest available version
138 of Snit, don't specify a version number:
139 package require Snit
140
141 Tcl will find and load the latest version that's available relative to
142 the version of Tcl being used. In this case, be careful to avoid using
143 any incompatible features.
144
145 HOW ARE SNIT 1.3 AND SNIT 2.2 INCOMPATIBLE?
146 To the extent possible, Snit 2.2 is intended to be a drop-in replace‐
147 ment for Snit 1.3. Unfortunately, some incompatibilities were
148 inevitable because Snit 2.2 uses Tcl 8.5's new namespace ensemble mech‐
149 anism to implement subcommand dispatch. This approach is much faster
150 than the mechanism used in Snit 1.3, and also results in much better
151 error messages; however, it also places new constraints on the imple‐
152 mentation.
153
154 There are four specific incompatibilities between Snit 1.3 and Snit
155 2.2.
156
157 · Snit 1.3 supports implicit naming of objects. Suppose you
158 define a new snit::type called dog. You can create instances of
159 dog in three ways:
160 dog spot ;# Explicit naming
161 set obj1 [dog %AUTO%] ;# Automatic naming
162 set obj2 [dog] ;# Implicit naming
163
164 In Snit 2.2, type commands are defined using the namespace
165 ensemble mechanism; and namespace ensemble doesn't allow an
166 ensemble command to be called without a subcommand. In short,
167 using namespace ensemble there's no way to support implicit nam‐
168 ing.
169
170 All is not lost, however. If the type has no type methods, then
171 the type command is a simple command rather than an ensemble,
172 and namespace ensemble is not used. In this case, implicit nam‐
173 ing is still possible.
174
175 In short, you can have implicit naming if you're willing to do
176 without type methods (including the standard type methods, like
177 $type info). To do so, use the -hastypemethods pragma:
178 pragma -hastypemethods 0
179
180 · Hierarchical methods and type methods are implemented differ‐
181 ently in Snit 2.2.
182
183 A hierarchical method is an instance method which has subcom‐
184 mands; these subcommands are themselves methods. The Tk text
185 widget's tag command and its subcommands are examples of hierar‐
186 chical methods. You can implement such subcommands in Snit sim‐
187 ply by including multiple words in the method names:
188 method {tag configure} {tag args} { ... }
189
190 method {tag cget} {tag option} {...}
191
192 Here we've implicitly defined a tag method which has two subcom‐
193 mands, configure and cget.
194
195 In Snit 1.3, hierarchical methods could be called in two ways:
196 $obj tag cget -myoption ;# The good way
197 $obj {tag cget} -myoption ;# The weird way
198
199 In the second call, we see that a hierarchical method or type
200 method is simply one whose name contains multiple words.
201
202 In Snit 2.2 this is no longer the case, and the "weird" way of
203 calling hierarchical methods and type methods no longer works.
204
205 · The third incompatibility derives from the second. In Snit 1.3,
206 hierarchical methods were also simply methods whose name con‐
207 tains multiple words. As a result, $obj info methods returned
208 the full names of all hierarchical methods. In the example
209 above, the list returned by $obj info methods would include tag
210 configure and tag cget but not tag, since tag is defined only
211 implicitly.
212
213 In Snit 2.2, hierarchical methods and type methods are no longer
214 simply ones whose name contains multiple words; in the above
215 example, the list returned by $obj info methods would include
216 tag but not tag configure or tag cget.
217
218 · The fourth incompatibility is due to a new feature. Snit 2.2
219 uses the new namespace path command so that a type's code can
220 call any command defined in the type's parent namespace without
221 qualification or importation. For example, suppose you have a
222 package called mypackage which defines a number of commands
223 including a type, ::mypackage::mytype. Thanks to namespace
224 path, the type's code can call any of the other commands defined
225 in ::mypackage::.
226
227 This is extremely convenient. However, it also means that com‐
228 mands defined in the parent namespace, ::mypackage:: can block
229 the type's access to identically named commands in the global
230 namespace. This can lead to bugs. For example, Tcllib includes
231 a type called ::tie::std::file. This type's code calls the
232 standard file command. When run with Snit 2.2, the code broke--
233 the type's command, ::tie::std::file, is itself a command in the
234 type's parent namespace, and so instead of calling the standard
235 file command, the type found itself calling itself.
236
237 ARE THERE OTHER DIFFERENCES BETWEEN SNIT 1.X AND SNIT 2.2?
238 Yes.
239
240 · Method dispatch is considerably faster.
241
242 · Many error messages and stack traces are cleaner.
243
244 · The -simpledispatch pragma is obsolete, and ignored if present.
245 In Snit 1.x, -simpledispatch substitutes a faster mechanism for
246 method dispatch, at the cost of losing certain features. Snit
247 2.2 method dispatch is faster still in all cases, so -simpledis‐
248 patch is no longer needed.
249
250 · In Snit 2.2, a type's code (methods, type methods, etc.) can
251 call commands from the type's parent namespace without qualify‐
252 ing or importing them, i.e., type ::parentns::mytype's code can
253 call ::parentns::someproc as just someproc.
254
255 This is extremely useful when a type is defined as part of a
256 larger package, and shares a parent namespace with the rest of
257 the package; it means that the type can call other commands
258 defined by the package without any extra work.
259
260 This feature depends on the new Tcl 8.5 namespace path command,
261 which is why it hasn't been implemented for V1.x. V1.x code can
262 achieve something similar by placing
263 namespace import [namespace parent]::*
264 in a type constructor. This is less useful, however, as it
265 picks up only those commands which have already been exported by
266 the parent namespace at the time the type is defined.
267
269 WHAT IS AN OBJECT?
270 A full description of object-oriented programming is beyond the scope
271 of this FAQ, obviously. In simple terms, an object is an instance of
272 an abstract data type--a coherent bundle of code and data. There are
273 many ways to represent objects in Tcl/Tk; the best known examples are
274 the Tk widgets.
275
276 A Tk widget is an object; it is represented by a Tcl command. The
277 object's methods are subcommands of the Tcl command. The object's
278 properties are options accessed using the configure and cget methods.
279 Snit uses the same conventions as Tk widgets do.
280
281 WHAT IS AN ABSTRACT DATA TYPE?
282 In computer science terms, an abstract data type is a complex data
283 structure along with a set of operations--a stack, a queue, a binary
284 tree, etc--that is to say, in modern terms, an object. In systems that
285 include some form of inheritance the word class is usually used instead
286 of abstract data type, but as Snit doesn't implement inheritance as
287 it's ordinarily understood the older term seems more appropriate.
288 Sometimes this is called object-based programming as opposed to object-
289 oriented programming. Note that you can easily create the effect of
290 inheritance using COMPONENTS and DELEGATION.
291
292 In Snit, as in Tk, a type is a command that creates instances --
293 objects -- which belong to the type. Most types define some number of
294 options which can be set at creation time, and usually can be changed
295 later.
296
297 Further, an instance is also a Tcl command--a command that gives access
298 to the operations which are defined for that abstract data type. Con‐
299 ventionally, the operations are defined as subcommands of the instance
300 command. For example, to insert text into a Tk text widget, you use
301 the text widget's insert subcommand:
302
303 # Create a text widget and insert some text in it.
304 text .mytext -width 80 -height 24
305 .mytext insert end "Howdy!"
306
307
308 In this example, text is the type command and .mytext is the instance
309 command.
310
311 In Snit, object subcommands are generally called INSTANCE METHODS.
312
313 WHAT KINDS OF ABSTRACT DATA TYPES DOES SNIT PROVIDE?
314 Snit allows you to define three kinds of abstract data type:
315
316 · snit::type
317
318 · snit::widget
319
320 · snit::widgetadaptor
321
322 WHAT IS A SNIT::TYPE?
323 A snit::type is a non-GUI abstract data type, e.g., a stack or a queue.
324 snit::types are defined using the snit::type command. For example, if
325 you were designing a kennel management system for a dog breeder, you'd
326 need a dog type.
327
328 % snit::type dog {
329 # ...
330 }
331 ::dog
332 %
333
334
335 This definition defines a new command (::dog, in this case) that can be
336 used to define dog objects.
337
338 An instance of a snit::type can have INSTANCE METHODS, INSTANCE VARI‐
339 ABLES, OPTIONS, and COMPONENTS. The type itself can have TYPE METHODS,
340 TYPE VARIABLES, TYPE COMPONENTS, and PROCS.
341
342 WHAT IS A SNIT::WIDGET?, THE SHORT STORY
343 A snit::widget is a Tk megawidget built using Snit; it is very similar
344 to a snit::type. See WIDGETS.
345
346 WHAT IS A SNIT::WIDGETADAPTOR?, THE SHORT STORY
347 A snit::widgetadaptor uses Snit to wrap an existing widget type (e.g.,
348 a Tk label), modifying its interface to a lesser or greater extent. It
349 is very similar to a snit::widget. See WIDGET ADAPTORS.
350
351 HOW DO I CREATE AN INSTANCE OF A SNIT::TYPE?
352 You create an instance of a snit::type by passing the new instance's
353 name to the type's create method. In the following example, we create
354 a dog object called spot.
355
356 % snit::type dog {
357 # ....
358 }
359 ::dog
360 % dog create spot
361 ::spot
362 %
363
364
365 In general, the create method name can be omitted so long as the
366 instance name doesn't conflict with any defined TYPE METHODS. (See TYPE
367 COMPONENTS for the special case in which this doesn't work.) So the
368 following example is identical to the previous example:
369
370 % snit::type dog {
371 # ....
372 }
373 ::dog
374 % dog spot
375 ::spot
376 %
377
378
379 This document generally uses the shorter form.
380
381 If the dog type defines OPTIONS, these can usually be given defaults at
382 creation time:
383
384 % snit::type dog {
385 option -breed mongrel
386 option -color brown
387
388 method bark {} { return "$self barks." }
389 }
390 ::dog
391 % dog create spot -breed dalmation -color spotted
392 ::spot
393 % spot cget -breed
394 dalmation
395 % spot cget -color
396 spotted
397 %
398
399
400 Once created, the instance name now names a new Tcl command that is
401 used to manipulate the object. For example, the following code makes
402 the dog bark:
403
404 % spot bark
405 ::spot barks.
406 %
407
408
409 HOW DO I REFER TO AN OBJECT INDIRECTLY?
410 Some programmers prefer to save the object name in a variable, and ref‐
411 erence it that way. For example,
412
413 % snit::type dog { ... }
414 ::dog
415 % set d [dog spot -breed dalmation -color spotted]
416 ::spot
417 % $d cget -breed
418 dalmation
419 % $d bark
420 ::spot barks.
421 %
422
423
424 If you prefer this style, you might prefer to have Snit generate the
425 instance's name automatically.
426
427 HOW CAN I GENERATE THE OBJECT NAME AUTOMATICALLY?
428 If you'd like Snit to generate an object name for you, use the %AUTO%
429 keyword as the requested name:
430
431 % snit::type dog { ... }
432 ::dog
433 % set d [dog %AUTO%]
434 ::dog2
435 % $d bark
436 ::dog2 barks.
437 %
438
439
440 The %AUTO% keyword can be embedded in a longer string:
441
442 % set d [dog obj_%AUTO%]
443 ::obj_dog4
444 % $d bark
445 ::obj_dog4 barks.
446 %
447
448
449 CAN TYPES BE RENAMED?
450 Tcl's rename command renames other commands. It's a common technique
451 in Tcl to modify an existing command by renaming it and defining a new
452 command with the original name; the new command usually calls the
453 renamed command.
454
455 snit::type commands, however, should never be renamed; to do so breaks
456 the connection between the type and its objects.
457
458 CAN OBJECTS BE RENAMED?
459 Tcl's rename command renames other commands. It's a common technique
460 in Tcl to modify an existing command by renaming it and defining a new
461 command with the original name; the new command usually calls the
462 renamed command.
463
464 All Snit objects (including widgets and widgetadaptors) can be renamed,
465 though this flexibility has some consequences:
466
467 · In an instance method, the implicit argument self will always
468 contain the object's current name, so instance methods can
469 always call other instance methods using $self.
470
471 · If the object is renamed, however, then $self's value will
472 change. Therefore, don't use $self for anything that will break
473 if $self changes. For example, don't pass a callback command to
474 another object like this:
475
476 .btn configure -command [list $self ButtonPress]
477
478 You'll get an error if .btn calls your command after your object
479 is renamed.
480
481 · Instead, your object should define its callback command like
482 this:
483
484 .btn configure -command [mymethod ButtonPress]
485
486 The mymethod command returns code that will call the desired
487 method safely; the caller of the callback can add additional
488 arguments to the end of the command as usual.
489
490 · Every object has a private namespace; the name of this namespace
491 is available in method bodies, etc., as the value of the
492 implicit argument selfns. This value is constant for the life
493 of the object. Use $selfns instead of $self if you need a
494 unique token to identify the object.
495
496 · When a snit::widget's instance command is renamed, its Tk window
497 name remains the same -- and is still extremely important. Con‐
498 sequently, the Tk window name is available in method bodies as
499 the value of the implicit argument win. This value is constant
500 for the life of the object. When creating child windows, it's
501 best to use $win.child rather than $self.child as the name of
502 the child window.
503
504 HOW DO I DESTROY A SNIT OBJECT?
505 Any Snit object of any type can be destroyed by renaming it to the
506 empty string using the Tcl rename command.
507
508 Snit megawidgets (i.e., instances of snit::widget and snit::widgetadap‐
509 tor) can be destroyed like any other widget: by using the Tk destroy
510 command on the widget or on one of its ancestors in the window hierar‐
511 chy.
512
513 Every instance of a snit::type has a destroy method:
514
515 % snit::type dog { ... }
516 ::dog
517 % dog spot
518 ::spot
519 % spot bark
520 ::spot barks.
521 % spot destroy
522 % spot barks
523 invalid command name "spot"
524 %
525
526
527 Finally, every Snit type has a type method called destroy; calling it
528 destroys the type and all of its instances:
529 % snit::type dog { ... }
530 ::dog
531 % dog spot
532 ::spot
533 % spot bark
534 ::spot barks.
535 % dog destroy
536 % spot bark
537 invalid command name "spot"
538 % dog fido
539 invalid command name "dog"
540 %
541
542
544 WHAT IS AN INSTANCE METHOD?
545 An instance method is a procedure associated with a specific object and
546 called as a subcommand of the object's command. It is given free
547 access to all of the object's type variables, instance variables, and
548 so forth.
549
550 HOW DO I DEFINE AN INSTANCE METHOD?
551 Instance methods are defined in the type definition using the method
552 statement. Consider the following code that might be used to add dogs
553 to a computer simulation:
554
555 % snit::type dog {
556 method bark {} {
557 return "$self barks."
558 }
559
560 method chase {thing} {
561 return "$self chases $thing."
562 }
563 }
564 ::dog
565 %
566
567
568 A dog can bark, and it can chase things.
569
570 The method statement looks just like a normal Tcl proc, except that it
571 appears in a snit::type definition. Notice that every instance method
572 gets an implicit argument called self; this argument contains the
573 object's name. (There's more on implicit method arguments below.)
574
575 HOW DOES A CLIENT CALL AN INSTANCE METHOD?
576 The method name becomes a subcommand of the object. For example, let's
577 put a simulated dog through its paces:
578
579 % dog spot
580 ::spot
581 % spot bark
582 ::spot barks.
583 % spot chase cat
584 ::spot chases cat.
585 %
586
587
588 HOW DOES AN INSTANCE METHOD CALL ANOTHER INSTANCE METHOD?
589 If method A needs to call method B on the same object, it does so just
590 as a client does: it calls method B as a subcommand of the object
591 itself, using the object name stored in the implicit argument self.
592
593 Suppose, for example, that our dogs never chase anything without bark‐
594 ing at them:
595
596 % snit::type dog {
597 method bark {} {
598 return "$self barks."
599 }
600
601 method chase {thing} {
602 return "$self chases $thing. [$self bark]"
603 }
604 }
605 ::dog
606 % dog spot
607 ::spot
608 % spot bark
609 ::spot barks.
610 % spot chase cat
611 ::spot chases cat. ::spot barks.
612 %
613
614
615 ARE THERE ANY LIMITATIONS ON INSTANCE METHOD NAMES?
616 Not really, so long as you avoid the standard instance method names:
617 configure, configurelist, cget, destroy, and info. Also, method names
618 consisting of multiple words define hierarchical methods.
619
620 WHAT IS A HIERARCHICAL METHOD?
621 An object's methods are subcommands of the object's instance command.
622 Hierarchical methods allow an object's methods to have subcommands of
623 their own; and these can in turn have subcommands, and so on. This
624 allows the programmer to define a tree-shaped command structure, such
625 as is used by many of the Tk widgets--the subcommands of the Tk text
626 widget's tag method are hierarchical methods.
627
628 HOW DO I DEFINE A HIERARCHICAL METHOD?
629 Define methods whose names consist of multiple words. These words
630 define the hierarchy implicitly. For example, the following code
631 defines a tag method with subcommands cget and configure:
632 snit::widget mytext {
633 method {tag configure} {tag args} { ... }
634
635 method {tag cget} {tag option} {...}
636 }
637
638 Note that there is no explicit definition for the tag method; it is
639 implicit in the definition of tag configure and tag cget. If you tried
640 to define tag explicitly in this example, you'd get an error.
641
642 HOW DO I CALL HIERARCHICAL METHODS?
643 As subcommands of subcommands.
644 % mytext .text
645 % .text tag configure redtext -foreground red -background black
646 % .text tag cget redtext -foreground
647 red
648 %
649
650
651 HOW DO I MAKE AN INSTANCE METHOD PRIVATE?
652 It's often useful to define private methods, that is, instance methods
653 intended to be called only by other methods of the same object.
654
655 Snit doesn't implement any access control on instance methods, so all
656 methods are de facto public. Conventionally, though, the names of pub‐
657 lic methods begin with a lower-case letter, and the names of private
658 methods begin with an upper-case letter.
659
660 For example, suppose our simulated dogs only bark in response to other
661 stimuli; they never bark just for fun. So the bark method becomes Bark
662 to indicate that it is private:
663
664 % snit::type dog {
665 # Private by convention: begins with uppercase letter.
666 method Bark {} {
667 return "$self barks."
668 }
669
670 method chase {thing} {
671 return "$self chases $thing. [$self Bark]"
672 }
673 }
674 ::dog
675 % dog fido
676 ::fido
677 % fido chase cat
678 ::fido chases cat. ::fido barks.
679 %
680
681
682 ARE THERE ANY LIMITATIONS ON INSTANCE METHOD ARGUMENTS?
683 Method argument lists are defined just like normal Tcl proc argument
684 lists; in particular, they can include arguments with default values
685 and the args argument.
686
687 However, every method also has a number of implicit arguments provided
688 by Snit in addition to those explicitly defined. The names of these
689 implicit arguments may not used to name explicit arguments.
690
691 WHAT IMPLICIT ARGUMENTS ARE PASSED TO EACH INSTANCE METHOD?
692 The arguments implicitly passed to every method are type, selfns, win,
693 and self.
694
695 WHAT IS $TYPE?
696 The implicit argument type contains the fully qualified name of the
697 object's type:
698
699 % snit::type thing {
700 method mytype {} {
701 return $type
702 }
703 }
704 ::thing
705 % thing something
706 ::something
707 % something mytype
708 ::thing
709 %
710
711
712 WHAT IS $SELF?
713 The implicit argument self contains the object's fully qualified name.
714
715 If the object's command is renamed, then $self will change to match in
716 subsequent calls. Thus, your code should not assume that $self is con‐
717 stant unless you know for sure that the object will never be renamed.
718
719 % snit::type thing {
720 method myself {} {
721 return $self
722 }
723 }
724 ::thing
725 % thing mutt
726 ::mutt
727 % mutt myself
728 ::mutt
729 % rename mutt jeff
730 % jeff myself
731 ::jeff
732 %
733
734
735 WHAT IS $SELFNS?
736 Each Snit object has a private namespace in which to store its INSTANCE
737 VARIABLES and OPTIONS. The implicit argument selfns contains the name
738 of this namespace; its value never changes, and is constant for the
739 life of the object, even if the object's name changes:
740
741 % snit::type thing {
742 method myNameSpace {} {
743 return $selfns
744 }
745 }
746 ::thing
747 % thing jeff
748 ::jeff
749 % jeff myNameSpace
750 ::thing::Snit_inst3
751 % rename jeff mutt
752 % mutt myNameSpace
753 ::thing::Snit_inst3
754 %
755
756
757 The above example reveals how Snit names an instance's private names‐
758 pace; however, you should not write code that depends on the specific
759 naming convention, as it might change in future releases.
760
761 WHAT IS $WIN?
762 The implicit argument win is defined for all Snit methods, though it
763 really makes sense only for those of WIDGETS and WIDGET ADAPTORS. $win
764 is simply the original name of the object, whether it's been renamed or
765 not. For widgets and widgetadaptors, it is also therefore the name of
766 a Tk window.
767
768 When a snit::widgetadaptor is used to modify the interface of a widget
769 or megawidget, it must rename the widget's original command and replace
770 it with its own.
771
772 Thus, using win whenever the Tk window name is called for means that a
773 snit::widget or snit::widgetadaptor can be adapted by a snit::wid‐
774 getadaptor. See WIDGETS for more information.
775
776 HOW DO I PASS AN INSTANCE METHOD AS A CALLBACK?
777 It depends on the context.
778
779 Suppose in my application I have a dog object named fido, and I want
780 fido to bark when a Tk button called .bark is pressed. In this case, I
781 create the callback command in the usual way, using list:
782
783 button .bark -text "Bark!" -command [list fido bark]
784
785
786 In typical Tcl style, we use a callback to hook two independent compo‐
787 nents together. But suppose that the dog object has a graphical inter‐
788 face and owns the button itself? In this case, the dog must pass one
789 of its own instance methods to the button it owns. The obvious thing
790 to do is this:
791
792 % snit::widget dog {
793 constructor {args} {
794 #...
795 button $win.barkbtn -text "Bark!" -command [list $self bark]
796 #...
797 }
798 }
799 ::dog
800 %
801
802
803 (Note that in this example, our dog becomes a snit::widget, because it
804 has GUI behavior. See WIDGETS for more.) Thus, if we create a dog
805 called .spot, it will create a Tk button called .spot.barkbtn; when
806 pressed, the button will call $self bark.
807
808 Now, this will work--provided that .spot is never renamed to something
809 else. But surely renaming widgets is abnormal? And so it is--unless
810 .spot is the hull component of a snit::widgetadaptor. If it is, then
811 it will be renamed, and .spot will become the name of the snit::wid‐
812 getadaptor object. When the button is pressed, the command $self bark
813 will be handled by the snit::widgetadaptor, which might or might not do
814 the right thing.
815
816 There's a safer way to do it, and it looks like this:
817
818 % snit::widget dog {
819 constructor {args} {
820 #...
821 button $win.barkbtn -text "Bark!" -command [mymethod bark]
822 #...
823 }
824 }
825 ::dog
826 %
827
828
829 The command mymethod takes any number of arguments, and can be used
830 like list to build up a callback command; the only difference is that
831 mymethod returns a form of the command that won't change even if the
832 instance's name changes.
833
834 On the other hand, you might prefer to allow a widgetadaptor to over‐
835 ride a method such that your renamed widget will call the widgetadap‐
836 tor's method instead of its own. In this case, using [list $self bark]
837 will do what you want...but this is a technique which should be used
838 only in carefully controlled circumstances.
839
840 HOW DO I DELEGATE INSTANCE METHODS TO A COMPONENT?
841 See DELEGATION.
842
844 WHAT IS AN INSTANCE VARIABLE?
845 An instance variable is a private variable associated with some partic‐
846 ular Snit object. Instance variables can be scalars or arrays.
847
848 HOW IS A SCALAR INSTANCE VARIABLE DEFINED?
849 Scalar instance variables are defined in the type definition using the
850 variable statement. You can simply name it, or you can initialize it
851 with a value:
852
853 snit::type mytype {
854 # Define variable "greeting" and initialize it with "Howdy!"
855 variable greeting "Howdy!"
856 }
857
858
859 HOW IS AN ARRAY INSTANCE VARIABLE DEFINED?
860 Array instance variables are also defined in the type definition using
861 the variable command. You can initialize them at the same time by
862 specifying the -array option:
863
864 snit::type mytype {
865 # Define array variable "greetings"
866 variable greetings -array {
867 formal "Good Evening"
868 casual "Howdy!"
869 }
870 }
871
872
873 WHAT HAPPENS IF I DON'T INITIALIZE AN INSTANCE VARIABLE?
874 Variables do not really exist until they are given values. If you do
875 not initialize a variable when you define it, then you must be sure to
876 assign a value to it (in the constructor, say, or in some method)
877 before you reference it.
878
879 ARE THERE ANY LIMITATIONS ON INSTANCE VARIABLE NAMES?
880 Just a few.
881
882 First, every Snit object has a built-in instance variable called
883 options, which should never be redefined.
884
885 Second, all names beginning with "Snit_" are reserved for use by Snit
886 internal code.
887
888 Third, instance variable names containing the namespace delimiter (::)
889 are likely to cause great confusion.
890
891 DO I NEED TO DECLARE MY INSTANCE VARIABLES IN MY METHODS?
892 No. Once you've defined an instance variable in the type definition, it
893 can be used in any instance code (instance methods, the constructor,
894 and the destructor) without declaration. This differs from normal Tcl
895 practice, in which all non-local variables in a proc need to be
896 declared.
897
898 There is a speed penalty to having all instance variables implicitly
899 available in all instance code. Even though your code need not declare
900 the variables explicitly, Snit must still declare them, and that takes
901 time. If you have ten instance variables, a method that uses none of
902 them must still pay the declaration penalty for all ten. In most
903 cases, the additional runtime cost is negligible. If extreme cases,
904 you might wish to avoid it; there are two methods for doing so.
905
906 The first is to define a single instance variable, an array, and store
907 all of your instance data in the array. This way, you're only paying
908 the declaration penalty for one variable--and you probably need the
909 variable most of the time anyway. This method breaks down if your
910 instance variables include multiple arrays; in Tcl 8.5, however, the
911 dict command might come to your rescue.
912
913 The second method is to declare your instance variables explicitly in
914 your instance code, while not including them in the type definition:
915 snit::type dog {
916 constructor {} {
917 variable mood
918
919 set mood happy
920 }
921
922 method setmood {newMood} {
923 variable mood
924
925 set mood $newMood
926 }
927
928 method getmood {} {
929 variable mood
930
931 return $mood
932 }
933 }
934
935 This allows you to ensure that only the required variables are included
936 in each method, at the cost of longer code and run-time errors when you
937 forget to declare a variable you need.
938
939 HOW DO I PASS AN INSTANCE VARIABLE'S NAME TO ANOTHER OBJECT?
940 In Tk, it's common to pass a widget a variable name; for example, Tk
941 label widgets have a -textvariable option which names the variable
942 which will contain the widget's text. This allows the program to
943 update the label's value just by assigning a new value to the variable.
944
945 If you naively pass the instance variable name to the label widget,
946 you'll be confused by the result; Tk will assume that the name names a
947 global variable. Instead, you need to provide a fully-qualified vari‐
948 able name. From within an instance method or a constructor, you can
949 fully qualify the variable's name using the myvar command:
950
951 snit::widget mywidget {
952 variable labeltext ""
953
954 constructor {args} {
955 # ...
956
957 label $win.label -textvariable [myvar labeltext]
958
959 # ...
960 }
961 }
962
963
964 HOW DO I MAKE AN INSTANCE VARIABLE PUBLIC?
965 Practically speaking, you don't. Instead, you'll implement public
966 variables as OPTIONS. Alternatively, you can write INSTANCE METHODS to
967 set and get the variable's value.
968
970 WHAT IS AN OPTION?
971 A type's options are the equivalent of what other object-oriented lan‐
972 guages would call public member variables or properties: they are data
973 values which can be retrieved and (usually) set by the clients of an
974 object.
975
976 Snit's implementation of options follows the Tk model fairly exactly,
977 except that snit::type objects usually don't interact with THE TK
978 OPTION DATABASE; snit::widget and snit::widgetadaptor objects, on the
979 other hand, always do.
980
981 HOW DO I DEFINE AN OPTION?
982 Options are defined in the type definition using the option statement.
983 Consider the following type, to be used in an application that manages
984 a list of dogs for a pet store:
985
986 snit::type dog {
987 option -breed -default mongrel
988 option -color -default brown
989 option -akc -default 0
990 option -shots -default 0
991 }
992
993
994 According to this, a dog has four notable properties: a breed, a color,
995 a flag that says whether it's pedigreed with the American Kennel Club,
996 and another flag that says whether it has had its shots. The default
997 dog, evidently, is a brown mutt.
998
999 There are a number of options you can specify when defining an option;
1000 if -default is the only one, you can omit the word -default as follows:
1001
1002 snit::type dog {
1003 option -breed mongrel
1004 option -color brown
1005 option -akc 0
1006 option -shots 0
1007 }
1008
1009
1010 If no -default value is specified, the option's default value will be
1011 the empty string (but see THE TK OPTION DATABASE).
1012
1013 The Snit man page refers to options like these as "locally defined"
1014 options.
1015
1016 HOW CAN A CLIENT SET OPTIONS AT OBJECT CREATION?
1017 The normal convention is that the client may pass any number of options
1018 and their values after the object's name at object creation. For exam‐
1019 ple, the ::dog command defined in the previous answer can now be used
1020 to create individual dogs. Any or all of the options may be set at
1021 creation time.
1022
1023 % dog spot -breed beagle -color "mottled" -akc 1 -shots 1
1024 ::spot
1025 % dog fido -shots 1
1026 ::fido
1027 %
1028
1029
1030 So ::spot is a pedigreed beagle; ::fido is a typical mutt, but his own‐
1031 ers evidently take care of him, because he's had his shots.
1032
1033 Note: If the type defines a constructor, it can specify a different
1034 object-creation syntax. See CONSTRUCTORS for more information.
1035
1036 HOW CAN A CLIENT RETRIEVE AN OPTION'S VALUE?
1037 Retrieve option values using the cget method:
1038
1039 % spot cget -color
1040 mottled
1041 % fido cget -breed
1042 mongrel
1043 %
1044
1045
1046 HOW CAN A CLIENT SET OPTIONS AFTER OBJECT CREATION?
1047 Any number of options may be set at one time using the configure
1048 instance method. Suppose that closer inspection shows that ::fido is
1049 not a brown mongrel, but rather a rare Arctic Boar Hound of a lovely
1050 dun color:
1051
1052 % fido configure -color dun -breed "Arctic Boar Hound"
1053 % fido cget -color
1054 dun
1055 % fido cget -breed
1056 Arctic Boar Hound
1057
1058
1059 Alternatively, the configurelist method takes a list of options and
1060 values; occasionally this is more convenient:
1061
1062 % set features [list -color dun -breed "Arctic Boar Hound"]
1063 -color dun -breed {Arctic Boar Hound}
1064 % fido configurelist $features
1065 % fido cget -color
1066 dun
1067 % fido cget -breed
1068 Arctic Boar Hound
1069 %
1070
1071
1072 In Tcl 8.5, the * keyword can be used with configure in this case:
1073
1074 % set features [list -color dun -breed "Arctic Boar Hound"]
1075 -color dun -breed {Arctic Boar Hound}
1076 % fido configure {*}$features
1077 % fido cget -color
1078 dun
1079 % fido cget -breed
1080 Arctic Boar Hound
1081 %
1082
1083
1084 The results are the same.
1085
1086 HOW SHOULD AN INSTANCE METHOD ACCESS AN OPTION VALUE?
1087 There are two ways an instance method can set and retrieve an option's
1088 value. One is to use the configure and cget methods, as shown below.
1089
1090 % snit::type dog {
1091 option -weight 10
1092
1093 method gainWeight {} {
1094 set wt [$self cget -weight]
1095 incr wt
1096 $self configure -weight $wt
1097 }
1098 }
1099 ::dog
1100 % dog fido
1101 ::fido
1102 % fido cget -weight
1103 10
1104 % fido gainWeight
1105 % fido cget -weight
1106 11
1107 %
1108
1109
1110 Alternatively, Snit provides a built-in array instance variable called
1111 options. The indices are the option names; the values are the option
1112 values. The method gainWeight can thus be rewritten as follows:
1113
1114 method gainWeight {} {
1115 incr options(-weight)
1116 }
1117
1118
1119 As you can see, using the options variable involves considerably less
1120 typing and is the usual way to do it. But if you use -configuremethod
1121 or -cgetmethod (described in the following answers), you might wish to
1122 use the configure and cget methods anyway, just so that any special
1123 processing you've implemented is sure to get done. Also, if the option
1124 is delegated to a component then configure and cget are the only way to
1125 access it without accessing the component directly. See DELEGATION for
1126 more information.
1127
1128 HOW CAN I MAKE AN OPTION READ-ONLY?
1129 Define the option with -readonly yes.
1130
1131 Suppose you've got an option that determines how instances of your type
1132 are constructed; it must be set at creation time, after which it's con‐
1133 stant. For example, a dog never changes its breed; it might or might
1134 not have had its shots, and if not can have them at a later time.
1135 -breed should be read-only, but -shots should not be.
1136
1137 % snit::type dog {
1138 option -breed -default mongrel -readonly yes
1139 option -shots -default no
1140 }
1141 ::dog
1142 % dog fido -breed retriever
1143 ::fido
1144 % fido configure -shots yes
1145 % fido configure -breed terrier
1146 option -breed can only be set at instance creation
1147 %
1148
1149
1150 HOW CAN I CATCH ACCESSES TO AN OPTION'S VALUE?
1151 Define a -cgetmethod for the option.
1152
1153 WHAT IS A -CGETMETHOD?
1154 A -cgetmethod is a method that's called whenever the related option's
1155 value is queried via the cget instance method. The handler can compute
1156 the option's value, retrieve it from a database, or do anything else
1157 you'd like it to do.
1158
1159 Here's what the default behavior would look like if written using a
1160 -cgetmethod:
1161
1162 snit::type dog {
1163 option -color -default brown -cgetmethod GetOption
1164
1165 method GetOption {option} {
1166 return $options($option)
1167 }
1168 }
1169
1170
1171 Any instance method can be used, provided that it takes one argument,
1172 the name of the option whose value is to be retrieved.
1173
1174 HOW CAN I CATCH CHANGES TO AN OPTION'S VALUE?
1175 Define a -configuremethod for the option.
1176
1177 WHAT IS A -CONFIGUREMETHOD?
1178 A -configuremethod is a method that's called whenever the related
1179 option is given a new value via the configure or configurelist instance
1180 methods. The method can pass the value on to some other object, store
1181 it in a database, or do anything else you'd like it to do.
1182
1183 Here's what the default configuration behavior would look like if writ‐
1184 ten using a -configuremethod:
1185
1186 snit::type dog {
1187 option -color -default brown -configuremethod SetOption
1188
1189 method SetOption {option value} {
1190 set options($option) $value
1191 }
1192 }
1193
1194
1195 Any instance method can be used, provided that it takes two arguments,
1196 the name of the option and the new value.
1197
1198 Note that if your method doesn't store the value in the options array,
1199 the options array won't get updated.
1200
1201 HOW CAN I VALIDATE AN OPTION'S VALUE?
1202 Define a -validatemethod.
1203
1204 WHAT IS A -VALIDATEMETHOD?
1205 A -validatemethod is a method that's called whenever the related option
1206 is given a new value via the configure or configurelist instance meth‐
1207 ods. It's the method's responsibility to determine whether the new
1208 value is valid, and throw an error if it isn't. The -validatemethod,
1209 if any, is called before the value is stored in the options array; in
1210 particular, it's called before the -configuremethod, if any.
1211
1212 For example, suppose an option always takes a Boolean value. You can
1213 ensure that the value is in fact a valid Boolean like this:
1214 % snit::type dog {
1215 option -shots -default no -validatemethod BooleanOption
1216
1217 method BooleanOption {option value} {
1218 if {![string is boolean -strict $value]} {
1219 error "expected a boolean value, got \"$value\""
1220 }
1221 }
1222 }
1223 ::dog
1224 % dog fido
1225 % fido configure -shots yes
1226 % fido configure -shots NotABooleanValue
1227 expected a boolean value, got "NotABooleanValue"
1228 %
1229
1230 Note that the same -validatemethod can be used to validate any number
1231 of boolean options.
1232
1233 Any method can be a -validatemethod provided that it takes two argu‐
1234 ments, the option name and the new option value.
1235
1237 WHAT IS A TYPE VARIABLE?
1238 A type variable is a private variable associated with a Snit type
1239 rather than with a particular instance of the type. In C++ and Java,
1240 the term static member variable is used for the same notion. Type
1241 variables can be scalars or arrays.
1242
1243 HOW IS A SCALAR TYPE VARIABLE DEFINED?
1244 Scalar type variables are defined in the type definition using the
1245 typevariable statement. You can simply name it, or you can initialize
1246 it with a value:
1247
1248 snit::type mytype {
1249 # Define variable "greeting" and initialize it with "Howdy!"
1250 typevariable greeting "Howdy!"
1251 }
1252
1253
1254 Every object of type mytype now has access to a single variable called
1255 greeting.
1256
1257 HOW IS AN ARRAY-VALUED TYPE VARIABLE DEFINED?
1258 Array-valued type variables are also defined using the typevariable
1259 command; to initialize them, include the -array option:
1260
1261 snit::type mytype {
1262 # Define typearray variable "greetings"
1263 typevariable greetings -array {
1264 formal "Good Evening"
1265 casual "Howdy!"
1266 }
1267 }
1268
1269
1270 WHAT HAPPENS IF I DON'T INITIALIZE A TYPE VARIABLE?
1271 Variables do not really exist until they are given values. If you do
1272 not initialize a variable when you define it, then you must be sure to
1273 assign a value to it (in the type constructor, say) before you refer‐
1274 ence it.
1275
1276 ARE THERE ANY LIMITATIONS ON TYPE VARIABLE NAMES?
1277 Type variable names have the same restrictions as the names of INSTANCE
1278 VARIABLES do.
1279
1280 DO I NEED TO DECLARE MY TYPE VARIABLES IN MY METHODS?
1281 No. Once you've defined a type variable in the type definition, it can
1282 be used in INSTANCE METHODS or TYPE METHODS without declaration. This
1283 differs from normal Tcl practice, in which all non-local variables in a
1284 proc need to be declared.
1285
1286 Type variables are subject to the same speed/readability tradeoffs as
1287 instance variables; see Do I need to declare my instance variables in
1288 my methods?
1289
1290 HOW DO I PASS A TYPE VARIABLE'S NAME TO ANOTHER OBJECT?
1291 In Tk, it's common to pass a widget a variable name; for example, Tk
1292 label widgets have a -textvariable option which names the variable
1293 which will contain the widget's text. This allows the program to
1294 update the label's value just by assigning a new value to the variable.
1295
1296 If you naively pass a type variable name to the label widget, you'll be
1297 confused by the result; Tk will assume that the name names a global
1298 variable. Instead, you need to provide a fully-qualified variable
1299 name. From within an instance method or a constructor, you can fully
1300 qualify the type variable's name using the mytypevar command:
1301
1302 snit::widget mywidget {
1303 typevariable labeltext ""
1304
1305 constructor {args} {
1306 # ...
1307
1308 label $win.label -textvariable [mytypevar labeltext]
1309
1310 # ...
1311 }
1312 }
1313
1314
1315 HOW DO I MAKE A TYPE VARIABLE PUBLIC?
1316 There are two ways to do this. The preferred way is to write a pair of
1317 TYPE METHODS to set and query the type variable's value.
1318
1319 Type variables are stored in the type's namespace, which has the same
1320 name as the type itself. Thus, you can also publicize the type vari‐
1321 able's name in your documentation so that clients can access it
1322 directly. For example,
1323
1324 snit::type mytype {
1325 typevariable myvariable
1326 }
1327
1328 set ::mytype::myvariable "New Value"
1329
1330
1332 WHAT IS A TYPE METHOD?
1333 A type method is a procedure associated with the type itself rather
1334 than with any specific instance of the type, and called as a subcommand
1335 of the type command.
1336
1337 HOW DO I DEFINE A TYPE METHOD?
1338 Type methods are defined in the type definition using the typemethod
1339 statement:
1340
1341 snit::type dog {
1342 # List of pedigreed dogs
1343 typevariable pedigreed
1344
1345 typemethod pedigreedDogs {} {
1346 return $pedigreed
1347 }
1348 }
1349
1350
1351 Suppose the dog type maintains a list of the names of the dogs that
1352 have pedigrees. The pedigreedDogs type method returns this list.
1353
1354 The typemethod statement looks just like a normal Tcl proc, except that
1355 it appears in a snit::type definition. Notice that every type method
1356 gets an implicit argument called type, which contains the fully-quali‐
1357 fied type name.
1358
1359 HOW DOES A CLIENT CALL A TYPE METHOD?
1360 The type method name becomes a subcommand of the type's command. For
1361 example, assuming that the constructor adds each pedigreed dog to the
1362 list of pedigreedDogs,
1363
1364 snit::type dog {
1365 option -pedigreed 0
1366
1367 # List of pedigreed dogs
1368 typevariable pedigreed
1369
1370 typemethod pedigreedDogs {} {
1371 return $pedigreed
1372 }
1373
1374 # ...
1375 }
1376
1377 dog spot -pedigreed 1
1378 dog fido
1379
1380 foreach dog [dog pedigreedDogs] { ... }
1381
1382
1383 ARE THERE ANY LIMITATIONS ON TYPE METHOD NAMES?
1384 Not really, so long as you avoid the standard type method names: cre‐
1385 ate, destroy, and info.
1386
1387 HOW DO I MAKE A TYPE METHOD PRIVATE?
1388 It's sometimes useful to define private type methods, that is, type
1389 methods intended to be called only by other type or instance methods of
1390 the same object.
1391
1392 Snit doesn't implement any access control on type methods; by conven‐
1393 tion, the names of public methods begin with a lower-case letter, and
1394 the names of private methods begin with an upper-case letter.
1395
1396 Alternatively, a Snit proc can be used as a private type method; see
1397 PROCS.
1398
1399 ARE THERE ANY LIMITATIONS ON TYPE METHOD ARGUMENTS?
1400 Method argument lists are defined just like normal Tcl proc argument
1401 lists; in particular, they can include arguments with default values
1402 and the args argument.
1403
1404 However, every type method is called with an implicit argument called
1405 type that contains the name of the type command. In addition, type
1406 methods should by convention avoid using the names of the arguments
1407 implicitly defined for INSTANCE METHODS.
1408
1409 HOW DOES AN INSTANCE OR TYPE METHOD CALL A TYPE METHOD?
1410 If an instance or type method needs to call a type method, it should
1411 use $type to do so:
1412
1413 snit::type dog {
1414
1415 typemethod pedigreedDogs {} { ... }
1416
1417 typemethod printPedigrees {} {
1418 foreach obj [$type pedigreedDogs] { ... }
1419 }
1420 }
1421
1422
1423 HOW DO I PASS A TYPE METHOD AS A CALLBACK?
1424 It's common in Tcl to pass a snippet of code to another object, for it
1425 to call later. Because types cannot be renamed, you can just use the
1426 type name, or, if the callback is registered from within a type method,
1427 type. For example, suppose we want to print a list of pedigreed dogs
1428 when a Tk button is pushed:
1429
1430 button .btn -text "Pedigrees" -command [list dog printPedigrees]
1431 pack .btn
1432
1433 Alternatively, from a method or type method you can use the
1434 mytypemethod command, just as you would use mymethod to define a call‐
1435 back command for INSTANCE METHODS.
1436
1437 CAN TYPE METHODS BE HIERARCHICAL?
1438 Yes, you can define hierarchical type methods in just the same way as
1439 you can define hierarchical instance methods. See INSTANCE METHODS for
1440 more.
1441
1443 WHAT IS A PROC?
1444 A Snit proc is really just a Tcl proc defined within the type's names‐
1445 pace. You can use procs for private code that isn't related to any
1446 particular instance.
1447
1448 HOW DO I DEFINE A PROC?
1449 Procs are defined by including a proc statement in the type definition:
1450
1451 snit::type mytype {
1452 # Pops and returns the first item from the list stored in the
1453 # listvar, updating the listvar
1454 proc pop {listvar} { ... }
1455
1456 # ...
1457 }
1458
1459
1460 ARE THERE ANY LIMITATIONS ON PROC NAMES?
1461 Any name can be used, so long as it does not begin with Snit_; names
1462 beginning with Snit_ are reserved for Snit's own use. However, the
1463 wise programmer will avoid proc names (set, list, if, etc.) that would
1464 shadow standard Tcl command names.
1465
1466 proc names, being private, should begin with a capital letter according
1467 to convention; however, as there are typically no public procs in the
1468 type's namespace it doesn't matter much either way.
1469
1470 HOW DOES A METHOD CALL A PROC?
1471 Just like it calls any Tcl command. For example,
1472
1473 snit::type mytype {
1474 # Pops and returns the first item from the list stored in the
1475 # listvar, updating the listvar
1476 proc pop {listvar} { ... }
1477
1478 variable requestQueue {}
1479
1480 # Get one request from the queue and process it.
1481 method processRequest {} {
1482 set req [pop requestQueue]
1483 }
1484 }
1485
1486
1487 HOW CAN I PASS A PROC TO ANOTHER OBJECT AS A CALLBACK?
1488 The myproc command returns a callback command for the proc, just as
1489 mymethod does for a method.
1490
1492 WHAT IS A TYPE CONSTRUCTOR?
1493 A type constructor is a body of code that initializes the type as a
1494 whole, rather like a C++ static initializer. The body of a type con‐
1495 structor is executed once when the type is defined, and never again.
1496
1497 A type can have at most one type constructor.
1498
1499 HOW DO I DEFINE A TYPE CONSTRUCTOR?
1500 A type constructor is defined by using the typeconstructor statement in
1501 the type definition. For example, suppose the type uses an array-val‐
1502 ued type variable as a look-up table, and the values in the array have
1503 to be computed at start-up.
1504
1505 % snit::type mytype {
1506 typevariable lookupTable
1507
1508 typeconstructor {
1509 array set lookupTable {key value...}
1510 }
1511 }
1512
1513
1515 WHAT IS A CONSTRUCTOR?
1516 In object-oriented programming, an object's constructor is responsible
1517 for initializing the object completely at creation time. The construc‐
1518 tor receives the list of options passed to the snit::type command's
1519 create method and can then do whatever it likes. That might include
1520 computing instance variable values, reading data from files, creating
1521 other objects, updating type and instance variables, and so forth.
1522
1523 The constructor's return value is ignored (unless it's an error, of
1524 course).
1525
1526 HOW DO I DEFINE A CONSTRUCTOR?
1527 A constructor is defined by using the constructor statement in the type
1528 definition. Suppose that it's desired to keep a list of all pedigreed
1529 dogs. The list can be maintained in a type variable and retrieved by a
1530 type method. Whenever a dog is created, it can add itself to the
1531 list--provided that it's registered with the American Kennel Club.
1532
1533 % snit::type dog {
1534 option -akc 0
1535
1536 typevariable akcList {}
1537
1538 constructor {args} {
1539 $self configurelist $args
1540
1541 if {$options(-akc)} {
1542 lappend akcList $self
1543 }
1544 }
1545
1546 typemethod akclist {} {
1547 return $akcList
1548 }
1549 }
1550 ::dog
1551 % dog spot -akc 1
1552 ::spot
1553 % dog fido
1554 ::fido
1555 % dog akclist
1556 ::spot
1557 %
1558
1559
1560 WHAT DOES THE DEFAULT CONSTRUCTOR DO?
1561 If you don't provide a constructor explicitly, you get the default con‐
1562 structor, which is identical to the explicitly-defined constructor
1563 shown here:
1564
1565 snit::type dog {
1566 constructor {args} {
1567 $self configurelist $args
1568 }
1569 }
1570
1571
1572 When the constructor is called, args will be set to the list of argu‐
1573 ments that follow the object's name. The constructor is allowed to
1574 interpret this list any way it chooses; the normal convention is to
1575 assume that it's a list of option names and values, as shown in the
1576 example above. If you simply want to save the option values, you
1577 should use the configurelist method, as shown.
1578
1579 CAN I CHOOSE A DIFFERENT SET OF ARGUMENTS FOR THE CONSTRUCTOR?
1580 Yes, you can. For example, suppose we wanted to be sure that the breed
1581 was explicitly stated for every dog at creation time, and couldn't be
1582 changed thereafter. One way to do that is as follows:
1583
1584 % snit::type dog {
1585 variable breed
1586
1587 option -color brown
1588 option -akc 0
1589
1590 constructor {theBreed args} {
1591 set breed $theBreed
1592 $self configurelist $args
1593 }
1594
1595 method breed {} { return $breed }
1596 }
1597 ::dog
1598 % dog spot dalmatian -color spotted -akc 1
1599 ::spot
1600 % spot breed
1601 dalmatian
1602
1603
1604 The drawback is that this syntax is non-standard, and may limit the
1605 compatibility of your new type with other people's code. For example,
1606 Snit assumes that it can create COMPONENTS using the standard creation
1607 syntax.
1608
1609 ARE THERE ANY LIMITATIONS ON CONSTRUCTOR ARGUMENTS?
1610 Constructor argument lists are subject to the same limitations as those
1611 on instance method argument lists. It has the same implicit arguments,
1612 and can contain default values and the args argument.
1613
1614 IS THERE ANYTHING SPECIAL ABOUT WRITING THE CONSTRUCTOR?
1615 Yes. Writing the constructor can be tricky if you're delegating
1616 options to components, and there are specific issues relating to
1617 snit::widgets and snit::widgetadaptors. See DELEGATION, WIDGETS, WID‐
1618 GET ADAPTORS, and THE TK OPTION DATABASE.
1619
1621 WHAT IS A DESTRUCTOR?
1622 A destructor is a special kind of method that's called when an object
1623 is destroyed. It's responsible for doing any necessary clean-up when
1624 the object goes away: destroying COMPONENTS, closing files, and so
1625 forth.
1626
1627 HOW DO I DEFINE A DESTRUCTOR?
1628 Destructors are defined by using the destructor statement in the type
1629 definition.
1630
1631 Suppose we're maintaining a list of pedigreed dogs; then we'll want to
1632 remove dogs from it when they are destroyed.
1633
1634 snit::type dog {
1635 option -akc 0
1636
1637 typevariable akcList {}
1638
1639 constructor {args} {
1640 $self configurelist $args
1641
1642 if {$options(-akc)} {
1643 lappend akcList $self
1644 }
1645 }
1646
1647 destructor {
1648 set ndx [lsearch $akcList $self]
1649
1650 if {$ndx != -1} {
1651 set akcList [lreplace $akcList $ndx $ndx]
1652 }
1653 }
1654
1655 typemethod akclist {} {
1656 return $akcList
1657 }
1658 }
1659
1660
1661 ARE THERE ANY LIMITATIONS ON DESTRUCTOR ARGUMENTS?
1662 Yes; a destructor has no explicit arguments.
1663
1664 WHAT IMPLICIT ARGUMENTS ARE PASSED TO THE DESTRUCTOR?
1665 The destructor gets the same implicit arguments that are passed to
1666 INSTANCE METHODS: type, selfns, win, and self.
1667
1668 MUST COMPONENTS BE DESTROYED EXPLICITLY?
1669 Yes and no.
1670
1671 Any Tk widgets created by a snit::widget or snit::widgetadaptor will be
1672 destroyed automatically by Tk when the megawidget is destroyed, in
1673 keeping with normal Tk behavior (destroying a parent widget destroys
1674 the whole tree).
1675
1676 Components of normal snit::types, on the other hand, are never
1677 destroyed automatically, nor are non-widget components of Snit megawid‐
1678 gets. If your object creates them in its constructor, then it should
1679 generally destroy them in its destructor.
1680
1681 IS THERE ANY SPECIAL ABOUT WRITING A DESTRUCTOR?
1682 Yes. If an object's constructor throws an error, the object's destruc‐
1683 tor will be called to clean up; this means that the object might not be
1684 completely constructed when the destructor is called. This can cause
1685 the destructor to throw its own error; the result is usually mislead‐
1686 ing, confusing, and unhelpful. Consequently, it's important to write
1687 your destructor so that it's fail-safe.
1688
1689 For example, a dog might create a tail component; the component will
1690 need to be destroyed. But suppose there's an error while processing
1691 the creation options--the destructor will be called, and there will be
1692 no tail to destroy. The simplest solution is generally to catch and
1693 ignore any errors while destroying components.
1694 snit::type dog {
1695 component tail
1696
1697 constructor {args} {
1698 $self configurelist $args
1699
1700 set tail [tail %AUTO%]
1701 }
1702
1703 destructor {
1704 catch {$tail destroy}
1705 }
1706 }
1707
1708
1710 WHAT IS A COMPONENT?
1711 Often an object will create and manage a number of other objects. A
1712 Snit megawidget, for example, will often create a number of Tk widgets.
1713 These objects are part of the main object; it is composed of them, so
1714 they are called components of the object.
1715
1716 But Snit also has a more precise meaning for COMPONENT. The components
1717 of a Snit object are those objects to which methods or options can be
1718 delegated. (See DELEGATION for more information about delegation.)
1719
1720 HOW DO I DECLARE A COMPONENT?
1721 First, you must decide what role a component plays within your object,
1722 and give the role a name. Then, you declare the component using its
1723 role name and the component statement. The component statement
1724 declares an instance variable which is used to store the component's
1725 command name when the component is created.
1726
1727 For example, suppose your dog object creates a tail object (the better
1728 to wag with, no doubt):
1729
1730 snit::type dog {
1731 component mytail
1732
1733 constructor {args} {
1734 # Create and save the component's command
1735 set mytail [tail %AUTO% -partof $self]
1736 $self configurelist $args
1737 }
1738
1739 method wag {} {
1740 $mytail wag
1741 }
1742 }
1743
1744
1745 As shown here, it doesn't matter what the tail object's real name is;
1746 the dog object refers to it by its component name.
1747
1748 The above example shows one way to delegate the wag method to the
1749 mytail component; see DELEGATION for an easier way.
1750
1751 HOW IS A COMPONENT NAMED?
1752 A component has two names. The first name is that of the component
1753 variable; this represents the role the component object plays within
1754 the Snit object. This is the component name proper, and is the name
1755 used to refer to the component within Snit code. The second name is
1756 the name of the actual component object created by the Snit object's
1757 constructor. This second name is always a Tcl command name, and is
1758 referred to as the component's object name.
1759
1760 In the example in the previous question, the component name is mytail;
1761 the mytail component's object name is chosen automatically by Snit
1762 since %AUTO% was used when the component object was created.
1763
1764 ARE THERE ANY LIMITATIONS ON COMPONENT NAMES?
1765 Yes. snit::widget and snit::widgetadaptor objects have a special com‐
1766 ponent called the hull component; thus, the name hull should be used
1767 for no other purpose.
1768
1769 Otherwise, since component names are in fact instance variable names
1770 they must follow the rules for INSTANCE VARIABLES.
1771
1772 WHAT IS AN OWNED COMPONENT?
1773 An owned component is a component whose object command's lifetime is
1774 controlled by the snit::type or snit::widget.
1775
1776 As stated above, a component is an object to which our object can dele‐
1777 gate methods or options. Under this definition, our object will usu‐
1778 ally create its component objects, but not necessarily. Consider the
1779 following: a dog object has a tail component; but tail knows that it's
1780 part of the dog:
1781 snit::type dog {
1782 component mytail
1783
1784 constructor {args} {
1785 set mytail [tail %AUTO% -partof $self]
1786 $self configurelist $args
1787 }
1788
1789 destructor {
1790 catch {$mytail destroy}
1791 }
1792
1793 delegate method wagtail to mytail as wag
1794
1795 method bark {} {
1796 return "$self barked."
1797 }
1798 }
1799
1800 snit::type tail {
1801 component mydog
1802 option -partof -readonly yes
1803
1804 constructor {args} {
1805 $self configurelist $args
1806 set mydog $options(-partof)
1807 }
1808
1809 method wag {} {
1810 return "Wag, wag."
1811 }
1812
1813 method pull {} {
1814 $mydog bark
1815 }
1816 }
1817
1818 Thus, if you ask a dog to wag its tail, it tells its tail to wag; and
1819 if you pull the dog's tail, the tail tells the dog to bark. In this
1820 scenario, the tail is a component of the dog, and the dog is a compo‐
1821 nent of the tail, but the dog owns the tail and not the other way
1822 around.
1823
1824 WHAT DOES THE INSTALL COMMAND DO?
1825 The install command creates an owned component using a specified com‐
1826 mand, and assigns the result to the component's instance variable. For
1827 example:
1828 snit::type dog {
1829 component mytail
1830
1831 constructor {args} {
1832 # set mytail [tail %AUTO% -partof $self]
1833 install mytail using tail %AUTO% -partof $self
1834 $self configurelist $args
1835 }
1836 }
1837
1838 In a snit::type's code, the install command shown above is equivalent
1839 to the set mytail command that's commented out. In a snit::widget's or
1840 snit::widgetadaptor's, code, however, the install command also queries
1841 THE TK OPTION DATABASE and initializes the new component's options
1842 accordingly. For consistency, it's a good idea to get in the habit of
1843 using install for all owned components.
1844
1845 MUST OWNED COMPONENTS BE CREATED IN THE CONSTRUCTOR?
1846 No, not necessarily. In fact, there's no reason why an object can't
1847 destroy and recreate a component multiple times over its own lifetime.
1848
1849 ARE THERE ANY LIMITATIONS ON COMPONENT OBJECT NAMES?
1850 Yes.
1851
1852 Component objects which are Tk widgets or megawidgets must have valid
1853 Tk window names.
1854
1855 Component objects which are not widgets or megawidgets must have fully-
1856 qualified command names, i.e., names which include the full namespace
1857 of the command. Note that Snit always creates objects with fully qual‐
1858 ified names.
1859
1860 Next, the object names of components and owned by your object must be
1861 unique. This is no problem for widget components, since widget names
1862 are always unique; but consider the following code:
1863
1864 snit::type tail { ... }
1865
1866 snit::type dog {
1867 delegate method wag to mytail
1868
1869 constructor {} {
1870 install mytail using tail mytail
1871 }
1872 }
1873
1874
1875 This code uses the component name, mytail, as the component object
1876 name. This is not good, and here's why: Snit instance code executes in
1877 the Snit type's namespace. In this case, the mytail component is cre‐
1878 ated in the ::dog:: namespace, and will thus have the name
1879 ::dog::mytail.
1880
1881 Now, suppose you create two dogs. Both dogs will attempt to create a
1882 tail called ::dog::mytail. The first will succeed, and the second will
1883 fail, since Snit won't let you create an object if its name is already
1884 a command. Here are two ways to avoid this situation:
1885
1886 First, if the component type is a snit::type you can specify %AUTO% as
1887 its name, and be guaranteed to get a unique name. This is the safest
1888 thing to do:
1889
1890 install mytail using tail %AUTO%
1891
1892
1893 If the component type isn't a snit::type you can create the component
1894 in the object's instance namespace:
1895
1896 install mytail using tail ${selfns}::mytail
1897
1898
1899 Make sure you pick a unique name within the instance namespace.
1900
1901 MUST I DESTROY THE COMPONENTS I OWN?
1902 That depends. When a parent widget is destroyed, all child widgets are
1903 destroyed automatically. Thus, if your object is a snit::widget or
1904 snit::widgetadaptor you don't need to destroy any components that are
1905 widgets, because they will generally be children or descendants of your
1906 megawidget.
1907
1908 If your object is an instance of snit::type, though, none of its owned
1909 components will be destroyed automatically, nor will be non-widget com‐
1910 ponents of a snit::widget be destroyed automatically. All such owned
1911 components must be destroyed explicitly, or they won't be destroyed at
1912 all.
1913
1914 CAN I EXPOSE A COMPONENT'S OBJECT COMMAND AS PART OF MY INTERFACE?
1915 Yes, and there are two ways to do it. The most appropriate way is usu‐
1916 ally to use DELEGATION. Delegation allows you to pass the options and
1917 methods you specify along to particular components. This effectively
1918 hides the components from the users of your type, and ensures good
1919 encapsulation.
1920
1921 However, there are times when it's appropriate, not to mention simpler,
1922 just to make the entire component part of your type's public interface.
1923
1924 HOW DO I EXPOSE A COMPONENT'S OBJECT COMMAND?
1925 When you declare the component, specify the component statement's -pub‐
1926 lic option. The value of this option is the name of a method which
1927 will be delegated to your component's object command.
1928
1929 For example, supposed you've written a combobox megawidget which owns a
1930 listbox widget, and you want to make the listbox's entire interface
1931 public. You can do it like this:
1932
1933 snit::widget combobox {
1934 component listbox -public listbox
1935
1936 constructor {args} {
1937 install listbox using listbox $win.listbox ....
1938 }
1939 }
1940
1941 combobox .mycombo
1942
1943
1944 Your comobox widget, .mycombo, now has a listbox method which has all
1945 of the same subcommands as the listbox widget itself. Thus, the above
1946 code sets the listbox component's width to 30.
1947
1948 Usually you'll let the method name be the same as the component name;
1949 however, you can name it anything you like.
1950
1952 WHAT IS A TYPE COMPONENT?
1953 A type component is a component that belongs to the type itself instead
1954 of to a particular instance of the type. The relationship between com‐
1955 ponents and type components is the same as the relationship between
1956 INSTANCE VARIABLES and TYPE VARIABLES. Both INSTANCE METHODS and TYPE
1957 METHODS can be delegated to type components.
1958
1959 Once you understand COMPONENTS and DELEGATION, type components are just
1960 more of the same.
1961
1962 HOW DO I DECLARE A TYPE COMPONENT?
1963 Declare a type component using the typecomponent statement. It takes
1964 the same options (-inherit and -public) as the component statement
1965 does, and defines a type variable to hold the type component's object
1966 command.
1967
1968 Suppose in your model you've got many dogs, but only one veterinarian.
1969 You might make the veterinarian a type component.
1970 snit::type veterinarian { ... }
1971
1972 snit::type dog {
1973 typecomponent vet
1974
1975 # ...
1976 }
1977
1978
1979 HOW DO I INSTALL A TYPE COMPONENT?
1980 Just use the set command to assign the component's object command to
1981 the type component. Because types (even snit::widget types) are not
1982 widgets, and do not have options anyway, the extra features of the
1983 install command are not needed.
1984
1985 You'll usually install type components in the type constructor, as
1986 shown here:
1987 snit::type veterinarian { ... }
1988
1989 snit::type dog {
1990 typecomponent vet
1991
1992 typeconstructor {
1993 set vet [veterinarian %AUTO%]
1994 }
1995 }
1996
1997
1998 ARE THERE ANY LIMITATIONS ON TYPE COMPONENT NAMES?
1999 Yes, the same as on INSTANCE VARIABLES, TYPE VARIABLES, and normal COM‐
2000 PONENTS.
2001
2003 WHAT IS DELEGATION?
2004 Delegation, simply put, is when you pass a task you've been given to
2005 one of your assistants. (You do have assistants, don't you?) Snit
2006 objects can do the same thing. The following example shows one way in
2007 which the dog object can delegate its wag method and its -taillength
2008 option to its tail component.
2009
2010 snit::type dog {
2011 variable mytail
2012
2013 option -taillength -configuremethod SetTailOption -cgetmethod GetTailOption
2014
2015
2016 method SetTailOption {option value} {
2017 $mytail configure $option $value
2018 }
2019
2020 method GetTailOption {option} {
2021 $mytail cget $option
2022 }
2023
2024 method wag {} {
2025 $mytail wag
2026 }
2027
2028 constructor {args} {
2029 install mytail using tail %AUTO% -partof $self
2030 $self configurelist $args
2031 }
2032
2033 }
2034
2035
2036 This is the hard way to do it, by it demonstrates what delegation is
2037 all about. See the following answers for the easy way to do it.
2038
2039 Note that the constructor calls the configurelist method after it cre‐
2040 ates its tail; otherwise, if -taillength appeared in the list of args
2041 we'd get an error.
2042
2043 HOW CAN I DELEGATE A METHOD TO A COMPONENT OBJECT?
2044 Delegation occurs frequently enough that Snit makes it easy. Any method
2045 can be delegated to any component or type component by placing a single
2046 delegate statement in the type definition. (See COMPONENTS and TYPE
2047 COMPONENTS for more information about component names.)
2048
2049 For example, here's a much better way to delegate the dog object's wag
2050 method:
2051
2052 % snit::type dog {
2053 delegate method wag to mytail
2054
2055 constructor {} {
2056 install mytail using tail %AUTO%
2057 }
2058 }
2059 ::dog
2060 % snit::type tail {
2061 method wag {} { return "Wag, wag, wag."}
2062 }
2063 ::tail
2064 % dog spot
2065 ::spot
2066 % spot wag
2067 Wag, wag, wag.
2068
2069
2070 This code has the same effect as the code shown under the previous
2071 question: when a dog's wag method is called, the call and its arguments
2072 are passed along automatically to the tail object.
2073
2074 Note that when a component is mentioned in a delegate statement, the
2075 component's instance variable is defined implicitly. However, it's
2076 still good practice to declare it explicitly using the component state‐
2077 ment.
2078
2079 Note also that you can define a method name using the method statement,
2080 or you can define it using delegate; you can't do both.
2081
2082 CAN I DELEGATE TO A METHOD WITH A DIFFERENT NAME?
2083 Suppose you wanted to delegate the dog's wagtail method to the tail's
2084 wag method. After all you wag the tail, not the dog. It's easily
2085 done:
2086
2087 snit::type dog {
2088 delegate method wagtail to mytail as wag
2089
2090 constructor {args} {
2091 install mytail using tail %AUTO% -partof $self
2092 $self configurelist $args
2093 }
2094 }
2095
2096
2097 CAN I DELEGATE TO A METHOD WITH ADDITIONAL ARGUMENTS?
2098 Suppose the tail's wag method takes as an argument the number of times
2099 the tail should be wagged. You want to delegate the dog's wagtail
2100 method to the tail's wag method, specifying that the tail should be
2101 wagged exactly three times. This is easily done, too:
2102
2103 snit::type dog {
2104 delegate method wagtail to mytail as {wag 3}
2105 # ...
2106 }
2107
2108 snit::type tail {
2109 method wag {count} {
2110 return [string repeat "Wag " $count]
2111 }
2112 # ...
2113 }
2114
2115
2116 CAN I DELEGATE A METHOD TO SOMETHING OTHER THAN AN OBJECT?
2117 Normal method delegation assumes that you're delegating a method (a
2118 subcommand of an object command) to a method of another object (a sub‐
2119 command of a different object command). But not all Tcl objects follow
2120 Tk conventions, and not everything you'd to which you'd like to dele‐
2121 gate a method is necessary an object. Consequently, Snit makes it easy
2122 to delegate a method to pretty much anything you like using the dele‐
2123 gate statement's using clause.
2124
2125 Suppose your dog simulation stores dogs in a database, each dog as a
2126 single record. The database API you're using provides a number of com‐
2127 mands to manage records; each takes the record ID (a string you choose)
2128 as its first argument. For example, saverec saves a record. If you
2129 let the record ID be the name of the dog object, you can delegate the
2130 dog's save method to the saverec command as follows:
2131 snit::type dog {
2132 delegate method save using {saverec %s}
2133 }
2134
2135 The %s is replaced with the instance name when the save method is
2136 called; any additional arguments are the appended to the resulting com‐
2137 mand.
2138
2139 The using clause understands a number of other %-conversions; in addi‐
2140 tion to the instance name, you can substitute in the method name (%m),
2141 the type name (%t), the instance namespace (%n), the Tk window name
2142 (%w), and, if a component or typecomponent name was given in the dele‐
2143 gate statement, the component's object command (%c).
2144
2145 HOW CAN I DELEGATE A METHOD TO A TYPE COMPONENT OBJECT?
2146 Just exactly as you would to a component object. The delegate method
2147 statement accepts both component and type component names in its to
2148 clause.
2149
2150 HOW CAN I DELEGATE A TYPE METHOD TO A TYPE COMPONENT OBJECT?
2151 Use the delegate typemethod statement. It works like delegate method,
2152 with these differences: first, it defines a type method instead of an
2153 instance method; second, the using clause ignores the %s, %n, and %w
2154 %-conversions.
2155
2156 Naturally, you can't delegate a type method to an instance compo‐
2157 nent...Snit wouldn't know which instance should receive it.
2158
2159 HOW CAN I DELEGATE AN OPTION TO A COMPONENT OBJECT?
2160 The first question in this section (see DELEGATION) shows one way to
2161 delegate an option to a component; but this pattern occurs often enough
2162 that Snit makes it easy. For example, every tail object has a -length
2163 option; we want to allow the creator of a dog object to set the tail's
2164 length. We can do this:
2165
2166 % snit::type dog {
2167 delegate option -length to mytail
2168
2169 constructor {args} {
2170 install mytail using tail %AUTO% -partof $self
2171 $self configurelist $args
2172 }
2173 }
2174 ::dog
2175 % snit::type tail {
2176 option -partof
2177 option -length 5
2178 }
2179 ::tail
2180 % dog spot -length 7
2181 ::spot
2182 % spot cget -length
2183 7
2184
2185
2186 This produces nearly the same result as the -configuremethod and -cget‐
2187 method shown under the first question in this section: whenever a dog
2188 object's -length option is set or retrieved, the underlying tail
2189 object's option is set or retrieved in turn.
2190
2191 Note that you can define an option name using the option statement, or
2192 you can define it using delegate; you can't do both.
2193
2194 CAN I DELEGATE TO AN OPTION WITH A DIFFERENT NAME?
2195 In the previous answer we delegated the dog's -length option down to
2196 its tail. This is, of course, wrong. The dog has a length, and the
2197 tail has a length, and they are different. What we'd really like to do
2198 is give the dog a -taillength option, but delegate it to the tail's
2199 -length option:
2200
2201 snit::type dog {
2202 delegate option -taillength to mytail as -length
2203
2204 constructor {args} {
2205 set mytail [tail %AUTO% -partof $self]
2206 $self configurelist $args
2207 }
2208 }
2209
2210
2211 HOW CAN I DELEGATE ANY UNRECOGNIZED METHOD OR OPTION TO A COMPONENT OBJECT?
2212 It may happen that a Snit object gets most of its behavior from one of
2213 its components. This often happens with snit::widgetadaptors, for
2214 example, where we wish to slightly the modify the behavior of an exist‐
2215 ing widget. To carry on with our dog example, however, suppose that we
2216 have a snit::type called animal that implements a variety of animal
2217 behaviors--moving, eating, sleeping, and so forth. We want our dog
2218 objects to inherit these same behaviors, while adding dog-like behav‐
2219 iors of its own. Here's how we can give a dog methods and options of
2220 its own while delegating all other methods and options to its animal
2221 component:
2222
2223 snit::type dog {
2224 delegate option * to animal
2225 delegate method * to animal
2226
2227 option -akc 0
2228
2229 constructor {args} {
2230 install animal using animal %AUTO% -name $self
2231 $self configurelist $args
2232 }
2233
2234 method wag {} {
2235 return "$self wags its tail"
2236 }
2237 }
2238
2239
2240 That's it. A dog is now an animal that has a -akc option and can wag
2241 its tail.
2242
2243 Note that we don't need to specify the full list of method names or
2244 option names that animal will receive. It gets anything dog doesn't
2245 recognize--and if it doesn't recognize it either, it will simply throw
2246 an error, just as it should.
2247
2248 You can also delegate all unknown type methods to a type component
2249 using delegate typemethod *.
2250
2251 HOW CAN I DELEGATE ALL BUT CERTAIN METHODS OR OPTIONS TO A COMPONENT?
2252 In the previous answer, we said that every dog is an animal by delegat‐
2253 ing all unknown methods and options to the animal component. But what
2254 if the animal type has some methods or options that we'd like to sup‐
2255 press?
2256
2257 One solution is to explicitly delegate all the options and methods, and
2258 forgo the convenience of delegate method * and delegate option *. But
2259 if we wish to suppress only a few options or methods, there's an easier
2260 way:
2261
2262 snit::type dog {
2263 delegate option * to animal except -numlegs
2264 delegate method * to animal except {fly climb}
2265
2266 # ...
2267
2268 constructor {args} {
2269 install animal using animal %AUTO% -name $self -numlegs 4
2270 $self configurelist $args
2271 }
2272
2273 # ...
2274 }
2275
2276
2277 Dogs have four legs, so we specify that explicitly when we create the
2278 animal component, and explicitly exclude -numlegs from the set of dele‐
2279 gated options. Similarly, dogs can neither fly nor climb, so we
2280 exclude those animal methods as shown.
2281
2282 CAN A HIERARCHICAL METHOD BE DELEGATED?
2283 Yes; just specify multiple words in the delegated method's name:
2284
2285 snit::type tail {
2286 method wag {} {return "Wag, wag"}
2287 method droop {} {return "Droop, droop"}
2288 }
2289
2290
2291 snit::type dog {
2292 delegate method {tail wag} to mytail
2293 delegate method {tail droop} to mytail
2294
2295 # ...
2296
2297 constructor {args} {
2298 install mytail using tail %AUTO%
2299 $self configurelist $args
2300 }
2301
2302 # ...
2303 }
2304
2305
2306 Unrecognized hierarchical methods can also be delegated; the following
2307 code delegates all subcommands of the "tail" method to the "mytail"
2308 component:
2309
2310 snit::type dog {
2311 delegate method {tail *} to mytail
2312
2313 # ...
2314 }
2315
2316
2318 WHAT IS A SNIT::WIDGET?
2319 A snit::widget is the Snit version of what Tcl programmers usually call
2320 a megawidget: a widget-like object usually consisting of one or more Tk
2321 widgets all contained within a Tk frame.
2322
2323 A snit::widget is also a special kind of snit::type. Just about every‐
2324 thing in this FAQ list that relates to snit::types also applies to
2325 snit::widgets.
2326
2327 HOW DO I DEFINE A SNIT::WIDGET?
2328 snit::widgets are defined using the snit::widget command, just as
2329 snit::types are defined by the snit::type command.
2330
2331 The body of the definition can contain all of the same kinds of state‐
2332 ments, plus a couple of others which will be mentioned below.
2333
2334 HOW DO SNIT::WIDGETS DIFFER FROM SNIT::TYPES?
2335 · The name of an instance of a snit::type can be any valid Tcl
2336 command name, in any namespace. The name of an instance of a
2337 snit::widget must be a valid Tk widget name, and its parent wid‐
2338 get must already exist.
2339
2340 · An instance of a snit::type can be destroyed by calling its
2341 destroy method. Instances of a snit::widget have no destroy
2342 method; use the Tk destroy command instead.
2343
2344 · Every instance of a snit::widget has one predefined component
2345 called its hull component. The hull is usually a Tk frame or
2346 toplevel widget; any other widgets created as part of the
2347 snit::widget will usually be contained within the hull.
2348
2349 · snit::widgets can have their options receive default values from
2350 THE TK OPTION DATABASE.
2351
2352 WHAT IS A HULL COMPONENT?
2353 Snit can't create a Tk widget object; only Tk can do that. Thus, every
2354 instance of a snit::widget must be wrapped around a genuine Tk widget;
2355 this Tk widget is called the hull component. Snit effectively piggy‐
2356 backs the behavior you define (methods, options, and so forth) on top
2357 of the hull component so that the whole thing behaves like a standard
2358 Tk widget.
2359
2360 For snit::widgets the hull component must be a Tk widget that defines
2361 the -class option.
2362
2363 snit::widgetadaptors differ from snit::widgets chiefly in that any kind
2364 of widget can be used as the hull component; see WIDGET ADAPTORS.
2365
2366 HOW CAN I SET THE HULL TYPE FOR A SNIT::WIDGET?
2367 A snit::widget's hull component will usually be a Tk frame widget; how‐
2368 ever, it may be any Tk widget that defines the -class option. You can
2369 explicitly choose the hull type you prefer by including the hulltype
2370 command in the widget definition:
2371
2372 snit::widget mytoplevel {
2373 hulltype toplevel
2374
2375 # ...
2376 }
2377
2378
2379 If no hulltype command appears, the hull will be a frame.
2380
2381 By default, Snit recognizes the following hull types: the Tk widgets
2382 frame, labelframe, toplevel, and the Tile widgets ttk::frame,
2383 ttk::labelframe, and ttk::toplevel. To enable the use of some other
2384 kind of widget as the hull type, you can lappend the widget command to
2385 the variable snit::hulltypes (always provided the widget defines the
2386 -class option. For example, suppose Tk gets a new widget type called a
2387 prettyframe:
2388
2389 lappend snit::hulltypes prettyframe
2390
2391 snit::widget mywidget {
2392 hulltype prettyframe
2393
2394 # ...
2395 }
2396
2397
2398 HOW SHOULD I NAME WIDGETS WHICH ARE COMPONENTS OF A SNIT::WIDGET?
2399 Every widget, whether a genuine Tk widget or a Snit megawidget, has to
2400 have a valid Tk window name. When a snit::widget is first created, its
2401 instance name, self, is a Tk window name; however, if the snit::widget
2402 is used as the hull component by a snit::widgetadaptor its instance
2403 name will be changed to something else. For this reason, every
2404 snit::widget method, constructor, destructor, and so forth is passed
2405 another implicit argument, win, which is the window name of the megaw‐
2406 idget. Any children should be named using win as the root.
2407
2408 Thus, suppose you're writing a toolbar widget, a frame consisting of a
2409 number of buttons placed side-by-side. It might look something like
2410 this:
2411
2412 snit::widget toolbar {
2413 delegate option * to hull
2414
2415 constructor {args} {
2416 button $win.open -text Open -command [mymethod open]
2417 button $win.save -text Save -command [mymethod save]
2418
2419 # ....
2420
2421 $self configurelist $args
2422
2423 }
2424 }
2425
2426
2427 See also the question on renaming objects, toward the top of this file.
2428
2430 WHAT IS A SNIT::WIDGETADAPTOR?
2431 A snit::widgetadaptor is a kind of snit::widget. Whereas a snit::wid‐
2432 get's hull is automatically created and is always a Tk frame, a
2433 snit::widgetadaptor can be based on any Tk widget--or on any Snit
2434 megawidget, or even (with luck) on megawidgets defined using some other
2435 package.
2436
2437 It's called a widget adaptor because it allows you to take an existing
2438 widget and customize its behavior.
2439
2440 HOW DO I DEFINE A SNIT::WIDGETADAPTOR?
2441 Use the snit::widgetadaptor command. The definition for a snit::wid‐
2442 getadaptor looks just like that for a snit::type or snit::widget,
2443 except that the constructor must create and install the hull component.
2444
2445 For example, the following code creates a read-only text widget by the
2446 simple device of turning its insert and delete methods into no-ops.
2447 Then, we define new methods, ins and del, which get delegated to the
2448 hull component as insert and delete. Thus, we've adapted the text wid‐
2449 get and given it new behavior while still leaving it fundamentally a
2450 text widget.
2451
2452 ::snit::widgetadaptor rotext {
2453
2454 constructor {args} {
2455 # Create the text widget; turn off its insert cursor
2456 installhull using text -insertwidth 0
2457
2458 # Apply any options passed at creation time.
2459 $self configurelist $args
2460 }
2461
2462 # Disable the text widget's insert and delete methods, to
2463 # make this readonly.
2464 method insert {args} {}
2465 method delete {args} {}
2466
2467 # Enable ins and del as synonyms, so the program can insert and
2468 # delete.
2469 delegate method ins to hull as insert
2470 delegate method del to hull as delete
2471
2472 # Pass all other methods and options to the real text widget, so
2473 # that the remaining behavior is as expected.
2474 delegate method * to hull
2475 delegate option * to hull
2476 }
2477
2478
2479 The most important part is in the constructor. Whereas snit::widget
2480 creates the hull for you, snit::widgetadaptor cannot -- it doesn't know
2481 what kind of widget you want. So the first thing the constructor does
2482 is create the hull component (a Tk text widget in this case), and then
2483 installs it using the installhull command.
2484
2485 Note: There is no instance command until you create one by installing a
2486 hull component. Any attempt to pass methods to $self prior to calling
2487 installhull will fail.
2488
2489 CAN I ADAPT A WIDGET CREATED ELSEWHERE IN THE PROGRAM?
2490 Yes.
2491
2492 At times, it can be convenient to adapt a pre-existing widget instead
2493 of creating your own. For example, the Bwidget PagesManager widget
2494 manages a set of frame widgets, only one of which is visible at a time.
2495 The application chooses which frame is visible. All of the These
2496 frames are created by the PagesManager itself, using its add method.
2497 It's convenient to adapt these frames to do what we'd like them to do.
2498
2499 In a case like this, the Tk widget will already exist when the
2500 snit::widgetadaptor is created. Snit provides an alternate form of the
2501 installhull command for this purpose:
2502
2503 snit::widgetadaptor pageadaptor {
2504 constructor {args} {
2505 # The widget already exists; just install it.
2506 installhull $win
2507
2508 # ...
2509 }
2510 }
2511
2512
2513 CAN I ADAPT ANOTHER MEGAWIDGET?
2514 Maybe. If the other megawidget is a snit::widget or snit::widgetadap‐
2515 tor, then yes. If it isn't then, again, maybe. You'll have to try it
2516 and see. You're most likely to have trouble with widget destruc‐
2517 tion--you have to make sure that your megawidget code receives the
2518 <Destroy> event before the megawidget you're adapting does.
2519
2521 WHAT IS THE TK OPTION DATABASE?
2522 The Tk option database is a database of default option values main‐
2523 tained by Tk itself; every Tk application has one. The concept of the
2524 option database derives from something called the X Windows resource
2525 database; however, the option database is available in every Tk imple‐
2526 mentation, including those which do not use the X Windows system (e.g.,
2527 Microsoft Windows).
2528
2529 Full details about the Tk option database are beyond the scope of this
2530 document; both Practical Programming in Tcl and Tk by Welch, Jones, and
2531 Hobbs, and Effective Tcl/Tk Programming by Harrison and McClennan.,
2532 have good introductions to it.
2533
2534 Snit is implemented so that most of the time it will simply do the
2535 right thing with respect to the option database, provided that the wid‐
2536 get developer does the right thing by Snit. The body of this section
2537 goes into great deal about what Snit requires. The following is a
2538 brief statement of the requirements, for reference.
2539
2540 · If the widget's default widget class is not what is desired, set
2541 it explicitly using the widgetclass statement in the widget def‐
2542 inition.
2543
2544 · When defining or delegating options, specify the resource and
2545 class names explicitly when necessary.
2546
2547 · Use the installhull using command to create and install the hull
2548 for snit::widgetadaptors.
2549
2550 · Use the install command to create and install all components
2551 which are widgets.
2552
2553 · Use the install command to create and install components which
2554 aren't widgets if you'd like them to receive option values from
2555 the option database.
2556
2557 The interaction of Tk widgets with the option database is a complex
2558 thing; the interaction of Snit with the option database is even more
2559 so, and repays attention to detail.
2560
2561 DO SNIT::TYPES USE THE TK OPTION DATABASE?
2562 No, they don't; querying the option database requires a Tk window name,
2563 and snit::types don't have one.
2564
2565 If you create an instance of a snit::type as a component of a
2566 snit::widget or snit::widgetadaptor, on the other hand, and if any
2567 options are delegated to the component, and if you use install to cre‐
2568 ate and install it, then the megawidget will query the option database
2569 on the snit::type's behalf. This might or might not be what you want,
2570 so take care.
2571
2572 WHAT IS MY SNIT::WIDGET'S WIDGET CLASS?
2573 Every Tk widget has a "widget class": a name that is used when adding
2574 option settings to the database. For Tk widgets, the widget class is
2575 the same as the widget command name with an initial capital. For exam‐
2576 ple, the widget class of the Tk button widget is Button.
2577
2578 Similarly, the widget class of a snit::widget defaults to the unquali‐
2579 fied type name with the first letter capitalized. For example, the
2580 widget class of
2581
2582 snit::widget ::mylibrary::scrolledText { ... }
2583
2584
2585 is ScrolledText.
2586
2587 The widget class can also be set explicitly using the widgetclass
2588 statement within the snit::widget definition:
2589
2590 snit::widget ::mylibrary::scrolledText {
2591 widgetclass Text
2592
2593 # ...
2594 }
2595
2596
2597 The above definition says that a scrolledText megawidget has the same
2598 widget class as an ordinary text widget. This might or might not be a
2599 good idea, depending on how the rest of the megawidget is defined, and
2600 how its options are delegated.
2601
2602 WHAT IS MY SNIT::WIDGETADAPTOR'S WIDGET CLASS?
2603 The widget class of a snit::widgetadaptor is just the widget class of
2604 its hull widget; Snit has no control over this.
2605
2606 Note that the widget class can be changed only for frame and toplevel
2607 widgets, which is why these are the valid hull types for snit::widgets.
2608
2609 Try to use snit::widgetadaptors only to make small modifications to
2610 another widget's behavior. Then, it will usually not make sense to
2611 change the widget's widget class anyway.
2612
2613 WHAT ARE OPTION RESOURCE AND CLASS NAMES?
2614 Every Tk widget option has three names: the option name, the resource
2615 name, and the class name. The option name begins with a hyphen and is
2616 all lowercase; it's used when creating widgets, and with the configure
2617 and cget commands.
2618
2619 The resource and class names are used to initialize option default val‐
2620 ues by querying the option database. The resource name is usually just
2621 the option name minus the hyphen, but may contain uppercase letters at
2622 word boundaries; the class name is usually just the resource name with
2623 an initial capital, but not always. For example, here are the option,
2624 resource, and class names for several Tk text widget options:
2625
2626 -background background Background
2627 -borderwidth borderWidth BorderWidth
2628 -insertborderwidth insertBorderWidth BorderWidth
2629 -padx padX Pad
2630
2631
2632 As is easily seen, sometimes the resource and class names can be
2633 inferred from the option name, but not always.
2634
2635 WHAT ARE THE RESOURCE AND CLASS NAMES FOR MY MEGAWIDGET'S OPTIONS?
2636 For options implicitly delegated to a component using delegate option
2637 *, the resource and class names will be exactly those defined by the
2638 component. The configure method returns these names, along with the
2639 option's default and current values:
2640
2641 % snit::widget mytext {
2642 delegate option * to text
2643
2644 constructor {args} {
2645 install text using text .text
2646 # ...
2647 }
2648
2649 # ...
2650 }
2651 ::mytext
2652 % mytext .text
2653 % .text configure -padx
2654 -padx padX Pad 1 1
2655 %
2656
2657
2658 For all other options (whether locally defined or explicitly dele‐
2659 gated), the resource and class names can be defined explicitly, or they
2660 can be allowed to have default values.
2661
2662 By default, the resource name is just the option name minus the hyphen;
2663 the the class name is just the option name with an initial capital let‐
2664 ter. For example, suppose we explicitly delegate "-padx":
2665
2666 % snit::widget mytext {
2667 option -myvalue 5
2668
2669 delegate option -padx to text
2670 delegate option * to text
2671
2672 constructor {args} {
2673 install text using text .text
2674 # ...
2675 }
2676
2677 # ...
2678 }
2679 ::mytext
2680 % mytext .text
2681 % .text configure -myvalue
2682 -myvalue myvalue Myvalue 5 5
2683 % .text configure -padx
2684 -padx padx Padx 1 1
2685 %
2686
2687
2688 Here the resource and class names are chosen using the default rules.
2689 Often these rules are sufficient, but in the case of "-padx" we'd most
2690 likely prefer that the option's resource and class names are the same
2691 as for the built-in Tk widgets. This is easily done:
2692
2693 % snit::widget mytext {
2694 delegate option {-padx padX Pad} to text
2695
2696 # ...
2697 }
2698 ::mytext
2699 % mytext .text
2700 % .text configure -padx
2701 -padx padX Pad 1 1
2702 %
2703
2704
2705 HOW DOES SNIT INITIALIZE MY MEGAWIDGET'S LOCALLY-DEFINED OPTIONS?
2706 The option database is queried for each of the megawidget's locally-
2707 defined options, using the option's resource and class name. If the
2708 result isn't "", then it replaces the default value given in widget
2709 definition. In either case, the default can be overridden by the call‐
2710 er. For example,
2711
2712 option add *Mywidget.texture pebbled
2713
2714 snit::widget mywidget {
2715 option -texture smooth
2716 # ...
2717 }
2718
2719 mywidget .mywidget -texture greasy
2720
2721
2722 Here, -texture would normally default to "smooth", but because of the
2723 entry added to the option database it defaults to "pebbled". However,
2724 the caller has explicitly overridden the default, and so the new widget
2725 will be "greasy".
2726
2727 HOW DOES SNIT INITIALIZE DELEGATED OPTIONS?
2728 That depends on whether the options are delegated to the hull, or to
2729 some other component.
2730
2731 HOW DOES SNIT INITIALIZE OPTIONS DELEGATED TO THE HULL?
2732 A snit::widget's hull is a widget, and given that its class has been
2733 set it is expected to query the option database for itself. The only
2734 exception concerns options that are delegated to it with a different
2735 name. Consider the following code:
2736
2737 option add *Mywidget.borderWidth 5
2738 option add *Mywidget.relief sunken
2739 option add *Mywidget.hullbackground red
2740 option add *Mywidget.background green
2741
2742 snit::widget mywidget {
2743 delegate option -borderwidth to hull
2744 delegate option -hullbackground to hull as -background
2745 delegate option * to hull
2746 # ...
2747 }
2748
2749 mywidget .mywidget
2750
2751 set A [.mywidget cget -relief]
2752 set B [.mywidget cget -hullbackground]
2753 set C [.mywidget cget -background]
2754 set D [.mywidget cget -borderwidth]
2755
2756
2757 The question is, what are the values of variables A, B, C and D?
2758
2759 The value of A is "sunken". The hull is a Tk frame which has been
2760 given the widget class Mywidget; it will automatically query the option
2761 database and pick up this value. Since the -relief option is implic‐
2762 itly delegated to the hull, Snit takes no action.
2763
2764 The value of B is "red". The hull will automatically pick up the value
2765 "green" for its -background option, just as it picked up the -relief
2766 value. However, Snit knows that -hullbackground is mapped to the
2767 hull's -background option; hence, it queries the option database for
2768 -hullbackground and gets "red" and updates the hull accordingly.
2769
2770 The value of C is also "red", because -background is implicitly dele‐
2771 gated to the hull; thus, retrieving it is the same as retrieving -hull‐
2772 background. Note that this case is unusual; the -background option
2773 should probably have been excluded using the delegate statement's
2774 except clause, or (more likely) delegated to some other component.
2775
2776 The value of D is "5", but not for the reason you think. Note that as
2777 it is defined above, the resource name for -borderwidth defaults to
2778 borderwidth, whereas the option database entry is borderWidth, in
2779 accordance with the standard Tk naming for this option. As with
2780 -relief, the hull picks up its own -borderwidth option before Snit does
2781 anything. Because the option is delegated under its own name, Snit
2782 assumes that the correct thing has happened, and doesn't worry about it
2783 any further. To avoid confusion, the -borderwidth option should have
2784 been delegated like this:
2785
2786 delegate option {-borderwidth borderWidth BorderWidth} to hull
2787
2788
2789 For snit::widgetadaptors, the case is somewhat altered. Widget adap‐
2790 tors retain the widget class of their hull, and the hull is not created
2791 automatically by Snit. Instead, the snit::widgetadaptor must call
2792 installhull in its constructor. The normal way to do this is as fol‐
2793 lows:
2794
2795 snit::widgetadaptor mywidget {
2796 # ...
2797 constructor {args} {
2798 # ...
2799 installhull using text -foreground white
2800 # ...
2801 }
2802 # ...
2803 }
2804
2805
2806 In this case, the installhull command will create the hull using a com‐
2807 mand like this:
2808
2809 set hull [text $win -foreground white]
2810
2811
2812 The hull is a text widget, so its widget class is Text. Just as with
2813 snit::widget hulls, Snit assumes that it will pick up all of its normal
2814 option values automatically, without help from Snit. Options delegated
2815 from a different name are initialized from the option database in the
2816 same way as described above.
2817
2818 In earlier versions of Snit, snit::widgetadaptors were expected to call
2819 installhull like this:
2820
2821 installhull [text $win -foreground white]
2822
2823
2824 This form still works--but Snit will not query the option database as
2825 described above.
2826
2827 HOW DOES SNIT INITIALIZE OPTIONS DELEGATED TO OTHER COMPONENTS?
2828 For hull components, Snit assumes that Tk will do most of the work
2829 automatically. Non-hull components are somewhat more complicated,
2830 because they are matched against the option database twice.
2831
2832 A component widget remains a widget still, and is therefore initialized
2833 from the option database in the usual way. A text widget remains a
2834 text widget whether it is a component of a megawidget or not, and will
2835 be created as such.
2836
2837 But then, the option database is queried for all options delegated to
2838 the component, and the component is initialized accordingly--provided
2839 that the install command is used to create it.
2840
2841 Before option database support was added to Snit, the usual way to cre‐
2842 ate a component was to simply create it in the constructor and assign
2843 its command name to the component variable:
2844
2845 snit::widget mywidget {
2846 delegate option -background to myComp
2847
2848 constructor {args} {
2849 set myComp [text $win.text -foreground black]
2850 }
2851 }
2852
2853
2854 The drawback of this method is that Snit has no opportunity to initial‐
2855 ize the component properly. Hence, the following approach is now used:
2856
2857 snit::widget mywidget {
2858 delegate option -background to myComp
2859
2860 constructor {args} {
2861 install myComp using text $win.text -foreground black
2862 }
2863 }
2864
2865
2866 The install command does the following:
2867
2868 · Builds a list of the options explicitly included in the install
2869 command--in this case, -foreground.
2870
2871 · Queries the option database for all options delegated explicitly
2872 to the named component.
2873
2874 · Creates the component using the specified command, after insert‐
2875 ing into it a list of options and values read from the option
2876 database. Thus, the explicitly included options (like -fore‐
2877 ground) will override anything read from the option database.
2878
2879 · If the widget definition implicitly delegated options to the
2880 component using delegate option *, then Snit calls the newly
2881 created component's configure method to receive a list of all of
2882 the component's options. From this Snit builds a list of
2883 options implicitly delegated to the component which were not
2884 explicitly included in the install command. For all such
2885 options, Snit queries the option database and configures the
2886 component accordingly.
2887
2888 You don't really need to know all of this; just use install to install
2889 your components, and Snit will try to do the right thing.
2890
2891 WHAT HAPPENS IF I INSTALL A NON-WIDGET AS A COMPONENT OF WIDGET?
2892 A snit::type never queries the option database. However, a snit::wid‐
2893 get can have non-widget components. And if options are delegated to
2894 those components, and if the install command is used to install those
2895 components, then they will be initialized from the option database just
2896 as widget components are.
2897
2898 However, when used within a megawidget, install assumes that the cre‐
2899 ated component uses a reasonably standard widget-like creation syntax.
2900 If it doesn't, don't use install.
2901
2903 WHAT IS AN ENSEMBLE COMMAND?
2904 An ensemble command is a command with subcommands. Snit objects are
2905 all ensemble commands; however, the term more usually refers to com‐
2906 mands like the standard Tcl commands string, file, and clock. In a
2907 sense, these are singleton objects--there's only one instance of them.
2908
2909 HOW CAN I CREATE AN ENSEMBLE COMMAND USING SNIT?
2910 There are two ways--as a snit::type, or as an instance of a snit::type.
2911
2912 HOW CAN I CREATE AN ENSEMBLE COMMAND USING AN INSTANCE OF A SNIT::TYPE?
2913 Define a type whose INSTANCE METHODS are the subcommands of your ensem‐
2914 ble command. Then, create an instance of the type with the desired
2915 name.
2916
2917 For example, the following code uses DELEGATION to create a work-alike
2918 for the standard string command:
2919 snit::type ::mynamespace::mystringtype {
2920 delegate method * to stringhandler
2921
2922 constructor {} {
2923 set stringhandler string
2924 }
2925 }
2926
2927 ::mynamespace::mystringtype mystring
2928
2929 We create the type in a namespace, so that the type command is hidden;
2930 then we create a single instance with the desired name-- mystring, in
2931 this case.
2932
2933 This method has two drawbacks. First, it leaves the type command
2934 floating about. More seriously, your shiny new ensemble command will
2935 have info and destroy subcommands that you probably have no use for.
2936 But read on.
2937
2938 HOW CAN I CREATE AN ENSEMBLE COMMAND USING A SNIT::TYPE?
2939 Define a type whose TYPE METHODS are the subcommands of your ensemble
2940 command.
2941
2942 For example, the following code uses DELEGATION to create a work-alike
2943 for the standard string command:
2944 snit::type mystring {
2945 delegate typemethod * to stringhandler
2946
2947 typeconstructor {
2948 set stringhandler string
2949 }
2950 }
2951
2952 Now the type command itself is your ensemble command.
2953
2954 This method has only one drawback, and though it's major, it's also
2955 surmountable. Your new ensemble command will have create, info and
2956 destroy subcommands you don't want. And worse yet, since the create
2957 method can be implicit, users of your command will accidentally be cre‐
2958 ating instances of your mystring type if they should mispell one of the
2959 subcommands. The command will succeed--the first time--but won't do
2960 what's wanted. This is very bad.
2961
2962 The work around is to set some PRAGMAS, as shown here:
2963 snit::type mystring {
2964 pragma -hastypeinfo no
2965 pragma -hastypedestroy no
2966 pragma -hasinstances no
2967
2968 delegate typemethod * to stringhandler
2969
2970 typeconstructor {
2971 set stringhandler string
2972 }
2973 }
2974
2975 Here we've used the pragma statement to tell Snit that we don't want
2976 the info typemethod or the destroy typemethod, and that our type has no
2977 instances; this eliminates the create typemethod and all related code.
2978 As a result, our ensemble command will be well-behaved, with no unex‐
2979 pected subcommands.
2980
2982 WHAT IS A PRAGMA?
2983 A pragma is an option you can set in your type definitions that affects
2984 how the type is defined and how it works once it is defined.
2985
2986 HOW DO I SET A PRAGMA?
2987 Use the pragma statement. Each pragma is an option with a value; each
2988 time you use the pragma statement you can set one or more of them.
2989
2990 HOW CAN I GET RID OF THE INFO" TYPE METHOD?"
2991 Set the -hastypeinfo pragma to no:
2992 snit::type dog {
2993 pragma -hastypeinfo no
2994 # ...
2995 }
2996
2997 Snit will refrain from defining the info type method.
2998
2999 HOW CAN I GET RID OF THE DESTROY" TYPE METHOD?"
3000 Set the -hastypedestroy pragma to no:
3001 snit::type dog {
3002 pragma -hastypedestroy no
3003 # ...
3004 }
3005
3006 Snit will refrain from defining the destroy type method.
3007
3008 HOW CAN I GET RID OF THE CREATE" TYPE METHOD?"
3009 Set the -hasinstances pragma to no:
3010 snit::type dog {
3011 pragma -hasinstances no
3012 # ...
3013 }
3014
3015 Snit will refrain from defining the create type method; if you call the
3016 type command with an unknown method name, you'll get an error instead
3017 of a new instance of the type.
3018
3019 This is useful if you wish to use a snit::type to define an ensemble
3020 command rather than a type with instances.
3021
3022 Pragmas -hastypemethods and -hasinstances cannot both be false (or
3023 there'd be nothing left).
3024
3025 HOW CAN I GET RID OF TYPE METHODS ALTOGETHER?
3026 Normal Tk widget type commands don't have subcommands; all they do is
3027 create widgets--in Snit terms, the type command calls the create type
3028 method directly. To get the same behavior from Snit, set the
3029 -hastypemethods pragma to no:
3030 snit::type dog {
3031 pragma -hastypemethods no
3032 #...
3033 }
3034
3035 # Creates ::spot
3036 dog spot
3037
3038 # Tries to create an instance called ::create
3039 dog create spot
3040
3041 Pragmas -hastypemethods and -hasinstances cannot both be false (or
3042 there'd be nothing left).
3043
3044 WHY CAN'T I CREATE AN OBJECT THAT REPLACES AN OLD OBJECT WITH THE SAME
3045 NAME?
3046 Up until Snit 0.95, you could use any name for an instance of a
3047 snit::type, even if the name was already in use by some other object or
3048 command. You could do the following, for example:
3049 snit::type dog { ... }
3050
3051 dog proc
3052
3053 You now have a new dog named "proc", which is probably not something
3054 that you really wanted to do. As a result, Snit now throws an error if
3055 your chosen instance name names an existing command. To restore the
3056 old behavior, set the -canreplace pragma to yes:
3057 snit::type dog {
3058 pragma -canreplace yes
3059 # ...
3060 }
3061
3062
3063 HOW CAN I MAKE MY SIMPLE TYPE RUN FASTER?
3064 In Snit 1.x, you can set the -simpledispatch pragma to yes.
3065
3066 Snit 1.x method dispatch is both flexible and fast, but the flexibility
3067 comes with a price. If your type doesn't require the flexibility, the
3068 -simpledispatch pragma allows you to substitute a simpler dispatch
3069 mechanism that runs quite a bit faster. The limitations are these:
3070
3071 · Methods cannot be delegated.
3072
3073 · uplevel and upvar do not work as expected: the caller's scope is
3074 two levels up rather than one.
3075
3076 · The option-handling methods (cget, configure, and configurelist)
3077 are very slightly slower.
3078
3079 In Snit 2.2, the -simpledispatch macro is obsolete, and ignored; all
3080 Snit 2.2 method dispatch is faster than Snit 1.x's -simpledispatch.
3081
3083 WHAT IS A MACRO?
3084 A Snit macro is nothing more than a Tcl proc that's defined in the Tcl
3085 interpreter used to compile Snit type definitions.
3086
3087 WHAT ARE MACROS GOOD FOR?
3088 You can use Snit macros to define new type definition syntax, and to
3089 support conditional compilation.
3090
3091 HOW DO I DO CONDITIONAL COMPILATION?
3092 Suppose you want your type to use a fast C extension if it's available;
3093 otherwise, you'll fallback to a slower Tcl implementation. You want to
3094 define one set of methods in the first case, and another set in the
3095 second case. But how can your type definition know whether the fast C
3096 extension is available or not?
3097
3098 It's easily done. Outside of any type definition, define a macro that
3099 returns 1 if the extension is available, and 0 otherwise:
3100 if {$gotFastExtension} {
3101 snit::macro fastcode {} {return 1}
3102 } else {
3103 snit::macro fastcode {} {return 0}
3104 }
3105
3106 Then, use your macro in your type definition:
3107 snit::type dog {
3108
3109 if {[fastcode]} {
3110 # Fast methods
3111 method bark {} {...}
3112 method wagtail {} {...}
3113 } else {
3114 # Slow methods
3115 method bark {} {...}
3116 method wagtail {} {...}
3117 }
3118 }
3119
3120
3121 HOW DO I DEFINE NEW TYPE DEFINITION SYNTAX?
3122 Use a macro. For example, your snit::widget's -background option
3123 should be propagated to a number of component widgets. You could
3124 implement that like this:
3125 snit::widget mywidget {
3126 option -background -default white -configuremethod PropagateBackground
3127
3128 method PropagateBackground {option value} {
3129 $comp1 configure $option $value
3130 $comp2 configure $option $value
3131 $comp3 configure $option $value
3132 }
3133 }
3134
3135 For one option, this is fine; if you've got a number of options, it
3136 becomes tedious and error prone. So package it as a macro:
3137 snit::macro propagate {option "to" components} {
3138 option $option -configuremethod Propagate$option
3139
3140 set body "\n"
3141
3142 foreach comp $components {
3143 append body "\$$comp configure $option \$value\n"
3144 }
3145
3146 method Propagate$option {option value} $body
3147 }
3148
3149 Then you can use it like this:
3150 snit::widget mywidget {
3151 option -background default -white
3152 option -foreground default -black
3153
3154 propagate -background to {comp1 comp2 comp3}
3155 propagate -foreground to {comp1 comp2 comp3}
3156 }
3157
3158
3159 ARE THERE ARE RESTRICTIONS ON MACRO NAMES?
3160 Yes, there are. You can't redefine any standard Tcl commands or Snit
3161 type definition statements. You can use any other command name,
3162 including the name of a previously defined macro.
3163
3164 If you're using Snit macros in your application, go ahead and name them
3165 in the global namespace, as shown above. But if you're using them to
3166 define types or widgets for use by others, you should define your
3167 macros in the same namespace as your types or widgets. That way, they
3168 won't conflict with other people's macros.
3169
3170 If my fancy snit::widget is called ::mylib::mywidget, for example, then
3171 I should define my propagate macro as ::mylib::propagate:
3172 snit::macro mylib::propagate {option "to" components} { ... }
3173
3174 snit::widget ::mylib::mywidget {
3175 option -background default -white
3176 option -foreground default -black
3177
3178 mylib::propagate -background to {comp1 comp2 comp3}
3179 mylib::propagate -foreground to {comp1 comp2 comp3}
3180 }
3181
3182
3184 This document, and the package it describes, will undoubtedly contain
3185 bugs and other problems. Please report such in the category snit of
3186 the Tcllib SF Trackers [http://source‐
3187 forge.net/tracker/?group_id=12883]. Please also report any ideas for
3188 enhancements you may have for either package and/or documentation.
3189
3191 BWidget, C++, Incr Tcl, adaptors, class, mega widget, object, object
3192 oriented, widget, widget adaptors
3193
3195 Copyright (c) 2003-2006, by William H. Duquette
3196
3197
3198
3199
3200snit 2.2 snitfaq(n)