0

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.

PeterE
  • 5,715
  • 5
  • 29
  • 51
  • I'm not entirely sure what you are trying to do, but you could call the proc with `theVar` and use `return` at the end of the proc to retrieve `theVar` with or without changes. Maybe you could put a small reproducible snippet of what you want to obtain, maybe with some `puts` here and there (before and after calling the different procs for example) and say what you were expecting to get. – Jerry Aug 18 '14 at 14:56
  • @Jerry Passing theVAr as parameter and returning it with return would work for 'normal' procs. But not which a tk dialog/window in between. The problem is that ::mydialog::show has no direct "connection"/relation to the callback function (e.g. for a buttonclick) in which the new value is supposed to be set. As Hoodiecrow pointed out one could use the global namespace as reference point to tie them together. I'm not sure I like that (from a design standpoint) but I don't see an other way. – PeterE Aug 18 '14 at 16:06

1 Answers1

1

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)

Peter Lewerin
  • 13,140
  • 1
  • 24
  • 27
  • I think your last point (You can't, however, use variables local to some procedure this way.) is what I stumbled over. If I understand you correctly I have to use fully qualified varNames, or else stuff them all into the global namespace :-( not exactly good style in my opinion. I tried using upvar with relative levels, wich did not work due to the "callback magic" practiced by tk (apparently tk callbacks are always level #2 and have no relation to the scope they where defined in; which in hindsight does make sense) – PeterE Aug 18 '14 at 15:59
  • @Peter: you should probably use namespace variables, here. In that case you can make the invocations a little less painful by registering the namespace and the variable name separately and using `namespace upvar` instead of regular `upvar`. Otherwise, yeah, that's how it is. It's not a bug though, it's a consequence of how a widget system works. See my added comment about Snit, though. – Peter Lewerin Aug 18 '14 at 16:09