[lexfiend] 13 Dec 2005: After noticing at least one request along the lines of "[[[event generate]]] without Tk", I decided to hack the following, which is almost-but-not-quite-like [trigger]: namespace eval notify { namespace export register deregister trigger array set narray {} proc register {name proc} { variable narray set narray(${name}_proc) $proc trace add variable narray(${name}_args) write ::notify::process_trigger } proc deregister {name} { variable narray if {[info exists narray(${name}_proc)]} { trace remove variable narray(${name}_args) write ::notify::process_trigger unset narray(${name}_proc) unset narray(${name}_args) } } proc process_trigger {narray args_key op} { set proc_key [regsub {_args$} $args_key _proc] after 1 [list eval $::notify::narray($proc_key) $::notify::narray($args_key)] } proc trigger {name args} { variable narray set narray(${name}_args) $args } } proc test_print {arg1 args} { puts stderr "arg1 = $arg1" puts stderr "args = $args" } proc test_notify {} { notify::register !test1 test_print notify::trigger !test1 "This is" a test notify::trigger !test1 "This is" another test notify::deregister !test1 notify::trigger !test1 "This should" not fire } after idle test_notify after 1000 {set ::forever 1} vwait forever No warranties, and I'm '''sure''' it can be improved. (As luck would have it, after I hacked the above up, I found [The observer design pattern]. Marco's implementation is certainly more elegant and can handle multiple registered procedures per trigger, but doesn't actually use the event queue as such.) [AMG]: I improved it a tiny bit. Now you can have events whose names end in '''_proc'''. Before if you had an event named '''foo''' and another named '''foo_proc''', things would break. ''[lexfiend]: True. I never expected anyone to name events with a '''_proc''' suffix, but better safe than sorry.'' Hey, is there a difference between '''[[[after] 1 $script]]''' and '''[[[after] 0 $script]]'''? ''[lexfiend]: Not that I've ever been able to notice. Both should work just as well -- I prefer '''[after] 1''' as the mental equivalent of "take a very short break, then get back to work". 8-)'' One design issue I often run into is multiple "objects" both interested in and able to generate some given event. When Object A generates the event, Object B and Object C should be notified. Should Object A be notified as well? Generally I say no, but how is this notification inhibited? Also what about notifications triggered by responses to other notifications? (I'll get to that in a bit.) Let's say the event is the modification of an [[[entry]]] box: A, B, and C each have one, and their values are all functions of some piece of shared data. A's [[[entry]]] is modified. B and C are notified and update their [[[entry]]]s. If A is also notified, it redundantly (and incorrectly, in the case of many possible [[[entry]]] contents mapping to any given shared data value; e.g. .5 == 0.5 == 5e-1) updates its own [[[entry]]], possibly squashing whatever the user was trying to type. [lexfiend]: I'd say that could be handled in one of two ways. Either: * Prepend the ''event originator'' identifier (in the above example, '''A''', '''B''' or '''C''') to the event data * Notify everybody * Ensure the registered event handlers all check the ''event originator'' arg and '''not''' take action if originator == me or take a page from the [GroupKit] book and implement separate '''trigger_all''' and '''trigger_others''' methods. Personally, I'd prefer the latter method; it's all too easy to forget about checking the originator, so the ability to ''explicitly'' say '''tell everybody''' in one place and '''tell everybody except myself''' in another is a Good Thing to have. [AMG]: Next, what if the event is raised by a write [trace] on the variable "backing" an [[[entry]]]? (Is this legal?) Without careful (i.e. kludgy) coding, now receiving an event can trigger ''that same event!'' Such loops are also possible but harder to spot with circular chains of events. [lexfiend]: I'd say that Really Bad Design. 8-) [AMG]: I have some ideas about how to handle this, but this lab room is so friggin' cold I can't think or type anymore.