TCL/TK bad window or path name when adding a button - tcl

I keep getting "bad window or path name" when trying to add the first button. And i do not have a clou why... I'm pretty shure to have used the same syntax elsewhere without problems...
tk::toplevel ".topbox"
wm title .topbox "SnapShooter"
wm geometry .topbox 200x200+0+0
wm resizable .topbox 0 0
button .topbox.btn_1 -text "Destroy" -width 150 -background green -command [destroy .topbox ]
pack .topbox.btn_1
button .topbox.btn_2 -text "Shoot Image" -width 150 -background green -command [ puts something]
pack .topbox.btn_2

Whenever you give a command, such as button ..., the Tcl interpreter attempts to substitute things like variables and commands as far as possible. This means that if (like in this case) you create a button and set its command with this invocation
button .top.btn -command [destroy .top]
Tcl executes the command destroy .top as part of executing your button invocation (and inserts the result value of the execution as a command string passed to the command option). You don't want that to happen (usually, and certainly not in this case, as it saws off the branch your GUI is sitting on), so you need to prevent Tcl from substituting this particular part of the invocation.
If you don't need to substitute anything in the command string that you pass to the command option, you can write it like this
button .top.btn -command {destroy .top}
or (a little more fragile)
button .top.btn -command \[destroy .top]
but in some cases you need to substitute some part of the command string without executing the command. This won't work:
set w .top
button $w.btn -command {destroy $w}
since that would have the button try to execute the unsubstituted command string destroy $w.
In such cases, it's usually sufficient to wrap the command string in list:
set w .top
button $w.btn -command [list destroy $w]
So, instead of
button .topbox.btn_1 \
-text "Destroy" \
-width 150 \
-background green \
-command [destroy .topbox]
you should write
button .topbox.btn_1 \
-text "Destroy" \
-width 150 \
-background green \
-command [list destroy .topbox]
or possibly give the command option as
-command {destroy .topbox}
since you don't need to substitute anything.

Related

set tcl a variable from gui

I want to get file name from gui. I wrote a code like below.
I am able to run all the scripts for once time. what i am looking for when i select the file form hwtk::openfileentry, i want to set a variable for the filename.
After i selected the file, if i run "set filename [$win.op1 get]" i am able to get the name. However i need automate this, i want to trigger the varible after selecting file from gui.
Best Regards
set win .window
catch {destroy $win}
toplevel $win -class TopClass
wm title $win "Tools"
wm geometry $win 420x200+100+150
wm resizable $win 1 1
wm deiconify $win
label $win.01 -text "openmodel"
place $win.01 -x 0 -y 10 -width 130 -height 32
hwtk::openfileentry $win.op1
place $win.op1 -x 135 -y 10 -width 250 -height 32
set filename [$win.op1 get]
According to the documentation, hwtk::openfileentry takes a -command option to specify a user callback script. I would imagine, based on normal usage for things Tk, that that might the place to trigger reading from the widget and writing to the variable:
proc updateTheFilename {window variable} {
# Double quotes just because of Stack Overflow's highlighting
upvar "#0" $variable v
set v [$window get]
}
# Forming callbacks like this is very much a best practice; least surprising in complex scenarios
hwtk::openfileentry $win.op1 -command [list updateTheFilename $win.op1 filename]

How to keep display up to date to monitor options' values of a tk widget

