Display tooltip when hovering on a tab - tcl

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 ""}

Related

tcl/tk script not behaving as desired

I am beginner with running a tcl/tk script. In my script i have created a popup window to select a file to open and then this file path is given to the source function. I was expecting this script to run stepwise but instead source function is running before i select any file. I also tried using vwait function. Unfortunately it is not running in the 1st run. But in the 2nd run script is working as desire. Can anybody help me to run this script?
destroy .buttons
toplevel .buttons -width 400 -height 100 -background red -relief ridge -borderwidth 8 -padx 10 -pady 10
wm title .buttons "Select a file containing nodes coordinates"
wm geometry .buttons 350x81
set count 0
proc add_button {title command} {
global count
button .buttons.$count -text $title -command $command
pack .buttons.$count -side top -pady 1 -padx 1 -fill x
incr count
}
set types { {{TCL Scripts} {.tcl}} }
add_button "File name" {set accept_button [tk_getOpenFile -filetypes $types]
puts "the path is: $accept_button"
destroy .buttons}
add_button "Exit" {destroy .buttons}
#puts above------------------------
#vwait [namespace which -variable accept_button]
#puts below-----------------------
source "$accept_button"
puts "the src is: $accept_button"
Looks like you are missing the idea of event-driving programming in Tk.
Lets try to find out what is going on in your script. When you run it, the only things are should be done: construct window with widgets for user and bind scripts to widgets events. That is all. After that program is doing nothing but waiting for users action. The command that you bind to a button does not evaluated instantly.
In you case, all work with selected file should be after user have chose it. You should run file reading from button's command. Try to run this script with tclsh
package require Tk
destroy .buttons
toplevel .buttons -width 400 -height 100 -background red -relief ridge -borderwidth 8 -padx 10 -pady 10
wm title .buttons "Select a file containing nodes coordinates"
wm geometry .buttons 350x81
set count 0
proc add_button {title command} {
global count
button .buttons.$count -text $title -command $command
pack .buttons.$count -side top -pady 1 -padx 1 -fill x
incr count
}
set types { {{TCL Scripts} {.tcl}} }
add_button "File name" {set accept_button [tk_getOpenFile -filetypes $types]
puts "the path is: $accept_button"
what_program_should_do_after_file_is_chosen $accept_button
destroy .buttons}
add_button "Exit" {destroy .buttons}
proc what_program_should_do_after_file_is_chosen {path} {
puts "You've chose file: $path"
}
vwait forever

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)

currently selected tab of ttk::notebook

