A Tkinter Treeview can store items in a hierarchy. A top-level item can contain child items, and those children can contain further descendants.
The relationship is created through the first argument of insert(). An empty string creates a top-level item. Passing another item's iid creates a child under that item.
iid identifies an item. The parent argument tells Treeview where that item belongs in the hierarchy.
The main syntax is:
tree.insert(parent, index, iid=None, text='', values=(), open=False)
For a top-level item:
parent_id=tree.insert('', tk.END, text='Languages')
For a child:
tree.insert(parent_id, tk.END, text='Python')
The important difference is the first argument.
| parent | Result |
|---|---|
'' | Creates a top-level item. |
An existing iid | Creates a child of that item. |
Every Treeview item has an identifier called an iid.
If you do not provide one, insert() creates an identifier and returns it.
parent_id=tree.insert('', tk.END, text='Languages')
print(parent_id)
You can also assign one yourself:
tree.insert('', tk.END, iid='languages', text='Languages')
and use the same ID when inserting children:
tree.insert('languages', tk.END, iid='python', text='Python')
iid raises a Tkinter error.tree.insert(
'',
tk.END,
iid='student_101',
text='Student',
values=(101, 'Alex')
)
Here:
student_101 | Internal Treeview item ID. |
Student | Text displayed in tree column #0. |
101, Alex | Values stored in the additional data columns. |
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.geometry('420x300')
root.title('Treeview Parent Child - plus2net')
tree=ttk.Treeview(root, columns=('id', 'name'), show='tree headings')
tree.grid(row=0, column=0, padx=20, pady=20)
tree.heading('#0', text='Type')
tree.heading('id', text='ID')
tree.heading('name', text='Name')
tree.column('#0', width=120)
tree.column('id', width=60, anchor='center')
tree.column('name', width=140)
tree.insert('', tk.END, iid='p1', text='Parent', values=(1, 'Alex'), open=True)
tree.insert('p1', tk.END, iid='c1', text='Child', values=(2, 'Child Alex'))
root.mainloop()
The relationship is created here:
tree.insert('p1', tk.END, ...)
because p1 is the parent item's iid.
The open option controls whether an item's children are expanded or collapsed.
tree.insert('', tk.END, iid='p1', text='Parent', open=True)
The children of p1 are displayed.
tree.insert('', tk.END, iid='p1', text='Parent', open=False)
The children still exist, but they remain hidden until the parent is expanded.
open=True does not make the item itself visible. It controls whether that item's children are displayed.tree.insert('', tk.END, iid='A', text='Level 1', open=True)
tree.insert('A', tk.END, iid='B', text='Level 2', open=False)
tree.insert('B', tk.END, iid='C', text='Level 3')
Level 2 is visible because Level 1 is open. Level 3 is hidden because Level 2 is closed.
| show | Display |
|---|---|
'tree' | Shows tree column #0 and hierarchy, without headings. |
'headings' | Shows headings and additional data columns, but hides #0. |
'tree headings' | Shows both tree hierarchy and headings. |
'' | Hides both tree column and headings. |
For a user-expandable parent-child interface, use:
tree['show']='tree headings'
or:
tree['show']='tree'
show='headings' hides the special #0 tree column and therefore hides the normal tree expand/collapse indicators.
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.geometry('430x300')
root.title('Treeview Hierarchy - plus2net')
style=ttk.Style()
style.configure('Treeview', rowheight=24)
tree=ttk.Treeview(root, columns=('id', 'name'), show='tree headings', selectmode='browse')
tree.grid(row=0, column=0, padx=20, pady=20)
tree.column('#0', width=120)
tree.column('id', width=60, anchor='center')
tree.column('name', width=140)
tree.heading('#0', text='Node')
tree.heading('id', text='ID')
tree.heading('name', text='Name')
tree.insert('', tk.END, iid='a', text='Root A', values=('a', 'Alex'))
tree.insert('', tk.END, iid='1', text='Parent 1', values=(1, 'Alex'), open=True)
tree.insert('1', tk.END, iid='1c', text='Child 1', values=('1c', 'Child Alex'))
tree.insert('', tk.END, iid='2', text='Parent 2', values=(2, 'Ron'), open=True)
tree.insert('2', tk.END, iid='2c', text='Child 2', values=('2c', 'Child Ron'), open=True)
tree.insert('2c', tk.END, iid='2cc', text='Grandchild', values=('2cc', 'Grandchild Ron'))
root.mainloop()
Use parent().
parent_id=tree.parent('2cc')
print(parent_id)
Sample Output
2c
A top-level item has an empty parent:
print(tree.parent('2'))
Sample Output
''
if tree.parent(item_id)=='':
print('Top-level item')
Use:
children=tree.get_children('2')
print(children)
To get top-level items:
top_level=tree.get_children()
or explicitly:
top_level=tree.get_children('')
get_children() without an item returns only the top-level items. It does not automatically return every nested descendant.names=[]
for item_id in tree.get_children():
values=tree.item(item_id, 'values')
names.append(values[1])
print(names)
Use a recursive function when the complete hierarchy is required.
def read_nodes(parent='', level=0):
for item_id in tree.get_children(parent):
text=tree.item(item_id, 'text')
values=tree.item(item_id, 'values')
print(' '*level, item_id, text, values)
read_nodes(item_id, level+1)
read_nodes()
The function:
This works for trees containing any practical number of nested levels.
tree.item('2', open=True)
tree.item('2', open=False)
is_open=tree.item('2', 'open')
print(is_open)
def toggle_node(item_id):
current=tree.item(item_id, 'open')
tree.item(item_id, open=not current)
tree.item('2', open=True)
tree.selection_set('2c')
tree.focus('2c')
tree.see('2c')
see() is especially useful because it makes an item visible and opens ancestors when required.
Use move() to change an item's parent or position.
tree.move('1c', '2', tk.END)
The item 1c now becomes a child of 2.
tree.move('1c', '', tk.END)
The empty parent moves it back to the root level.

