how to get the value from ttk:entry - tcl

I am trying to get the value from ttk:entry. I have the following code.
variable DefaultRoot
ttk::label $wi.custcfg.dlabel -text "Default Root:"
ttk::entry $wi.custcfg.daddr -width 10 -textvariable ::DefaultRoot -validate focusout -validatecommand { puts $::DefaultRoot; return 1}
puts $DefaultRoot
But I am getting error on the last puts

The variable won't exist until you set it to some value. Merely declaring it as a variable (eg: variable DefaultRoot) won't make it spring into existence.
With the code you posted, you're executing the last puts about a microsecond after creating the entry widget. The user won't have the ability to enter any text before the puts happens. Thus, the variable won't yet exist and the puts will fail.
A simple solution is to make sure to set the variable before you call puts, though that only means that the puts will print the default value.
In other words, this will print "this is the default":
variable DefaultRoot
set DefaultRoot "this is the default"
ttk::entry $wi.custcfg.daddr -textvariable ::DefaultRoot
puts $DefaultRoot
To answer your specific question, however, you can use $::DefaultRoot anywhere you want after the variable has been created.
For example, you could create a button that prints the value like this:
proc print_variable {} {
puts "DefaultRoot=$::DefaultRoot"
}
ttk::button $wi.custcfg.button -text foo -command print_variable

You can access the variable anywhere via
global DefaultRoot
puts $DefaultRoot
or
puts $::DefaultRoot

Related

I am need select a option at the tk_menuOption and after invoke um event

What I want is for it to fire the event bind to each choice made.
So I will not use a button to call any event.
This is my The doubt:
set choice [list \
{News} \
{Nature} \
{City} \
{Food} \
{People} \
{Anime} \
{Tecnology} \
{Objects} \
{Others}]
tk_optionMenu . chose {*}$choice
bind . <1> {
clipboard clear
clipboard append [%W cget -text]
bell
# Procedure to be invoke, this bellow:
load
}
proc load { } {
# Code here!
tk_messageBox -message "You picked the option: $chose"
}
How to can I have some a event bind for tk_optionMenu, since that I can change first each option so to after do call procedure.
As in bind . <1>, there is no time to choose. Then, it is triggered by clicking on tk_menuOption.
I even tried to switch to other properties of the bind itself, but I didn't get the expected result.
I think something is missing to make it work as proposed in the question. I think it must be a stopwatch, which waits a few milliseconds before calling the event. This is what I imagine.
First off, tk_optionMenu is just a procedure.
Here's the source for it, but it's really quite short:
proc ::tk_optionMenu {w varName firstValue args} {
upvar #0 $varName var
if {![info exists var]} {
set var $firstValue
}
menubutton $w -textvariable $varName -indicatoron 1 -menu $w.menu \
-relief raised -highlightthickness 1 -anchor c \
-direction flush
menu $w.menu -tearoff 0
$w.menu add radiobutton -label $firstValue -variable $varName
foreach i $args {
$w.menu add radiobutton -label $i -variable $varName
}
return $w.menu
}
If it doesn't do what you want, just take the code and modify it.
Secondly, events on menus are really tricky! In particular, if you are on Windows or macOS then many standard events are not delivered at all due to the way that their native menuing system works. Instead, you get two possible, useful callbacks:
The -command callback on the menu items; this is supported by most of the menu items, especially including all the ones which end an interaction session with the menu (such as command items and radiobutton items).
The -postcommand callback on the overall menu which is called when the menu is posted to the screen, giving you an opportunity to alter the contents of the menu dynamically (if you need it).
If what you want can't be done with those, it probably shouldn't be done with a menu in the first place as the interaction pattern with menus is pretty strongly fixed. A listbox might be more suitable? Or something custom (you often can do those with a text or canvas; no C required).
While the tk_menuOption does not provide a binding for when an option is selected, it has an associated variable. So you can put a trace on that variable and do whatever you want in the handler:
trace add variable chose write {apply {{name arg op} {
upvar 1 $name var
clipboard clear
clipboard append $var
bell
# Procedure to be invoked
load
}}}
Note: It is not a good idea to overwrite built-in commands, like load. It is likely to break a lot of things.
Even though we already have the answers as good solutions that this problem addresses.
Before I even received the answer, everyone found this link - https://wiki.tcl-lang.org/page/tk_optionMenu
The description of the "snippet" in the link is as follows:
tk_optionMenu $w varName a b c d
for {set i 0} {$i < [[$w cget -menu] index end]} {incr i} {
[$w cget -menu] entryconfig $i -command $cmd
}
That was the discovery of the answer to solve my problem. So, I want to share with friends:
My Example:
#!/bin/sh
# \
exec wish "$0" "$#"
package require Tk
. configure -width 300 -height 200
set choice [list \
{News} \
{Nature} \
{City} \
{Food} \
{People} \
{Anime} \
{Tecnology} \
{Objects} \
{Others} ]
tk_optionMenu .foo chose {*}$choice
for {set i 0} {$i < [[.foo cget -menu] index end]} {incr i} {
[.foo cget -menu] entryconfig $i -command {load;bell}
}
proc load { } {
global chose
# Code here!
tk_messageBox -message "You picked the option: $chose"
}
pack .foo -side left -fill both -expand 1
.foo config -indicatoron 0 -relief flat -justify center -bg white
Print:
But, the answers given by them .. Schelte Bron and Donal Fellows, were big help. Now I learn more things that I didn't know before. Since the words load are embedded in Tcl, it is not a good idea to use them.

