Create multiple entry box using for loop in tcl/tk - tcl

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.

Related

I am trying to form a rectangle with 2 pairs of Coordinate in TCL

I am new to TCL and hardware design. I am currently doing a script that could verify all the ports at an edge and make a rectangle box that cover all the ports at an edge.
Since I am new to TCL, I think there is something wrong in the syntax.
The error msg is:
Error: Invalid coordinates '$bbox_coor_x1 [expr $bbox_coor_y2-30]' in list. (NDMUI-100)
Error: Empty or invalid geometry specified for the '-boundary' argument of the command. (NDMUI-337).
Please help me.
Your problem is that variables and commands are not interpolated inside curly braces. Please learn the difference between curlies and double quotes.
For example:
set x 5
set y 10
# Curlies
puts {$x $y}
--> $x $y (literally)
puts {$x [expr $y+1]}
--> $x [expr $y+1] (literally)
# Double quotes
puts "$x $y"
--> 5 10
# List command
puts [list $x $y]
--> 5 10
puts [list $x [expr $y+1]]
--> 5 11
When making a list of lists, like a bbox, anything inside outer-most curlies will interpolate:
puts "{$x $x} {$y $y}"
--> {5 5} {10 10}
One more thing, note that lindex can take multiple indexes. Do this instead of calling lindex twice.
lindex $bbox 0 1

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.

TK/TCL, trouble understanding this tic tac toe code snippet

