Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

vanishing variables? 1

Status
Not open for further replies.

egal

Programmer
Jun 5, 2001
4
0
0
DE
Hi,
I'm trying to get a button to do something, with a binding command. In order to get the right action, i'm using an array (2D). With a label just before the button i'm getting the right value of the array, but in the binding, the value (or the first dimension of the array) seems to have disappeared. Here's the piece of code i have trouble with:
Code:
label .waitpop.fr1.lab3   -text "Executing $arraystart($i,$k). \n Press OK to continue"
    button .waitpop.fr1.but3 -text OK			
    pack .waitpop.fr1.lab3 -side top
    pack .waitpop.fr1.but3 -side bottom
    bind .waitpop.fr1.but3 <ButtonRelease-1> {
				puts &quot;i ist $i und k ist $k&quot;
				source $arraystart($i,$k)
				incr k
				destroy .waitpop
			}

Even in the puts command i get no value for the &quot;i&quot; variable, although the label shows the right value, so the &quot;i&quot; variable seems to vanish somehow.

Thanks for your help in advance,

Egal
 
This sounds like a scoping problem. Is this chunk of code inside of a procedure? Check whether i is a local variable. If so, there's your problem. All callbacks in Tcl (binding actions, Button commands, after events, etc.) are executed in the global scope.

In your code, the &quot;&quot; quotes for your label -text argument allow immediate variable substitution when the Tcl interpreter parses the command. The {} quotes in your bind command prevent all substitutions on the argument when the bind command is parsed. The argument is then executed by the Tcl interpreter using standard parsing rules (in the global scope) when the binding fires.

By the way, is there any reason you're explictly binding to your Button's <ButtonRelease-1> event? Unless you have very unusual circumstances, you really should use the Button's -command option to register your callback. For example:

Code:
button -text Ok -command {
    puts &quot;i ist $i und k ist $k&quot;
    source $arraystart($i,$k)
    incr k
    destroy .waitpop
}

- Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax
 
The proper way to use the i variable in a binding is to use the list command, as in &quot;This is the current value of the variable i:[list $i]&quot;.
 
It depends on what you're trying to accomplish, charlsut, though I wouldn't use the list command around just the variable value, but the entire command I'm trying to quote. Let's take a look at some examples.

For the sake of argument, let's assume that I've got a button that I create and initialize within a procedure:

Code:
proc init {msg} {
    # msg is some text to display when the user
    # clicks the button
    button .b -text &quot;Click me!&quot; -command {
        puts $msg
    }
}

This code fails. The {} around the -command argument prevents the Tcl interpreter from performing any substitutions on the argument. The text is simply stored away in memory until the user clicks the button. At that time, the interpreter executes the text as a command in the global scope. This is why the code fails. When the user clicks, The interpreter looks for the value of a global variable named msg, but msgwas a local variable.

Some people then try to use &quot;&quot; to quote the argument. Sounds reasonable: if one set of quoting characters doesn't work, try the other. However, &quot;&quot; often fail in a situation like this. Let's see why:

Code:
proc init {msg} {
    # msg is some text to display when the user
    # clicks the button
    button .b -text &quot;Click me!&quot; -command &quot;
        puts $msg
    &quot;
}

The &quot;&quot; permit immediate substitution when the Tcl interpreter parses and executes the button command. This sounds like what we want, and if the value of msg is a string like &quot;Hello&quot;, it actually works. But it fails when the value of msg is a multi-word string like &quot;This is a test&quot; with the following error message when the user clicks the button:

Code:
wrong # args: should be &quot;puts ?-nonewline? ?channelId? string&quot;

Yes, the &quot;&quot; permit variable substitution, but within the &quot;&quot;, Tcl does simple string concatenation. Thus, the spaces within the value of msg look the same as any whitespace characters separating the command arguments. In this case, the string stored away in memory was:

Code:
puts This is a test

The idea of &quot;This is a test&quot; being a distinct argument was lost.

That's why you'll often see the list command used to create callback commands (code that's executed in response to an action like a button click, an event, etc.). Why? Because a list is a sequence of whitespace-separated elements, and a command is a sequence of whitespace-separated arguments. A command is simply a special case of a Tcl list, one which uses the first element as the command name, and all subsequent elements as arguments to the command. Lists and commands use the same quoting mechanisms for handling elements or arguments that contain whitespace characters or other problematic characters. Thus, we can use the list command to create a well-formed Tcl list, which is guaranteed to be a well-formed Tcl command.

In this example, executing:

Code:
list puts $msg

creates a 2-element Tcl list. The first element is &quot;puts&quot; and the second element is the value of msg. The list command automatically handles any quoting required to ensure that the value of msg remains a single element in the resulting list. Therefore, we can run the list command as a subcommand and use the return value as our -command argument as follows:

Code:
proc init {msg} {
    # msg is some text to display when the user
    # clicks the button
    button .b -text &quot;Click me!&quot; -command         [list puts $msg]
}

This works correctly no matter what the value of msg is.

In my classes, I often refer to the list command as the &quot;third quoting mechanism&quot; in Tcl. If you have a chunk of Tcl code you need to quote, you've got three possibilities:

[ol][li]{} - Prevent substitutions until the command is executed. Argument boundaries are maintained.[/li][li]&quot;&quot; - Permit immediate substitutions. Argument boundaries might be lost.[/li][li][ignore]
  • [/ignore] - Permit immediate substitutions. Argument boundaries are maintained.[/li][/ol]
    In most cases, 1) or 3) are the correct choices, depending upon your needs. Rarely is 2) correct, though many people use it because it's simpler. - Ken Jones, President
    Avia Training and Consulting
    866-TCL-HELP (866-825-4357) US Toll free
    415-643-8692 Voice
    415-643-8697 Fax
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top