namespacestcltk-toolkitupvar

how do I update a variable via a tk window by name


Consider the following situation:

namespace eval ::mydialog {}

proc ::mydialog::show {w varName args} { 
   upvar 1 $varName theVar
   # now I can access theVar

   # (1)

   # code defining/creating my window
   # here some widgets for user interaction are created, 
   #   some of which will call ::mydialog::_someCallback

   wm protocol $w  WM_DELETE_WINDOW [list ::mydialog::close $w]
}

proc ::mydialog::_someCallback {}  {
   # how do I access theVar here?

   # (2)
}

proc ::mydialog::close { w } {
   # here some changes are supposed to be written back into varName in the calling scope,
   #    how do I do that?!

   # (3)

   destroy $w
}

Im trying to figure out how to (a) get a variable from the calling scope (b) have it available in all three procs and (c) writing any changes back into said variable.

(a) I would normally solve using 'upvar 1 $varName theVar' (b) I would normally solve with a namespace variable (c) As long as we only have one proc that would happen automaticly with (a) due to the fact that we would be working on a local alias of that variable

The problem is that upvar only works (at least as intended) in (1). I could use upvar in (1) and save/copy into a namespace variable, that would solve (a) and (b), but not (c).

I would be gratefull if someone could point me in the right direction here.

Also, as I'm relativly new to Tcl/Tk my concept might not be ideal, suggestions toward a better design are welcome too.


Solution

  • I suggest you use a namespace variable that keeps the name of the variable, and upvar using the global scope.

    namespace eval ::mydialog {
        variable varName
    }
    
    proc ::mydialog::show {w _varName args} { 
        variable varName $_varName
        upvar #0 $varName theVar
    
    }
    
    proc ::mydialog::_someCallback {}  {
        variable varName
        upvar #0 $varName theVar
        puts $theVar
    }
    
    proc ::mydialog::close { w } {
        variable varName
        upvar #0 $varName theVar
        set theVar newval
    }
    
    set globalvar oldval
    # => oldval
    ::mydialog::show {} globalvar
    ::mydialog::_someCallback
    # => oldval
    ::mydialog::close {}
    # => newval
    puts $globalvar
    # => newval
    

    Note that the syntax highlighting fails: #0 $varName theVar isn't really a comment.

    This works with namespace variables too: if you have a variable called nsvar in the ::foobar namespace you can use it like this:

    set ::foobar::nsvar oldval
    ::mydialog::show {} ::foobar::nsvar
    ::mydialog::_someCallback
    ::mydialog::close {}
    puts $::foobar::nsvar
    

    with the same effects.

    You can't, however, use variables local to some procedure this way.

    One way to make this really simple is to use Snit widgets instead of collections of Tcl procedures.

    Documentation: namespace, proc, puts, set, upvar, variable

    Snit documentation: man page, faq (the faq serves as a kind of introduction as well)