Can anyone help explain to me what the snippet below does. This code snippet is taken from http://wiki.tcl.tk/12374. It is meant to create a tic tac toe game. There are not many resources out there for understanding Tk/Tcl so this is giving me significant difficulty.
proc DrawBoard {{redraw 0}} {
global S B GAME C
if {$redraw} { ;# Must redraw everything
.c delete all
set w2 [expr {$B(w2) - 15}] ;# Make a little margins
set h2 [expr {$B(h2) - 15}]
set hbar [expr {$h2 / 3.0}]
set vbar [expr {$w2 / 3.0}]
set B(0) [list -$w2 -$h2 -$vbar -$hbar] ;# All 9 cells
set B(1) [list -$vbar -$h2 $vbar -$hbar]
set B(2) [list $vbar -$h2 $w2 -$hbar]
set B(3) [list -$w2 -$hbar -$vbar $hbar]
set B(4) [list -$vbar -$hbar $vbar $hbar]
set B(5) [list $vbar -$hbar $w2 $hbar]
set B(6) [list -$w2 $hbar -$vbar $h2]
set B(7) [list -$vbar $hbar $vbar $h2]
set B(8) [list $vbar $hbar $w2 $h2]
for {set i 0} {$i < 9} {incr i} { ;# Rectangle for each cell
.c create rect $B($i) -tag b$i -fill {} -outline {}
.c bind b$i <Button-1> [list DoClick $i]
set B($i) [ShrinkBox $B($i) 25]
}
.c create line -$w2 $hbar $w2 $hbar -tag bar ;# Draw the cross bars
.c create line -$w2 -$hbar $w2 -$hbar -tag bar
.c create line $vbar -$h2 $vbar $h2 -tag bar
.c create line -$vbar -$h2 -$vbar $h2 -tag bar
.c itemconfig bar -width 20 -fill $::C(bars) -capstyle round
}
.new config -state [expr {$GAME(tcnt) == 0 ? "disabled" : "normal"}]
for {set i 0} {$i < 9} {incr i} {
.c itemconfig b$i -fill {} ;# Erase any win lines
DrawXO $GAME(board,$i) $i
}
foreach i $GAME(win) { ;# Do we have a winner???
.c itemconfig b$i -fill $C(win)
}
}
Ok, the most significant question I have regards w2, h2, hbar, vbar variables. Particularly with how they are declared. For instance, set w2 [expr {$B(w2) - 15}]. How can w2 be defined referring to itself??? The author uses these variables to draw the tic tac toe lines, but I don't even know how what these variables mean. Does these variables specify some dimension of the canvas so that the author can use it to bind particular regions to click activities?
If I understand these four variables, everything else will make sense!
Here is the image of what the board looks like:
Variables in Tcl (Tk is just a window drawing toolkit that lives on top of Tcl) are defined when they are written to; there's usually no explicit declaration. The only exception to this is with variables directly in a namespace, where it is best practice to use the variable command to declare them before first use, like this:
namespace eval exampleNamespace {
variable xmpl1 "abc def"
# Or equivalently...
variable xmpl2
set xmpl2 "abc def"
# You have to use the second style with arrays...
variable arrayXmpl
set arrayXmpl(1) "pqr stu"
set arrayXmpl(2) "qwerty uiop"
}
Local variables in procedures don't need declaring, though if you want to access a variable that isn't local you have to use a command (often global or upvar) to bring it into scope.
proc variableExample {formalArgument1 formalArgument2} {
set localVar1 "abc"
set localVar2 "def"
global thisOtherVar
append thisOtherVar "ghi" $formalArgument1
puts "Currently, got $localVar1 $localVar2 and '$thisOtherVar'"
}
It's pretty conventional to put the global at the top of the procedure, but it's totally not necessary. It's effect persists from where you do it until the end of the procedure call. Tcl's semantics are strictly operational with extremely tightly defined evaluation order (it's left-to-right, always).
Now, arrays are aggregate variables. Each element of an array is a variable itself. They're distinct from normal simple variables, though the name of the overall array is in the same naming scheme as the simple variables. You can't have a simple foo and a foo(bar) in the same scope (without unsetting one first, which removes the variable). The keys into the elements are strings — the implementation behind the scenes is a high-performance hash table — that are entirely not variables and this means that the B(w2) and w2 variables are entirely distinct; they're not the same thing at all. However, we can use variables (and other Tcl substitutions) in computing the string to use as a key, so we can do this:
set name "w2"
set B($name) "example of "
append B(w2) "array key handling"
puts "this is an $B($name)"
Let's look at the example that you were puzzling over:
set w2 [expr {$B(w2) - 15}]
Breaking it into pieces:
set w2 […]
It's going to write to the variable w2. That's what the set command does with two arguments. The thing that is going to be written in is the result of evaluating another command. We need to look deeper.
expr {$B(w2) - 15}
Breaking it into pieces:
expr {…}
That yields the result of evaluating the expression in the braces. It's strongly recommended that you put braces around all your expressions; it's safer and much faster. What's the expression? (Note that expressions use a different syntax to the rest of Tcl.)
$B(w2) - 15
OK, that's subtracting 15 (the number) from the value read (because of the $) from the w2 element of the array B. The w2 here is just a string. That there's a variable elsewhere with the same name is coincidence.
And that's it. Reassembling the pieces, we see that:
set w2 [expr {$B(w2) - 15}]
Assigns the result of subtracting 15 from the contents of B(w2) to the variable w2. That's all it does. (The array B is a global array; see the global at the top of the procedure.)
The lines:
set w2 [expr {$B(w2) - 15}] ;# Make a little margins
set h2 [expr {$B(h2) - 15}]
set hbar [expr {$h2 / 3.0}]
set vbar [expr {$w2 / 3.0}]
These get the half height and width of the canvas from the global array B, remove 15 pixels for a margin, and then set hbar/vbar to a third of that value so that the coordinates for drawing are easier. It helps once you realize that the canvas has had its drawing origin moved (similar to in scrolling) to the center of its window. And be aware that doing -$hbar is actually being a bit naughty, though cute; it's using string concatenation to negate a value, which is OK when the value is positive and doesn't have an explicit sign, but would be fragile if that ever changed. And it's slow by comparison with doing [expr {-$hbar}], though that's longer; the cost of computation isn't exactly matched with the length of the command.

expected integer but got "floating point number" error

I try to write a very simple program in TCL using list.
Below is the list
list { 1 2 3 4 5 6 1.5 7 }
Below is my code
set sum 0
for {set i 0} {$i < [llength $list]} {incr i} {
incr sum [lindex $list $i]
}
puts $sum
On executing the above program I am getting the below error due to floating point value of 1.5 in the list
expected integer but got "1.5"
(reading increment)
invoked from within
"incr sum [lindex $list $i]"
I searched on internet and could not find anything relevant.
Please advise how do I handle the floating point value?
While using incr command, variable must have value that can be interpreted as a an integer. See tcl wiki.
If variable is a non-integral real number, [incr] could not be used, but [set] could:
set sum 0
for {set i 0} {$i < [llength $list]} {incr i} {
set sum [expr {$sum + [lindex $list $i]}]
}
puts $sum
Omsai's answer should solve your problem, but a cleaner solution is to use foreach:
set sum 0
foreach n $list {
set sum [expr {$sum + $n}]
}
puts $sum
Summing up a list of numeric values can also be done with the ::tcl::mathop::+ command:
::tcl::mathop::+ {*}$list
This looks more complicated that it is. The + command isn't available in the regular namespace, so you need to specify where it comes from (the ::tcl::mathop namespace). The command expects to get each operand as a separate argument, so if they are in a list you need to expand that list using the {*} prefix.
foreach and the various mathop commands are documented here: foreach, mathop.
(Note: the 'Hoodiecrow' mentioned in the comments is me, I used that nick earlier.)
Tcl gives an error if you will try
incr a 1.5
you have to change the logic.
clearly you want to add all the numbers in the list. and answers are easy and many. But i will give you the shortest way:
set l { 1 2 3 4 5 6 1.5 7 }
set sum [expr [join $l +]]
NO LOOPING REQUIRED.

Combinations of all charcaters and all lengths with using less number of loops?

Brain Teaser: I self originated this question, but stuck completely.
I want to create all possible combination of all characters, but of all possible lengths. Suppose, [a-z] combination of 1 length, then [a-z] combination of 2 length, and so on till the maximum length achieved.
this could be very easily done by iterative looping.
Example for 3 length:
proc triples list {
foreach i $list {
foreach j $list {
foreach k $list {
puts [list $i $j $k]
}
}
}
}
But, it should solve using less loops (looping needs to be dynamic)
set chars "abcdefghijklmnopqrstuvwxyz"
set chars [split $chars ""]
set complete_length [llength $chars]
set start 0
set maximum_length 15
while {1} {
if {$start > $maximum_length} {
break
}
for {set i [expr $maximum_length-$start]} {$i >= 0} {incr i -1} {
# dump combinations
}
incr start
}
In this chunk, what algorithm or method i should apply? Any kind of suggestions/help/code will be appreciated.
Sry, this is not an answer, but hopefully some interesting discussion anyway:
The word "combinations" is often used way too generally, so it can be interpreted in many different ways. Let's say that you have a source list of 26 different elements, the english letters, and you want to pick 3 of them and combine in a 3 element destination list:
Can you always pick any letter from the source list, or do the elements disappear from it as you pick them? Either define "pick" (are the elements copied or moved during a pick), or define the set of source values (is there 1 of each of A-Z or an infinite amount of A-Z).
Does the order in the destination list matter? Is AHM considered to be the same combination as HAM? Define "combine".
If you have a list where not all elements are different, e.g. {2 10 10 64 100}, you have even more possibilities. Define your set of values.
Your first example prints permutations, not combinations. If that's what you want, the easiset way is a recursive procedure. Combinations are more complicated to generate.
EDIT:
I wrote this procedure for a Project Euler program. It picks all the elements, but maybe you can modify it to pick n. It takes a command prefix as argument, so you don't have to store all permutations.
package require Tcl 8.5.0
proc forEachPerm {list cmdPrefix} {
_forEachPerm {} $list $cmdPrefix
}
proc _forEachPerm {head list cmdPrefix} {
if {![llength $list]} {
{*}$cmdPrefix $head
} else {
for {set i 0} {$i < [llength $list]} {incr i} {
_forEachPerm [concat $head [lrange $list $i $i]] [lreplace $list $i $i] $cmdPrefix
}
}
}
# example use:
forEachPerm {a b c} {apply {{list} {puts [join $list]}}}