I want to create a table in Tcl/Tk gui. The minimum size of every boxes of the table will be (x=10, y=2) and if any words(inserted in any box) exceeds the box's size, then it will be adjusted towards Y-axis.
Please help me.
I have tried with this code :
proc table {w content args} {
frame $w -bg black
set r 0
foreach row $content {
set fields {}
set c 0
foreach col $row {
lappend fields [label $w.$r/$c -text $col]
incr c
}
eval grid $fields -sticky news -padx 1 -pady 1
incr r
}
set w
}
#--- Test:
table .t {
{Row Head1 Head2}
{1 abc 123123}
{2 bcd 12341234}
{3 cde 12345678901234567890}
}
pack .t
The target table will be like this :
I suggest the following. It doesn't cater for the case when a word in a sentence is longer than the maximum column size, but it works otherwise. It will word wrap if the text is a sentence (has a space in it), or character wrap otherwise.
Note that character wrap will still take effect if there is a word with length exceeding the maximum size of a column.
package require Tk
proc wrap {text len} {
if {[string len $text] <= $len} {
# No need for wrapping
} else {
if {[string first " " $text] > -1} {
# Word wrap
set flag "\\M"
} else {
# Character wrap
set flag ""
}
set re "^.{1,$len}$flag"
set result [list]
while {1} {
regexp $re [string trim $text] res
regsub $re [string trim $text] "" text
lappend result $res
if {$text eq ""} {break}
}
set text [join $result \n]
}
return $text
}
proc table {w content args} {
frame $w -bg black
set r 0
foreach row $content {
set fields {}
set c 0
foreach col $row {
set text [wrap $col 10]
lappend fields [label $w.$r/$c -text $text]
incr c
}
eval grid $fields -sticky news -padx 1 -pady 1
incr r
}
set w
}
table .t {
{Row Head1 Head2}
{1 abc 123123}
{2 bcd 12341234}
{3 cde 12345678901234567890}
{4 fgh 123456789098765432101234567890}
{5 ijk "Data Bits per Mask"}
}
pack .t
Output:
Related
When I tried to compile a program taken from internet
proc demo {} {
canvas .c -bg white
frame .f
button .f.c -text Clear -command {.c delete all}
button .f.co -text Complete -command {showComplete .c}
button .f.tr -text Triangulate -command {showTriangulate .c}
eval pack [winfo children .f] -side left
pack .c .f -fill x -expand 1
bind .c <1> {addVertex %W %x %y}
.c bind vertex <3> {%W delete current}
}
proc showComplete w {
$w delete edge
foreach edge [completeGraph [get vertex $w]] {
showEdge $w $edge
}
}
proc showEdge {w edge {fill gray}} {
regexp {(.+)/(.+),(.+)/(.+)} $edge -> x0 y0 x1 y1
set ::length($edge) [expr {hypot($x1-$x0,$y1-$y0)}]
$w create line $x0 $y0 $x1 $y1 -tags "edge $edge" -fill $fill
}
proc get {tag w} {
set res {}
foreach v [$w find withtag $tag] {
lappend res [lindex [$w gettags $v] 1]
}
set res
}
proc completeGraph vertices {
set graph {}
foreach i $vertices {
foreach j $vertices {
if {$i<$j} {lappend graph $i,$j}
}
}
set graph
}
proc showTriangulate w {
$w delete edge
showComplete $w
wm title . Wait...
set t0 [clock clicks -milliseconds]
foreach edge [triangulate [get edge $w]] {
showEdge $w $edge red
}
wm title . [expr {[clock clicks -milliseconds] - $t0}]
}
proc triangulate graph {
while 1 {
set found 0
foreach i $graph {
foreach j $graph {
if {$i!=$j && [crossing $i $j]} {
lremove graph [longer $i $j]
set found 1
break
}
}
if $found break
}
if {!$found} break
}
set graph
}
proc crossing {edge1 edge2} {
regexp {(.+)/(.+),(.+)/(.+)} $edge1 -> x0 y0 x1 y1
regexp {(.+)/(.+),(.+)/(.+)} $edge2 -> x2 y2 x3 y3
if [adjacent $x0/$y0 $x1/$y1 $x2/$y2 $x3/$y3] {return 0}
set m1 [slope $x0 $y0 $x1 $y1]
set b1 [expr {$y0-$m1*$x0}]
set m2 [slope $x2 $y2 $x3 $y3]
set b2 [expr {$y2-$m2*$x2}]
set x [slope $m2 $b1 $m1 $b2]
expr {[between $x0 $x $x1] && [between $x2 $x $x3]}
}
proc adjacent args {
expr {[llength [lsort -unique $args]]<[llength $args]}
}
proc slope {x0 y0 x1 y1} {
# slightly "bend" a vertical line, to avoid division by zero
if {$x1==$x0} {set x1 [expr {$x1+0.00000001}]}
expr {double($y1-$y0)/($x1-$x0)}
}
proc between {a b c} {
expr {$b==[lindex [lsort -real [list $a $b $c]] 1]}
}
proc longer {edge1 edge2} {
global length
expr {$length($edge1) > $length($edge2)? $edge1: $edge2}
}
proc addVertex {w x y} {
$w create rect [expr $x-2] [expr $y-2] [expr $x+2] [expr $y+2] \
-tags "vertex $x/$y" -fill blue
}
proc lremove {varName element} {
upvar 1 $varName var
set pos [lsearch $var $element]
set var [lreplace $var $pos $pos]
}
demo
bind . <Escape> {exec wish $argv0 &; exit}
bind . <F1> {console show}
invalid command name "canvas"
while executing
"canvas .c -bg white"
(procedure "demo" line 2)
invoked from within
"demo"
In this canvas command is not reading. I am having nam(network animator) and I could compile other programs. I want to know how this can be run. anybody please help.
while compiling in ns2 I get like
wish → → ns-allinone-2.35/bin/wish8.5 ......... Example using : ./wish8.5 canvas.tcl .......... 'ns' is the otcl interpreter. 'ns' is not meant to understand all tcl commands.
I have an input file with fields in several lines. In this file, the field pattern is repeated according to query size.
ZZZZ
21293
YYYYY XXX WWWW VV
13242 MUTUAL BOTH NO
UUUUU TTTTTTTT SSSSSSSS RRRRR QQQQQQQQ PPPPPPPP
3 0 3 0
NNNNNN MMMMMMMMM LLLLLLLLL KKKKKKKK JJJJJJJJ
2 0 5 3
IIIIII HHHHHH GGGGGGG FFFFFFF EEEEEEEEEEE DDDDDDDDDDD
5 3 0 3
My desired output is one line per total group of fields. Empty
fields should be marked. Example:"x"
21293 13242 MUTUAL BOTH NO 3 0 X 3 0 X 2 0 X 5 3 5 3 0 X 3 X
12345 67890 MUTUAL BOTH NO 3 0 X 3 0 X 2 0 X 5 3 5 3 0 X 3 X
I have been thinking about how can I get the desired output with awk/unix scripts but can't figure it out. Any ideas? Thank you very much!!!
This isn't really a great fit for awk's style of programming, which is based on fields that are delimited by a pattern, not fields with variable positions on the line. But it can be done.
When you process the first line in each pair, scan through it finding the positions of the beginning of each field name.
awk 'NR%3 == 1 {
delete fieldpos;
delete fieldlen;
lastspace = 1;
fieldindex = 0;
for (i = 1; i <= length(); i++) {
if (substr($0, i, 1) != " ") {
if (lastspace) {
fieldpos[fieldindex] = i;
if (fieldindex > 0) {
fieldlen[fieldindex-1] = i - fieldpos[fieldindex-1];
}
fieldindex++;
}
lastspace = 0;
} else {
lastspace = 1;
}
}
}
NR%3 == 2 {
for (i = 0; i < fieldindex; i++) {
if (i in fieldlen) {
f = substr($0, fieldpos[i], fieldlen[i]);
} else { # last field, go to end of line
f = substr($0, fieldpos[i]);
}
gsub(/^ +| +$/, "", f); # trim surrounding spaces
if (f == "") { f = "X" }
printf("%s ", f);
}
}
NR%15 == 14 { print "" } # print newline after 5 data blocks
'
Assuming your fields are separated by blank chars and not tabs, GNU awk's FIELDWITDHS is designed to handle this sort of situation:
/^ZZZZ/ { if (rec!="") print rec; rec="" }
/^[[:upper:]]/ {
FIELDWIDTHS = ""
while ( match($0,/\S+\s*/) ) {
FIELDWIDTHS = (FIELDWIDTHS ? FIELDWIDTHS " " : "") RLENGTH
$0 = substr($0,RLENGTH+1)
}
next
}
NF {
for (i=1;i<=NF;i++) {
gsub(/^\s+|\s+$/,"",$i)
$i = ($i=="" ? "X" : $i)
}
rec = (rec=="" ? "" : rec " ") $0
}
END { print rec }
$ awk -f tst.awk file
2129 13242 MUTUAL BOTH NO 3 0 X 3 0 X 2 0 X 5 3 5 3 0 X 3 X
In other awks you'd use match()/substr(). Note that the above isn't perfect in that it truncates a char off 21293 - that's because I'm not convinced your input file is accurate and if it is you haven't told us why that number is longer than the string on the preceding line or how to deal with that.
I want to create a list of unique values. The values are taken from different sources and. There are 2 ways to populate my final list.
Put all the values in and then perform lrmdups:
set finalList [list ]
foreach selcetion $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
lappend finalList $i
}
}
set finalList [lrmdups $finalList]
or check if a value exists in the list, and only if it doesn't add it:
set finalList [list ]
foreach selcetion $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
if {[lsearch $finalList $i] == -1} {
lappend finalList $i
}
}
}
Which of the two methods is faster?
Use the time command to test the performance of Tcl code. Ensure you place your code in a procedure to gain the benefit of having it byte-compiled then use the time command to run the test a number of times and get an average time per iteration. For instance, here is an example that shows why expr expressions should always be braced.
% proc a {} {expr 1 + 2 + 3}
% proc b {} {expr {1 + 2 + 3}}
% time a 1000
4.491 microseconds per iteration
% time b 1000
0.563 microseconds per iteration
To deal with the specific task - I would add each new value into an array and let that eat the duplicates and then just turn it into a list at the end.
proc getUniques {wantedSize} {
array set uniques {}
while {[array size uniques] != $wantedSize} {
set uniques([getNewValue]) {}
}
return [array names uniques]
}
I also use the time command to benchmark. Here is my code, which I added two more methods, one to use array and the other uses struct::set to eliminate duplicates.
#!/usr/bin/env tclsh
#http://stackoverflow.com/questions/18337534/what-way-is-faster-to-populate-a-list-with-unique-values-in-tcl
package require Tclx
package require struct::set
proc removeDupMethod {selectionList} {
set finalList [list ]
foreach selection $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
lappend finalList $i
}
}
set finalList [lrmdups $finalList]
return $finalList
}
proc searchBeforInsertMethod {selectionList} {
set finalList [list ]
foreach selection $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
if {[lsearch $finalList $i] == -1} {
lappend finalList $i
}
}
}
}
proc useArrayMethod {selectionList} {
array set tally {}
foreach selection $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
incr tally($i)
}
}
set finalList [array names tally]
return $finalList
}
proc useStructSetMethod {selectionList} {
set finalList {}
foreach selection $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
struct::set include finalList $i
}
}
return $finalList
}
# Performs the benchmark on a method
proc bench {methodName} {
set selectionList {1:10 5:20 10:30 4:30}
set timeInfo [time {$methodName $selectionList} 1000]
puts "$methodName - $timeInfo"
}
# main
bench removeDupMethod
bench searchBeforInsertMethod
bench useArrayMethod
bench useStructSetMethod
The result:
removeDupMethod - 281.961364 microseconds per iteration
searchBeforInsertMethod - 93.984991 microseconds per iteration
useArrayMethod - 122.354889 microseconds per iteration
useStructSetMethod - 576.293311 microseconds per iteration
Discussion
Your second method, searchBeforInsertMethod, is the fastest.
useArrayMethod, which uses an array to ensure uniqueness, comes in second. This is to say that the TCL's built-in list commands are very optimized.
To my surprise, the useStructSetMethod is the slowest. I thought a library command should be optimized, but I was wrong.
Update
I took Siyb's hint and replace:
regexp {(\d+):(\d+)} $selection -> start end
with:
set range [split $selection :]
set start [lindex $selection 0]
set end [lindex $selection 1]
And see a dramatic increase in speed:
removeDupMethod - 9.337442 microseconds per iteration
searchBeforInsertMethod - 5.528975999999999 microseconds per iteration
useArrayMethod - 6.8120519999999996 microseconds per iteration
useStructSetMethod - 5.774831 microseconds per iteration
useNative - 6.105141 microseconds per iteration
Notes
The fastest is still searchBeforInsertMethod, the speed increase is nearly 17 times.
useStructSetMethod now rises to take second place
Update 2
Per potrzebie's request, I added 5000:6000 to the beginning and the numbers do not change much:
removeDupMethod - 10.826106 microseconds per iteration
searchBeforInsertMethod - 6.296769 microseconds per iteration
useArrayMethod - 7.752042 microseconds per iteration
useStructSetMethod - 6.910305999999999 microseconds per iteration
useNative - 7.274724 microseconds per iteration
I have tried using lsort -unique $list instead of lrmdups. On my box, this is the fastest method:
proc useNative {selectionList} {
foreach selection $selectionList {
regexp {(\d+):(\d+)} $selection -> start end
for {set i $start} {$i <= $end} {incr i} {
lappend finalList $i
}
}
set finalList [lsort -unique $finalList]
return $finalList
}
removeDupMethod - 171.573 microseconds per iteration
searchBeforInsertMethod - 58.264 microseconds per iteration
useArrayMethod - 96.109 microseconds per iteration
useStructSetMethod - 386.889 microseconds per iteration
useNative - 41.556 microseconds per iteration
EDIT: using split instead of the regular expression speeds up things as well (if the regex is actually part of your problem):
useNative - 20.938 microseconds per iteration
EDIT2: try adding -integer as a lsort parameter, should speed up things a little as well, if your are planning on sorting integers that is
I had a input which is a result from text comparison. It is in a very simple format. It has 3 columns, position, original texts and new texts.
But some of the records looks like this
4 ATCG ATCGC
10 1234 123
How to write the short script to normalize it to
7 G GC
12 34 3
probably, the whole original texts and the whole new text is like below respectively
ACCATCGGA1234
ACCATCGCGA123
"Normalize" means "trying to move the position in the first column to the position that changes gonna occur", or "we would remove the common prefix ATG, add its length 3 to the first field; similarly on line 2 the prefix we remove is length 2"
This script
awk '
BEGIN {OFS = "\t"}
function common_prefix_length(str1, str2, max_len, idx) {
idx = 1
if (length(str1) < length(str2))
max_len = length(str1)
else
max_len = length(str2)
while (substr(str1, idx, 1) == substr(str2, idx, 1) && idx < max_len)
idx++
return idx - 1
}
{
len = common_prefix_length($2, $3)
print $1 + len, substr($2, len + 1), substr($3, len + 1)
}
' << END
4 ATCG ATCGC
10 1234 123
END
outputs
7 G GC
12 34 3
What should do this code sample?
set l { A B C D }
lreplace $l 1 2 \[ \]
It returns {A {[} \] D}, however I want to have {A [ ] D}.
What am I doing wrong?
Your code does exactly what you want it to, you're just reading the string rep of your output and misunderstanding it:
% set l { A B C D }
A B C D
% foreach elem $l { puts $elem }
A
B
C
D
% set j [lreplace $l 1 2 \[ \]]
A {[} \] D
% foreach elem $j { puts $elem }
A
[
]
D
% join $j
A [ ] D
When you read the string rep, you're seeing it escape the [ and ]. As you can see from the foreach output, the actual values are what you're asking for. You can use join to get the string you're interested in if what you want is just a string with the characters in question.