ttk::treeview

A Ttk widget that provides both tree-browsing capabilities and multicolumn listbox capabilities.

https://www.tcl-lang.org/man/tcl/TkCmd/ttk_treeview.htm

Related widgets can be found on the wiki page treeview.

Some configuration options can be found on ttk::style examples


The screenshot below shows the treeview as part of a larger program (using the clam theme). There is a header and 6 entries (where two are one level down).

https://web.archive.org/web/20070208094122/tcl.typoscriptics.de/misc/tile-treeview.png


The basics of this widget are:

 package require tile
 tile::setTheme clam
 # create it:
 ttk::treeview .tree
 pack .tree -expand 1 -fill both
 # insert an entry at the root level:
 set entry1 [.tree insert {} end -text "first item"]
 # and a second one:
 set entry2 [.tree insert {} end -text "second item"]
 # insert a new level with entry1 as parent:
 .tree insert $entry1 end -text "a sublevel"
 # insert another item under this one:
 .tree insert $entry1 end -text "another item"
 # suppose, one item is selected.
 # we can then delete it thus:
 .tree delete [.tree selection]

BAS

 # Also, to add the heading:
 .tree heading #0 -text Features
 # I also like the heading a little closer to the frame
 .tree configure -padding {0 0 0 0}

SLB For an example of how to implement sorting in treeview see Tile Table


If you want to programmatically select a specific item in the treeview, it is best to give the items an -id when they get inserted into the treewidget using the insert subcommand. This option must be given after the parent and index arguments and before adding other options. Then, use this id to select an item:

.tree insert {} end -id myitem2 -text "my text here"
.tree selection set myitem2 

Note: you cannot use the -tags option for an item to identify it with the `selection` subcommand.


MHo 2008-04-09: Does someone know why a horizontal scrollbar does not work properly in conjunction with this widget? It seems that the hsb never gets updated.
D. McC 2008-04-12: Have you specified minimum column widths with the -minwidth option? I used to have this problem too, and it went away when I specified minimum widths.
MHo: No, haven't tried yet. Switched the whole GUI of my app to BWidgets because I had other problems with tile, too. MHo 09-nov-2008: With -stretch 1, the scrollbar is missing, with -stretch 0, it's there!
Luis Calvo 2008-11-28: Hi! I have detected the same "bug" even with -minwidth option at columns. You can find the bug in the 8.5.5 tcl demo: If you take the tcl multi-column list of countries sample and you increase a width column, the x-scroll is working but you can not select or resize the new area. Thanks anyway!


JH I found the old-school border style of treeview on xpnative to be unsatisfactory. Pat pointed out this helpful alternative layout:

    ttk::style layout Treeview {
      Entry.field -sticky news -border 2 -children {
        Treeview.padding -sticky news -children {
            Treeview.treearea -sticky news
        }
      }
    }

You can remove the Entry.field outer part to have purely no border as well. This is good when wrapping it in something like widget::scrolledwindow.


ofv 2009-05-30: I've looked at ttk::treeview source and it seems that one key requirement was completely overlooked: when a node is expanded, the column holding the tree may need to expand for accommodating the new visible items, reflecting the width change on the associated horizontal scrollbar (if any). Right now the code handles the tree as a fixed-width column, much on the way a listbox does. This problem appears on different instances, even if you get the horizontal scrollbar to work with the -minwidth trick. It is not possible to select a node by clicking on it when it is too nested so that it is drawn to the right of the initial visible area. When you have several columns, the tree items are partially drawn over the adyacent columns if there is no space on the tree's column, etc. See the bugs on the SF project page for details. I don't recommend using ttk::treeview unless you know in advance that the tree items will not require more horizontal space than the assigned column width.


snehanish - 2010-05-27 04:25:40

in tree mode ( -show tree) is it not possible to put index number in integer foam.

i mean first column in tree mode always give row numbers. whenever we delete an item the row number should be updated.

for example lets have height of treeview is 10, so we have 10 rows , column 0 indicate 1 to 10 in integer. if i delete item 5 then it should update all index numbers.

if you could add this feature then it would be great.

Thanks

hae 2010-05-27

If you want this feature, then put it into the Tcl feature request tracker


makr 2011-05-17: Rich gave a small example in his clt post "Is this a bug or feature of ttk::treeview?" <https://groups.google.com/groups/search?as_umsgid=9414edd2-90a3-4ccb-a974-47f512f9577b%40l6g2000vbn.googlegroups.com%|%[email protected]%|% >:

