How do you access a procedure's local variables in a namespace eval {} inside that same procedure? - tcl

I'm sure I'm just being stupid but would you please tell me how to get access to $name inside the namespace in order to set variable n to $name? I can only find how to do this when the procedure is in the namespace but not the other way 'round. No matter what I try, this errors stating no such variable name. Thank you.
proc getNS {name} {
namespace eval ns::$name {
variable n $name
}
}
This works but isn't really an answer unless the answer is simply that it cannot be done directly. Got it from this SO question. Bryan Oakley gave the answer but used [list set...] instead of [list variable...] and that will fail if there is a global variable of the same name. (It will modify the global rather than creating a new variable in the namespace.) It may have been different, of course, in 2009 when that answer was provided.
proc getNS {name} {
namespace eval ns::$name [list variable n $name]
namespace eval ns::$name {
variable a abc
}
}
set n xyz
getNS WEBS
chan puts stdout "ns n: $ns::WEBS::n; a $ns::WEBS::a, global n: $n"
# => ns n: WEBS; a: abc; global n: xyz

You can just use set with a fully qualified variable name that uses the desired namespace:
proc getNS {name} {
namespace eval ns::$name {} ;# Create namespace if it doesn't already exist
set ns::${name}::n $name
}
getNS foo
puts $ns::foo::n ;# foo
Another way is to use uplevel to refer to the scope of the proc that calls namespace eval:
proc getNS {name} {
namespace eval ns::$name {
set n [uplevel 1 {set name}]
}
}

Related

Tcl: How to get namespace and procedures of calling namespace?

I have some generic procedure. I would like this procedure to be able to get the name of the namespace and names of the procedures within the namespace where this procedure is called.
I have tried following code:
proc register {} {
puts [info procs]
puts [namespace current]
}
namespace eval Foo {
proc bar {} {
puts bar
}
proc _baz {} {
puts baz
}
register
}
However, this prints results for the namespace where register is defined, not for the namespace where it is executed. It looks like there are no dedicated commands for these tasks or at least these are not info or namespace commands.
To get information about the calling context, use uplevel:
proc register {} {
puts [uplevel 1 [list info procs]]
puts [uplevel 1 [list namespace current]]
}

How to access a variable defined in proc 'a' from a different proc 'b' which does not call proc 'a'?

I am trying to execute a tcl script which makes exclusive calls to procs a and b. The two procs are not related to each other.
proc a {} {
set var1 "a"
}
proc b {} {
# Do something here with: $var1
}
# script.tcl
a
b
I do not have access to the script.tcl as well. When proc 'a' is called, I need to store the var1 somehow such that I can access it later within proc 'b' when it is called. How can I get the value of var1 in proc b? Doesn't seem like I can use 'global' and 'upvar' here?
A simple way is to define the variable in the global scope by preceding the variable name with ::
proc a {} {
set ::var1 "a"
}
proc b {} {
puts $::var1
}
Other methods would be to use the global command in each proc or to define the variable in a special namespace of its own.
Using variable instead of global offers a bit more flexibility if namespaces are involved
namespace eval n {
proc a {{value A}} {
variable var1
set var1 $value
return
}
proc b {} {
variable var1
puts "var1 is <$var1>"
}
}
n::a
n::b ;# => var1 is <A>
namespace eval n {a 42; b} ;# => var1 is <42>
puts $::var1 ;# => can't read "::var1": no such variable

Meaning of a proc name ending with ::

In the tk code base I found the construct:
proc ::tk::dialog::file::chooseDir:: {args} {
Normally I would expect the procedure name after the last set of :: but here it is empty. Is this some sort of constructor in a namespace?
(Might look like a trivial question but I'm not a tcl programmer and need to know it to, automatically, generate some documentation.
Some more of the code (maybe gives some background, it is the beginning of the file)
namespace eval ::tk::dialog {}
namespace eval ::tk::dialog::file {}
namespace eval ::tk::dialog::file::chooseDir {
namespace import -force ::tk::msgcat::*
}
proc ::tk::dialog::file::chooseDir:: {args} {
variable ::tk::Priv
set dataName __tk_choosedir
upvar ::tk::dialog::file::$dataName data
Config $dataName $args
...
Normally I would expect the procedure name after the last set of ::
but here it is empty
The empty string is a valid name for a procedure in Tcl (as it for variables).
% namespace eval ::tk::dialog::file::chooseDir {}
% proc ::tk::dialog::file::chooseDir:: {args} { return "called!" }
% ::tk::dialog::file::chooseDir::
called!
% namespace eval ::tk::dialog::file::chooseDir { "" }
called!
% info procs ::tk::dialog::file::chooseDir::*
::tk::dialog::file::chooseDir::
I don't know the history behind these Tk internals, but a procedure named using the empty string might be the main procedure for the same-named namespace chooseDir (as a kind of naming convention), rather than just duplicating the name: proc ::tk::dialog::file::chooseDir::chooseDir {args} {;}. Or, it is because the entire directory-picking functionality is auto_loaded, which requires a proc (command) name rather than a namespace name?
automatically, generate some documentation.
Maybe, when harvesting a Tcl interpreter for pieces to document, take the containing namespace name chooseDir as the documented name of such a procedure?

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.
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)

Unable to pass a variable to a procedure using upvar in Tcl

I need a procedure that will be able to access, read and change a variable from the namespace of the caller. The variable is called _current_selection. I have tried to do it using upvar in several different ways, but nothing worked. (I've written small test proc just to test the upvar mechanism). Here are my attempts:
call to proc:
select_shape $this _current_selection
proc:
proc select_shape {main_gui var_name} {
upvar $var_name curr_sel
puts " previously changed: $curr_sel"
set curr_sel [$curr_sel + 1]
}
For my second attempt:
call to proc:
select_shape $this
proc:
proc select_shape {main_gui} {
upvar _current_selection curr_sel
puts " previously changed: $curr_sel"
set curr_sel [$curr_sel + 1]
}
In all the attempts, once it reaches this area in the code it says can't read "curr_sel": no such variable
What am I doing wrong?
EDIT:
The call for the function is made from a bind command:
$this/zinc bind current <Button-1> [list select_shape $this _current_selection]
at start I thought that it doesn't matter. but maybe It does.
I believe that bind commands operate in the global namespace, so that's where the variable is expected to be found. This might work:
$this/zinc bind current <Button-1> \
[list select_shape $this [namespace current]::_current_selection]
for upvar to work the variable must exist in the scope that you are calling it in. consider the following:
proc t {varName} {
upvar $varName var
puts $var
}
#set x 1
t x
If you run it as it is you'll get the error you are reporting, uncomment the set x 1 line and it will work.
In the example below I've tried to cover the most variants of changing variables from other namespace. It 100% works for me. Maybe it will help.
proc select_shape {main_gui var_name} {
upvar $var_name curr_sel
puts " previously changed: $curr_sel"
incr curr_sel
}
namespace eval N {
variable _current_selection 1
variable this "some_value"
proc testN1 {} {
variable _current_selection
variable this
select_shape $this _current_selection
puts " new: $_current_selection"
}
# using absolute namespace name
proc testN2 {} {
select_shape [set [namespace current]::this] [namespace current]::_current_selection
puts " new: [set [namespace current]::_current_selection]"
}
select_shape $this _current_selection
puts " new: $_current_selection"
}
N::testN1
N::testN2
#-------------------------------------
# Example with Itcl class
package require Itcl
itcl::class C {
private variable _current_selection 10
public method testC {} {
select_shape $this [itcl::scope _current_selection]
puts " new: $_current_selection"
}
}
set c [C #auto]
$c testC