I've created a window with menu and two notebook tabs. In the menu I have a button "Open" and it's opening me a file aaa.txt in a text widget in notebook tab 1. The problem is that I would like to open it not in tab 1 but in current selected/displayed/active notebook tab (could be tab 1 or tab 2). Code below:
proc CommandOpen { } {
set f [open aaa.txt r]
set x [read $f]
.f.nb.f1.f11.t1 insert 1.0 $x
close $f
}
wm title .
wm geometry . 640x460
pack [frame .f] -fill both
pack [ttk::notebook .f.nb] -fill both
.f.nb add [frame .f.nb.f1] -text "tab1"
pack [frame .f.nb.f1.f11] -side top -fill both
pack [text .f.nb.f1.f11.t1 -bg white] -side left -fill both
.f.nb add [frame .f.nb.f2] -text "tab2"
pack [text .f.nb.f2.t1 -bg white] -side left -fill both
menu .mbar -borderwidth 1
. configure -menu .mbar
.mbar add cascade -label "File" -underline 0 -menu [menu .mbar.file -tearoff 0]
set mf .mbar.file
$mf add command -label "Open" -command CommandOpen -underline 0
Thanks
You can get the currently selected tab index using:
.f.nb index current
This is documented on the manual page (it's the index method and the current tabid).
To get the slave widget which is managed by a particular tab, you index into the results of the tabs method. Overall, you get:
set currentSubwindow [lindex [.f.nb tabs] [.f.nb index current]]

Creating a Tk Tcl entry that acts like a label

I want to create labels that the text in them can be selected for copy/paste. To do this I tried to use entries that are read-only. But I can't seem to initialize the text value in them. The labels are generated inside a loop and the number of labels and their content is unknown. The code to produce the labels is:
proc test_labels {} {
toplevel .labels
# Main Frame
frame .labels.main_frame -relief "groove" -bd 2
pack .labels.main_frame
set r 1
foreach t [list banana apple grapes orange lemon peach] {
set lbl [label .labels.main_frame.lbl_$r -text "fruit $r:"]
set lbl2 [label .labels.main_frame.val_$r -text $t]
grid $lbl -row $r -column 1 -sticky nse
grid $lbl2 -row $r -column 2 -sticky nsw
incr r
}
set ok_btn [button .labels.main_frame.ok_b -text "OK" -command {prop_menu_ok_button}]
grid $ok_btn -row [expr $r+2] -column 1 -columnspan 2 -sticky nsew
grab release .
grab set .labels
center_the_toplevel .labels
bind .labels <Key-Return> {test_labels_ok_button}
}
And it creates the fallowing window:
Then I try to replace the line set lbl2 [label .labels.main_frame.val_$r -text $t] with the lines:
eval "set text_val_$r $t"
eval "set lbl2 [entry .labels.main_frame.val_$r -relief flat -state readonly -textvar text_val_$r]"
But this only creates empty lines:
How can I put default values to entry widgets?
Related to the question How to make the text in a Tk label selectable?
These lines are almost certainly not what you want! (If you're using eval, you should always ask whether it's really necessary; from 8.5 onwards, the likely answer is “it's not necessary”.)
eval "set text_val_$r $t"
eval "set lbl2 [entry .labels.main_frame.val_$r -relief flat -state readonly -textvar \$\{text_var_$r\}]"
The key problem — apart from the use of eval — is that the -textvariable option takes the name of a variable. Let's fix that by using an array to hold the values:
set text_val($r) $t
set lbl2 [entry .labels.main_frame.val_$r -relief flat -state readonly \
-textvariable text_val($r)]
Also, be aware that the text_val array needs to be global (or in a namespace, if you fully qualify the name when giving it to the -textvariable option). This is because it is accessed from places which are outside the scope of any procedure.
Of course, it turns out that if we are keeping values constant then we can avoid using a variable at all and just insert the value manually.
set lbl2 [entry .labels.main_frame.val_$r -relief flat]
$lbl2 insert 0 $t
$lbl2 configure -state readonly
If you're never changing the value, that will work fine.

How can I dynamically populate the values of TK combobox?

I am new to tcl/tk programming. Here is a small code snippet on combo box. How can I dynamically add and remove values from the combo box?
set ff [ frame f]
set label [Label $ff.label -text "Name:" ]
set name [ComboBox $ff.name \
-editable yes \
-textvariable name]
set addButton [Button $ff.addButton -text "+" -width 1 -command {addNameToComboBox}]
set removeButton [Button $ff.removeButton -text "-" -width 1 -command removeNameFromComboBox}]
grid $ff.addButton -row 0 -column 2 -sticky w
grid $ff.removeButton -row 0 -column 3 -sticky sw -padx 5
proc addNameToComboBox {name} {
}
proc removeNameFromComboBox {name} {
}
Cheers!
Your example code has a few bugs (*), and it's not entirely clear what you want to do. Are you wanting to add the current value of the combobox to the dropdown list, or is the value you want to add coming from somewhere else?
Here's a solution that adds the current value of the combobox to the list. It uses the built-in versions of the combobox, label and button widgets. Whatever combobox widget you are using probably works similarly, though maybe not exactly.
(*) Button, Label and ComboBox aren't standard widgets -- did you mean "button", "label" and "ttk::combobox" or are you using some custom widgets?. Also, you forgot to use grid to manage the combobox and label, and your procs are expecting arguments but you aren't passing any in).
This solution works with tcl/tk 8.5 and the built-in ttk::combobox widget:
package require Tk 8.5
set ff [frame .f]
set label [label $ff.label -text "Name:" ]
set name [ttk::combobox $ff.name -textvariable name]
set addButton [button $ff.addButton -text "+" -width 1 \
-command [list addNameToComboBox $name]]
set removeButton [button $ff.removeButton -text "-" -width 1 \
-command [list removeNameFromComboBox $name]]
grid $label $name
grid $ff.addButton -row 0 -column 2 -sticky w
grid $ff.removeButton -row 0 -column 3 -sticky sw -padx 5
pack $ff -side top -fill both -expand true
proc addNameToComboBox {name} {
set values [$name cget -values]
set current_value [$name get]
if {$current_value ni $values} {
lappend values $current_value
$name configure -values $values
}
}
proc removeNameFromComboBox {name} {
set values [$name cget -values]
set current_value [$name get]
if {$current_value in $values} {
set i [lsearch -exact $values $current_value]
set values [lreplace $values $i $i]
$name configure -values $values
}
}