"Invalid command name "W"' while runing tcl script - tcl

I was making some GUI at TCL in ModelSim, but when i run that it causes some STRANGE error
# invalid command name "W"
# while executing
# "$w nearest $y"
# (procedure "ListSelectEnd" line 2)
It was kinda odd, because almost similar expression was just before that one. That's the code:
global a
proc ScrolledListbox { parent args } {
frame $parent
eval {listbox $parent.list \
-yscrollcommand [list $parent.sy set] \
-xscrollcommand [list $parent.sx set]} $args
scrollbar $parent.sx -orient horizontal \
-command [list $parent.list xview]
scrollbar $parent.sy -orient vertical \
-command [list $parent.list yview]
pack $parent.sx -side bottom -fill x
pack $parent.sy -side right -fill y
pack $parent.list -side left -fill both -expand true
return $parent.list
}
#-------------------------------------------
proc ListSelect { parent choices } {
global a
frame $parent
ScrolledListbox $parent.choices -width 20 -height 5 \
-setgrid true
ScrolledListbox $parent.picked -width 20 -height 5 \
-setgrid true
pack $parent.choices $parent.picked -side left \
-expand true -fill both
bind $parent.choices.list <ButtonPress-1> \
{ListSelectStart %W %y}
bind $parent.choices.list <ButtonRelease-1> \
lappend a [ListSelectEnd %W %y .top.f.picked.list]
eval {$parent.choices.list insert 0} $choices
}
#----------------------------------------
proc ListSelectStart { w y } {
$w select anchor [$w nearest $y]
}
#-----------------------------------------
proc ListSelectEnd { w y list } {
$w select set anchor [$w nearest $y]
foreach i [$w curselection] {
$list insert end [$w get $i]
lappend listin [$w get $i]
}
return $listin
}
#--------------------------------------------
proc tosignal {parent val} {
global a
for {set i 0} {$i<[llength $a]} {incr i} {
force -freeze sim:/chema_tb/m1/[lindex $a $i] $val 0
}
run 1000 ns
destroy $parent
return 1
}
#------------------------------------------------
proc form {} {
global a
toplevel .top
set filename signalfile.txt
set in [open $filename]
while {[gets $in var]>-1} {
lappend spisn [lindex $var 0]
}
ListSelect .top.f $spisn
button .top.okb -text OK -width 20 -height 2 -font {-size 15 -family Times -weight bold} \
-fg blue -anchor center -command {tosignal .top 0 }
pack .top.f .top.okb -expand true
}
I'll be really thankful if you could help me. :)

The problem is with these lines:
bind $parent.choices.list <ButtonRelease-1> \
lappend a [ListSelectEnd %W %y .top.f.picked.list]
That performs immediate execution of what appears to be a callback, and passes very unlikely arguments to bind too. I bet what you want is either this:
bind $parent.choices.list <ButtonRelease-1> \
{ListSelectEnd %W %y .top.f.picked.list}
or this:
bind $parent.choices.list <ButtonRelease-1> {
ListSelectEnd %W %y .top.f.picked.list
}
or this:
bind $parent.choices.list <ButtonRelease-1> \
[list ListSelectEnd %W %y .top.f.picked.list]
In this case, it doesn't matter which you use. (The first two are equivalent except for whitespace. The third one is much more useful when you're binding variables in the script, but you're not doing that in this case.)

Related

Is there a possibility to get the content of the popdown list of a ttk::combobox before it is opened the first time?

