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