How to solve the problem? Thanks.
try
global commit
vwait commit
or even better
vwait ::commit
perhaps own namespace or OO-Tcl to avoid some unexpected side effects
in global context
See implemtation of Tk dialog for good example
in file dialog.tcl
- use a helper proc if -command gets lengthy
- use list to build up the command
proc button_helper {e1 e2} {
set ::commit [proc_to_run_other_thing $e1 $e2]
puts "entry1:$e1, entry2:$e"
}
button $confirm -text Confirm \
-command [list button_helper $entry1 $entry2]
See also
http://wiki.tcl.tk/9330
HTH
R'
Excellent advice. Personally, I define "lengthy" as "more than one
command". It almost always makes all quoting problems vanish WRT
-command and for bindings.
and those entry variable are not global value, so I cannot use them
with {}.
Create a helper proc and your quoting problems should vanish. If they
don't, show us what you did and we can help.
As for your comment "those entry variable are not global", I think
you're mistaken. If they are a -textvariable, they are global.
Your original code was this:
entry $entry1 -width 20 -relief sunken -textvariable entry1
entry $entry2 -width 50 -relief sunken -textvariable entry2
button $confirm -text Confirm -command {
set commit [proc_to_run_other_thing $entry1 $entry2];
puts "entry1:$entry1, entry2:$entry2" }
button $w.command.cancel -text Cancel -command { set commit 0 }
In the above, you use or create two different variables with the same
name. You have local variables named "entry1" and "entry2" which
contains the path of the widgets. You also create two global variables
named "entry1" and "entry2" which are the textvariables associated with
the widget.
That is very confusing. I strongly recommend against using the same
variable name in two different ways like that. Tk can handle it but it
is obviously causing you (and us) some confusion.
In the above code, what do you expect to pass to
proc_to_run_other_thing? Is is the name of the widgets or the variable
name associated with the entry widgets or the value associated with the
widgets?
In either case, a helper proc would really help. There's simply no
reason not to do it, especially if you are just learning tcl.
Here's one way to solve your problem:
entry $entry1 -width 20 -relief sunken
entry $entry2 -width 20 -relief sunken
button .confirm -text Confirm \
-command [list _do_confirm $entry1 $entry2]
proc _do_confirm {w1 w2} {
puts "widget $w1 has this value: [$w1 get]"
puts "widget $w2 has this value: [$w2 get]"
# if proc_to_run_other_thing expects widgets:
# proc_to_run_other_thing $w1 $w2
# if proc_to_run_other_thing expects values:
# proc_to_run_other_thing [$w1 get] [$w2 get]
}
The above isn't necessarily the best way to solve your problem but I
think it might be the most straight-forward. Once you can understand why
the above works and your code doesn't we can move on to other solutions.
As my example showed, you don't need to use -textvariable at all. If you
do, it must represent a variable accessible from the global scope. That
means either a global variable or a fully qualified namespace variable.
There is no way to have it use a local variable.
> 2. I hope pass the updated variable value to my proc_to_run_other_thing
> directly by "-command" in one line, can I do it? I don't like to create
> too many procedures.
Why don't you like to "create too many procedures"? Procedures are a
tool to get your job done, and in this case will make your job easier.
However, you can certainly do what you want without using a proc. It
requires all quoting tricks that will make your code hard to read and
hard to maintain.
Here's how to do it. It is untested, but I think I got it right. That's
the problem with this approach however: it's _really_ hard to know just
by looking at it if it's correct or not, and it's difficult to modify
the code later:
button $confirm -text Confirm -command "
set commit \[proc_to_run_other_thing \$entry1_variable \
\$entry2_variable\];
puts \"entry1:\$entry1_variable, entry2:\$entry2_variable\""
This will, BTW, set the global variable "commit". Commands, like
bindings, execute in the global level.
If I were reviewing that code for use in a production environment I
would reject it.
It strikes me that now is the time to point out that Tcl 8.5a4 has a new
trick up its sleeve that will allow a bridge between these two ways of
operating: the [apply] command, as described in TIP#194:
button $confirm -text Confirm -command [list apply {{x} {
global entry1_variable entry2_variable
set commit [proc_to_blah $entry1_variable $entry2_variable]
puts "entry1:$entry1_variable, entry2:$entry2_variable"
puts "Gratuitously, x = $x"
}} "gratuitous extra argument"]
At the moment, I suspect that using a helper procedure will continue to
be clearer, but the "too many procs" argument will be moot... :-)
Donal.
> It strikes me that now is the time to point out that Tcl 8.5a4 has a new
> trick up its sleeve that will allow a bridge between these two ways of
> operating: the [apply] command, as described in TIP#194:
>
> button $confirm -text Confirm -command [list apply {{x} {
> ...
> }} "gratuitous extra argument"]
>
I wish we could remove one or two layers of list-ness. I haven't thought
about it much; it's just a knee jerk reaction, but isn't this a bit cleaner:
button .b ... -command [apply {x} {body} arg arg ...]
I'm not sure I see the benefits of all the nesting. Can't the usage be:
apply arglist body ?-namespace namespace? ?--? ?arg ...?
That to me looks a lot cleaner than
apply {arglist body ?namespace?} ?arg ...?
Those lists are there for one particular purpose: the whole anonymous
function is one string - internally, one Tcl_Obj. This allows for the
bytecompiled version to be cached and reused - huge performance difference.
The syntax is rather
apply anonFunction ?arg ...?
where an anonymous function has a structure
argList body ?namespace?
So you can do
set anon {{x y} {expr {$y+$x}}}
apply $anon 1 2
apply $anon 3 4
and avoid having to recompile that body at each call.
In your proposed version, keeping the anon function in a variable would
require the use of {expand} or [eval]. Not good, quite apart from
performance considerations. IMO.
Miguel
... -textvariable ::widgets::entry::my_variable
or something similiar.
another little question, Can I set a default value for my entry? before
, I used a textvariable, then I can set the default value by the
variable, now, I don't use textvariable any more. Is there another way
to set a default value for entry widget?
thanks.
As others have pointed out, a -textvariable is always global (or
namespace-global).
| 2. I hope pass the updated variable value to my proc_to_run_other_thing
| directly by "-command" in one line, can I do it?
Not reliably and directly. If you use quoting tricks some weird
contents of the entry *will* break the command.
So here's how to do it:
- using no -textvariable:
as Bryan has already shown in another post, you have to pass the
name of the entries to your proc and use the entry 'get' command to
get the contents:
set entry1 .e1
set entry2 .e2
set confirm .confirm
entry $entry1
entry $entry2
button $confirm -text Confirm \
-command [list proc_to_run_other_thing $entry1 $entry2]
proc proc_to_run_other_thing {entry1 entry2} {
# get the current contents of the entries
set contents1 [$entry1 get]
set contents2 [$entry2 get]
# now we can use $contents1 and $contents2
puts "entry1 has: $contents1"
puts "entry2 has: $contents2"
...
# set the global indicator _here_
set ::commit ...
}
Without a textvariable, you can set initial contents for the
entries via
$entry1 insert 0 "some initial text"
- using -textvariable:
you can pass the *name* of the variable to your proc and then use
'upvar' to look at the variable contents
set entry1 .e1
set entry2 .e2
set confirm .confirm
entry $entry1 -textvariable entry1_variable
entry $entry2 -textvariable entry2_variable
button $confirm -text Confirm \
-command [list proc_to_run_other_thing entry1_variable entry2_variable]
proc proc_to_run_other_thing {varname1 varname2} {
# get the current contents of the variables via upvar
upvar \#0 $varname1 contents1
upvar \#0 $varname2 contents2
# now we can use $contents1 and $contents2
puts "entry1 has: $contents1"
puts "entry2 has: $contents2"
...
# set the global indicator _here_
set ::commit ...
}
With a textvariable, you can set initial contents for the
entries either as above or by setting the textvariable:
set ::entry1_variable "some initial text"
HTH
R'
Ah, a misunderstanding. The [apply] is interpreted later on, when the
callback is interpreted. What you really want is the following helper
procedure:
proc callback {arglist body args} {
set ns [uplevel 1 {namespace current}]
return [list apply [list $arglist $body $ns] {expand}$args]
}
Which then lets you write:
button .b -command [callback {x} {body} arg arg ...]
Donal.
> Ah, a misunderstanding. The [apply] is interpreted later on, when the
> callback is interpreted. What you really want is the following helper
> procedure:
>
> proc callback {arglist body args} {
> set ns [uplevel 1 {namespace current}]
> return [list apply [list $arglist $body $ns] {expand}$args]
> }
>
> Which then lets you write:
>
> button .b -command [callback {x} {body} arg arg ...]
>
Yeah, I realized a little later that I could create what I want from the
apply primitive.
It seems like a very common use for [apply ...] is this very situation
so I'm wondering if a [callback ...]-like command should be a built-in.
I betcha I won't be the only person with this misunderstanding.
I'm not complaining though! I can't wait to get my hands on 8.5 for real
work. We were talking about that just yesterday, if we should take the
plunge and upgrade now. I was the wet blanket saying to at least wait
until it goes beta. Fortunately for us (us being the tcl community at
large) that the betas are consistently of remarkably high quality.