I create a mask which shows only a combobox. I want to color certain elements in red.
For several reasons I have to hold a given structure, so that I need three files for it. The first creates the toplevel, in the second the mask is created and there are also the methods to fill the combobox. The third file
includes all methods to create and to handle the combobox.
So this is the first file
(vmHelmert2.tcl):
#!/bin/sh
#\
exec vmwish "$0" ${1+"$#"}
package require itcl
auto_mkindex . vmCombobox2.itcl vmMaskHelmert2.itcl
lappend auto_path /myPath
namespace eval vmHelmert2 {
variable helmert
}
proc vmHelmert2::grundmaske {} {
set top [toplevel .top -class Helmert]
set frMain [frame $top.centrum]
pack $frMain -expand 1 -fill both
set helmertWidget [vmMaskHelmert2 #auto $frMain]
set helmert(widget) [$helmertWidget getThis]
}
vmHelmert2::grundmaske
This is the second file
(vmMaskHelmert2.itcl)
package require Itcl
::itcl::class vmMaskHelmert2 {
public method getThis {}
private method createMaskHelmert {w}
private method setAnsatzList {liste}
private method faerbeAnsatzListe {}
private variable pfd
private variable data
constructor {w} {
createMaskHelmert $w
return $this
}
destructor {
#puts "DESTRUCTOR wird jetzt gestartet."
}
}
::itcl::body vmMaskHelmert2::getThis {} {
return $this
}
::itcl::body vmMaskHelmert2::createMaskHelmert {w} {
set pfd(frMain) [frame $w.frMain]
pack $pfd(frMain) -anchor nw -expand 1 -fill both
set pfd(c_ansatznr) [vmCombobox2 $pfd(frMain).c_ansatznr \
-state normal \
-width 15\
-justify right]
pack $pfd(c_ansatznr) -side left
[$pfd(c_ansatznr) component combobox] configure -postcommand "[itcl::code $this faerbeAnsatzListe]"
set data(ansatzList) [list 1 0 2 1 3 1]
setAnsatzList $data(ansatzList)
}
::itcl::body vmMaskHelmert2::setAnsatzList {liste} {
# Alle Inhalte vorher loeschen
$pfd(c_ansatznr) delete entry 0 end
$pfd(c_ansatznr) delete list 0 end
foreach {einElement status} $liste {
$pfd(c_ansatznr) insert list end $einElement
}
return
}
::itcl::body vmMaskHelmert2::faerbeAnsatzListe {} {
foreach {elem state} $data(ansatzList) {
if { $state } {
# puts "TODO: Farbe Ansatz $elem verändern!!!"
$pfd(c_ansatznr) itemconfigure $elem red
}
}
}
And this is the last file for the combobox
(vmCombobox2.itcl):
package require Itcl
package require Iwidgets
itcl::class vmCombobox2 {
inherit itk::Widget
constructor {args} {}
destructor {}
public method insert {component index args}
public method delete {component first {last {}}}
public method itemconfigure {bez farbe}
private variable popdown
private method create {top}
protected method _deleteList {first {last {}}}
}
itcl::body vmCombobox2::constructor {args} {
ttk::style configure Combobox$this.TCombobox\
-selectbackground #52719c\
-borderwidth 1\
-insertwidth 2\
-selectforeground white\
-fieldbackground white
ttk::style map Combobox$this.TCombobox -background [list disabled #a3a3a3 readonly #a3a3a3]
ttk::style map Combobox$this.TCombobox -foreground [list disabled #d9d9d9 readonly #d9d9d9]
ttk::style map Combobox$this.TCombobox -arrowcolor [list disabled darkgrey readonly black]
create $itk_interior
itk_initialize {*}$args
# wenn -values vor -textvariable steht wird die Variable nicht initialisiert deshalb:
set idx [lsearch $args "-textvariable"]
if {$idx != -1} {
setVar [lindex [$itk_component(combobox) cget -values] end]
}
}
itcl::body vmCombobox2::create {top} {
# puts "createCombobox"
# Label
itk_component add label {
set label [label $top.label -anchor w]
set label
} {
rename -font -labelfont labelFont Font
}
# Frame fuer highlightthickness
itk_component add frame {
set frame [frame $top.frame -highlightcolor black]
set frame
} {
}
# combobox
itk_component add combobox {
set combobox [ttk::combobox $top.frame.combo -style Combobox$this.TCombobox]
set combobox
} {
keep -textvariable -values -cursor -exportselection -justify -height -state -width -takefocus -postcommand\
-invalidcommand -foreground
rename -validate -validateart validateArt ValidateArt
}
grid $itk_component(label) -row 0 -column 0 -sticky ne
grid $itk_component(frame) -row 0 -column 1 -sticky ew
pack $itk_component(combobox) -fill x -expand 1
grid columnconfigure $top 1 -weight 1
grid rowconfigure $top 0 -weight 1
# aufgeklappte Liste
set pd [ttk::combobox::PopdownWindow $itk_component(combobox)]
set popdown $pd.f.l
}
itcl::body vmCombobox2::_deleteList {first {last {}}} {
if {$last == {}} {
set last $first
}
set valueList [$itk_component(combobox) cget -values]
set newValuesList [lreplace $valueList $first $last]
# remove the item if it is no longer in the list
set text [$itk_component(combobox) get]
if {$text != ""} {
set index [lsearch -exact $newValuesList $text]
if {$index == -1} {
$itk_component(combobox) set ""
}
}
$itk_component(combobox) configure -values $newValuesList
return
}
itcl::body vmCombobox2::delete {component first {last {}}} {
switch -- $component {
entry {
if {$last == {}} {
#set last [expr {$first + 1}]
set last $first
}
set text [$itk_component(combobox) get]
set newText [string replace $text $first $last]
$itk_component(combobox) set $newText
}
list {
_deleteList $first $last
}
default {
error "falsches Combobox component \"$component\":\
zugelassen sind: entry or list."
}
}
}
itcl::body vmCombobox2::insert {component index args} {
set nargs [llength $args]
if {$nargs == 0} {
error "Kein Einfuegestring fuer parameter \"string\" in function\"vmCombobox2::insert\""
}
switch -- $component {
list {
if {$itk_option(-state) == "normal"} {
set aktuell [$itk_component(combobox) cget -values]
if {[lsearch -exact $aktuell $args] != -1} {
return
}
set neu [linsert $aktuell $index [join $args]]
$itk_component(combobox) configure -values $neu
}
}
default {error "Falsches vmCombobox2 component \"$component\": zugelassen be entry oder list."}
}
}
itcl::body vmCombobox2::itemconfigure {bez farbe} {
puts "content popdownListe >>[$popdown get 0 end]<<"
# index des Elements in popDownListe
set index [lsearch [$popdown get 0 end] $bez]
try {
$popdown itemconfigure $index -foreground red
} on error {err errOpts} {
puts "Error >>$err<<"
}
}
In the method vmCombobox2::itemconfigure I put the content of the popDownList. If the popDownList is opened for the first time, the content is empty and none of the elements are colored red (
content popdownListe
. I got the error
item number "-1" out of range
(for sure, the popDownList is empty). If I open it for the second time, the elements 2 and 3 are colored red as expected.
Is there a way to fill content to the popdown list before it is opened the first time?
One possible solution would be to configure the items after the list is displayed.
You need for this:
new Version of vmCombobox (check out svn)
Example:
!/bin/sh
\
exec vmwish "$0" ${1+"$#"}
package require Itcl
package require vmWidgets
package require vmTclTools
toplevel .t
frame .t.fr
pack .t.fr
global wms
variable var
set cb1 [vmCombobox .t.fr.li\
-textvariable ::wms(capabilitiesAddr)\
-selectioncommand getV\
-textfont {helvetica 10 bold}\
-labelfont {helvetica 10 bold}\
-values [list 1 2 3 33i 1000 7]\
-textvariable ::wms(var)\
-height 20\
-validate all\
-validate {valMass %P}\
-labeltext testcombobox\
]
pack $cb1
$cb1 insert list end jojo
set pd [$cb1 getPopdown]
$cb1 configure -postcallback [list configureLB $pd]
proc configureLB {pd} {
foreach i [$pd get 0 end] {
# hier Items konfigurieren
puts $i
}
$pd itemconfigure end -foreground red
}
To illustrate, what was meant with the answer here the changes I made in vmCombobox2.itcl and vmMaskHelmert2.itcl
vmMaskHelmert2.itcl
I added a new option -postcallback to the combobox and configured it in faerbeAnsatzListe.
:itcl::body vmMaskHelmert2::faerbeAnsatzListe {} {
set listIndex [list ]
foreach {elem state} $data(ansatzList) {
if { $state } {
# puts "TODO: Farbe Ansatz $elem verändern!!!"
set values [$pfd(c_ansatznr) getValues]
set index [lsearch $values $elem]
lappend listIndex $index
}
}
set pd [$pfd(c_ansatznr) getPopdown]
$pfd(c_ansatznr) configure -postcallback [list configureLB $pd $listIndex]
}
getValues is a method, which lists all items of the list in the combobox.
vmCombobox2.itcl
I added the option postcallback in vmCombobox2.itcl
itcl::class vmCombobox {
...
itk_option define -postcallback postCallback PostCallback ""
...
}
In constructor I added the following lines:
itcl::body vmCombobox::constructor {args} {
...
if {$idx != -1} {
setVar [lindex [$itk_component(combobox) cget -values] end]
}
...
set pd [ttk::combobox::PopdownWindow $itk_component(combobox)]
set oldTags [bindtags $pd]
set tagList [concat $oldTags "callBack$this"]
bind callBack$this <Map> [itcl::code $this popUpCallback]
bindtags $pd $tagList
bind $pd <Unmap> [::itcl::code $this clearAfterSelect]
And I added three more methods (I also declared them in class as public method)
itcl::body vmCombobox::getPopdown {} {
return $popdown
}
itcl::body vmCombobox::popUpCallback {} {
if {$itk_option(-postcallback) != ""} {
eval $itk_option(-postcallback)
}
}
::itcl::body vmCombobox::configureLB {pd listIndex} {
foreach index $listIndex {
$pd itemconfigure $index -foreground red
}
}
For me it worked, with the changes I can colour certain items.

TCL/TK How to generate comboboxs/buttons in the for loop and call the function?

I want to generate several comboboxs and buttons in the for loop, and the button command will call the function and check the combobox content, how to get the variable "com$num" and pass to the "get_optimizer" function? How to correct below script? Pls help, thanks!
set num 1
foreach SQ {1 2 3 4 5} {
ttk::combobox $twind.frame.dpcom$num -textvariable com$num -values {Global Definitive Adaptive Cmaes}
button $twind.frame.but$num -text "Optimizer Setting" -command [list get_optimizer]
grid $twind.frame.dpcom$num -row $num -column 0
grid $twind.frame.but$num -row $num -column 1
incr num}
proc get_optimizer {} {
global [set com$num]
if {[set com$num]=='Global'} {
...
} elseif {[set com$num]=='Definitive'} {
...
} elseif {...} {
...}
...
}
You should pass the whole name of the variable into get_optimizer, and use upvar #0 to give that a fixed local alias name inside the procedure.
# backslash-newline for readability only
button $twind.frame.but$num -text "Optimizer Setting" \
-command [list get_optimizer com$num]
proc get_optimizer {varname} {
upvar #0 $varname theVar
if {$theVar=='Global'} {
...
} elseif {$theVar=='Definitive'} {
...
} elseif {...} {
...
}
...
}
Also, it's more efficient to use the eq operator for string equality. And consider whether it would be better to use an array (i.e., com(1) instead of com1).
Use
global com$num
(giving you, say, global com1)
instead of
global [set com$num]
(giving you, say, global Definitive)

How to attach a proc as variable name to a button created by a loop

Help me through one of my problem where i seized my thinking to proceed further.
I am creating a radiobutton within foreach loop.Also wanted to have a -command option different for each these buttons, since i am not sure how many buttons will be created(it always dynamic) i put it in a foreach loop.
Further, I am attaching a proc as a variable so whenever any of these radio button is clicked the function body of proc will execute, but this code doesn't functioning properly and throwing error when radiobutton is selected.
I think variables $elem and $w are not getting passed to proc
proc check_lib_name {} {
global ENTRYfilename5 ENTRYfilename f mylist elem w
cd $ENTRYfilename
set dirnames [glob -type d *]
set b 7
set mylist {}
foreach f $dirnames {
set r 2
lappend mylist $f
}
set w 0
foreach elem $mylist {
radiobutton .top.d.$elem -text $elem -command $elem.$w -value $elem.abc
grid .top.d.$elem -row $b -column $r -sticky nsew
incr b
incr w
}
#### Proc attached with radiobuttons
proc $elem.$w {} {
global ENTRYfilename5 ENTRYfilename elem w
cd $elem
set rrpath [glob -type d *]
puts $rrpath
set del "/"
set klpath [concat [string trim $ENTRYfilename][string trim $del][string trim $elem][string trim $del][string trim $rrpath]]
puts $klpath
cd $klpath
exec [myRun.sh]
}
}
After the foreach loop ran, the value of $elem and $w will be the last value they had, which also means you have only 1 proc created and this is for the last radio button.
I don't think it is a good idea to make 1 proc for each ration button. Use 1 generic proc that takes arguments.
...
radiobutton .top.d.$elem -text $elem -command [list radiofunction $elem $w] -value $elem.abc
...
then
proc radiofunction {elem w} {...}
radiofunction is only an example name.

How to perform an arithmetic action on all the elements of a list in Tcl

I have a very long list of values and I want to divide them all by the same number, is there a way to do this in one command instead of doing this by member:
set new_list [list ]
foreach member $list {
lappend new_list [expr $member / 1E9]
}
Nope. And always brace your expressions.
proc map {lst expr} {
foreach item $lst { lappend r [expr [string map {%x $item} $expr]] }
return $r
}
map [list 2.3 4.5 6.7] {%x * %x + 0.5}
map [list ...] {%x / 1e9}
might be a solution if you want - but there is nothing builtin for that.
Tcllib's ::struct::list package has a mapping function:
package require struct::list
set lst {1 2 3 4 5}
set new [::struct::list mapfor x $lst {expr {$x * $x}}]
# => 1 4 9 16 25

Tcl/Tk - how to get the prompt after executing an command using eval?

The code to reproduce my problem is given below. I named the file as test.tcl
#-------------------------------------------------------------------
# test.tcl
#-------------------------------------------------------------------
namespace eval Gui {
}
proc Gui::test {} {
toplevel .test
wm title .test "Test"
wm resizable .test 0 0 ;# not resizable
# create a frame to hold the check widgets
set f [frame .test.boolean -borderwidth 10]
pack $f -side top
# OK and Cancel buttons
button .test.ok -text "OK" -command [list Gui::Ok .test ]
button .test.cancel -text "Cancel" -command [list Gui::cancel .test ]
pack .test.cancel .test.ok -side right
bind .test <Return> {Gui::Ok .test ; break}
bind .test <Escape> {Gui::cancel .test ; break}
}
proc Gui::Ok { arg } {
set x [list puts "hello world!"]
eval $x
destroy $arg
}
proc Gui::cancel { arg } {
destroy $arg
}
#-------------------------------------------------------------------
# Gui main window
#-------------------------------------------------------------------
proc Gui::initialize { } {
# build the frame which contains menu options
frame .mbar -relief raised -bd 2
frame .mdummy -width 200 -height 240
pack .mbar .mdummy -side top -fill x
# menu options
menubutton .mbar.command -text Command -underline 0 -menu .mbar.command.menu
pack .mbar.command -side left
# menu under command options
menu .mbar.command.menu -tearoff 0
.mbar.command.menu add command -label "Test..." -command [list Gui::test]
}
#-------------------------------------------------------------------
# main code
#-------------------------------------------------------------------
Gui::initialize
When I type
% wish
% source test.tcl
%
and then I click Command -> Test ... -> OK which gives me
% hello world!
I don't get the prompt % back after it prints hello world!. Though I can still execute tcl commands in that space. For example:
% hello world!
puts "hi"
hi
%
which returns the prompt.
My question:
How to get the prompt % back after tcl/tk executes the eval command which prints hello world!
The prompt % came from tcl interpreter and shown in the terminal just because it's in interactive mode. If you run your script as wish test.tcl you will never get %.
You can implement your own interactive mode and call it after all initialization steps of your app. Here the example how it can be done:
proc Gui::interactive {} {
set prompt1 "tcl>"
set prompt2 "?"
set cmd {}
set prompt "$prompt1 "
fconfigure stdin -blocking false -buffering line
fileevent stdin readable {set Gui::stdinReady 1}
while true {
puts -nonewline $prompt
flush stdout
vwait Gui::stdinReady
set str [gets stdin]
lappend cmd $str
set cmdStr [join $cmd "\n"]
if {[info complete $cmdStr]} {
set cmd {}
if {$cmdStr != ""} {
if {[catch {eval $cmdStr} result]} {
puts stderr "ERROR: $result"
} elseif {$result != ""} {
puts $result
}
}
set prompt "$prompt1 "
} else {
set prompt "$prompt2 "
}
if {[eof stdin]} {
puts ""
break
}
}
}
Just call this function after Gui::test execution and you;ll get your own prompt.
But even with this example the prompt will not be redrawn if a text will be printed to the terminal from some other procedure.
You never lost the % prompt. Here's what's happening:
You have a prompt:
%
Then you print a string on that same line:
% hello world!
Your "current" prompt is still that same thing. The following command is "on" that prompt:
puts "hi"
Which, because it is running in tclsh and because you just inserted a newline, comes on the next line:
hi
And you get another prompt:
%
You didn't get "another" prompt from your GUI thing because puts "hello world" wasn't processed by tclsh directly. Basically, as far as tclsh is concerned, the "hello world" came from Mars and screwed up your terminal. It doesn't even know that is there.
Maybe a better way to explain it is this: If your puts "hello world" was printing to a file, then you would still have your % prompt. But someone took those characters and shoved them onto your display (including the newline).