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