- Arrange to the same height.
- Be standard with the original listbox.
- Support Options.
- Scroll together.
- Select together.
- -listvariable list (Each row is provided as a list).
- -width list of widths (The number of elements in the list determines the number of columns)
- insert command
- get
- -title
proc multilistbox {path args} {
frame $path -class Multilistbox
rename $path $path/top
uplevel 1 [list interp alias {} $path {} [namespace current]::handle $path]
array set options { -width {} -title {}}
array set options $args
set i -1
foreach width $options(-width) title $options(-title) {
incr i
pack [frame $path.$i] -expand 1 -fill both -anc nw -side left
if {$title != {} } {
pack [label $path.$i.t -text $title] -side top -fill x -anc nw
}
pack [listbox $path.$i.lb -width $width -exportselection 0 ] -side top -anc nw -expand 1 -fill both
bind $path.$i.lb <<ListboxSelect>> [namespace code "sel $path $i"]
}
bind $path <Destroy> "[namespace current]::cleanup $path"
variable $path/cols
set $path/cols $i
configurelist $path $args
set path
}
proc handle {path cmd args} {uplevel 1 $cmd $path $args}We have satisfied the first three requirements; using the minimal of subwidgets as our containers, we have standard listboxes that arrange themselves to the same height, accept standard options, and have their own set of options.For example:option add *Multilistbox*Label.relief sunkenWill specify the way the headings are displayed. Options for the listboxes likewise can be specified.Configuration:
proc configurelist {path arglist} {
variable $path/cols
array set options $arglist
if {[info exists options(-listvariable)]} {
variable $path/lv
set $path/lv $options(-listvariable)
for {set i 0} {$i <= [set $path/cols]} {incr i} {
variable $path/$i/lv
set $path/$i/lv {}
$path.$i.lb configure -listvariable [namespace current]::$path/$i/lv
}
listvariable $path
set cmd "[namespace current]::listvariable $path ;#"
trace remove variable ::[set $path/lv] write $cmd
trace add variable ::[set $path/lv] write $cmd
}
array unset options -width
array unset options -listvariable
array unset options -title
for {set i 0} {$i <= [set $path/cols]} {incr i} {
uplevel 1 $path.$i.lb configure [array get options]
}
}
proc configure {path args} {uplevel 1 [list configurelist $path $args]}
proc listvariable {path} {
variable $path/cols
variable $path/lv
upvar #0 [set $path/lv] listvar
for {set i 0} {$i <= [set $path/cols]} {incr i} {
variable $path/$i/lv
set $path/$i/lv [list]
}
foreach item $listvar {
for {set i 0} {$i <= [set $path/cols]} {incr i} {
lappend $path/$i/lv [lindex $item $i]
}
}
}
# Basic cleanup
proc cleanup {path} {
variable $path/cols
for {set i 0} {$i <= [set $path/cols]} {incr i} {
variable $path/$i/lv
unset -nocomplain $path/$i/lv
}
unset $path/cols
variable $path/lv
if {[info exists $path/lv]} {
set cmd "[namespace current]::listvariable $path ;#"
trace remove variable ::[set $path/lv] write $cmd
} ariable ::[set $path/lv] write $cmd
unset -nocomplain $path/lv
destroy $path/top ;# and all will follow
}We need to use special handling for -listvariable but we can let the scrollbar be updated by all and any listbox. While this is inefficient there is no harm in having the scroll bar set multiple times.Scrolling:
proc yview {path args} {
variable $path/cols
for {set i 0} {$i <= [set $path/cols]} {incr i} {
eval $path.$i.lb yview $args
}
return {}
}When the command $path yview is called we will scroll all widgets, in this we don't support retrieving the current yview settings, but would be a simple addition.Selecting:
proc sel {path who} {
variable $path/cols
for {set i 0} {$i <= [set $path/cols]} {incr i} {
if {$who == $i} {continue}
$path.$i.lb selection clear 0 end
foreach item [$path.$who.lb cursel] { $path.$i.lb selection set $item}
}
return {}
}This is enough to force all in the set to follow the selection of any one of the others.What this doesn't cover is:- Click and drag scrolling of a listbox.
- PageUp / PageDn events on a listbox.
destroy .mlb .mlb2 .sc
set fred { {1 2 3} {4 5 6} {7 8 9}}
set fred2 [string repeat "{aaaa bbbbb cccc} " 1000]
pack [multilistbox .mlb2 -selectmode multiple -width {30 20 10} \
-listvariable fred2 -title {One Two Three} \
-yscrollcommand ".sc set"] -expand 1 -fill both -side left
pack [scrollbar .sc -orient v -command ".mlb2 yview"] -side right \
-anc nw -fill yMissing Functions:While procs for insert, and other listbox functions are missing, they are easy to implement as we can query one of the set of listboxes as they all have the same settings. Settings options likewise is simply a case of iterating for each listbox.Conclusion:- Using options can remove the need to create new and incompatible options for our widgets.
- Bindings are the biggest headache in creating new widgets or higher level widgets.
E.g., (use -listvar and bind to PageUp, PageDn et al).
See alsoulis I don't understand why this link was not added by the author of this page (which was able to add a link in the referred page). Is this a Tcl attitude?LV Tcl is a programming language - it doesn't have attitudes. It isn't an attitude by most who write wiki pages - most of us love to add links. I suggest continuing to add links when you find them missing.

