The tablelist curselection goes at calling the directory dialog - tcl

I have the test code:
package require Tk
package require tablelist
set ::tv {{N1 qwe} {N3 rty} {N4 uio}}
set ::dir [pwd]
tablelist::tablelist .tbl -columns {0 Name 0 Value} -listvariable ::tv
button .but -text "Directory..." -command {
set sel1 [.tbl curselection]
set sel2 [.tree selection]
tk_messageBox -message ".tbl curselection = \"$sel1\"\n\n.tree curselection = \"$sel2\""
set ::dir [tk_chooseDirectory -initialdir "$::dir"]
}
ttk::treeview .tree -columns Value
.tree heading "#0" -text "Name"
.tree heading "#1" -text "Value"
foreach t $::tv {
lassign $t t1 t2
.tree insert {} end -text $t1 -values $t2
}
.tbl selection set 0; #.tbl activate 0
.tree selection set I001
pack .tbl .tree .but -side left -anchor n -padx 9 -pady 9
At first pressing "Directory" button, I see "tablelist curselection=0" okay.
But at calling the directory dialog, the tablelist curselection disappears. The treeview selection remains, as it should be.
I couldn't find how to make the tablelist curselection to be untouched.
tablelist v6.8
TIA

This sort of weird behaviour is the kind of thing that occurs when several widgets are fighting over the PRIMARY selection, a feature of X11 which is used to mean that only one widget at a time has anything selected. (It also used to be used a lot for select-and-paste style text manipulation, but that's fallen out of favour and the more cross-platform CLIPBOARD style selections are used for that sort of thing.) I've no idea why it is turning on in your specific case and not elsewhere, but it's probably something to do with defaults in the X properties on Debian; that's not very visible but can be listed with this shell command.
xrdb -query -all
Tk widgets support the PRIMARY select by default (on X11; it's not really meaningful on other platforms) and many third-party and synthetic widgets do as well, but they can be told not to by setting the standard boolean -exportselection option on the widget to any false value. Once that's done, the widget continues to maintain a notion of what it has selected but does not export that notion outside of itself (unless you do Ctrl+C or something like that).
tablelist::tablelist .tbl -columns {0 Name 0 Value} -listvariable ::tv \
-exportselection false

Related

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.

Display tooltip when hovering on a tab

I'm using DynamicHelp to display tooltips. The problem is that it only displays help when the cursor is on the body of the tab: not when it is on the tab itself. What I'd like to happen is for the help text to be displayed when the user is hovering over the tabs instead of having to select the tab, then move the cursor to the body before the help is displayed.
package require BWidget
## create a notebook with 2 text panes
NoteBook .n
.n insert 0 tb1 -text "Tab 1"
.n insert 1 tb2 -text "Tab 2"
foreach panel {tb1 tb2} {
set pane [.n getframe $panel]
text $pane.t
pack $pane.t -fill both -expand 1
}
pack .n
.n raise tb1
# ,-- How do I get the tab?
DynamicHelp::add [.n getframe tb1] -text "The essence of silly\nsally silica"
DynamicHelp::add [.n getframe tb2] -text "acetyl sali cylic\nacid is aspirin"
I found this piece of code on the notebook implementation - I don't know if it helps. I can't figure out how it gets the handle of the tab from this.
proc NoteBook::_highlight { type path page } {
variable $path
upvar 0 $path data
if { [string equal [Widget::cget $path.f$page -state] "disabled"] } {
return
}
switch -- $type {
on {
$path.c itemconfigure "$page:poly" \
-fill [_getoption $path $page -activebackground]
$path.c itemconfigure "$page:text" \
-fill [_getoption $path $page -activeforeground]
}
off {
$path.c itemconfigure "$page:poly" \
-fill [_getoption $path $page -background]
$path.c itemconfigure "$page:text" \
-fill [_getoption $path $page -foreground]
}
}
}
I have written a small extension to the Notebook widget what does exactly what you want. You can download it from notebook-tip.tcl. Use it as follows:
After package require, source this file. Create your tabs and add the balloons. Multiple lines are possible.
Example:
package require BWidget
source notebook-tip.tcl
NoteBook .n
.n insert 0 tb1 -text "Tab 1"
.n balloon tb1 "balloon text for Tab 1"
.n insert 1 tb2 -text "Tab 2"
.n balloon tb2 "balloon text for Tab 2"
foreach panel {tb1 tb2} {
# add contents
set pane [.n getframe $panel]
text $pane.t
pack $pane.t -fill both -expand 1
}
.n raise tb1
grid .n -sticky ew
You can change the balloon text dynamically with itemconfigure:
$path itemconfigure $page -balloon text
For example:
.n itemconfigure tb1 -balloon "another text"
Really, you can. You must add the the option -helptext in the command "insert".
According to Bwidget doc :
[...]
pathName insert index page ?option value...?
Insert a new page identified by page at position index in the pages list. index must be numeric or end. The pathname of the new page
is returned. Dynamic help, if it is specified by the options, is
displayed when the pointer hovers over the tab that belongs to the
page.
-helpcmd
Has no effect. See also DynamicHelp.
-helptext
Text for dynamic help. If empty, no help is available for this page. See also DynamicHelp.
-helptype
Type of dynamic help. Use balloon (the default for a NoteBook page) or variable. See also DynamicHelp.
-helpvar
Variable to use when -helptype option is variable. See also DynamicHelp.
[...]
Not quite the solution I was looking for but it is good enough. Create a label for the help text and bind the entry of the tab to the label
package require BWidget
# Creat a bar for help
grid [label .l1 -textvariable tabhelp -justify left] -sticky w -row 0
## create a notebook with 2 text panes
NoteBook .n
.n insert 0 tb1 -text "Tab 1"
.n insert 1 tb2 -text "Tab 2"
foreach panel {tb1 tb2} {
set pane [.n getframe $panel]
text $pane.t
pack $pane.t -fill both -expand 1
}
.n raise tb1
grid .n -sticky ew -row 1
DynamicHelp::add [.n getframe tb1] -text "The essence of silly\nsally silica"
DynamicHelp::add [.n getframe tb2] -text "acetyl sali cylic\nacid is aspirin"
# Add help on entry into the tabs
.n.c bind p:tb1 <Enter> {set tabhelp "Woody Woodpecker"}
.n.c bind p:tb1 <Leave> {set tabhelp ""}
.n.c bind p:tb2 <Enter> {set tabhelp "Aspirins are great"}
.n.c bind p:tb2 <Leave> {set tabhelp ""}

entry with new window

I want a new window appearing with simple entry to put one line of text and button ok to set value of variable. When I use simple entry command, it appear in my main window. I need something like tk_dialog with option to put text to some variable. Is there any predefined tk_* function?
you have to create another window with tk toplevel command
% set top [toplevel .top]
.top
% focus $top
% grab $top
% set entryBox [entry $top.ent -textvariable x]
.top.ent
% pack $entryBox
% set btn [button $top.btn -text "Click Me"]
.top.btn
% pack $btn
This procedure:
proc entrybox varName {
set top [toplevel .top[clock seconds]]
entry $top.eb -textvariable $varName
button $top.bu -command [list incr ${top}done] -text OK
pack {*}[winfo children $top]
vwait ::${top}done
unset -nocomplain ::${top}done
destroy $top
}
when given a global or fully qualified name, creates a new toplevel dialog with an entry and a button. It waits for the button to be clicked and then destroys the toplevel dialog. The text in the entrybox remains in the variable.
Documentation:
button (widget),
clock,
destroy,
entry (widget),
incr,
list,
pack,
proc,
set,
toplevel,
unset,
vwait,
winfo,
{*} (syntax)

Create and place widget from subroutine

My form is a basic two label frames with one of them containing check boxes and the other is an image. Below these two frames is a back and a start button. The window is preset so that it cannot be altered but when the start button is pressed additional widgets appear on the screen. However, I would like the widgets to only be created and placed on the screen after the checkbox has been selected and the start button has been clicked. The start button then calls a function called "Balanced". Within this code it creates the new widgets and places them on the window. However, it returns an error: "bad window path name '.lblfrmProgress'"
#Set Dual UTA Window as top-level
set UTA .dual_uta
wm state . withdrawn
catch {destroy $UTA}
toplevel $UTA
#Window Properties
wm title $UTA {Device: Dual UTA}
wm maxsize $UTA 522 231 ;#x-500, y-231
wm minsize $UTA 522 231 ;#x-500, y-231
The above is a section of the code that creates a window under the alias of UTA. I thought that this window is the top-level window and as such could be referenced using $UTA.[pathname].
global UTA .dual_uta
#**************** DO NOT MODIFY - USER INTERFACE CODE *******************
#Setup window with labels to show progress
labelframe $UTA.lblfrmProgress -text "Test Progress" -padx 1 -relief groove -height 145 -width 520
label $UTA.lblUTASetup -text "Dual UTA setup according to image"
label $UTA.lblVQuadStart -text "VQuad is initializing"
label $UTA.lblVQTStart -text "VQT is initializing"
label $UTA.lblLMC -text "Load 'Balanced' Master Configuration"
label $UTA.lblTxRx1 -text "Side 1 Tx - Side 2 Rx"
label $UTA.lblTxRx2 -text "Side 1 Rx - Side 2 Tx"
Am I referencing the window variable name incorrectly? Do I need to pass the window variable via procedure call? I just call the file by using 'source Balanced.tcl'
Thanks for the help!
Your use of global appears to be somewhat off. In particular, each argument to global is the name of a variable to map in; initialization should be done separately. Or you can both bring the variable in and (optionally) initialize it with the variable command:
proc whatever {} {
variable UTA .dual_uta
destroy $UTA; # No error if $UTA doesn't exist
toplevel $UTA
wm title $UTA {Device: Dual UTA}
labelframe $UTA.lblfrmProgress -text "Test Progress" \
-padx 1 -relief groove -height 145 -width 520
# Etc.
}
It's usually considered better to use that form of variable only within the enclosing namespace (i.e., the global namespace, ::, unless you say otherwise) and only use the single argument form inside a procedure.
variable UTA .dual_uta
proc whatever {} {
variable UTA
destroy $UTA
toplevel $UTA
wm title $UTA {Device: Dual UTA}
labelframe $UTA.lblfrmProgress -text "Test Progress" \
-padx 1 -relief groove -height 145 -width 520
# Etc.
}
Myself, I'd structure the procedure so that the “root name” of the window hierarchy to build was a parameter to the procedure, binding the name into any callbacks during creation:
proc whatever {UTA} {
destroy $UTA
toplevel $UTA
wm title $UTA {Device: Dual UTA}
labelframe $UTA.lblfrmProgress -text "Test Progress" \
-padx 1 -relief groove -height 145 -width 520
# Etc.
button $UTA.thingamijig -text "Fluffy Bunny" -command [list doTheCallback $UTA]
# ...
}
I'd also be saving the names of widgets in variables for use in later pack/grid calls, so as to avoid having to write long widget paths quite so often. It's just slightly more mnemonic IMO, but certainly not necessary. (Binding the pathnames into callbacks à la the use of list above instead of using a global/namespace variable is better style though, and less problematic than writing callbacks with string substitutions.)
Do you create the UTA variable in a proc? If so, you have to declare it global there too.
The global command takes one or more variable names, so global UTA .dual_uta doesn't make sense.

Create multiple entry box using for loop in tcl/tk

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.