Richard Suchenwirth 2006-12-22 - The tk_optionMenu offers a handy alternative to a combobox. But for a larger number of structured choices, it is sometimes better to have cascading menus instead of one. Here's a quick hack for that. It follows the same simple API as tk_optionMenu, but if an item is a list of more than one elements, it is turned into a submenu. (Add an extra layer of braces around labels that contain spaces - see example below.) Oh, and the special item "--" makes a separator.
proc tk_optionCascade {w varName args} { set dn [image create bitmap -data "#define i_width 7\n\#define i_height 5 static char i_bits[] = {\n0,127,62,28,8\n}"] set it [lindex $args 0] while {[llength $it]>1} {set it [lindex $it 0]} set ::$varName $it menubutton $w -menu $w.m -text $it -relief raised \ -image $dn -compound right menu $w.m -tearoff 0 tk_optionCascade_add $w.m $varName $args trace add variable ::$varName write "$w config -text \${$varName} ;\#" return $w.m } proc tk_optionCascade_add {w varName argl} { set n 0 set colbreak 0 foreach arg $argl { if {$arg eq "--"} { $w add separator } elseif {$arg eq "|"} { set colbreak 1; continue } elseif {[llength $arg] == 1} { $w add radiobutton -label [join $arg] -variable $varName } else { set child [menu $w.[incr n] -tearoff 0] $w add cascade -label [lindex $arg 0] -menu $child tk_optionCascade_add $child $varName [lrange $arg 1 end] } if $colbreak { $w entryconfigure end -columnbreak 1 set colbreak 0 } } } # Demo and testing: package require Tk tk_optionCascade .o myvar \ {color red green blue -- {other yellow magenta cyan}} \ {hue dark medium light} \ -- {"multi word example"} ok pack .o
RS 2007-02-05: added "|" to be treated as column-break, for data-driven multicolumn menus, such that e.g.
tk_optionCascade .x myvar northwest southwest | northeast southeast
shows a menu of two columns, with directions in the plausible place :)
Teo 2008-04-13: A fairly common case when tk_optionMenu fails to work properly is a big list of unstructured items (e.g. a list of font families). The following tk_optionCascade2 adds cascading menus for items which don't fit the screen height.
proc tk_optionCascade2 {path varName value args} { upvar #0 $varName v if {![info exists v]} { set v $value } set m [tk_optionMenu $path $varName $v] tk_optionCascade2_menu $m $varName [linsert $args 0 $value] return $m } proc tk_optionCascade2_menu {m varName argl} { $m delete 0 end set next [menu $m.n -tearoff 0] $m add cascade -label "More" -menu $next set index 0 set len [llength $argl] set height [winfo screenheight $m] foreach arg $argl { $m insert $index radiobutton -label $arg -variable $varName incr index update idletasks if {[winfo reqheight $m] > $height && $index < $len} { incr index -1 $m delete $index tk_optionCascade2_menu $next $varName [lrange $argl $index end] return } } $m delete end } # demo eval tk_optionCascade2 .q1 myvar2 [lsort [font families]] pack .q1
aplsimple - 2020-04-27 17:44:46
Below is a bit modified tk_optionCascade widget providing three options for customizing:
Also, this version of tk_optionCascade amends few things:
namespace eval t { variable myvar "red" namespace eval t { variable myvar "blue" } puts "t::myvar = $t::myvar" ... tk_optionCascade .o t::myvar $items \ ... }
Please:
proc tk_optionCascade {w vname items {mbopts ""} {precom ""} args} { # A bit modified tk_optionCascade widget made by Richard Suchenwirth. # w - widget name # vname - variable name for current selection # items - list of items # mbopts - ttk::menubutton options (e.g. "-width -4") # precom - command to get entry's options (%a presents its label) # args - additional options of entries # See also: # https://wiki.tcl-lang.org/page/tk_optionCascade set it [lindex $items 0] while {[llength $it]>1} {set it [lindex $it 0]} set $vname $it ttk::menubutton $w -menu $w.m -text $it {*}$mbopts menu $w.m -tearoff 0 tk_optionCascade_add $w.m $vname $items $precom {*}$args trace add variable $vname write "$w config -text \${$vname} ;\#" return $w.m } proc tk_optionCascade_add {w vname argl precom args} { set n [set colbreak 0] foreach arg $argl { if {$arg eq "--"} { $w add separator } elseif {$arg eq "|"} { if {[tk windowingsystem] ne "aqua"} { set colbreak 1 } continue } elseif {[llength $arg] == 1} { set label [join $arg] if {$precom eq ""} { set adds "" } else { set adds [eval [string map [list %a $label] $precom]] } $w add radiobutton -label $label -variable $vname {*}$args {*}$adds } else { set child [menu $w.[incr n] -tearoff 0] $w add cascade -label [lindex $arg 0] -menu $child tk_optionCascade_add $child $vname [lrange $arg 1 end] $precom {*}$args } if $colbreak { $w entryconfigure end -columnbreak 1 set colbreak 0 } } return } # Demo and testing: package require Tk ttk::style theme use clam set items [list {color red green blue -- {colored yellow magenta cyan | #52CB2F #FFA500 #CB2F6A | #FFC0CB #90EE90 #8B6914}} \ {hue dark medium light} -- {{multi word example}} ok] set comm { if {"%a" in {yellow magenta cyan} || ![string first "#" "%a"]} \ {set _ "-background %a -activeforeground %a"} else {set _ ""} } tk_optionCascade .o ::myvar $items "-width -4" $comm \ -command {puts "::myvar = $::myvar"} pack .o
A screenshot of this test: