Version 2 of Custom event notification

Updated 2005-12-13 19:38:19

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.

Hey, is there a difference between [after 1 $script] and [after 0 $script]?

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.

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.

I have some ideas about how to handle this, but this lab room is so friggin' cold I can't think or type anymore.