I'm developing a tk window to monitor the value of certain options of a widget.
I know I can monitor the latest value of a scalar variable (please allow me to use the term 'scalar' in perl) simply by specifying the variable name as the value of label's -textvariable switch. However, when it comes to monitoring widget options, if I use this form, say .button1 cget -bg to refer to the background option of a button, I don't know how to update the display of this option's value automatically.
label .label1 -textvariable ____ # <-- what should I put here?
or should I use another command?
To track when a widget option is changed, the easiest way is probably to rename the widget command and put your own proc in its place. That proc just forwards the calls to the original command and then updates the label as necessary:
button .button1
rename .button1 __.button1
proc .button1 {method args} {
set rc [__.button1 $method {*}$args]
if {$method eq "configure"} {
.label1 configure -text [__.button1 cget -bg]
}
return $rc
}
You may need to add some cleanup code to delete your .button1 proc when the widget is destroyed (bind to the event).
This can also be made prettier when errors occur. But this is the basic method.
When the -textvariable option is set to something other than the empty string, it contains the name of a global variable that the label will display instead of the value of the -text option. To update the displayed value, you just write to the global variable; Tk's built-in machinery handles the rest.
In theory, you could use a variable trace to emulate the -textvariable option using the -text option:
# Emulating: label .lbl -textvariable foo
label .lbl
trace add variable foo write {apply {{name1 name2 op} {
# We ignore name2 and op
upvar 1 $name1 var
.lbl configure -text $var
}}}
However, this is a pain to keep on typing; much more convenient to use the short form!
(Yes, Tk widgets use traces to implement things like -textvariable, but they're the C API version of traces so you can't see them from Tcl code. There's a lot of complexity beneath the surface.)
If you want to watch something that is part of a composite value (as opposed to a simple variable or array element) then the easiest method is to use a trace.
trace add variable foo write {apply {args {
global foo displayed_foo_element
set displayed_foo_element [lindex $foo 1]
}}}
label .lbl -textvariable displayed_foo_element
Like this, every time the container of the thing that you care about (foo in the above code) is updated, the trace ensures that the displayed_foo_element variable is updated to follow, and the label can just watch that.
You can use much more complex ways of preparing the value for displayed_foo_element if you want. Perhaps like this:
set displayed_foo_element "foo\[1\] = '[lindex $foo 1]'"
Also, instead of writing to an intermediate variable you can instead update the -text option on the widget directly. This option is particularly useful if you also want to adjust other features of the widget at the same time:
if {[lindex $foo 0] >= $CRITICAL_VALUE} {
set color red
} else {
set color black
}
.lbl configure -text "foo\[1\] = '[lindex $foo 1]'" -foreground $color

Different methods to access value of an entry field in Tk

I am using following simple code to change label text:
#! /usr/bin/wish8.6
label .a_lab -text "Enter text: "
entry .ent -textvariable tt
button .a_button -text "Change" -command changer
pack .a_lab -fill both -expand 1
pack .ent -fill both -expand 1
pack .a_button -fill both -expand 1
proc changer {} {
.a_lab config -text $::tt ;# How can I access 'tt' using pathname '.ent'?
}
wm geometry . 300x200+300+300
Are there any other methods to access the value of 'tt' apart from '$::tt'?
You want .ent get.
The configure and cget subcommands to a widget are used to access a widget's own traits. The text content in an entry widget isn't intrinsic and shouldn't be accessed that way, but widgets often have a specific subcommand for any reasonable task one would want it to perform.
Note also that you can set both the label and the entry to use the same content variable, which gives you instant and automatic updates.
ETA: updating the label with processed content from the entry
Some widgets signal changes through a virtual event (listbox generates a <<ListboxSelect>> event, for instance). The entry widget doesn’t. To setup update triggers for the entry widget, you can:
bind the <Return> event to the entry widget: bind .ent <Return> +mycallback. This lets the Enter key trigger the update. The + can be omitted as there is no standard action for this event.
bind the <Key> event to the Entry class*: bind Entry <Key> +mycallback: any key will trigger an update, including editing keys. Note that if the event is bound to the widget, it fires before the keystroke edits the content of the entry. If you bind it to Entry but omit the +, the callback will be run instead of the usual action to edit the entry.
add a watching trace to the variable: trace add variable tt write {apply {args mycallback}}, or
hijack the validation mechanism: .ent config -validate key -validatecommand {.a_lab config -text [string toupper %P];expr 1}
The mycallback callback can be either
proc mycallback {} {
.a_lab config -text [string toupper [.ent get]]
}
or
proc mycallback {} {
.a_lab config -text [string toupper $::tt]
}
If you set the parameter list of the callback to args, you don't need to wrap it in apply when tracing. If you use the validation mechanism, read the docs so you know how it works (you should always do that, but it's really easy to get it wrong in confusing ways in this case).
Documentation:
apply,
bind,
entry,
trace
*) i.e. X Window class, not OOP class.

arithmetic commands in tcl/tk programming

I am programming in tcl\tk.
The code is showing following error:
"missing operand at _#_
in expression "+_#_""
entry .e1 -textvar a
entry .e2 -textvar b
message .m -textvar c
button .b -text "press here" -command "set c [expr $a+$b]"
The error is showing in the last line. I am running it in tclsh and showing the same error. I have also tried using it in function proc but the same error is popping up.
I am trying to do arithmetic operations using tk.
When the 4th line is evaluated by the interpreter it expands the contents of the quoted part and will execute the expr command with the contents of a and b expanded. However, these variables have no value at that point in time. You meant to evaluate that command when the user clicks the button but it is being evaluated when the button is created.
The quick fix is you need:
button .b -text "press here" -command {set c [expr {$a + $b}]}
provided a and b are global as the command will be evaluated in the global namespace when the button is clicked.
The longer fix is you need to read the Tcl man page quite carefully with attention to the description of the differences between quoted "" and grouped {} expressions in Tcl.

TCL/TK How do I pass multiple arguments to a button call back function?

I have a code like this
proc press2 {v sbit} {
puts $v
puts $sbit
}
:
:
button .t.ok2 -text "OKI" -command "press2 $v $sbit"
with this I get the error wrong # args: should be "press2 v sbit"
if I change it to button .t.ok2 -text "OKI" -command {press2 $v $sbit} I get can't read "v": no such variable and finally I tried button .t.ok2 -text "OKI" -command [press2 $v $sbit] which doesn't give any errors but doesn't work also. Just asking is there any good documentation available for TCL/TK ? The usual man pages and googling isnt helping me much. I am doing much by trial and error.
Passing multiple arguments to a procedure is easy, but the "correct" way depends on what you want:
Early Binding: If you want to pass the current values later (e.g. if you create the widgets in a loop) you need to use list:
button .t.ok2 -text OKI -command [list press2 $v $sbit]
list creates a command that is free from any further substitution*.
Late Binding: If you want pass the value when this command is executed, simply brace it with {}
button .t.ok2 -text OKI -command {press2 $v $sbit}
The variables v and sbit are subsituted when the button is pressed. You only have access to global variables (or variables in a namespace, but not local variables).
* Tk's bind replaces % and a following character with something special. This is done using string substitution, not Tcl substitution, so list does not guard against this.