Purpose: [??] Hmm. Let's set the ball rolling... ---- A simple way to get a read-only global variable is to use a suitable trace: set ROglobal someValue trace variable ROglobal w "[list set ::ROglobal $ROglobal];error read-only;#" Notice the time- and hair-saving tricks: 1. Using [[list]] to construct a ''guaranteed'' safe command for later execution. 1. Using colon notation to force a reference to a global variable, whatever the context. 1. Inserting the global name of the variable in the trace command, instead of working with its local referent. 1. Using a trailing ";#" to trim the undesirable extra arguments from the trace command. '''DKF''' This is great code, but hard to find if you're thinking of a constant or constants or C's #define. (There! Now the Wiki search will find this page!) - RWT ---- Neat... I remember having had trouble when the trace was set on '''::var''', yet usage was based on '''global var''', or the other way around. Does anyone know what the exact behavior is when mixing these two approaches? '''JCG''' The thing to remember is that traces are always called in the context of the operation that is performing the read, write or unset, and the standard parameters passed in from the Tcl system refer to the way in which the variable was referred to ''in that context''. Whenever you are setting up a trace on a variable that exists independently of the stack (i.e. a global or namespace variable - the two are really the same thing anyway) then it is much less hassle to pass a fixed name for that variable in as one of your own parameters to the trace (you can sort-of form closures in Tcl using appropriate scripts, and some of the more cunning lambda-evaluation schemes I've seen have been based on this.) The only time this is not going to work (in standard Tcl) is when you are dealing with variables that only exist on the call-stack - procedure locals. With these, you have to carefully build an [[upvar]] reference to the variable and then use that name to figure out what is going on. This is not easy, but it has been adequately covered in many books, like [BOOK Tcl and the Tk Toolkit] which is where I learnt all this stuff (if I can remember that far back!) '''DKF''' ---- Donal, you might be talking about the following: trace variable ::var w monitor proc monitor {name args} { upvar $name value puts "'[info level 1]' changes '$name' to '$value'" } ### Change 1 proc change1 {} { set ::var bar } change1 ### Change 2 proc change2 {} { global var set var foo } change2 ### Change 3 proc change3 {} { upvar var local set local bar } change3 The above code produces the following output: 'change1' changes '::var' to 'bar' 'change2' changes 'var' to 'foo' 'change3' changes 'local' to 'bar' The problem is that there is no way of knowing which variable is actually being modified, all one has access to is its alias name as used in the ''set'' command which trigers the trace, i.e., in ''monitor'' the parameter ''name'' is equal to ''local'', how do you resolve that name back to the original ''::var'' variable? -- JC There is ''no'' standard mechanism for doing this. So I instead pass a globally-valid (fully-quantified) variable name as part of the trace script that I supply. Like that, I don't care about how the variable was accessed since I can always access it myself using a clearly defined route. And, because of the restrictions involved with the use of variables and Tk, I find sticking to only putting traces on globally-valid names to not be a problem. Note that [[namespace current]] is very useful when you want to put in code to automatically figure out what the globally-valid variable name actually is. '''DKF''' ---- If you want to set up a trace on an expression, then you implement this using the style advocated in the following example: Suppose you want to have a trace on a pair of variables such that some callback (which I'll imaginatively describe as ''callback'' here) is called whenever some complex set of conditions (either ''$VAR_I>0 && $VAR_B=="true"'' or ''$VAR_I>=10 && $VAR_B=="false"'') then you would do it like this: trace variable VAR_I w doTest trace variable VAR_B w doTest proc doTest args { upvar #0 VAR_I i VAR_B b if { ($i>0 && [string equal $b "true"]) || ($i>=10 && [string equal $b "false"]) } then { upvar #0 callback } } You can't extend this to a general watch of an expression, since an expression can include a call to a general command, and solving exactly what variables a general command accesses is tough. Even if we restricted ourselves to procedures, we would still need to solve the [Halting Problem] (a famously insoluble thing in Computer Science, where there is a ''proof'' of insolubility...) Since you can usually tell straight off what variables in an expression actually matter, you can shortcut all that stuff and just tell Tcl exactly what to watch yourself... '''DKF''' ---- Come on people, this page isn't just "Ask Dr. Donal" - fill in more of this stuff yourselves! '''DKF''' ---- Here is an idea I have been batting around for using traces to create a kind of super-set of events. Yes, Tcl gives you the ability to define virtual events, but they must be defined in terms of existing X-type events. I was looking for the ability to fire off an event that is completely unrelated to any existing Tcl events, that says, for example, "you have a new meeting scheduled and the details are attached". Something like the following seems like it will probably work, but my gut tells me there is much room for refinement. The basic idea is to use a namespace to wrap the "Uber-events", use a set of namespace variables with write traces attached to track the events and the "details" attached to an event, and namespace procs to register for, trigger, or delete the events. namespace eval ::UberEvents { namespace export RegisterForEvent \ DeleteEvent \ GetEventDetails \ TriggerEvent set Events(List) {} ####################################################################### # # RegisterForEvent # # Registers for the UberEvent named $EventName, so that Command is # called when the event is "triggered." # ####################################################################### proc RegisterForEvent {EventName Command} { variable ::UberEvents::Events set ID [trace add variable ::UberEvents::$EventName {write} "[list eval $Command];#"] lappend ::UberEvents::Events(List) [list $EventName $ID $Command] return $ID } ####################################################################### # # DeleteEvent # ####################################################################### proc DeleteEvent {EventName} { unset ::UberEvents::$EventName } ####################################################################### # # GetEventDetails # # Fetches the details of the event. # ####################################################################### proc GetEventDetails {EventName} { upvar #0 ::UberEvents::$EventName Details return $Details } ####################################################################### # # TriggerEvent # # Triggers the UberEvent named $EventName, giving it $Data # ####################################################################### proc TriggerEvent {EventName {Data ""}} { set ::UberEvents::$EventName $Data } } Then you can import the namespace and use RegisterForEvent NewMeeting MyMeetingCallback to set up to be informed whenever the NewMeeting event triggers, and TriggerEvent NewMeeting "Details Of The Meeting" will trigger the NewMeeting event, and everyone registered for that event will be notified. GetEventDetails will return the data associated with the event. DeleteEvent is used to remove the event and all associated callbacks. This can easily be extended to have a common "event handler" which is set up to be called periodically, triggering the event when the handler detects it has occurred. The event details could be passed automatically to each of the callbacks, similar to the way that Tcl handles events, but then you would probably have cases where you would end up putting ";#" at the end of your callback the same way as was demonstrated in code at the beginning of this page. Making the extra data optional but retrievable is ''imho'' preferable.... Feedback? '''KCH''' [jmn] While using trace here seems fine, I'm not sure exactly what it buys you. You have the command stored in a list, why not just uplevel #0 it from within TriggerEvent? (or use 'after 0' if TriggerEvent needs to return first...(?)) ---- See also [trace]. ---- [Category Tutorial]