ttk::treeview .tree
pack .tree -side top

.tree insert {} end -id "Item 1" -text "Item 1"
.tree insert {} end -id "Item 2" -text "Item 2"
.tree insert {} end -id "Item 3" -text "Item 3"

.tree insert "Item 1" end -id "Item 1-1" -text "Item 1-1"
.tree insert "Item 1" end -id "Item 1-2" -text "Item 1-2"
.tree insert "Item 1" end -id "Item 1-3" -text "Item 1-3"

.tree insert "Item 2" end -id "Item 2-1" -text "Item 2-1"
.tree insert "Item 2" end -id "Item 2-2" -text "Item 2-2"
.tree insert "Item 2" end -id "Item 2-3" -text "Item 2-3"

Ensure "Item 2" is closed, i.e. the sub-items are not displayed. With this example you can then observe an interesting selection behavior:

  • Click "Item 2", then Shift-Click "Item 3". Will lead to the following result
% .tree selection
{Item 2} {Item 2-1} {Item 2-2} {Item 2-3} {Item 3}
  • Click "Item 2", then Ctrl-Click "Item 3". Will lead to the following result
% .tree selection
{Item 2} {Item 3}

MG 2013-01-31 I wrote some code to allow a user to type in a treeview to select the next item which starts with the given prefix. Pressing any non-printing character (or space, as there's an existing binding for that) cancels, and it resets after a short delay (1.3 seconds, by default).

If a row has a -text value set, it uses that. Otherwise, it uses the first non-empty item in its -values. Lightly tested in Tk 8.5 and 8.6.

bind Treeview <KeyPress> [list ::tv::treeviewKeyPress %W %A %K]
bind Treeview <KeyRelease> [list ::tv::treeviewKeyPressReset %A]
bind Treeview <FocusIn> [list ::tv::treeviewKeyPressReset ""]
bind Treeview <FocusOut> [list ::tv::treeviewKeyPressReset ""]

::tv::treeviewKeyPressReset

#: proc ::tv::treeviewKeyPress
#: arg tree Treeview widget
#: arg char The character typed; may be empty
#: arg keysym The keysym for the key pressed
#: desc Handle a keypress in a Treeview to allow typing to select an entry
#: return nothing
proc ::tv::treeviewKeyPress {tree char keysym} {
  variable tvkp;

  if { $char in [list "" " " "\t" "\n"] || $keysym eq "space"} {
       treeviewKeyPressReset;
       return;
     }

  catch {after cancel $tvkp(afterid)}

  if { $tvkp(error) } {
       bell -displayof $tree
     } elseif { $tvkp(reset) } {
       # set everything up
       set tvkp(reset) 0
       set sel [$tree selection]
       if { ![llength $sel] } {
            set tvkp(startid) ""
          } else {
            set tvkp(startid) [lindex $sel 0]
          }
       set tvkp(str) $char
       set tvkp(ids) [treeviewRecursiveListIDs $tree ""]
       if { ![llength $tvkp(ids)] } {
            set tvkp(error) 1
            bell -displayof $tree
          }
       set inc 0
     } else {
       append tvkp(str) $char
       set inc 1
     }

  $tree selection set [list]
  $tree focus {}
  if { !$tvkp(error) } {
       set len [string length $tvkp(str)]
       if { $tvkp(startid) eq "" } {
            set index 0
          } else {
            set index [lsearch -exact $tvkp(ids) $tvkp(startid)]
            if { !$inc } {
                 incr index
                 if { $index == [llength $tvkp(ids)] } {
                      set index 0
                    }
               }
          }
       set ids [concat [lrange $tvkp(ids) $index end] [lrange $tvkp(ids) 0 $index-1]]
       set match ""
       foreach x $ids {
         if { [set text [$tree item $x -text]] eq "" } {
              set text [lsearch -inline -glob [$tree item $x -values] "?*"]
            }
         if { $text eq "" } {
              continue;
            }
         if { [string equal -nocase -length $len $tvkp(str) $text] } {
              set match $x
              break;
            }
       }
       if { $match ne "" } {
            $tree sel set [list $x]
            $tree focus $x
            $tree see $x
            set tvkp(startid) $x
          } else {
            set tvkp(error) 1
            bell -displayof $tree
          }
     }

  set tvkp(afterid) [after $tvkp(aftertime) [list ::tv::treeviewKeyPressReset]]

  return;


};# ::tv::treeviewKeyPress

#: proc ::tv::treeviewKeyPressReset
#: arg char Character generated if this was triggered by a key release; only reset for non-printable keys ($char eq "")
#: desc Reset the $tvkp array used to hold state data for treeview keypresses
#: return nothing
proc ::tv::treeviewKeyPressReset {{char ""}} {
  variable tvkp;

  if { $char ne "" } {
       return;
     }

  set tvkp(str) ""
  set tvkp(startid) ""
  set tvkp(ids) [list]
  set tvkp(reset) 1
  set tvkp(error) 0
  if { [info exists tvkp(afterid)] } {
       catch {after cancel $tvkp(afterid)}
     }
  set tvkp(afterid) ""
  set tvkp(aftertime) 1300

  return;

};# ::tv::treeviewKeyPressReset

#: proc ::tv::treeviewRecursiveListIDs
#: arg tree Tree widget
#: arg id parent id
#: desc Return a list of $id and all its children in the tree widget $tree. Used
#: desc recursively for building a list of all IDs in order
#: return list of ids
proc ::tv::treeviewRecursiveListIDs {tree id} {

  if { ![winfo exists $tree] || ![$tree exists $id] } {
       return;
     }

  set res [list]
  if { $id ne "" } {
       lappend res $id
     }
  foreach x [$tree children $id] {
    lappend res {*}[treeviewRecursiveListIDs $tree $x]
  }

  return $res;

};# ::tv::treeviewRecursiveListIDs

Sarnold uses a BWidget Tree in which some data is associated with each entry. ttk::treeview does not seem - to my great surprise - to handle other data than the text. Is it true ? Is adding such a feature planned ?


nscerqueira 2013-10-3 How to delete all the items from a ttk::tree widget

    .tree delete [.tree children {}]

Big fonts overlap

HaO 2016-05-12

  • Ref: CLT thread "ttk::treeview lines overlap with big fonts" 2016-11-05
  • Tk Ticket: 2439015

Items with big fonts overlap:

image create photo Img -height 5 -width 5
Img put red -to 0 0 4 4
pack [ttk::treeview .t] -fill both -expand true
.t tag configure t -font {size 20} -image Img
set p [.t insert {} end -text A -tag t -open 1]
.t insert $p end -text B -tag t -open 1

Rich wrote:

The rowheight on ttk::treeview does not auto-adjust for the font height of that which it contains. You have to adjust it, using an undocumented method.

See here: [L1 ]. Look at the -rowheight option.

bll 2016-5-12: This:

-rowheight [expr {round($fontsize*2/[tk scaling])}]

seems to work right (assuming fontsize is in points).

HaO 2017-11-06: Here is the full example again:

image create photo Img -height 5 -width 5
Img put red -to 0 0 4 4
font create LabelFont -size 20
label .test -text M -font LabelFont
ttk::style configure Treeview -rowheight [winfo reqheight .test] -font LabelFont
destroy .test
pack [ttk::treeview .t] -fill both -expand true
set p [.t insert {} end -text Text -image Img]
.t insert $p end -text B -open 1

MHo 2016-06-29: with -selectmode none, cursor up/down beyond the visible items does not scroll the visible items...


No focus indicators

AndyM 2019-08-02: When tabbing into a treeview I would have expected to see some focus indicator on the item in the treeview window, for example a dotted border around the item with the focus. There is an example above of pro-grammatically adding a border to an entry, but I have been unable to make this work.

Use of treeview as a table

APE 2020-02-15 : a simple example of treeview use to display a table. It needs to configure the style, but I did not find working examples for treeview.

# use of ttk::treeview to build a table

# proc newTable
# creates a new treeview configured as a table
proc newTable {w colnames} {
    # create the tree showing headings only, and define column names
    set t [ttk::treeview $w.tree -show headings -columns $colnames]

    # set the text display for columns headers
    foreach colname $colnames {
        $t heading $colname -text $colname
    }
    pack $t -expand 1 -fill both
    return $t
}

# proc newRow
# creates a new row in the treeview and optionally set values
proc newRow {t {values ""}} {
    
    # insert a new item
    set item [$t insert {} end]

    # fill each column with valu
    foreach col [$t cget -columns] val $values {
        $t set $item $col $val
    }
}

# example
# -- create a new table
set table [newTable . [list col1 col2 col3 col4]]
# -- set values for all columns
newRow $table [list 1 2 3 4]
# -- add an empty row
newRow $table 
# -- add a row with 2 values (fills 2 first columns)
newRow $table [list "value one" "value two"]