Tk : how to pass variable values with -command?

一世执手 提交于 2019-12-20 03:56:15

问题


button .mltext.button -text "Apply" -command {set top_tlbl [update_text $top_tlbl $spc ] }

I get an error :

can't read "spc": no such variable
while executing
"update_text $top_tlbl $spc "
invoked from within
".mltext.button invoke"

How can I pass the values of the variables to the update_text function?

Maybe I can start by understanding this :

(System32) 3 % expr 2 + 2
4
(System32) 4 % list expr 2 + 2
expr 2 + 2
(System32) 5 % [list expr 2 + 2]
invalid command name "expr 2 + 2"
(System32) 6 % 

According to me, the last one should generate expr 2 + 2, which is the same as the first command - so, why does TCL have a problem?

Thanks..


回答1:


In your examples with expr, we start by counting the words.

expr 2 + 2 has four words: expr, 2, + and 2. The first one is the command name, expr, and the others are passed as arguments; the documentation for expr says that it concatenates its arguments and evaluates the resulting expression.

list expr 2 + 2 has five words: list, expr, 2, + and 2. The first one is the command name, list, and the others are passed as arguments; the documentation for list say that it returns the list that has its arguments as elements. Though we don't see it here explicitly, what this does is introduce exactly the quoting needed to make a single substitution-free command. Good Tcl code uses list quite a lot when generating code.

[list expr 2 + 2] has one word, which is the result of calling list expr 2 + 2. If you just feed that into Tcl directly, it has a weird but legal command name, and you probably don't have a command called that. Hence you get an error.


Now let's consider:

button .mltext.button -text "Apply" -command {set top_tlbl [update_text $top_tlbl $spc]}

From your comments, you're calling this inside a procedure. This means that you need to bind the variables inside the callback (which is evaluated inside uplevel #0, and probably long after your current procedure has returned) without doing the callback immediately. Curiously, it looks like you're changing top_tlbl at runtime too.

Let's start by thinking about the innermost part. We can generate it with list just fine (ignoring the different lifetime bindings):

list update_text $top_tlbl $spc

Now we've just got to make the other parts work as well. That's where it gets fiddly and you end up with something like this:

… -command "set top_tlbl \[[list update_text $top_tlbl $spc]\]"

Now let's fix the lifetime stuff:

… -command "set top_tlbl \[update_text \$top_tlbl [list $spc]\]"

This sort of thing is more than a bit error-prone in complicated callbacks! At that point, it's much easier (and good practice) to have a little helper procedure:

proc do_update_text {varname value} {
    upvar #0 $varname var
    set var [update_text $var $value]
}

Then you can do (note: passing the name of top_tlbl, not the value):

… -command [list do_update_text top_tlbl $spc]

The use of global variable names might be considered to be problematic, except you've got to remember that the global namespace is mainly owned by your application. If you want to store variables in globals for your app, do it! You have the complete right.

Library code needs to be a bit more careful of course. The usual techniques there include using variables in other namespaces (Tk does this internally) or in objects. The code doesn't really get that much more complicated; it's still building on the practice of using helper procedures listed above. Passing a namespaced variable name is pretty easy though:

… -command [list do_update_text ::mynamespace::top_tlbl $spc]



回答2:


-command {set top_tlbl [update_text $top_tlbl $spc]} will be invoked in global context when the button is clicked. Is your variable spc accessible in global context? Try to specify the variable's namespace explicitly, for example $::spc.


expr 2 + 2 and "expr 2 + 2" are not the same. Tcl identifiers can contain spaces and "expr 2 + 2" is the whole identifier, which is not found.




回答3:


You should probably just call update_text and have top_tlbl be a global and set from the called function rather than returned.

Its generally simplest to just try and call a simple function and use list to ensure everything is quoted correctly.

button .mltext.button -text "Apply" \
    -command [list update_text $top_tlbl $spc]

This will capture the current values of top_tlbl and spc when you define this button and its command. If you want the values at the time you press the button then you should probably be passing the names of the variables and using the variable or global commands.

Anyway, the rules are that things inside curly braces {} have no substututions performed on them. So {$v} is passed to the callee as exactly that - dollar v. Using list or double quotes lets variable substitution be performed and so you pass the value of the variable. List properly handles quoting for cases where the variable value might contain spaces and so on.



来源:https://stackoverflow.com/questions/33355737/tk-how-to-pass-variable-values-with-command

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!