'''`[Tcllib%|%hook]`''' is a [Tcllib] package tht implements the subject/observer ([Tcl implementations of publish-subscribe mechanisms%|%publish/subscribe]) pattern. ** What the Critics Say ** ''[hook] has become an essential part of my toolbox. I can't imagine developing a medium size app without having such functionality'': [eg%|%Emiliano Gavilán], [Tcl Chatroom], 2022-04-08 ** Description ** `hook` allows subjects, which may be modules, objects, widgets, and so forth, to synchronously call hooks which may be bound to an arbitrary number of subscribers, called observers. A subject may call any number of distinct hooks, and any number of observers can bind callbacks to a particular hook called by a particular subject. Hook bindings can be queried and deleted. Below is an incomplete but illustrative example from [eg%|%Emiliano Gavilán]. The Reader module (code inside a namespace which manages the connection and communication with an instrument) issues ====== hook call Reader <> ====== when trying to connect, and ====== hook call Reader <> $conn ====== upon connection/disconnection, with `$conn` being `1` on connect and `0` on disconnect: ====== set connbutton [ttk::button $f.startstop \ -text Connect \ -command [fqcmd connect] \ -image ::img::connect0] hook bind Reader <> $connbutton [lambda {w} { $w state disabled status set-clear "Connecting to reader" } $connbutton] hook bind Reader <> $connbutton [lambda {w conn} { $w state !disabled if {$conn} { set spec [list Disconnect [fqcmd disconnect] ::img::connect4] status set-clear "Reader connected" } else { set spec [list Connect [fqcmd connect] ::img::connect0] status set-clear "Reader disconnected" } foreach {txt cmd img} $spec { $w configure -text $txt -command $cmd -image $img } } $connbutton] ====== A simple but more complete example. In this case a clock (a TclOO object) is created with just two public methods: start and stop. It provides three ''hooks'': <>, <> and <>. Two ''observers'' are defined: a simple one which just prints the value and a more elaborated one with gui, all defined inside a namespace. ====== package require hook oo::class create Clock { constructor {interval {shift 0}} { variable ms $interval variable sh $shift variable aid "" } method Tick {} { variable ms variable sh variable aid set aid [after $ms [namespace code {my Tick}]] set base [clock seconds] set time [clock format [clock add $base $sh hours] -format "%H:%M:%S"] hook call [self] <> $time } method start {} { variable aid if {$aid ne ""} { after cancel $aid } hook call [self] <> 1 my Tick } method stop {} { variable aid if {$aid ne ""} { after cancel $aid set aid "" } hook call [self] <> 0 } destructor { my stop hook call [self] <> hook forget [self] } } set clock1 [Clock new 200] # simple observer set obs [hook bind $clock1 <> {} [list apply {{val} { puts $val }}]] # let's add a second, shifted clock and a gui namespace eval gui { proc main {} { package require Tk global clock1 set clock(1) $clock1 set clock(2) [Clock new 500 -3] foreach c {1 2} { set l [label .l$c -width 12] set b [button .b$c \ -command [namespace code [list start $clock($c)]] \ -text "Start" -width 10] set db [button .db$c -text "Destroy" \ -command [list catch [list $clock($c) destroy]]] grid $b $l $db -sticky news hook bind $clock($c) <> $l [list apply {{l val} { $l configure -text $val }} $l] hook bind $clock($c) <> $l [list apply {{l} { $l configure -text "" }} $l] bind $l [list hook forget $l] hook bind $clock($c) <> $b [list apply [list {b clock start} { if {$start} { $b configure \ -command [namespace code [list stop $clock]] \ -text Stop } else { $b configure \ -command [namespace code [list start $clock]] \ -text Start } } [namespace current]] $b $clock($c)] hook bind $clock($c) <> $b [list apply {{b} { $b configure \ -command {} \ -text "---" }} $b] bind $b [list hook forget $b] } } proc start {clock} { $clock start } proc stop {clock} { $clock stop } } gui::main ====== <> Control Structure | Package | Tcllib