I've got this small loop cycle in TCL
for {set i 1} {$i <= $user} {incr i} {
grid [ttk::button .seluser.$i -text "$i" -command { set ::user $i }] -column $i -row 1
}
and I'm getting the message
ERROR can't read "i": no such variable
I think it's because -command works like a new proc and that's why it can not identify the variable i.
I don't know how to do it. Can anybody help me?
Try quotes instead of braces, so that $i is pre-interpolated. For example,
for {set i 1} {$i <= $user} {incr i} {
grid [ttk::button .seluser.$i -text "$i" -command " set ::user $i "] -column $i -row 1
}
Related
I am building a dialog from a database and using the key as the variable for the radio buttons. Whenever I click on a radio button, all the buttons in the same column change. There are top level variables with the names in the dictionary
#!/usr/bin/tclsh
package require Tk
variable dict_config
set dict_config [dict create]
dict set dict_config config_single_step { 0 "Single step" "Single step" "Run" }
dict set dict_config config_load { 0 "Load" "No load on startup" "Load oad on startup" }
dict set dict_config config_news { 0 "News" "No news" "News Feed" }
# Callback to set the value
proc SetValue { ix val } {
puts "Setting $ix to $val"
variable dict_config
set list_item [dict get $dict_config $ix]
dict set dict_config $ix [lreplace $list_item 0 0 $val]
}
proc todo {} {
puts "Coming soon to a screen near you"
}
proc DisplayOptions { dict_config } {
set row 0
dict for { ix list_item } $dict_config {
# Extract data from the list
set lab [lindex $list_item 1]
set zero [lindex $list_item 2]
set one [lindex $list_item 3]
incr row
# set dummy variable so the radio buttons respond correctly.
set $ix [lindex $list_item 0]
upvar 0 $ix debvar
# Create widgets
label .lab_$row -text $lab
radiobutton .zero_$row -text $zero -variable debvar -value 0 -command "SetValue $ix 0"
radiobutton .one_$row -text $one -variable debvar -value 1 -command "SetValue $ix 1"
if { $debvar == 0 } {
.zero_$row select
} else {
.one_$row select
}
# Layout widgets
grid .lab_$row -sticky e -row $row -column 0
grid .zero_$row -sticky w -row $row -column 1
grid .one_$row -sticky w -row $row -column 2
}
incr row
grid [button .butSave -text "Save" -command "todo"] -row $row -column 0
}
# Let the user change them
DisplayOptions $dict_config
I have also tried $debvar - same thing happens. I can get it to work if I change the loop body to
# Extract data from the list
set lab [lindex $list_item 1]
set zero [lindex $list_item 2]
set one [lindex $list_item 3]
incr row
# set dummy variable so the radio buttons respond correctly.
set r_$row [lindex $list_item 0]
# Create widgets
label .lab_$row -text $lab
radiobutton .zero_$row -text $zero -variable r_$row -value 0 -command "SetValue $ix 0"
radiobutton .one_$row -text $one -variable r_$row -value 1 -command "SetValue $ix 1"
if { r_$row == 0 } {
.zero_$row select
} else {
.one_$row select
}
# Layout widgets
grid .lab_$row -sticky e -row $row -column 0
grid .zero_$row -sticky w -row $row -column 1
grid .one_$row -sticky w -row $row -column 2
I'm just wondering why r_$row works i.e. the buttons do not change as one but $debvar, which is an upvar alias does not.
The -variable option of widgets refers to a global variable. So that's in a different scope than the debvar in your DisplayOptions proc. They happen to have the same name, but otherwise have no relation at all.
By using the same global variable for all radiobuttons, they all work as one group. To have several groups of radiobuttons, you have to use a different global variable for each group, as you did with the r_$row version. Note that once again the r_$row local variable in your proc has no relation to the r_$row variable you use for the widgets.
You would actually be better off using a different (simpler) local variable, because if { r_$row == 0 } will always be false and it's overly complicated to get the value of a variable whose name is constructed as r_$row.
The problem is in the "shared" variable debvar. You forgot apply the same approach for each row, on this way, you can do for example ...
# set dummy variable so the radio buttons respond correctly.
set $ix [lindex $list_item 0]
upvar 0 $ix debvar_$row;# <-- Here add the row number
# Create widgets
label .lab_$row -text $lab
;#<-- Below these two lines are changed too
radiobutton .zero_$row -text $zero -variable debvar_$row -value 0 -command "SetValue $ix 0"
radiobutton .one_$row -text $one -variable debvar_$row -value 1 -command "SetValue $ix 1"
;#<-- Finally you must use a way of dereferencing in order to use each debvar
if { [set debvar_$row] == 0 } {
.zero_$row select
} else {
.one_$row select
}
I hope this help you,
Saludos!
How to use Tktable package to realize the sample result, add a combobox into a cell? Or is there any other package that can be used to realized it conveniently? Thanks! sample
You should try executing something on your own first and then post what you've tried and ask specific questions where you are stuck. Simply reading the documentation will get you something that looks like this.
package require Tktable
package require Ttk
pack [table .t -cols 5 -rows 5 -variable tabarray]
set cb [ttk::combobox .t.cb]
array set tabarray [list 1,0 1 2,0 2 3,0 3 4,0 4]
array set tabarray [list 0,1 "Layer Name" 0,2 Transmission 0,3 Phase 0,4 Nested]
.t window config 1,1 -window $cb -sticky news
Or if you want to accomplish the same without using a tktable, you can leverage the grid geometry that comes with Tk. But you're probably better off using the table because it manages the geometry for you, provides column and row expansion, etc.
package require Ttk
set coltitles {"Layer Name" "Transmission" "Phase" "Nested"}
# Frame to hold the 'cells'
grid [frame .f] -row 0 -column 0 -sticky news
grid columnconfigure .f {1 2 3 4} -weight 1 -uniform 1
# Row numbers
for {set row 1} {$row < 5} {incr row} {
# Row labels
grid [label .f.tr$row -text $row -anchor e -relief groove -bd 1 -width 2] -row $row -column 0 -sticky news
}
# Column titles
for {set col 1} {$col < 5} {incr col} {
grid [label .f.tc$col -text [lindex $coltitles [expr $col - 1]] -anchor c -relief groove -bd 1] -row 0 -column $col -sticky news
}
# The table cells
grid [ttk::combobox .f.cb] -row 1 -column 1 -sticky news
for {set row 1} {$row < 5} {incr row} {
for {set col 1} {$col < 5} {incr col} {
# Cell 1,1 already has the combobox. Skip it.
if {($row == 1) && ($col == 1)} continue
set id r[set row]c[set col]
grid [entry .f.$id -relief groove -bd 1] -row $row -column $col -sticky news
}
}
Can someone help me on this situation? I am trying to make a GUI that is used for colors demo of all RGB matrix in the canvas. Unfortornately, the GUI is not responding and it doesn't change colors as expected until the loop is finished. Is there anything wrong? I often encounter this problem if I configure a widget in a loop.
package require Tk
package require math
proc changeColor {rM gM bM} {
for {set r 0} {$r<=$rM} {incr r} {
for {set g 0} {$g<=$gM} {incr g} {
for {set b 0} {$b<=$bM} {incr b} {
set rHex [format %02X $r]
set gHex [format %02X $g]
set bHex [format %02X $b]
set mark #
set color [append mark $rHex $gHex $bHex]
.cv config -bg $color
.lb config -text "[format %03d $r] [format %03d $g] [format %03d $b]"
after 500
}
}
}
}
canvas .cv
ttk::label .lb
ttk::button .bt -text Run -command {changeColor 255 255 255}
grid .cv -row 0 -column 0 -sticky news
grid .lb -row 1 -column 0 -sticky we
grid .bt -row 2 -column 0
Code_Snapshot
GUI_Snapshot
Tk (and Tcl) processes no events at all during a synchronous after 500. It just stops the process for that 500 ms.
You need to instead process events for that time. Replace the after 500 with:
after 500 {set go_on yes}
vwait go_on
Be aware that the go_on there is global, and that this can cause problems with code-reentrancy. You'll want to disable the button that runs the procedure while your code is running.
Or you can use Tcl 8.6 and convert everything to be a coroutine. Then you'll be able to do an asynchronous sleep without danger of filling the stack:
proc changeColor {rM gM bM} {
for {set r 0} {$r<=$rM} {incr r} {
for {set g 0} {$g<=$gM} {incr g} {
for {set b 0} {$b<=$bM} {incr b} {
set rHex [format %02X $r]
set gHex [format %02X $g]
set bHex [format %02X $b]
set mark #
set color [append mark $rHex $gHex $bHex]
.cv config -bg $color
.lb config -text "[format %03d $r] [format %03d $g] [format %03d $b]"
####### Notice these two lines... ########
after 500 [info coroutine]
yield
}
}
}
}
##### Also this one needs to be altered #####
ttk::button .bt -text Run -command {coroutine dochange changeColor 255 255 255}
# Nothing else needs to be altered
My file looks like this below. I want to extract only id, pos and type from file so that I can use it further. Should I need to treat this data as a list and use lindex syntax to retrieve.
{particles {id pos type v f}
{0 442.3601602032813 775.8494355067412 339.7428247245854 0 -1.0649468656691174 0.3118359585805807 0.7974629587243917 -7.856415738784473 120.82920096524781 80.7680149353967}
{1 75.78431491144367 187.28007812237516 279.3569202413006 0 0.3317344469183915 3.0716559473604916 -1.679965732986453 2.573367640795655 -11.46026754809828 125.75306472245369}
{2 44.167251258358164 371.8839725825084 80.32709197838003 0 -0.6260768510694417 0.9493261445672099 0.9445444874655268 -98.8132600015945 -80.10617403827258 43.578514821777155}
{3 289.0168944249348 193.4364952458922 96.30251497465443 0 -0.5327035586676473 1.028492567403681 4.364969924730662 139.09290151549465 75.46717320097427 -29.955066397359094}
{4 324.94257366360085 404.9215380451672 799.3481016151578 0 -1.2801842708841038 -4.320355658821216 2.9394195915181567 -109.72971491904342 -44.06068452005151 118.2802261191011}
{5 598.4521733790556 447.74320547029174 750.4399422142899 0 1.740414834859398 -0.5926143788565617 1.5937397085063156 -155.08309023301794 186.08101953841978 177.1804659692657}
}
This is the code I have used below. Can anyone tel me the code which I used is correct or not.
set num_part 6
set mol1 0.1666
set mol2 0.8333
set num_conf 2
for {set i 0} {$i < $num_conf} {incr i} {
set f [open "config_$i" "r"]
set part [while { [blockfile $f read auto] != "eof" } {} ]
set g [open "positions" "w"]
blockfile $g write particles {id pos type}
close $f
close $g
set g [open "positions" "r"]
set data [read $g]
close $g
set num0 0
for {set j 0} {$j < [expr { $num_part + 1 }]} {incr j} {
set type [lindex $data 0 $j 4]
if { $type == 0 } {
set tlist [expr $i]
set x0 [lindex $data 0 $j 1]
set y0 [lindex $data 0 $j 2]
set z0 [lindex $data 0 $j 3]
set total1 [ expr { sqrt(($x0 * $x0) + ($y0 * $y0)+ ($z0 * $z0)) }]+0]
incr num0
puts " $i :: $num0 "
set dum 0
for {set k 0} {$k < [expr { $num_part + 1 }]} {incr k} {
set type [lindex $data 0 $k 4]
if { $type == 1 } {
set tlist [expr $i]
set x1 [lindex $data 0 $k 1]
set y1 [lindex $data 0 $k 2]
set z1 [lindex $data 0 $k 3]
set total2 [ expr { sqrt(($x1 * $x1) + ($y1 * $y1)+ ($z1 * $z1)) }]+0]
incr dum
puts " $i :: $dum "
}
}
}
}
}
set h [open "dist12" "w"]
set dist12 [ expr {($mol1 * $total1)-($mol2 * $total2)}]
puts "Distance between cross particles $dist12"
puts $h "\# t $dist12 "
foreach t $tlist dist" $dist12 { puts $h "$t $dist_12" }
close $h
You've a few lines that look suspicious.
1:
set part [while { [blockfile $f read auto] != "eof" } {} ]
The result of while is an empty string, so the above code probably isn't doing what you hope. Not quite sure how to fix it though; blockfile isn't a standard Tcl command.
2:
for {set j 0} {$j < [expr { $num_part + 1 }]} {incr j} {
Not really a correctness issue, but that could be written as:
for {set j 0} {$j < $num_part + 1} {incr j} {
The bytecode generated will be virtually identical, but it's shorter and easier to read.
3:
set tlist [expr $i]
This looks unnecessary and suspcious. We know i is a numeric variable (in fact it contains an integer), so there's no need to pretend it is an expression. It slows things down for no benefit.
You've two occurrences of this.
4:
set total1 [ expr { sqrt(($x0 * $x0) + ($y0 * $y0)+ ($z0 * $z0)) }]+0]
This line is definitely wrong. The number of ] characters doesn't match the number of [ characters, so what you get will be “unexpected”, and that +0 is either useless or harmful. It's probably best to write a procedure to help you with this. Put the procedure at the top of the script.
proc length {x y z} {
expr { sqrt($x*$x + $y*$y + $z*$z) }
}
Then just call it later on:
set total1 [length $x0 $y0 $z0]
The same applies to the calculation of total2 later.
5:
foreach t $tlist dist" $dist12 { puts $h "$t $dist_12" }
Looks like this has a typo: dist" instead of dist. The failure to use dist inside the loop also looks odd; I think you're going wrong here, and should take another look and think about what you actually want to do.
I am using ttk:notebook for creating frames.
Attaching children's to these slaves, but it didn't resize properly, when listbox widget gets created.
I am using following code:
ttk::notebook .top.d -width 880 -height 600 -padding 5
ttk::frame .top.d.f1;
ttk::frame .top.d.f2;
.top.d add .top.d.f2 -text "Memory Characterization" -padding 5
.top.d add .top.d.f1 -text "Standard cells Characterization" -padding 5
When more widgets are added they hide, until i have to manually resize it.
As Jerry said, what did you expect, when giving a width and height?
Maybe your confusion comes from the width resizing when adding notebook tabs, but this is by intention, because otherwise you cannot see all configured tabs. Unfortunately there is no standard code for scrolling tab headers.
The following code shows the effect:
#!/usr/bin/env wish
set conf(width) 200
set conf(height) 100
ttk::button .b1 -command addNewPage -text "Add"
ttk::button .b2 -command toggleSize -text "Toggle Size"
ttk::notebook .d -width 200 -height 100 -padding 5
grid .b1 .b2
grid .d - -sticky eswn
grid columnconfigure . all -weight 1
grid rowconfigure . 2 -weight 1
set numpages 0
set pages [dict create \
.d.f1 "Memory Characterization" \
.d.f2 "Standard cells Characterization" \
.d.f3 "Just another long title" \
.d.f4 "Hope this is long enough"]
proc addNewPage {} {
variable pages
variable numpages
if {$numpages < [dict size $pages]} {
set w [lindex [dict keys $pages] ${numpages}]
ttk::frame $w
set title [dict get $pages $w]
.d add $w -text $title -padding 5
addChildren $w
incr numpages
if {$numpages >= [dict size $pages]} {
.b configure -state disabled
}
}
}
proc addChildren {w} {
for {set i 1} {$i < 9} {incr i} {
for {set j 1} {$j < 9} {incr j} {
grid [ttk::button $w.b$i$j -text "Button $i:$j"] -row $i -column $j -padx 5 -pady 5
}
}
}
proc toggleSize {} {
variable conf
if {[.d cget -width] == $conf(width)} {
set width 0
set height 0
} else {
set width $conf(width)
set height $conf(height)
}
.d configure -width $width -height $height
}
I would recommend to use relative placement of child widgets w.r.t your notebook by using
Place geometry manager