i am having a following code:
proc testList {setupFile ""} {
if {$setupFile == ""} {
set setupFile location
}
}
proc run {} {
puts "$setupFile"
}
I am getting syntax error. I know if i declare the setupFile variable outside the proc i.e in the main proc then i can append it with namespace say ::65WL::setupFile to make it global but not getting how to do that if a variable itself is defined in the proc only.
You can refer to the global namespace with ::.
proc testList {{local_setupFile location}} {
# the default value is set in the arguments list.
set ::setupFile $local_setupFile
}
proc run {} {
puts $::setupFile
}
Tcl variables that are not local to a specific procedure run need to be bound to a namespace; the namespace can be the global namespace (there's a special command for that) but doesn't need to be. Thus, to have a variable that is shared between two procedures, you need to give it an exposed name:
proc testList {{setup_file ""}} {
# Use the 'eq' operator; more efficient for string equality
if {$setup_file eq ""} {
set setup_file location
}
global setupFile
set setupFile $setup_file
}
proc run {} {
global setupFile
puts "$setupFile"
}
Now, that's for sharing a full variable. There are some other alternatives provided you only want to share a value. For example, these two possibilities:
proc testList {{setup_file ""}} {
if {$setup_file eq ""} {
set setup_file location
}
# Create a procedure body at run-time
proc run {} [concat [list set setupFile $setup_file] \; {
puts "$setupFile"
}]
}
proc testList {{setup_file ""}} {
if {$setup_file eq ""} {
set setup_file location
}
# Set the value through combined use of aliases and a lambda term
interp alias {} run {} apply {setupFile {
puts "$setupFile"
}} $setup_file
}
There are more options with Tcl 8.6, but that's still in beta.
you can use uplevel, upvar and/or global
proc testList {{setupFile ""}} {
if {$setupFile eq ""} {
set setupFile location;
uplevel set setupFile $setupFile;
}
}
proc run {} {
upvar setupFile setupFile;
puts "$setupFile";
}
or
proc run {} {
global setupFile;
puts "$setupFile";
}
the first is prefered.
Related
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}]
}
}
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]]
}
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
Is it possible to find the procedure name by using the content of that procedure?
For example,
proc test {args} {
set varA "exam"
puts "test program"
}
Using the statement set varA, is it possible to find its procedure name test?
Because, I need to find a procedure for which i know the output [it's printing something, i need to find the procedure using that].
I tried many ways like info frame, command. But, nothing helps.
Is it possible to find the procedure name by using the content of that procedure?
Yes. You use info level 0 to get the argument words to the current procedure (or info level -1 to get its caller's argument words). The first word is the command name, as resolved in the caller's context. That might be enough, but if not, you can use namespace which inside an uplevel 1 to get the fully-qualified name.
proc foo {args} {
set name [lindex [info level 0] 0]
set FQname [uplevel 1 [list namespace which $name]]
# ...
}
Note that this does not give you the main name in all circumstances. If you're using aliases or imported commands, the name you'll get will vary. Mostly that doesn't matter too much.
With info proc, we can get the content of a procedure which may helps you in what you expect.
The following procedure will search for the given word in all the namespaces. You can change it to search in particular namespace as well. Also, the search word can also be case insensitive if altered in terms of regexp with -nocase. It will return the list of procedure names which contains the search word.
proc getProcNameByContent {searchWord} {
set resultProcList {}
set nslist [namespace children ::]; # Getting all Namespaces list
lappend nslist ::; # Adding 'global scope namespace as well
foreach ns $nslist {
if {$ns eq "::"} {
set currentScopeProcs [info proc $ns*]
} else {
set currentScopeProcs [info proc ${ns}::*]
}
foreach myProc $currentScopeProcs {
if {[regexp $searchWord [info body $myProc]]} {
puts "found in $myProc"
lappend resultProcList $myProc
}
}
}
return $resultProcList
}
Example
% proc x {} {
puts hai
}
% proc y {} {
puts hello
}
% proc z {} {
puts world
}
% namespace eval dinesh {
proc test {} {
puts "world is amazing"
}
}
%
% getProcNameByContent world
found in ::dinesh::test
found in ::z
::dinesh::test ::z
%
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