The user can select an existing Treeview row and use its iid as the parent of a new item. Leaving the parent field blank creates a top-level item.
import tkinter as tk
from tkinter import ttk
def selection_changed(event):
selected=tree.selection()
parent_var.set(selected[0] if selected else '')
def insert_item():
parent_id=parent_var.get().strip()
item_id=iid_var.get().strip()
node_text=text_var.get().strip()
value=value_var.get().strip()
if parent_id and not tree.exists(parent_id):
status_var.set('Parent iid does not exist')
return
if item_id and tree.exists(item_id):
status_var.set('iid already exists')
return
if not node_text:
status_var.set('Enter node text')
return
if item_id:
new_id=tree.insert(parent_id, tk.END, iid=item_id, text=node_text, values=(value,))
else:
new_id=tree.insert(parent_id, tk.END, text=node_text, values=(value,))
if parent_id:
tree.item(parent_id, open=True)
tree.selection_set(new_id)
tree.focus(new_id)
tree.see(new_id)
iid_var.set('')
text_var.set('')
value_var.set('')
status_var.set('Item inserted')
root=tk.Tk()
root.geometry('620x320')
root.title('Insert Parent Child Treeview - plus2net')
style=ttk.Style()
style.configure('Treeview', rowheight=24)
tree=ttk.Treeview(root, columns=('name',), show='tree headings', selectmode='browse')
tree.grid(row=0, column=0, rowspan=6, padx=10, pady=20)
tree.heading('#0', text='Node')
tree.heading('name', text='Name')
tree.column('#0', width=140)
tree.column('name', width=150)
tree.insert('', tk.END, iid='p1', text='Parent 1', values=('Alex',), open=True)
tree.insert('p1', tk.END, iid='c1', text='Child 1', values=('Child Alex',))
parent_var=tk.StringVar()
iid_var=tk.StringVar()
text_var=tk.StringVar()
value_var=tk.StringVar()
status_var=tk.StringVar()
tk.Label(root, text='Parent iid').grid(row=0, column=1, sticky='e')
tk.Entry(root, textvariable=parent_var).grid(row=0, column=2)
tk.Label(root, text='iid').grid(row=1, column=1, sticky='e')
tk.Entry(root, textvariable=iid_var).grid(row=1, column=2)
tk.Label(root, text='Tree text').grid(row=2, column=1, sticky='e')
tk.Entry(root, textvariable=text_var).grid(row=2, column=2)
tk.Label(root, text='Value').grid(row=3, column=1, sticky='e')
tk.Entry(root, textvariable=value_var).grid(row=3, column=2)
tk.Button(root, text='Insert', command=insert_item).grid(row=4, column=2, pady=10)
tk.Label(root, textvariable=status_var).grid(row=5, column=1, columnspan=2)
tree.bind('<<TreeviewSelect>>', selection_changed)
root.mainloop()
If the parent field is blank:
parent_id=''
the new item becomes a root item.
If it contains:
p1
the new item becomes a child of p1.
This check prevents an invalid parent:
if parent_id and not tree.exists(parent_id):
return
This prevents duplicate item IDs:
if item_id and tree.exists(item_id):
return
open controls whether an item's children are expanded. The item itself depends on its parent's state.
This is invalid if parent_99 does not exist:
tree.insert('parent_99', tk.END, text='Child')
Check first:
if tree.exists(parent_id):
tree.insert(parent_id, tk.END, text='Child')
Every Treeview item identifier must be unique.
The identifier is used by Treeview internally, while text is displayed in column #0.
This hides the tree column and its normal expand/collapse indicators. Use tree or tree headings when users need to navigate the hierarchy.
Without a parent it returns only root items. Traverse recursively when all descendants are needed.
A selection may be empty.
selected=tree.selection()
if selected:
parent_id=selected[0]
This is a string:
values=('Alex')
This is a one-item tuple:
values=('Alex',)
Avoid:
style.configure(tree, rowheight=20)
Use:
style.configure('Treeview', rowheight=20)
'' creates a top-level item.iid used as the parent creates a child.iid uniquely identifies each Treeview item.iid is supplied, insert() creates and returns one.iid, text and values represent different parts of an item.#0.open=True expands an item's children.open=False collapses an item's children.item(iid, open=True) expands an existing item.parent(iid) returns an item's parent.get_children(iid) returns direct children.get_children() returns top-level items only.exists(iid) checks whether an item ID is valid.move() can move an existing item to another parent.see() helps bring nested items into view.tree or tree headings when the hierarchy should be visible to users.Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.