treeview laboratory example

The script is a simple environment to experiment with ttk::treeview (Man Tcl/Tk 8.6 Man Tcl/Tk 9.0 )

  • Import and Export of Dictionary and Tree Structures:

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.

  • Banding/Striping for Treeview Rows:

Functions (band, bandInit, bandEvent) to apply alternating row colors (banding/striping) in a treeview, which improves readability by visually distinguishing adjacent rows.

  • Additional Treeview Utility Procedures:

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.

  • Key Collection and Manipulation Utilities:

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.

  • Example Data Generators:

Predefined example datasets (exampleDatas) and functions (testAddNodes, testCreateTreeStruct) that generate sample tree structures for testing and demonstration purposes.

  • Search and Open Tree Nodes:

Functions (openParentNodes, showVisibleItems, showVisibleChildren) to search within treeview nodes and programmatically expand or collapse branches based on search criteria.

  • creates a new treeview configured as a table
    • new Row, Rows, Cell, Cells
    • update Row, Rows, Cell, Cells
    • upsert Row, Rows, Cell, Cells
    • delete Row, Rows, cell, Cells
  • global index for items

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.

treeview-lib.tcl

 treeview-lib.tcl
#! /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
  }
}


Example treeview-lib-example.tcl

 Example
#! /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




Example treeview-table.tcl

 Example
#!/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]"
}

Example treeview-table-tree-sort.tcl

 Example
#!/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]


Links

 Links