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