The script is a simple environment to experiment with ttk::treeview (Man Tcl/Tk 8.6 Man Tcl/Tk 9.0 )
Functions for converting between Tcl dictionaries and tree structures (dict2tvtree, tvtree2dict). These functions allow you to import data from dictionaries into tree views and export tree structures back into dictionary format.
Functions (band, bandInit, bandEvent) to apply alternating row colors (banding/striping) in a treeview, which improves readability by visually distinguishing adjacent rows.
Functions like treesize, treedepth, itemdepth, and tv2list that provide extra functionality for analyzing and manipulating treeview structures, such as calculating tree depth and size or converting tree structures to lists.
Functions to collect keys from nested dictionary structures (collectKeys, collectKeysPoint) and to process key paths, such as extracting heads or tails of keys in hierarchical structures.
Predefined example datasets (exampleDatas) and functions (testAddNodes, testCreateTreeStruct) that generate sample tree structures for testing and demonstration purposes.
Functions (openParentNodes, showVisibleItems, showVisibleChildren) to search within treeview nodes and programmatically expand or collapse branches based on search criteria.
The items of the tree are also the values for the values column in some example data. The example treegreat has over 1 million items. The functions are not optimized for this.
#! /usr/bin/env tclsh # 20240819 # treeview-lib.tcl # https://core.tcl-lang.org/tk/tktview/2a6c62afd9 package require struct::list # ::struct::list flatten is used in the proc showVisibleItems package require dicttool # dict is_dict is used in the procs dict2tvtree, collectKeys, and collectKeysPoint # a. Import and export of dict and tree structures # b. Band (stripe) functionality for treeview rows # c. Additional treeview utility procs # d. Additional key management procs # e. Example data # f. Searching and opening nodes in treeview # g. Creating a new treeview configured as a table with options for adding, updating, upserting data etc. # h. helper procs exppandAll, collapseAll, isLeaf # i. global indeex ################################ # a. Import and export of dict and tree structures ################################ namespace eval tvlib { # Checks if the first elements of all sublists are equal proc checkFirstElementsEqual {listOfLists} { if {[llength $listOfLists] < "2"} { return 0 } set firstElement "" foreach sublist $listOfLists { if {[string is list $sublist]} { lassign $sublist first _ } else { set first $sublist } if {$firstElement eq ""} { set firstElement $first } elseif {$firstElement ne $first} { return 0 } } return 1 } # Converts a dictionary into a tree structure # Special case: if the key is ":", the value is treated as a list proc dict2tvtree {widget parent dict} { foreach {key value} [dict get $dict] { if {[dict exists $dict $key]} { set keyValue [dict get $dict $key] if { [checkFirstElementsEqual $keyValue] } { set stdList [list] set newList [list] foreach sublist $keyValue { if {[lindex $sublist 0] eq ":"} { lappend newList [lindex $sublist 1] } else { lappend stdList [lindex $sublist 1] } } if {$stdList ne {}} { puts $newList set newList $stdList } $widget insert $parent end -text $key -values [list $newList] continue } if {[dict is_dict $keyValue] && [llength $keyValue] != "2"} { set newParent [$widget insert $parent end -text $key -values ""] dict2tvtree $widget $newParent $keyValue } elseif {[llength $keyValue] == "2" && [dict is_dict [lindex $value 1]] } { set newParent [$widget insert $parent end -text $key -values ""] dict2tvtree $widget $newParent $keyValue } else { if {[lindex $keyValue 0] eq ":" } { $widget insert $parent end -text $key -values [list [lrange $keyValue 1 end]] } elseif {[lindex $keyValue 1 0 0] eq ":" } { set nparent [$widget insert $parent end -text $key ] set newkeyValue [list] foreach val {*}[lrange $keyValue 1 end] { lappend newkeyValue [lindex $val 1] } $widget insert $nparent end -text [lindex $keyValue 0 ] -values [list $newkeyValue] } else { if {[string match {\{: *} $value]} { $widget insert $parent end -text $key -values [string range $keyValue 2 end-1] } else { $widget insert $parent end -text $key -values [list $keyValue] } } } } } } # Recursively converts a tree structure into a dictionary # Uses custom interpretation with the same keys proc tvtree2dict {tree node} { set result {} # To handle equal keys set checkFEE 0 set checkkey "" # Get the children of the current node set children [$tree children $node] foreach child $children { set key [$tree item $child -text] if {($checkFEE eq "1") && ($key ne $checkkey)} { puts " ch if: $checkkey k $key :: $checkFEE " set checkFEE 0 set checkkey $key set result [expandList $result] } #set value [lindex [$tree item $child -values] 0] set value [concat {*}[$tree item $child -values]] # Check if the child itself has children if {[$tree children $child] > 0} { set childDict [tvtree2dict $tree $child] dict set result $key $childDict if {$value ne ""} { dict lappend result $key $value } } else { if {[dict exists $result $key]} { set tmplist [dict get $result $key] lappend tmplist $value dict set result $key $tmplist if {!$checkFEE} { set checkFEE 1 set checkkey $key } } else { dict set result $key $value } } } if {($checkFEE eq "1")} { set checkFEE 0 set checkkey $key set result [expandList $result] } return $result } } ################ # b. Band (stripe) functionality for treeview rows ################ # tvlib::bandInit $tree # tvlib::band $tree ## Use event: # tvlib::band_event $tree # # For striped bands, see at: # https://wiki.tcl-lang.org/page/dgw%3A%3Atvmixins # https://chiselapp.com/user/dgroth/repository/tclcode/index # https://wiki.tcl-lang.org/page/Tile+Table # https://www.tcl-lang.org/man/tcl9.0/TkCmd/ttk_treeview.html#M100 namespace eval tvlib { # Recursively apply alternating background colors to rows proc band {tree {parent {}} {i 0} } { foreach item [$tree children $parent] { set t [expr {$i % 2}] $tree tag remove band0 $item $tree tag remove band1 $item $tree tag add band$t $item incr i set i [band $tree $item $i] } return $i } # Initialize banding with specific colors proc bandInit {tree {color0 #FFFFFF} {color1 #E0E0E0}} { $tree tag configure band0 -background $color0 $tree tag configure band1 -background $color1 bind $tree <<TVItemsChanges>> [list [namespace current]::band $tree] } # Trigger a banding event proc bandEvent {tree} { event generate $tree <<TVItemsChanges>> -data [$tree selection] } } ######################### # c. Additional treeview utility procs ######################### namespace eval tvlib { # Recursively calculates the total number of nodes in the tree proc treesize {tree {p {}}} { set size 0 foreach c [$tree children $p] { incr size [llength $c] incr size [treesize $tree $c] } return $size } # Recursively calculates the depth of the tree proc treedepth {tree {parent {}} {depth 0}} { set max $depth foreach item [$tree children $parent] { set currentDepth [treedepth $tree $item [expr {$depth + 1}]] if {$currentDepth > $max} { set max $currentDepth } } return $max } # Calculates the depth of a specific item in the tree proc itemdepth {tree item} { set depth 0 while {$item ne ""} { set item [$tree parent $item] incr depth } return $depth } # Converts the tree structure into a nested dictionary proc tv2list {tree {parent {}}} { set data {} foreach c [$tree children $parent] { dict set data $c [tv2list $tree $c] } return $data } } #################### # d. Additional key management procs #################### namespace eval tvlib { # Recursively collect all keys from a dictionary proc collectKeys {dictVar {keysList {}}} { foreach {key value} [dict get $dictVar] { if { [checkFirstElementsEqual $value] } { lappend keysList ${key} continue } if {[dict is_dict $value] && [llength $value] != "2"} { lappend keysList ${key} set keysList [collectKeys $value $keysList] } elseif {[llength $value] == "2" && [dict is_dict [lindex $value 1]] } { lappend keysList ${key} set keysList [collectKeys $value $keysList] } else { lappend keysList ${key} } } return $keysList } # Recursively collect all keys from a dictionary with full paths using dots proc collectKeysPoint {dictVar {prefix ""} {keysList {}}} { foreach {key value} [dict get $dictVar] { if { [checkFirstElementsEqual $value] } { lappend keysList ${prefix}${key} continue } if {[dict is_dict $value] && [llength $value] != "2"} { lappend keysList ${prefix}${key} set keysList [collectKeysPoint $value "${prefix}${key}." $keysList] } elseif {[llength $value] == "2" && [dict is_dict [lindex $value 1]] } { lappend keysList ${prefix}${key} set keysList [collectKeysPoint $value "${prefix}${key}." $keysList] } else { lappend keysList ${prefix}${key} } } return $keysList } # Extract the last parts (tails) of the keys separated by dots proc extractTails {keys} { set tails {} foreach key $keys { set parts [split $key "."] lappend tails [lindex $parts end] } return $tails } # Extract the first parts (heads) of the keys separated by dots proc extractHeads {keys} { set heads {} foreach key $keys { set parts [split $key "."] lappend heads [lindex $parts 0] } return [uniqueList2 $heads] } # Returns a list of unique elements (used in extractHeads) proc uniqueList2 {list} { set dict {} foreach item $list { dict set dict $item "" } dict keys $dict } # Expands a list by separating the key-value pairs # Used in tvtree2dict proc expandList {inputList} { set key [lindex $inputList 0] set values [lindex $inputList 1] set result {} foreach value $values { lappend result [list $key $value] } return $result } } ##################################### # e. Example data # Usage: tvlib::testCreateTreeStruct $tree 4 ##################################### namespace eval tvlib { variable exampleDatas # e.1 Example data dictionary dict set exampleDatas abc12 {a1 {b11 {a11 {b1111 c1 b1112 c1}} b12 {a12 {b1211 c1 b1212 c1}}} a2 {b21 {a21 {b2111 c1 b2112 c1}} b22 {a22 {b2211 c1 b2212 c1}}}} dict set exampleDatas person {person {name "John Doe" age 30.8 address {street "123 Main St" city "Anytown"} employees { {name "Alice Smith" } {name "Bob Smith"} {name "John Good"} {name "Jane Good"}} } job {title "Developer" company "Works"}} # e.2 Two procs for generating test data for tree structures proc testAddNodes {tree parent depth} { if {$depth <= 0} { return } set numChildren [expr {1 + int(rand() * 11)}] for {set i 0} {$i < $numChildren} {incr i} { set id [$tree insert $parent end -text "Node $i Depth $depth"] $tree item $id -values $id testAddNodes $tree $id [expr {$depth - 1}] } } proc testCreateTreeStruct {tree {depth 5} } { foreach txt {first second third fourth five} { set id [$tree insert {} end -text "$txt item" -open 1] $tree item $id -values $id testAddNodes $tree $id $depth } } # Collects and returns Tcl/Tk environment information in a dictionary proc infotcltk {} { lappend infodata hostname [info hostname] lappend infodata library [info library] lappend infodata nameofexecutable [info nameofexecutable] lappend infodata patchlevel [info patchlevel] lappend infodata sharedlibextension [info sharedlibextension] lappend infodata tclversion [info tclversion] dict set data info $infodata dict set data tm [tcl::tm::path list] foreach i [lsort [package names]] { if {[string length [package provide $i]]} { lappend loaded $i [package present $i] } } dict set data package loaded $loaded foreach p [lsort [package names]] { lappend allp $p [package versions $p] } dict set data package all $allp # Add namespace information to the data dictionary dict set data namespace [listns] set ns :: set pat [set ns]::* foreach proc [lsort [info procs $pat]] { dict lappend data procs [list : $proc] } foreach command [lsort [info commands $pat]] { dict lappend data commands [list : $command] } foreach function [lsort [info functions $pat]] { dict lappend data functions [list : $function] } foreach var [info vars $pat] { if {[array exists $var]} { dict lappend date array $var [list {*}[array get $var]] } else { dict lappend date variable $var [list [set $var]] } } dict set data vars $date return $data } # Generate example data for the table with a specified number of entries and columns proc generateLargeList {numEntries numColumns} { set largeList {} for {set i 1} {$i <= $numEntries} {incr i} { set entry [list] for {set j 1} {$j <= $numColumns} {incr j} { lappend entry "Item_${i}_${j}" } lappend largeList $entry } return $largeList } # Recursively lists namespaces, commands, functions, and variables proc listns {{parentns ::}} { set result [dict create] dict set result commands [listnscommands $parentns] dict set result functions [listnsfunctions $parentns] dict set result procs [listnsprocs $parentns] dict set result vars [listnsvars $parentns] foreach ns [namespace children $parentns] { dict set result $ns [listns $ns] } return $result } # List procedures in the specified namespace proc listnsprocs {ns} { set result "" foreach proc [lsort [info procs ${ns}::*]] { lappend result [list ":" $proc] } return $result } # List commands in the specified namespace proc listnscommands {ns} { set result "" foreach command [lsort [info commands ${ns}::*]] { lappend result [list ":" $command] } return $result } # List functions in the specified namespace proc listnsfunctions {ns} { set result "" foreach function [lsort [info functions ${ns}::*]] { lappend result [list ":" $function] } return $result } # List variables in the specified namespace, including arrays proc listnsvars {ns} { set date "" foreach var [lsort [info vars ${ns}::*]] { if {[array exists $var]} { dict set date array $var [list {*}[array get $var]] } else { if {[catch {set $var} msg]} { puts "catch error: proc listnsvar :: $var" dict set date variable $var [list catch_error] } else { dict set date variable $var [list ":" [list [set $var]]] } } } dict set data variablen $date return $data } } ######################### # f. Searching and opening nodes in treeview ######################### namespace eval tvlib { # Open all parent nodes of the specified item proc openParentNodes {tree item} { set parent [$tree parent $item] if {$parent ne ""} { $tree item $parent -open true openParentNodes $tree $parent } } # Find and return a list of all visible items matching the search string proc showVisibleItems {tree searchString} { set resultList [list] foreach item [$tree children {}] { if {[string match $searchString [$tree item $item -text]]} { openParentNodes $tree $item } else { $tree item $item -open false } lappend resultList [showVisibleChildren $tree $item $searchString] } return [::struct::list flatten -full $resultList] } # Recursively search for matching visible children proc showVisibleChildren {tree parent searchString} { set resultList [list ] foreach item [$tree children $parent] { if {[string match $searchString [$tree item $item -text]]} { lappend resultList $item openParentNodes $tree $item } else { $tree item $item -open false } lappend resultList [showVisibleChildren $tree $item $searchString] } return $resultList } } ######################### # g. Creating a new treeview configured as a table ######################### namespace eval tvlib { # Create a new treeview widget configured as a table # colname anchor minwidth stretch width # Create a new treeview widget configured as a table # colname anchor minwidth stretch width proc newTable {w cols {default 0}} { if {$default} { set myOptionsDefault [dict create anchor e minwidth 20 stretch 1 width 200] } else { set myOptionsDefault [dict create anchor {} minwidth {} stretch {} width {}] } set colsOptions {} foreach col $cols { set myOptions $myOptionsDefault lassign $col colname newAnchor newMinwidth newStretch newWidth if {$newAnchor ne {}} { dict set myOptions anchor $newAnchor } if {$newMinwidth ne {}} { dict set myOptions minwidth $newMinwidth } if {$newStretch ne {}} { dict set myOptions stretch $newStretch } if {$newWidth ne {}} { dict set myOptions width $newWidth } dict set colsOptions $colname $myOptions lappend colnames $colname } set frt [ttk::frame $w.frt] #set frt $w set tree [ttk::treeview $frt.tree -show headings -columns $colnames \ -yscrollcommand [list $frt.vsb set] -xscrollcommand [list $frt.hsb set] -selectmode browse] set vsb [::ttk::scrollbar $frt.vsb -orient vertical -command [list $tree yview]] set hsb [::ttk::scrollbar $frt.hsb -orient horizontal -command [list $tree xview]] set font [::ttk::style lookup [$tree cget -style] -font] foreach colname $colnames { set myOptions [dict get $colsOptions $colname] dict with myOptions { $tree heading $colname -text $colname if {$anchor ne {}} { $tree column $colname -anchor $anchor } if {$minwidth ne {}} { $tree column $colname -minwidth $minwidth } if {$stretch ne {}} { $tree column $colname -stretch $stretch } if {$width ne {}} { $tree column $colname -width $width } } } #parent pack or grid set wtmp $w set manager [winfo manager $wtmp] while {[winfo manager $wtmp] ni [list pack grid]} { set wtmp [winfo parent $wtmp] set manager [winfo manager $wtmp] } switch $manager { pack { pack $frt -expand 1 -fill both pack $vsb -expand 0 -fill y -side right pack $tree -expand 1 -fill both pack $hsb -expand 0 -fill x } grid { #pack $frt -expand 1 -fill both grid $frt -sticky nsew grid $tree $vsb -sticky nsew grid $hsb -sticky nsew grid column $frt 0 -weight 1 grid row $frt 0 -weight 1 } } return $tree } proc newTree {w cols {default 0}} { # Standardoptions if {$default} { set myOptionsDefault [dict create anchor w minwidth 20 stretch 1 width 200] } else { set myOptionsDefault [dict create anchor {} minwidth {} stretch {} width {}] } set colsOptions {} # Konfiguration der Spaltenoptionen foreach col $cols { set myOptions $myOptionsDefault lassign $col colname newAnchor newMinwidth newStretch newWidth if {$newAnchor ne {}} { dict set myOptions anchor $newAnchor } if {$newMinwidth ne {}} { dict set myOptions minwidth $newMinwidth } if {$newStretch ne {}} { dict set myOptions stretch $newStretch } if {$newWidth ne {}} { dict set myOptions width $newWidth } dict set colsOptions $colname $myOptions lappend colnames $colname } set key [lindex $colnames 0] set colnames [lrange $colnames 1 end] # Frame und Scrollbars erstellen set frt [ttk::frame $w.frt] set tree [ttk::treeview $frt.tree -show {tree headings} -columns $colnames \ -yscrollcommand [list $frt.vsb set] -xscrollcommand [list $frt.hsb set] -selectmode browse] set vsb [::ttk::scrollbar $frt.vsb -orient vertical -command [list $tree yview]] set hsb [::ttk::scrollbar $frt.hsb -orient horizontal -command [list $tree xview]] # Spalten- und Überschriftenoptionen konfigurieren foreach colname $colnames { set myOptions [dict get $colsOptions $colname] dict with myOptions { $tree heading $colname -text $colname if {$anchor ne {}} { $tree column $colname -anchor $anchor } if {$minwidth ne {}} { $tree column $colname -minwidth $minwidth } if {$stretch ne {}} { $tree column $colname -stretch $stretch } if {$width ne {}} { $tree column $colname -width $width } } } # for tree $tree heading #0 -text $key # #parent pack or grid set wtmp $w set manager [winfo manager $wtmp] while {[winfo manager $wtmp] ni [list pack grid]} { set wtmp [winfo parent $wtmp] set manager [winfo manager $wtmp] } switch $manager { pack { pack $frt -expand 1 -fill both pack $vsb -expand 0 -fill y -side right pack $tree -expand 1 -fill both pack $hsb -expand 0 -fill x } grid { grid $frt -sticky nsew grid $tree $vsb -sticky nsew grid $hsb -sticky nsew grid columnconfigure $frt 0 -weight 1 grid rowconfigure $frt 0 -weight 1 } } return $tree } # Add a new row to the table proc addRow {t {values ""}} { set item [$t insert {} end] foreach col [$t cget -columns] val $values { $t set $item $col $val } event generate $t <<TVItemsChanges>> } # Add multiple rows to the table proc addRows {t {valueslist ""}} { foreach values $valueslist { set item [$t insert {} end] foreach col [$t cget -columns] val $values { $t set $item $col $val } } event generate $t <<TVItemsChanges>> } # Add a single cell to the table proc addCell {t col value {pos end}} { set item [$t insert {} $pos] $t set $item $col $val event generate $t <<TVItemsChanges>> } # Add multiple cells to the table proc addCells {t col values {pos end}} { set item [lindex [$t children {}] $pos] set index [$t index $item] foreach val $values { set item [$t insert {} $index] $t set $item $col $val incr index } event generate $t <<TVItemsChanges>> } # Update a specific row in the table proc updateRow {t values index} { set item [lindex [$t children {}] $index] if { $item eq "" } { return 0 } foreach col [$t cget -columns] val $values { $t set $item $col $val } return 1 } # Update multiple rows in the table proc updateRows {t values index} { foreach val $values { updateRow $t $val $index incr index } } # Update a specific cell in the table proc updateCell {t col value index} { set item [lindex [$t children {}] $index] if { $item eq "" } { return 0 } $t set $item $col $value return 1 } # Update multiple cells in the table proc updateCells {t col values index} { foreach val $values { updateCell $t $col $val $index incr index } } # Insert or update a specific row in the table proc upsertRow {t values index} { set items [$t children {}] if {$index < [llength $items]} { set item [lindex $items $index] foreach col [$t cget -columns] val $values { $t set $item $col $val } } else { while {[llength $items] <= $index} { set item [$t insert {} end] lappend items $item } foreach col [$t cget -columns] val $values { $t set $item $col $val } } event generate $t <<TVItemsChanges>> } # Insert or update multiple rows in the table proc upsertRows {t valueslist index} { foreach values $valueslist { set items [$t children {}] if {$index < [llength $items]} { set item [lindex $items $index] foreach col [$t cget -columns] val $values { $t set $item $col $val } } else { while {[llength $items] <= $index} { set item [$t insert {} end] lappend items $item } foreach col [$t cget -columns] val $values { $t set $item $col $val } } incr index } event generate $t <<TVItemsChanges>> } # Insert or update a specific cell in the table proc upsertCell {t col value index} { set items [$t children {}] if {$index < [llength $items]} { set item [lindex $items $index] $t set $item $col $value } else { while {[llength $items] <= $index} { set item [$t insert {} end] lappend items $item } $t set $item $col $value } event generate $t <<TVItemsChanges>> } # Insert or update multiple cells in the table proc upsertCells {t col values index} { foreach value $values { set items [$t children {}] if {$index < [llength $items]} { set item [lindex $items $index] $t set $item $col $value } else { while {[llength $items] <= $index} { set item [$t insert {} end] lappend items $item } $t set $item $col $value } incr index } event generate $t <<TVItemsChanges>> } # Delete all rows in the table proc deleteAllRows {t} { foreach item [$t children {}] { $t delete $item } } # Delete a specific row in the table proc deleteRow {t index} { set item [lindex [$t children {}] $index] if { $item eq "" } { return 0 } $t delete $item event generate $t <<TVItemsChanges>> return 1 } # Delete multiple rows in the table based on their indices proc deleteRows {t indices} { set items [$t children {}] set sortedIndices [lsort -integer -decreasing $indices] foreach index $sortedIndices { if {$index < [llength $items]} { set item [lindex $items $index] $t delete $item } } event generate $t <<TVItemsChanges>> } # Delete a specific cell in the table proc deleteCell {t col index} { return [updateCell $t $col "" $index] } # Delete multiple cells in the table based on their indices proc deleteCells {t col indices} { set items [$t children {}] foreach index $indices { if {$index < [llength $items]} { set item [lindex $items $index] $t set $item $col "" } } } # Retrieve data from a specific row in the table proc getRow {t index} { set item [lindex [$t children {}] $index] if { $item ne "" } { set rowData {} foreach col [$t cget -columns] { lappend rowData [$t set $item $col] } return $rowData } return } # Retrieve data from all rows in the table proc getAllRows {t} { set rowsData {} foreach item [$t children {}] { set rowData {} foreach col [$t cget -columns] { lappend rowData [$t set $item $col] } lappend rowsData $rowData } return $rowsData } # Retrieve data from multiple rows based on their indices proc getRows {t indices} { set rowsData {} set items [$t children {}] foreach index $indices { if {$index < [llength $items]} { set item [lindex $items $index] set rowData {} foreach col [$t cget -columns] { lappend rowData [$t set $item $col] } lappend rowsData $rowData } } return $rowsData } # Retrieve data from a specific cell in the table proc getCell {t col index} { set item [lindex [$t children {}] $index] if { $item ne "" } { return [$t set $item $col] } return "" } # Retrieve data from all cells in a specific column proc getAllCells {t col} { set cellsData {} foreach item [$t children {}] { lappend cellsData [$t set $item $col] } return $cellsData } # Retrieve data from multiple cells based on their indices proc getCells {t col indices} { set cellsData {} set items [$t children {}] foreach index $indices { if {$index < [llength $items]} { set item [lindex $items $index] lappend cellsData [$t set $item $col] } } return $cellsData } } # h. helper procs ################################ namespace eval tvlib { # Expand all nodes in the tree proc expandAll {tree item} { # Expand the specified item $tree item $item -open true # Loop through all children of the item foreach child [$tree children $item] { # Recursive call to expand all children expandAll $tree $child } } # Collapse all nodes in the tree proc collapseAll {tree item} { # Collapse the specified item $tree item $item -open false # Loop through all children of the item foreach child [$tree children $item] { # Recursive call to collapse all children collapseAll $tree $child } } # Check if the specified item is a leaf (no children) proc isLeaf {tree item} { if {[llength [$tree children $item]] == 0} { return 1 } else { return 0 } } # Find the previous non-leaf item relative to the specified item proc findPreviousNonLeaf {tree item} { set prevItem [$tree prev $item] set abst 1 while {$prevItem ne ""} { incr abst if {![isLeaf $tree $prevItem]} { return [list $prevItem $abst] } set prevItem [$tree prev $prevItem] } if {$prevItem eq "" } { set prevItem [$tree parent $item] } return [list $prevItem $abst] } } # i. global index # The dictionary is constructed in such a way that each node stores # the entire "tree extent" of its children. # This allows the global index to be calculated efficiently. namespace eval {tvlib} { variable rowsparentidx set rowsparentidx {} # Build a dictionary that stores the child count for each node proc buildChildCountDict {tree {depth 1}} { variable rowsparentidx set rowsparentidx [addChildrenToDict $tree {} $depth] return 1 } proc getDict {} { variable rowsparentidx return $rowsparentidx } # Recursively add child nodes and their counts to the dictionary proc addChildrenToDict {tree parent {depth 1}} { set dictRef {} set childDepth [expr {$depth + 1}] foreach item [$tree children $parent] { set count [countChildren $tree $item] dict set dictRef $depth $item count $count dict set dictRef $depth $item cdepth [addChildrenToDict $tree $item $childDepth] } return $dictRef } # Count the number of children for a given item proc countChildren {tree item} { set count 1 ;# Counts itself foreach child [$tree children $item] { incr count [countChildren $tree $child] } return $count } # Get the keys from the rowsparentidx dictionary at depth 0 proc keysrowsidx {} { variable rowsparentidx set keys [dict keys [dict get $rowsparentidx 0]] return $keys } # Calculate the row index based on the items and their depth proc rowindexCount {items depth} { variable rowsparentidx set rowindex 0 foreach item $items { set rowindex [expr {$rowindex + [dict get $rowsparentidx $depth $item count]}] } return $rowindex } proc findRowFromDict {tree item {rowindex 0}} { variable rowsparentidx set rowindex 0 if {[tvlib::isLeaf $tree $item] } { # Find the previous non-leaf item and adjust the item and rowindex set previousNonLeafList [tvlib::findPreviousNonLeaf $tree $item] set item [lindex $previousNonLeafList 0] set rowindex [expr {$rowindex + [lindex $previousNonLeafList 1]}] } if {[$tree children $item] eq ""} { set result [$tree index $item] set rowindex [expr {$rowindex + [$tree index $item]}] } else { set result $item } set index 0 set currentItem $item # Traverse upwards to find all parent nodes while { $currentItem ne "" } { set parent [$tree parent $currentItem] lappend result $parent set currentItem $parent } set result [lrange $result 0 end-1] set currentItem [lindex $result end] # Find previous siblings at the same depth set prevroot [list] while { $currentItem ne "" } { set prev [$tree prev $currentItem] lappend prevroot $prev set currentItem $prev } # Calculate the row index based on previous siblings set rowindex [expr {$rowindex + [rowindexCount [lrange $prevroot 0 end-1] 1]}] # Look up the depth-based dictionary and adjust the row index set dvar [dict get $rowsparentidx 1 [lindex $result end] cdepth] if {$item in [dict keys [dict get $rowsparentidx 1]]} { set key -10 } else { set key 2 } # Traverse the dictionary to accumulate row counts while {[dict exists $dvar $key]} { dict for {key value} $dvar { foreach k [dict keys [dict get $value]] { if {$k eq $item} { set rowindex [expr {$rowindex + 1}] set key -10 break } else { set rowindex [expr {$rowindex + [dict get $value $k count]}] set key -10 } } #incr key } } return $rowindex } } # https://wiki.tcl-lang.org/page/Multi%2Dcolumn+Listbox+with+Button # j. sorting namespace eval tvlib { proc sortColumn {tree col direction {sortMethod "-dictionary"}} { if {$col eq "#0"} { # Sort by -text for column #0 (tree structure) set sortData [lmap row [$tree children {}] {list [$tree item $row -text] $row}] } else { # Sort by -values for other columns set sortData [lmap row [$tree children {}] {list [$tree set $row $col] $row}] } set dir [expr {$direction ? "-decreasing" : "-increasing"}] # Apply the sorting method passed as argument set sortedData [lsort {*}$sortMethod -index 0 $dir $sortData] # Rearrange the order of rows in the treeview set r -1 foreach rinfo $sortedData { $tree move [lindex $rinfo 1] {} [incr r] } # Switch sorting direction set cmd [list [namespace current]::sortColumn $tree $col [expr {!$direction}] $sortMethod] $tree heading $col -command $cmd tvlib::bandEvent $tree } }
#! /usr/bin/env tclsh #20240819 # treeview-lib-example.tcl package require Tk package require ctext package require scrollutil_tile package require tooltip # procs with namespace tvlib:: and example datas source treeview-lib.tcl # ctext widget for info display variable textw variable allTVWidgets proc createCTW {w} { set frt [ttk::frame $w.frt ] set textw [ctext $frt.text -width 100 -yscrollcommand [list $frt.vsb set]] set vsb [ttk::scrollbar $frt.vsb -orient vertical -command [list $textw yview]] pack $frt -side top -fill both -expand 1 pack $textw -expand 1 -fill both return $textw } # combobox for selection example data and $tree configure -selectmode proc createButton {w tree} { variable allTVWidgets set frt [ttk::frame $w.frt] # combobox for example datas set cbdatas [ttk::combobox $frt.cbdatas -values {table tree treerand treemedium "treemedium with rowidx" treegreat abc12 person info} -exportselection 0 -width 15] $cbdatas current 1 bind $cbdatas <<ComboboxSelected>> [namespace code [list cbComboSelected %W $tree data]] cbComboSelected $cbdatas $tree data # $tree confgure -selectmode extended, browse, or none. set cbselectmode [ttk::combobox $frt.cbselectmode -values {extended browse none} -exportselection 0 -width 15] $cbselectmode current 1 bind $cbselectmode <<ComboboxSelected>> [namespace code [list cbComboSelected %W $tree selectmode]] cbComboSelected $cbselectmode $tree selectmode # $tree confgure -selectmode extended, browse, or none. set cbthemen [ttk::combobox $frt.cbthemen -values [ttk::style theme names] -exportselection 0 -width 15] $cbthemen current 0 bind $cbthemen <<ComboboxSelected>> [namespace code [list cbComboSelected %W $tree themen]] cbComboSelected $cbthemen $tree themen dict set allTVWidgets searchWord [ttk::entry $frt.search] set searchWord [dict get $allTVWidgets searchWord] tooltip::tooltip $searchWord "Entry for search" pack $cbdatas $cbselectmode $cbthemen $searchWord -side left pack $frt -side top -expand 0 -fill x return $cbdatas } # callback combobox proc cbComboSelected {w tree type} { switch $type { data { $tree delete [$tree children {}] set wid [winfo parent [winfo parent $tree]] destroy [winfo parent $tree] set tree [tvlib::newTree $wid [list Keys Values Rowidx]] dataTotree $tree [$w get] tvlib::bandInit $tree tvlib::band $tree tvlib::bandEvent $tree bind $tree <<TreeviewSelect>> [list show %W %X %Y %# %d %x %y %T] } selectmode { $tree configure -selectmode [$w get] } themen { ttk::style theme use [$w get] } } } # Simple callback for handling button bar actions # When the length of args is: # 1: Executes the provided commands in the context of the tree widget. # 2: Executes the commands and displays the output in the provided text widget. # 3: Executes the commands, uses a specific treeview widget, and displays the output in the text widget. proc cbbtnbar {tree args } { variable allTVWidgets set buttoncmd [dict get $allTVWidgets buttoncmd] # for info display $buttoncmd delete 1.0 end # Insert the first and last commands from the stack frame into the display widget $buttoncmd insert end "[lindex [dict get [info frame 1] cmd] 0]\n\n" $buttoncmd insert end [join [split [lindex [dict get [info frame -1] cmd] end] ";"] "\n"] switch [llength $args] { 1 { set cmds {*}$args foreach cmd [split $cmds ";"] { eval [string trimleft $cmd] } } 2 { set textw [lindex $args 0] set cmds [lindex $args 1] foreach cmd [split $cmds ";"] { $textw insert end "[eval [string trimleft $cmd]]\n" } $textw see end } 3 { puts 3 set textw [lindex $args 0] set tvwid [lindex $args 1] set cmds [lindex $args 2] foreach cmd [split $cmds ";"] { $textw insert end "[eval [string trimleft $cmd]]\n" } $textw see end } } } proc searchResult {values} { variable allTVWidgets set searchresultbox [dict get $allTVWidgets searchresultbox] set tree [dict get $allTVWidgets tree] # tv listboox data $searchresultbox delete [$searchresultbox children {}] tvlib::addCells $searchresultbox 0 $values 0 tvlib::addCells $searchresultbox 0 $values 0 foreach item [$searchresultbox children {}] { set treeitem [$searchresultbox set $item #1] set cellsData [$tree item $treeitem -text] $searchresultbox set $item #2 $cellsData } } proc buttonbar {w tree textw } { variable allTVWidgets set tvbox [dict get $allTVWidgets tvbox] set table [dict get $allTVWidgets table] set searchWord [dict get $allTVWidgets searchWord] set f [ttk::frame $w.f] set cf [ttk::frame $f.cf] # for command: # [list cbbtnbar $tree { cmds }] # [list cbbtnbar $tree textw { cmds }] ttk::button $cf.b100 -text "Test Button" -command [list cbbtnbar $tree $textw $searchWord {$tvwid get}] ttk::button $cf.b101 -text "Info tree" -command [list cbbtnbar $tree $textw $tree {set tmpOutput \$tree;set tvwid; \ $tvwid configure; $tvwid column #0;$tvwid column #1;$tvwid column #2;$tvwid heading #0;$tvwid heading #1;$tvwid heading #2}] ttk::button $cf.b102 -text "Info tvbox" -command [list cbbtnbar $tree $textw $tvbox {set tmpOutput \$tvbox;set tvwid; \ $tvwid configure; $tvwid column #0;$tvwid column #1;$tvwid heading #0;$tvwid heading #1}] ttk::button $cf.b103 -text "Info table" -command [list cbbtnbar $tree $textw $table {set tmpOutput \$table;set tvwid; \ $tvwid configure; \ $tvwid column #0;$tvwid column #1;$tvwid column #2;$tvwid column #3;$tvwid column #4;$tvwid column #5;$tvwid column #6;$tvwid column #7; \ $tvwid heading #0;$tvwid heading #1;$tvwid heading #2;$tvwid heading #3;$tvwid heading #4;$tvwid heading #5;$tvwid heading #6;$tvwid heading #7}] ttk::button $cf.b104 -text "ctext clean" -command [list cbbtnbar $tree [list $textw delete 1.0 end]] ttk::button $cf.b110 -text "Set Focus I001" -command [list cbbtnbar $tree {$tree focus I001}] ttk::button $cf.b111 -text "Get Focus" -command [list cbbtnbar $tree $textw {$tree focus}] ttk::button $cf.b112 -text "Get Focus Item \nand info" -command [list cbbtnbar $tree $textw {set ouput "[$tree focus] index: [$tree index [$tree focus]] \ -tags [$tree item [$tree focus] -tags] -open [$tree item [$tree focus] -open] \ -text [$tree item [$tree focus] -text] -values [$tree item [$tree focus] -values]"}] ttk::button $cf.b120 -text "expandAll {}" -command [list cbbtnbar $tree {tvlib::expandAll $tree {}}] ttk::button $cf.b121 -text "collapseAll {}" -command [list cbbtnbar $tree {tvlib::collapseAll $tree {}}] ttk::button $cf.b122 -text "expandAll sel" -command [list cbbtnbar $tree {tvlib::expandAll $tree [$tree selection]}] ttk::button $cf.b123 -text "collapseAll sel" -command [list cbbtnbar $tree {tvlib::collapseAll $tree [$tree selection]}] ttk::button $cf.b130 -text "Clear Selection" -command [list cbbtnbar $tree {$tree selection set ""}] ttk::button $cf.b131 -text "Delete Selected" -command [list cbbtnbar $tree {$tree delete [$tree selection]; tvlib::bandEvent $tree}] ttk::button $cf.b132 -text "Remove Selection" -command [list cbbtnbar $tree {$tree selection remove [$tree selection]}] ttk::button $cf.b133 -text "Select Item add 'Entry' list \nand see first" -command [list cbbtnbar $tree $textw $searchWord {$tree selection add [$tvwid get];$tree see [lindex [$tvwid get] 0]}] ttk::button $cf.b134 -text "Select Item toggle 'Entry'" -command [list cbbtnbar $tree $textw $searchWord { $tree selection toggle [$tvwid get]}] ttk::button $cf.b135 -text "Select Item 'Entry' \nand see" -command [list cbbtnbar $tree $textw $searchWord { $tree selection set [$tvwid get];$tree see [$tvwid get]}] ttk::button $cf.b140 -text "Add item" -command [list cbbtnbar $tree {$tree insert {} end -text "Item # [expr {[tvlib::treesize $tree] +1}]";\ $tree item [lindex [$tree children {}] end] -values [lindex [$tree children {}] end]; tvlib::bandEvent $tree}] ttk::button $cf.b201 -text "tree depth" -command [list cbbtnbar $tree $textw {tvlib::treedepth $tree}] ttk::button $cf.b202 -text "item depth in tree" -command [list cbbtnbar $tree $textw {tvlib::itemdepth $tree [$tree selection]}] ttk::button $cf.b203 -text "tree size" -command [list cbbtnbar $tree $textw {tvlib::treesize $tree}] ttk::button $cf.b210 -text "tree children {}" -command [list cbbtnbar $tree $textw {$tree children {}}] ttk::button $cf.b211 -text "childrens col k {}" -command [list cbbtnbar $tree $textw {tvlib::collectKeys [tvlib::tv2list $tree]}] ttk::button $cf.b212 -text "childrens P col k {}" -command [list cbbtnbar $tree $textw {tvlib::collectKeysPoint [tvlib::tv2list $tree]}] ttk::button $cf.b213 -text "childrens tails {}" -command [list cbbtnbar $tree $textw {tvlib::extractTails [tvlib::collectKeysPoint [tvlib::tv2list $tree {}]]}] ttk::button $cf.b214 -text "childrens heads {}" -command [list cbbtnbar $tree $textw {tvlib::extractHeads [tvlib::collectKeysPoint [tvlib::tv2list $tree {}]]}] ttk::button $cf.b220 -text "tree children sel" -command [list cbbtnbar $tree $textw {$tree children [$tree selection]}] ttk::button $cf.b221 -text "childrens col k sel" -command [list cbbtnbar $tree $textw {tvlib::collectKeys [tvlib::tv2list $tree [$tree selection]]}] ttk::button $cf.b222 -text "childrens P col k sel" -command [list cbbtnbar $tree $textw {tvlib::collectKeysPoint [tvlib::tv2list $tree [$tree selection]]}] ttk::button $cf.b223 -text "childrens tails sel" -command [list cbbtnbar $tree $textw {tvlib::extractTails [tvlib::collectKeysPoint [tvlib::tv2list $tree [$tree selection]]]}] ttk::button $cf.b224 -text "childrens heads sel" -command [list cbbtnbar $tree $textw {tvlib::extractHeads [tvlib::collectKeysPoint [tvlib::tv2list $tree [$tree selection]]]}] ttk::button $cf.b230 -text "tvtree2dict {}" -command [list cbbtnbar $tree $textw {tvlib::tvtree2dict $tree {}}] ttk::button $cf.b231 -text "tvtree2dict sel" -command [list cbbtnbar $tree $textw {tvlib::tvtree2dict $tree [$tree selection]}] ttk::button $cf.b240 -text "search Keys and \nsel 'Entry'" -command [list cbbtnbar $tree $textw $searchWord {tvlib::showVisibleItems $tree [$tvwid get];\ $tree selection set "";$tree selection add [tvlib::showVisibleItems $tree [$tvwid get]];\ searchResult [tvlib::showVisibleItems $tree [$tvwid get]]}] ttk::button $cf.b241 -text "search and \nsel grandchild 3" -command [list cbbtnbar $tree $textw {tvlib::showVisibleItems $tree "grandchild 3";\ $tree selection set "";$tree selection add [tvlib::showVisibleItems $tree "grandchild 3"]}] ttk::button $cf.b250 -text "BuildChildCountDict" -command [list cbbtnbar $tree $textw {tvlib::buildChildCountDict $tree}] ttk::button $cf.b251 -text "rowsparentidx" -command [list cbbtnbar $tree $textw {tvlib::getDict}] ttk::button $cf.b252 -text "Find Index sel" -command [list cbbtnbar $tree $textw {tvlib::findRowFromDict $tree [$tree selection]}] ttk::button $cf.b253 -text "index sel" -command [list cbbtnbar $tree $textw {$tree index [$tree selection]}] set font [::ttk::style lookup [$tree cget -style] -font] ttk::style configure AnotherButton.TButton -font "$font 9" # Use grid to arrange the ttk::buttons in .. columns set row 0 set col 0 foreach btn [winfo children $cf] { $btn configure -style AnotherButton.TButton grid $btn -row $row -column $col -sticky snwe -padx 2 -pady 2 incr col if {$col > 10} { set col 0 incr row } } grid $cf -sticky news grid columnconfigure $cf 0 -weight 1 grid columnconfigure $cf 1 -weight 1 grid rowconfigure $cf $row -weight 1 #grid rowconfigure $cf all -uniform AllRows #grid columnconfigure $cf 1 -weight 1 # Configure the scrollableframe to expand grid $f -sticky news grid rowconfigure $f 0 -weight 1 grid columnconfigure $f 0 -weight 1 buttonbartooltip $cf } # mini docu proc buttonbartooltip {cf} { tooltip::tooltip $cf.b100 "Test Button: Executes a test action." tooltip::tooltip $cf.b101 "Displays information about the 'tree' widget." tooltip::tooltip $cf.b102 "Displays information about the 'tvbox' widget." tooltip::tooltip $cf.b103 "Displays information about the 'table' widget." tooltip::tooltip $cf.b104 "Clears the entire content of the text widget." tooltip::tooltip $cf.b110 "Sets focus on the item with ID 'I001'." tooltip::tooltip $cf.b111 "Retrieves the ID of the currently focused item and displays it in the text widget." tooltip::tooltip $cf.b112 "Gets detailed information about the focused item and displays it." tooltip::tooltip $cf.b120 "Expands all nodes in the treeview." tooltip::tooltip $cf.b121 "Collapses all nodes in the treeview." tooltip::tooltip $cf.b122 "Expands all nodes under the selected item." tooltip::tooltip $cf.b123 "Collapses all nodes under the selected item." tooltip::tooltip $cf.b130 "Clears the current selection in the treeview." tooltip::tooltip $cf.b131 "Deletes the selected item(s) from the treeview." tooltip::tooltip $cf.b132 "Removes the selected item(s) from the selection list." tooltip::tooltip $cf.b133 "Adds an item to the selection and scrolls to the first item." tooltip::tooltip $cf.b134 "Toggles the selection of an item." tooltip::tooltip $cf.b135 "Selects an item and scrolls to it." tooltip::tooltip $cf.b140 "Adds a new item to the treeview." tooltip::tooltip $cf.b201 "Calculates and displays the depth of the treeview." tooltip::tooltip $cf.b202 "Calculates and displays the depth of the selected item in the treeview." tooltip::tooltip $cf.b203 "Counts and displays the total number of items in the treeview." tooltip::tooltip $cf.b210 "Displays the children of the root node ({})." tooltip::tooltip $cf.b211 "Lists all keys from the root node ({})." tooltip::tooltip $cf.b212 "Lists all keys from the root node ({}), with dot-separated paths." tooltip::tooltip $cf.b213 "Extracts and displays the tail keys from the root node ({})." tooltip::tooltip $cf.b214 "Extracts and displays the head keys from the root node ({})." tooltip::tooltip $cf.b220 "Displays the children of the selected node." tooltip::tooltip $cf.b221 "Lists all keys from the selected node." tooltip::tooltip $cf.b222 "Lists all keys from the selected node, with dot-separated paths." tooltip::tooltip $cf.b223 "Extracts and displays the tail keys from the selected node." tooltip::tooltip $cf.b224 "Extracts and displays the head keys from the selected node." tooltip::tooltip $cf.b230 "Exports the entire treeview as a dictionary from the root node." tooltip::tooltip $cf.b231 "Exports the treeview as a dictionary from the selected node." tooltip::tooltip $cf.b240 "Searches for keys and selects the matching entries." tooltip::tooltip $cf.b241 "Searches for 'grandchild 3' and selects the matching items." tooltip::tooltip $cf.b250 "Builds a child count dictionary for the treeview." tooltip::tooltip $cf.b251 "Displays the dictionary with child counts." tooltip::tooltip $cf.b252 "Finds and displays the global index of the selected item." tooltip::tooltip $cf.b253 "Displays the index of the selected item within its parent." } # selection for example datas in tree proc dataTotree {tree select} { switch $select { table { for {set i 1} {$i < 6} {incr i} { set item [$tree insert {} end -text "Item # $i"] $tree item $item -values $item } } tree { foreach txt {first second third} { set id [$tree insert {} end -text "$txt item" -open 1] $tree item $id -values [list $id] for {set i 1} {$i < 5} {incr i} { set child [$tree insert $id end -text "child $i"] $tree item $child -values [list $child] if {$i eq "2"} { continue } for {set j 1} {$j < 4} {incr j } { set grandchild [$tree insert $child end -text "grandchild $i"] $tree item $grandchild -values [list $grandchild] } } } set r -1 foreach item [tvlib::collectKeys [tvlib::tv2list $tree]] { $tree item $item -values [list [$tree item $item -values] [incr r]] } tvlib::expandAll $tree {} tvlib::buildChildCountDict $tree } treerand { foreach txt {first second third} { set id [$tree insert {} end -text "$txt item" -open 1] $tree item $id -values [list $id] for {set i [expr {1+int(rand()*5)}]} {$i > 0} {incr i -1} { set child [$tree insert $id 0 -text "child $i"] $tree item $child -values [list $child] for {set j [expr {int(rand()*4)}]} {$j > 0} {incr j -1} { set grandchild [$tree insert $child 0 -text "grandchild $i"] $tree item $grandchild -values [list $grandchild] } } } set r -1 foreach item [tvlib::collectKeys [tvlib::tv2list $tree]] { $tree item $item -values [list [$tree item $item -values] [incr r]] } tvlib::expandAll $tree {} tvlib::buildChildCountDict $tree } abc12 { # d.1 data dict for example datas tvlib::dict2tvtree $tree {} [dict get $tvlib::exampleDatas abc12] set r -1 foreach item [tvlib::collectKeys [tvlib::tv2list $tree]] { $tree item $item -values [list [$tree item $item -values] [incr r]] } tvlib::buildChildCountDict $tree } person { # d.1 data dict for example datas tvlib::dict2tvtree $tree {} [dict get $tvlib::exampleDatas person] tvlib::buildChildCountDict $tree } treegreat { # d.2 test data creator tvlib::testCreateTreeStruct $tree 7 tvlib::buildChildCountDict $tree } treemedium { # d.2 test data creator tvlib::testCreateTreeStruct $tree 6 tvlib::buildChildCountDict $tree } "treemedium with rowidx" { # d.2 test data creator tvlib::testCreateTreeStruct $tree 6 set r -1 foreach item [tvlib::collectKeys [tvlib::tv2list $tree]] { $tree item $item -values [list [$tree item $item -values] [incr r]] } tvlib::buildChildCountDict $tree } info { # d.2 test data creator tvlib::dict2tvtree $tree {} [tvlib::infotcltk] tvlib::buildChildCountDict $tree } } } proc cbsrb {args} { variable allTVWidgets set tree [dict get $allTVWidgets tree] set searchresultbox [dict get $allTVWidgets searchresultbox] set item [$searchresultbox set [$searchresultbox focus] #1] $tree see $item $tree focus $item } # for info in ctext widget proc show {args} { variable textw variable allTVWidgets set table [dict get $allTVWidgets table] set tvbox [dict get $allTVWidgets tvbox] lassign $args W X Y Raute d x y T rest set tree $W set values [list] # only column values, set values [lindex [$tree item [$tree focus] -values] 0] # tv listboox data $tvbox delete [$tvbox children {}] tvlib::addCells $tvbox 0 $values 0 tvlib::bandInit $tvbox tvlib::band $tvbox tvlib::addRow $table [list $W $X $Y $Raute $d $x $y $T] $textw insert end "\nTreeviewSelect Current selection is '\[\$tree selection\]' [$tree selection ] :: focus: [$tree focus]\n" $textw see end } # main gui proc mainGui {} { # ctext widget for info display variable textw variable allTVWidgets ### ________________________ _________________________ ### set pwv1 [ttk::panedwindow .pwv1 -orient vertical -width 1800] set fv11 [ttk::frame $pwv1.fv11] set fv12 [ttk::frame $pwv1.fv12] $pwv1 add $pwv1.fv11 $pwv1 add $pwv1.fv12 pack $pwv1 -expand 1 -fill both -side top set pwh [ttk::panedwindow $fv12.pwh -orient horizontal -width 1800 ] set fh1 [ttk::frame $pwh.fh1] set fh2 [ttk::frame $pwh.fh2] set fh3 [ttk::frame $pwh.fh3] $pwh add $pwh.fh1 $pwh add $pwh.fh2 $pwh add $pwh.fh3 pack $pwh -expand 1 -fill both -side top set pwv2 [ttk::panedwindow $fh1.pwv2 -orient vertical -width 560 ] set fv1 [ttk::frame $pwv2.fv1] set fv2 [ttk::frame $pwv2.fv2] $pwv2 add $pwv2.fv1 $pwv2 add $pwv2.fv2 pack $pwv2 -expand 1 -fill both # treeview box for value list from treeview ttk::frame $fh2.fr2 dict set allTVWidgets tvbox [tvlib::newTable $fh2.fr2 [list {Value w 40 1 200}]] # ctext as info text ttk::frame $fh3.fr3 set textw [createCTW $fh3.fr3] set nb [ttk::notebook $fh3.fr3.frti] $nb add [frame $nb.f1] -text "Tree Info" $nb add [frame $nb.f2] -text "button cmd" $nb add [frame $nb.f3] -text "configure" $nb add [frame $nb.f4] -text "column" $nb add [frame $nb.f5] -text "table" $nb select $nb.f1 ttk::notebook::enableTraversal $nb $nb hide $nb.f1 #$nb hide $nb.f2 $nb hide $nb.f3 $nb hide $nb.f4 dict set allTVWidgets buttoncmd [createCTW $nb.f2] dict set allTVWidgets table [tvlib::newTable $nb.f5 \ [list {W w 20 1 300} {X e 20 1 80} {Y e 20 1 80} {Raute center 20 1 80} {x e 20 1 80} {y e 20 1 80} {d e 20 1 80} {T n 20 0 80}] ] set table [dict get $allTVWidgets table] tvlib::bandInit $table tvlib::band $table $table configure -height 5 dict set allTVWidgets treeinfo [tvlib::newTree $nb.f1 [list widget achor minwidth stretch width id]] dict set allTVWidgets configure [tvlib::newTree $nb.f3 [list widget achor minwidth stretch width id]] dict set allTVWidgets column [tvlib::newTable $nb.f4 [list widget achor minwidth stretch width id]] pack $fh2.fr2 $fh3.fr3 $fh3.fr3.frti -side right -expand 1 -fill both # treeview and buttonbar set ftr [ttk::frame $fv1.ftr] pack $ftr -sid top -expand 1 -fill both ttk::frame $ftr.fr1 dict set allTVWidgets tree [tvlib::newTree $ftr.fr1 [list Keys Values Rowidx]] set tree [dict get $allTVWidgets tree] # combobox for example data und select -selectmode tree ttk::frame $ftr.frbtn createButton $ftr.frbtn $tree # search result tv box ttk::frame $fv2.frsrb dict set allTVWidgets searchresultbox [tvlib::newTable $fv2.frsrb [list {result w 40 1 100} {keys w 40 300}]] set searchresultbox [dict get $allTVWidgets searchresultbox] bind $searchresultbox <<TreeviewSelect>> [list cbsrb %W %X %Y %# %d %x %y %T] # button bars for cmds ttk::frame $fv11.frbar buttonbar $fv11.frbar $tree $textw pack $ftr.frbtn -side top -expand 0 -fill x pack $ftr.fr1 $fv11.frbar -side top -expand 1 -fill both pack $fv2.frsrb -side left -expand 0 -fill both #band stripes tvlib::bandInit $tree tvlib::band $tree # event generate $tree <<TVItemsChanges>> -data [$tree selection] # use: # tvlib::bandEvent $tree set output "[array get ttk::treeview::State]\n" $textw insert end $output } ################################### #main ################################### mainGui
#!/usr/bin/env tclsh package require Tk # 20240819 # treeview-table.tcl # procs with namespace tvlib source treeview-lib.tcl # from https://wiki.tcl-lang.org/page/ttk::treeview # g. use of ttk::treeview to build a table # proc newTable # creates a new treeview configured as a table # add Row, Rows, Cell, Cells # update Row, Rows, Cell, Cells # upsert Row, Rows, Cell, Cells # delete Row, Rows, Cell, Cells # -- create a new table # "pseudo play" the display with #update #after delay set delay 1000 set delay2 2000 ttk::frame .fr pack .fr -side top -expand 1 -fill both set table [tvlib::newTable .fr [list col1 col2]] tvlib::bandInit $table tvlib::band $table tvlib::bandEvent $table $table configure -height 20 $table heading col1 -text "Column 1" -command [list tvlib::sortColumn .tree col1 0] $table heading col2 -text "Column 2" -command [list tvlib::sortColumn .tree col2 0] # -- set values for all columns tvlib::addRow $table [list 1] tvlib::addRow $table [list 2] # -- add an empty row tvlib::addRow $table tvlib::addRow $table [list "value one"] tvlib::addRows $table [list [list 3] [list 4] [list 5]] tvlib::addCells $table 1 w 1 update after $delay tvlib::addCells $table 1 [list 1 2 3] 1 puts [tvlib::updateCell $table 1 m 8] puts [tvlib::updateCell $table 1 x 9] tvlib::updateCells $table 0 [list 1 2 3 4 5 6 7 8 9 10 11 12] 1 update after $delay tvlib::upsertCells $table 1 [list 1 2 3 4 5 6 7 8 9 10 11 12] 1 update after $delay tvlib::upsertCell $table 0 k 15 tvlib::deleteRows $table [list 1 3] puts [tvlib::updateRow $table [list "new value 1" "new value 2"] 2] tvlib::upsertRow $table [list "upsert value 1" "upsert value 2"] 5 tvlib::upsertRows $table \ [list [list "upsert value 3" "upsert value 4"] [list "upsert value 5" "upsert value 6"] ] 5 update after $delay tvlib::updateRows $table \ [list [list "update value 7" "update value 8"] [list "update value 9" "update value 10"] ] 5 update after $delay puts [tvlib::deleteRow $table 13] puts [tvlib::deleteCell $table 1 8] set res 1 while {$res} { update after $delay2 set res [tvlib::deleteRow $table 0] } update after $delay tvlib::upsertCells $table 1 [list 0 1 2 3 4 5 6 7 8 9 10 11 12] 0 for {set i 0} { $i <= 12} {incr i} { update after $delay tvlib::upsertCell $table 0 $i $i update after $delay tvlib::deleteCell $table 1 $i } for {set i 0} { $i <= 12} {incr i} { update after $delay tvlib::updateCell $table 1 [expr {[tvlib::getCell $table 0 $i] *2}] $i } set datas2 [tvlib::getAllRows $table] update after $delay2 tvlib::deleteAllRows $table after $delay2 tvlib::addRows $table $datas2 puts [tvlib::getAllCells $table 1] puts [tvlib::getCells $table 1 [list 2 4 6] ] ######################### # tests with large lists # use 1 if {0} { puts "delete: [time {tvlib::deleteAllRows $table} 1]" update puts "addRows f: [time {tvlib::addRows $table [tvlib::generateLargeList 1000000 2]} 1]" # set data3 [tvlib::generateLargeList 1000000 2] puts "delete AR: [time {tvlib::deleteAllRows $table} 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" puts "delete AR: [time {tvlib::deleteAllRows $table} 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" puts "delete AR: [time {tvlib::deleteAllRows $table} 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" puts "delete TV: [time {$table delete [$table children {}]} 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" destroy [winfo parent $table] update puts "Number two" set table [tvlib::newTable . [list col1 col2]] tvlib::bandInit $table tvlib::band $table tvlib::bandEvent $table $table configure -height 50 update puts "addRows d: [time {tvlib::addRows $table $data3} 1]" puts "delete TV: [time {$table delete [$table children {}]} 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" puts "delete AR: [time {tvlib::deleteAllRows $table} 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" update puts "Number three" puts "delete Wi: [time { destroy [winfo parent $table] set table [tvlib::newTable . [list col1 col2]] tvlib::bandInit $table tvlib::band $table tvlib::bandEvent $table $table configure -height 50 } 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" puts "delete Wi: [time { destroy [winfo parent $table] set table [tvlib::newTable . [list col1 col2]] tvlib::bandInit $table tvlib::band $table tvlib::bandEvent $table $table configure -height 50 } 1]" puts "addRows d: [time {tvlib::addRows $table $data3} 1]" }
#!/usr/bin/env tclsh package require Tk # 20240819 # treeview-table-tree-sort.tcl # procs with namespace tvlib source treeview-lib.tcl # example for -command proc compare {a b} { set a0 [lindex $a 0] set b0 [lindex $b 0] if {$a0 < $b0} { return -1 } elseif {$a0 > $b0} { return 1 } return [string compare [lindex $a 1] [lindex $b 1]] } ttk::frame .fr pack .fr -side left -expand 1 -fill both set table [tvlib::newTable .fr [list col1 col2]] tvlib::bandInit $table tvlib::band $table tvlib::bandEvent $table $table configure -height 20 $table heading col1 -text "Column 1" -command [list tvlib::sortColumn $table col1 0] $table heading col2 -text "Column 2" -command [list tvlib::sortColumn $table col2 0] tvlib::addCells $table 0 [list 0 1 2 3 4 5 6 7 8 9 10 11 12] 0 tvlib::upsertCells $table 1 [list 21 000 203 3751 5 4 1 7 2 9 456 32 38] 0 puts [tvlib::updateCell $table 1 m 8] puts [tvlib::updateCell $table 1 M 5] puts [tvlib::updateCell $table 1 x 9] ##################################################################################### ttk::frame .fr2 pack .fr2 -side right -expand 1 -fill both set tree2 [tvlib::newTree .fr2 [list Keys Values Rowidx]] tvlib::bandInit $tree2 tvlib::band $tree2 #tvlib::bandEvent $tree foreach txt {first second third} { set id [$tree2 insert {} end -text "$txt item" -open 1] $tree2 item $id -values [list $id] for {set i 1} {$i < 5} {incr i} { set child [$tree2 insert $id end -text "child $i"] $tree2 item $child -values [list $child] if {$i eq "2"} { continue } for {set j 1} {$j < 3} {incr j } { set grandchild [$tree2 insert $child end -text "grandchild $i"] $tree2 item $grandchild -values [list $grandchild] } } } set r -1 foreach item [tvlib::collectKeys [tvlib::tv2list $tree2]] { $tree2 item $item -values [list [$tree2 item $item -values] [incr r]] } tvlib::bandEvent $tree2 tvlib::expandAll $tree2 {} $tree2 configure -height 20 $tree2 heading #0 -command [list tvlib::sortColumn $tree2 #0 0] $tree2 heading Values -command [list tvlib::sortColumn $tree2 Values 0 "-command compare"] $tree2 heading Rowidx -command [list tvlib::sortColumn $tree2 Rowidx 0 -integer]