entry with new window

I want a new window appearing with simple entry to put one line of text and button ok to set value of variable. When I use simple entry command, it appear in my main window. I need something like tk_dialog with option to put text to some variable. Is there any predefined tk_* function?
you have to create another window with tk toplevel command
% set top [toplevel .top]
.top
% focus $top
% grab $top
% set entryBox [entry $top.ent -textvariable x]
.top.ent
% pack $entryBox
% set btn [button $top.btn -text "Click Me"]
.top.btn
% pack $btn
This procedure:
proc entrybox varName {
set top [toplevel .top[clock seconds]]
entry $top.eb -textvariable $varName
button $top.bu -command [list incr ${top}done] -text OK
pack {*}[winfo children $top]
vwait ::${top}done
unset -nocomplain ::${top}done
destroy $top
}
when given a global or fully qualified name, creates a new toplevel dialog with an entry and a button. It waits for the button to be clicked and then destroys the toplevel dialog. The text in the entrybox remains in the variable.
Documentation:
button (widget),
clock,
destroy,
entry (widget),
incr,
list,
pack,
proc,
set,
toplevel,
unset,
vwait,
winfo,
{*} (syntax)

TCL/Expect - $argv VS $::argv VS {*}$argv

What is difference between following variables:
$argv
$::argv
{*}$argv
First two are possible to print via puts command and they returns following output:
param0 param1 {param 2} param3
param0 param1 {param 2} param3
The arguments that was passed to script were:
param0 param1 "param 2" param3
The last one end up with error:
wrong # args: should be "puts ?-nonewline? ?channelId? string"
while executing
"puts {*}$argv"
I've done some research in this area using following code:
if {[array exists $argv]} {
puts "\$argv IS ARRAY"
} else {
puts "\$argv IS NOT AN ARRAY"
}
if {[string is list $argv]} {
puts "\$argv IS LIST"
} else {
puts "\$argv IS NOT LIST"
}
if {[array exists $::argv]} {
puts "\$::argv IS ARRAY"
} else {
puts "\$::argv IS NOT AN ARRAY"
}
if {[string is list $::argv]} {
puts "\$::argv IS LIST"
} else {
puts "\$::argv IS NOT LIST"
}
if {[array exists {*}$argv]} {
puts "{*}\$::argv IS ARRAY"
} else {
puts "{*}\$::argv IS NOT AN ARRAY"
}
if {[string is list {*}$argv]} {
puts "{*}\$::argv IS LIST"
} else {
puts "{*}\$::argv IS NOT LIST"
}
The last two if-else statements which contain {*}$argv ends with following error:
wrong # args: should be "array exists arrayName"
while executing
"array exists {*}$argv"
invoked from within
"if {[array exists {*}$argv]} {
puts "{*}\$::argv IS ARRAY"
} else {
puts "{*}\$::argv IS NOT AN ARRAY"
}"
Commenting out those two statements shows that $argv and $::argv are lists:
argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST
Both those lists can be traversed as standard list e.g.:
foreach item $argv {
puts $item
}
or
foreach item $::argv {
puts $item
}
Attempt to traverse {*}$argv the same way leads to following error again:
wrong # args: should be "foreach varList list ?varList list ...? command"
while executing
"foreach item {*}$argv {
puts $item
}"
I am using TCL version 8.5
What is difference between following variables:
$argv
$::argv
{*}$argv
There are two types of difference here.
Unqualified and Qualified Variables
In Tcl, unqualified and qualified variables can be a bit different, but it depends on the context (in a pretty simple way though). Firstly, a qualified variable name is one that contains at least one :: within it. If the variable name (the thing after the $ — in Tcl, $ just means “read this variable now and use its contents here”) starts with ::, it is an absolute variable name, otherwise a qualified variable name is a relative variable name and is resolved with respect to the current namespace (which you can find out with namespace current if you're uncertain). Absolute variable names always refer to the same thing, in all contexts. Thus, ::argv is an absolute variable name, and indeed it refers to a variable called argv in the top-level, global namespace. That happens to be a variable that tclsh and wish write their arguments into.
But if there is no ::, it is an unqualified variable name. If you are not in a procedure (or procedure-like thing, which includes a lambda term such as you'd use with apply or the methods defined by various OO systems) then the variable is (mostly) treated as if it was a relative variable name and resolved with respect to the current namespace. namespace eval and namespace code are two of the things that can change the current namespace (the others are more obscure). All this is provided you use variable to declare all your namespace variables. Otherwise, you can hit some weird problems with variable resolution which are really nasty. So do use variable. Really.
If you are in a procedure(-like entity) though, that unqualified name refers to a local variable, whose life is coupled to that of the stack frame pushed on the stack when the procedure is entered. That can be linked to variables in other scopes (including the global namespace) through various commands: global, upvar, variable, and namespace upvar. However, the actual resolution of the variable is to something local.
Finally, there might also be a custom variable resolver in place. Since you're using Tcl 8.5, the place where you're most likely to see this in use is if you're using Incr Tcl, an object system for Tcl. Custom variable resolvers can do some complex stuff. (If you were using Tcl 8.6, the most likely place to see a custom variable resolver at work is in TclOO. The variable resolver there is very conservative and cautious, but allows local variables to be bound to object variables without having to explicitly declare this in each method).
Normal and Expanding Substitution
The difference between $argv and {*}$argv is totally different.
$argv is a normal substitution. It says “read this variable here and use the contents of it instead”. It can be used in the middle of a word, so $argv$argv$argv is a thing, consisting of the concatenation of the contents of the argv variable three times.
{*}, when placed at the start of a word (it's not special elsewhere), marks that word for expansion. When a word is expanded, it's parsed as a Tcl list after all other normal substitutions have been done, and the words of that list are used as words in the resulting command being built up. {*}$argv is a degenerate case where the remainder of the word is just the a read from a variable; the words that are used in the command are the elements of the list in the argv variable. Since that's normally a list, this is all hunky-dory.
Here's an example:
set abc {a b c}
set dabcf [list d $abc f]
puts $dabcf; # ===> “d {a b c} f”
set eabcg [list e {*}$abc g]
puts $eabcg; # ===> “e a b c g”
See the difference? One produces three elements in the list, the other produces five. It makes even more sense with something somewhat longer:
set options {
-foreground blue
-background yellow
-text "This is eye-watering stuff!"
}
button .b1 {*}$options -command {puts "Ouch 1"}
button .b2 {*}$options -command {puts "Ouch 2"}
button .b3 {*}$options -command {puts "Ouch 3"}
pack .b1 .b2 .b3
With expansion, that all Just Works™. Without, you'd have to do something horrific with eval:
eval [list button .b1] [lrange $options 0 end] [list -command {puts "Ouch 1"}]
# etc.
This was difficult to get right, and tedious, so it caused lots of people (including Tcl and Tk maintainers!) many problems because they tended to take shortcuts and get it wrong. It was to address this that expansion syntax was created in Tcl 8.5 to make this all less error prone. (The prototypical example in plain Tcl tends to involve things with exec, and meant that quite a few people actually had security holes because of this.)
As a bonus, using {*} is much faster than using eval since expansion can guarantee that it is never doing complicated reparsing of things. In Tcl, faster virtually always correlates with safer.
Note that this is independent of whether the variable is qualified. Yes, that means that you can also have {*}$::argv if you want.
You confuse the effects of substitution with the effects of argument expansion.
Please study the Dodekalogue http://wiki.tcl.tk/10259.
You mix the Rule #5: Argument Expansion (the {*} thing) with Variable Substitution (Rule #8).
The three forms you listed above are equivalent to the following:
$argv -> [set argv]
Get the value of a simple variable in the currently active scope.
$::argv -> [namespace eval :: { set argv }] -> [set ::argv]
Get the value of the variable in the namespace :: (the global namespace)
{*}$argv -> [eval [set argv]]
Expand the variables content to multiple arguments.

Create multiple entry box using for loop in tcl/tk

Hi i have a integer value and based on the value i have to create the entry boxes in a tcl/tk gui. So i did something like this:
set frame1 [::hwt::LabeledFrame [::hwt::WindowRecess editThicknessWindow].frame1 "Current List: " \
-expand 0 -relief flat -anchor e -side top -pady 15]
for {set i 0} {$i < $length_Thickness} {incr i} {
set Entry_No_$i [::hwt::AddEntry $frame1.Entry_No_[eval $i] label "List Values_$i :" labelwidth 15 \
entryWidth 10 anchor nw validate real text [namespace current]::arr_attribOptionsValue(Thickness)[$i] state normal \
withoutPacking -textvariable [namespace current]::lst_Value[$i] ]
pack $Entry_No_[eval $i] -side top -anchor nw -padx 10 -pady 10
}
So let's say i have the integer number as 3 then i have to create 3 entry boxes. I have a list "arr_attribOptionsValue(Thickness)" with 3 values in it so i want the values from the list to be populated in the entryboxes. Since i am new to tcl/tk so not sure if the syntax is correct or if i am missing something. I am confused like i am creating the tk variable Entry_No_$i and in the pack i am using $Entry_No_[eval $i] so will these two refer to the same value or is it wrong syntax.
Firstly, if you're creating variables like $Entry_No_[eval $i] then I really think you're going about it the wrong way. Such compound variables are really much more easily done as array elements, such as Entry_No($i), with no eval in there to confuse things (or $Entry_No($i) to read from the element instead of name it).
Secondly, [namespace current]::arr_attribOptionsValue(Thickness)[$i] is even more likely to be wrong, especially as $i will be an integer. You can't address an element of a list like that (it't been suggested that it ought to be possible, but that's not going to happen in the next few months for sure, and certainly not in combination with an array like that). Instead, the simplest mechanism is to use another array that you populate from the source list and to use a trace to couple back if necessary. (Traces are a more advanced technique; ask another question if you need them.) Populating the working array from list might be done like this:
set i 0
foreach item $arr_attribOptions(Thickness) {
set varname arr_attribOptions(Thickness,$i)
set $varname $item
incr i
}
Yes, you can store a variable name in a variable. (When reading, use [set $varname] to do the double dereference.)
Thirdly, widget names are best if they don't contain most non-alphanumeric characters (except ., of course) and don't start any component with a capital letter (for technical reasons relating to window classes).
Fourthly, please use variable (or upvar or namespace upvar) to avoid having to use fully-qualified variables.
Overall, we can use these techniques together to get something like this:
set frame1 [::hwt::LabeledFrame [::hwt::WindowRecess editThicknessWindow].frame1 "Current List: " \
-expand 0 -relief flat -anchor e -side top -pady 15]
namespace upvar [namespace current] \
arr_attribOptions attribs arr_values values lst_Value valueList
set i 0
foreach item $attribs(Thickness) {
set attribs(Thickness,$i) $item
set values($i) [lindex $valueList $i]
incr i
}
for {set i 0} {$i < $length_Thickness} {incr i} {
set Entry_No($i) [::hwt::AddEntry $frame1.entry_No_$i label "List Values_$i :" labelwidth 15 \
entryWidth 10 anchor nw validate real text arr_attribOptions(Thickness,$i) state normal \
withoutPacking -textvariable [namespace current]::arr_values($i)]
pack $Entry_No($i) -side top -anchor nw -padx 10 -pady 10
}
I don't know that I've identified all the problems yet (and most Tcl programmers don't use Hungarian type prefixes on variable names) but it is a lot closer to idiomatic now.

Tcl/Tk: can't bind to Escape and Return

I can't bind the Escape and Return keys in my Tcl/Tk code. The following code reproduces the bug. When I hit Esc or Enter key I get the following error message:
Error: can't read "cmd": no such variable
proc OkCancel { cmd } {
button .${cmd}.ok -text "OK" -command [list myOk $cmd ]
button .${cmd}.cancel -text "Cancel" -command [list myCancel .$cmd]
grid .${cmd}.ok .${cmd}.cancel -sticky e
bind .$cmd <Return> { myOk $cmd ; break }
bind .$cmd <Escape> { myCancel .${cmd} ; break }
}
proc myOk { cmd } {
puts "The command name is = $cmd"
}
proc myCancel { arg } {
destroy $arg
}
proc test { } {
set cmd "hello"
toplevel .$cmd
OkCancel $cmd
}
test
That's because when an event fires, the script bound to it gets evaluated at the global level (as per the bind manual). That is, in case of your <Return> binding, the script myOk $cmd ; break will be executed, literally. Hence, if there exists no variable named "cmd" in the global namespace at the time your script runs, the error you have shown will be triggered.
To fix the problem, there are several ways:
Make the $cmd substitution be evaluated at the time the script is created and bound. To do this, just replace {} with "" to allow variable substitution, that is, if you'd write bind .$cmd <Return> " myOk $cmd ; break " the bound script would be myOK hello ; break
Turn the callback script into a procedure call and pass it explicit parameters, like this: bind .$cmd <Return> [list mycallback $cmd] and then make sure you have defined mycallback procedure accepting one parameter. This way you can write generic event handlers which are parameterized by whatever parameters you need at the time of the binding.
Use namespace code or a similar tool to make your script executed in a specified namespace which has the specified variable defined.
In all cases beware that since your $cmd may in certain cases expand to something odd, it's a good idea to protect the whole script from such a situation--that's what [list ...] does in the second example (see this for more info).