ttk.Treeview使用指南
- [1. 基本概念](#1. 基本概念)
- [2. 快速开始](#2. 快速开始)
-
- [2.1 最简单的Treeview](#2.1 最简单的Treeview)
- [2.2 表格模式(带列)](#2.2 表格模式(带列))
- [3. 核心概念](#3. 核心概念)
-
- [3.1 列(Columns)](#3.1 列(Columns))
- [3.2 列标题(Headings)](#3.2 列标题(Headings))
- [3.3 show参数](#3.3 show参数)
- [3.4 插入数据](#3.4 插入数据)
- [4. 完整示例:文件浏览器](#4. 完整示例:文件浏览器)
- [5. 常用操作详解](#5. 常用操作详解)
-
- [5.1 插入、删除、修改](#5.1 插入、删除、修改)
- [5.2 选择操作](#5.2 选择操作)
- [5.3 展开/折叠](#5.3 展开/折叠)
- [5.4 移动和排序](#5.4 移动和排序)
- [6. 事件绑定](#6. 事件绑定)
- [7. 样式定制](#7. 样式定制)
- [8. 带复选框的Treeview](#8. 带复选框的Treeview)
- [9. 常用方法速查表](#9. 常用方法速查表)
- [10. 总结](#10. 总结)
tkinter.ttk.Treeview是tkinter中最强大的数据展示控件,可以同时显示树形结构和表格数据,非常适合做文件管理器、目录树、数据表格等
1. 基本概念
Treeview有两种展示模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 树形模式 | 显示层级关系(如目录结构) | 文件浏览器、组织架构 |
| 表格模式 | 显示二维表格数据 | 数据列表、日志查看 |
| 混合模式 | 树形 + 表格 | 带详细信息的层级目录 |
2. 快速开始
2.1 最简单的Treeview
python
import tkinter
from tkinter import ttk
# 创建主窗口
root = tkinter.Tk()
# 设置标题
root.title('Treeview 基础')
# 设置窗口初始大小和显示位置
root.geometry('800x600+300+200')
# 创建 Treeview
tree = ttk.Treeview(root)
tree.pack(fill='both', expand=True, padx=10, pady=10)
# 添加根节点
tree.insert('', 'end', text='根节点1')
tree.insert('', 'end', text='根节点2')
# 添加子节点
item = tree.insert('', 'end', text='根节点3')
tree.insert(item, 'end', text='子节点3.1')
tree.insert(item, 'end', text='子节点3.2')
# 启动事件循环(主窗口保持响应)
root.mainloop()
运行结果

2.2 表格模式(带列)
python
import tkinter
from tkinter import ttk
# 创建主窗口
root = tkinter.Tk()
# 设置标题
root.title('Treeview 表格模式')
# 设置初始窗口大小和位置
root.geometry('500x300+300+200')
# 创建带列的Treeview
columns = ('name', 'age', 'city')
tree = ttk.Treeview(root, columns=columns, show='headings')
# 设置列标题
tree.heading('name', text='姓名')
tree.heading('age', text='年龄')
tree.heading('city', text='城市')
# 设置列宽度
tree.column('name', width=120)
tree.column('age', width=80, anchor='center')
tree.column('city', width=120)
# 添加数据
tree.insert('', 'end', value=('张三', 20, '上海'))
tree.insert('', 'end', value=('李四', 40, '北京'))
tree.insert('', 'end', value=('王五', 30, '深圳'))
# 布局
tree.pack(fill='both', expand=True, padx=10, pady=10)
# 启动事件循环(主窗口保持响应)
root.mainloop()
运行结果

3. 核心概念
3.1 列(Columns)
- 两种列类型
- #0 列:主列(图标列),用于显示树形结构
- 自定义列:用于显示表格数据
python
# 方式1:创建时指定
tree = ttk.Treeview(root, columns=('col1', 'col2', 'col3'))
# 方式2:创建后指定
tree = ttk.Treeview(root)
tree[columns] = ('col1', 'col2', 'col3')
# 等价于
tree.configure(columns=('col1', 'col2', 'col3'))
# 配置列
tree.column('#0', width=200, minwidth=100, anchor='w')
tree.column('col1', width=100, anchor='center')
tree.column('col2', width=150, stretch=True) # stretch 是否位伸
3.2 列标题(Headings)
python
# 设置主列标题
tree.heading('#0', text='项目名称')
# 设置其他列标题
tree.heading('name', text='姓名')
tree.heading('age', text='年龄')
# 带排序指示器的标题
tree.heading('age', text='年龄', command=lambda: sort_by_column('age'))
3.3 show参数
python
# show 参数控制显示哪些部分
tree = ttk.Treeview(root, show='tree') # 只显示树形线
tree = ttk.Treeview(root, show='headings') # 只显示列标题
tree = ttk.Treeview(root, show='tree headings') # 两者都显示(最常用)
tree = ttk.Treeview(root, show='') # 都不显示
3.4 插入数据
- 函数签名
- insert(parent, index, iid=None, text='', values=(), ...)
- parent: 父节点ID(''表示顶级)
- index: 插入位置('end'表示末尾,0表示开头,数字表示位置)
- iid: 自定义项目ID(不指定则自动生成)
- text: 主列显示的文字
- values: 其他列的值(元组)
python
# 基本插入
tree.insert('', 'end', text='项目1') # 只有主列
tree.insert('', 'end', values=('值1', '值2', '值3')) # 只有数据列
tree.insert('', 'end', text='项目1', values=('值1', '值2')) # 两者都有
4. 完整示例:文件浏览器
python
import tkinter as tk
from tkinter import ttk
import os
class FileBrowser:
def __init__(self):
self.root = tk.Tk()
self.root.title("文件浏览器")
self.root.geometry("800x600")
# 创建主框架
main_frame = ttk.Frame(self.root, padding=10)
main_frame.pack(fill='both', expand=True)
# 路径栏
path_frame = ttk.Frame(main_frame)
path_frame.pack(fill='x', pady=5)
ttk.Label(path_frame, text="路径:").pack(side='left')
self.path_var = tk.StringVar(value=os.getcwd())
self.path_entry = ttk.Entry(path_frame, textvariable=self.path_var)
self.path_entry.pack(side='left', fill='x', expand=True, padx=5)
ttk.Button(path_frame, text="刷新", command=self.refresh).pack(side='left')
# 创建 Treeview(带滚动条)
tree_container = ttk.Frame(main_frame)
tree_container.pack(fill='both', expand=True, pady=10)
# 定义列
self.tree = ttk.Treeview(
tree_container,
columns=("type", "size", "modified"),
show="tree headings"
)
# 设置列标题
self.tree.heading("#0", text="名称", anchor='w')
self.tree.heading("type", text="类型")
self.tree.heading("size", text="大小")
self.tree.heading("modified", text="修改时间")
# 设置列宽
self.tree.column("#0", width=300)
self.tree.column("type", width=100)
self.tree.column("size", width=100, anchor='e')
self.tree.column("modified", width=150)
# 滚动条
v_scroll = ttk.Scrollbar(tree_container, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=v_scroll.set)
# 布局
self.tree.pack(side='left', fill='both', expand=True)
v_scroll.pack(side='right', fill='y')
# 绑定事件
self.tree.bind("<<TreeviewOpen>>", self.on_expand)
self.tree.bind("<Double-1>", self.on_double_click)
# 状态栏
self.status_var = tk.StringVar(value="就绪")
status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief='sunken')
status_bar.pack(fill='x', pady=5)
# 初始加载
self.refresh()
def refresh(self):
"""刷新当前目录"""
path = self.path_var.get()
if not os.path.exists(path):
self.status_var.set(f"路径不存在: {path}")
return
# 清空树
for item in self.tree.get_children():
self.tree.delete(item)
# 加载目录内容
self.load_directory('', path)
self.status_var.set(f"已加载: {path}")
def load_directory(self, parent, path):
"""加载目录内容"""
try:
items = os.listdir(path)
# 分离文件夹和文件
folders = []
files = []
for item in items:
full_path = os.path.join(path, item)
if os.path.isdir(full_path):
folders.append(item)
else:
files.append(item)
# 先添加文件夹
for folder in sorted(folders):
node = self.tree.insert(parent, 'end', text=folder,
values=("文件夹", "", ""))
# 添加占位符(用于显示展开图标)
self.tree.insert(node, 'end', text="加载中...")
# 再添加文件
for file in sorted(files):
full_path = os.path.join(path, file)
size = os.path.getsize(full_path)
size_str = self.format_size(size)
modified = os.path.getmtime(full_path)
modified_str = self.format_time(modified)
self.tree.insert(parent, 'end', text=file,
values=("文件", size_str, modified_str))
except PermissionError:
pass
def on_expand(self, event):
"""展开节点时加载子目录"""
item = self.tree.focus()
if not self.tree.get_children(item):
# 清除占位符
for child in self.tree.get_children(item):
self.tree.delete(child)
# 获取完整路径
path = self.get_full_path(item)
if path:
self.load_directory(item, path)
def on_double_click(self, event):
"""双击进入目录"""
item = self.tree.focus()
if item:
path = self.get_full_path(item)
if path and os.path.isdir(path):
self.path_var.set(path)
self.refresh()
def get_full_path(self, item):
"""获取项目的完整路径"""
# 构建路径
path_parts = []
while item:
text = self.tree.item(item, "text")
path_parts.insert(0, text)
item = self.tree.parent(item)
if path_parts:
return os.path.join(self.path_var.get(), *path_parts)
return None
def format_size(self, size):
"""格式化文件大小"""
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if size < 1024.0:
return f"{size:.1f} {unit}"
size /= 1024.0
return f"{size:.1f} PB"
def format_time(self, timestamp):
"""格式化时间"""
import time
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
def run(self):
self.root.mainloop()
# 运行
# browser = FileBrowser()
# browser.run()
运行结果

5. 常用操作详解
5.1 插入、删除、修改
python
# 插入
item_id = tree.insert('', 'end', text="项目", values=("值1", "值2"))
# 在指定位置插入
tree.insert(parent, 0, text="插到开头")
tree.insert(parent, 'end', text="插到末尾")
# 删除
tree.delete(item_id) # 删除单个
for item in tree.get_children(): # 删除全部
tree.delete(item)
# 修改
tree.item(item_id, text="新文本") # 修改主列
tree.item(item_id, values=("新值1", "新值2")) # 修改数据列
tree.item(item_id, text="新文本", values=("新值1", "新值2"))
# 获取
text = tree.item(item_id, "text") # 获取主列
values = tree.item(item_id, "values") # 获取数据列
5.2 选择操作
python
# selectmode 参数
tree = ttk.Treeview(root, selectmode="browse") # 单选
tree = ttk.Treeview(root, selectmode="extended") # 多选(Ctrl/Shift)
tree = ttk.Treeview(root, selectmode="none") # 禁止选择
# 获取选中项
selected = tree.selection() # 返回元组
item = tree.selection()[0] if tree.selection() else None
# 设置选中
tree.selection_set(item_id) # 选中
tree.selection_set((id1, id2)) # 多选
tree.selection_remove(item_id) # 取消选中
tree.selection_toggle(item_id) # 切换选中
# 焦点
tree.focus(item_id) # 设置焦点
current_focus = tree.focus() # 获取焦点项
5.3 展开/折叠
python
# 展开
tree.item(item_id, open=True)
# 滚动到可见
tree.see(item_id)
# 折叠
tree.item(item_id, open=False)
# 全部展开
def expand_all(tree, parent=''):
children = tree.get_children(parent)
for child in children:
tree.item(child, open=True)
expand_all(tree, child)
# 全部折叠
def collapse_all(tree, parent=''):
children = tree.get_children(parent)
for child in children:
tree.item(child, open=False)
collapse_all(tree, child)
5.4 移动和排序
python
# 移动节点
tree.move(item_id, new_parent, index)
# 示例:移动节点到另一个父节点下
tree.move(item_id, target_parent, 'end')
# 获取节点层级
def get_level(tree, item, level=0):
parent = tree.parent(item)
if parent:
return get_level(tree, parent, level + 1)
return level
6. 事件绑定
python
# 常用事件
tree.bind("<<TreeviewSelect>>", on_select) # 选择改变
tree.bind("<<TreeviewOpen>>", on_expand) # 展开节点
tree.bind("<<TreeviewClose>>", on_collapse) # 折叠节点
tree.bind("<Double-1>", on_double_click) # 双击
tree.bind("<Button-3>", on_right_click) # 右键
tree.bind("<Delete>", on_delete) # 删除键
def on_select(event):
tree = event.widget
selected = tree.selection()
if selected:
print(f"选中: {selected[0]}")
def on_double_click(event):
tree = event.widget
item = tree.focus()
if item:
print(f"双击: {tree.item(item, 'text')}")
def on_right_click(event):
tree = event.widget
item = tree.identify_row(event.y)
if item:
tree.selection_set(item)
# 弹出右键菜单
menu.post(event.x_root, event.y_root)
7. 样式定制
python
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
# 创建样式
style = ttk.Style()
style.theme_use('clam') # 使用可定制主题
# 配置 Treeview 样式
style.configure("Custom.Treeview",
background="#2b2b2b",
foreground="white",
fieldbackground="#2b2b2b",
rowheight=25, # 行高
font=('Arial', 10))
# 配置列标题样式
style.configure("Custom.Treeview.Heading",
background="#404040",
foreground="white",
font=('Arial', 10, 'bold'),
relief='flat')
# 配置选中项样式
style.map("Custom.Treeview",
background=[('selected', '#2E8B57')],
foreground=[('selected', 'white')])
# 应用样式
tree = ttk.Treeview(root, style="Custom.Treeview")
8. 带复选框的Treeview
python
class CheckboxTreeview(ttk.Treeview):
"""带复选框的 Treeview"""
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.bind("<Button-1>", self.on_click)
self.checked_items = set()
def on_click(self, event):
"""点击复选框"""
region = self.identify_region(event.x, event.y)
if region == "tree":
item = self.identify_row(event.y)
if item:
self.toggle_checkbox(item)
def toggle_checkbox(self, item):
"""切换复选框状态"""
if item in self.checked_items:
self.checked_items.remove(item)
self.set_checked_state(item, False)
self.item(item, text=f"☐ {self.item(item, 'text')[2:]}")
else:
self.checked_items.add(item)
self.set_checked_state(item, True)
self.item(item, text=f"☑ {self.item(item, 'text')[2:]}")
def set_checked_state(self, item, state):
"""递归设置子节点状态"""
for child in self.get_children(item):
if state:
self.checked_items.add(child)
self.item(child, text=f"☑ {self.item(child, 'text')[2:]}")
else:
self.checked_items.discard(child)
self.item(child, text=f"☐ {self.item(child, 'text')[2:]}")
self.set_checked_state(child, state)
9. 常用方法速查表
| 方法 | 说明 |
|---|---|
| insert(parent, index, **kwargs) | 插入项目 |
| delete(item) | 删除项目 |
| item(item, **kwargs) | 获取/设置项目属性 |
| get_children(item) | 获取子项目 |
| parent(item) | 获取父项目 |
| selection() | 获取选中项目 |
| focus() | 获取/设置焦点 |
| see(item) | 滚动到可见 |
| move(item, parent, index) | 移动项目 |
| column(col, **kwargs) | 配置列 |
| heading(col, **kwargs) | 配置列标题 |
| yview(*args) | 垂直滚动 |
| xview(*args) | 水平滚动 |
10. 总结
Treeview 是 Tkinter 中最强大的数据展示控件,通过 insert 添加树形结构,通过 columns 添加表格列,配合滚动条和事件绑定,可以实现文件浏览器、数据表格等复杂界面。
| 要点 | 说明 |
|---|---|
| 核心用途 | 树形结构 + 表格数据 |
| 最简模式 | ttk.Treeview(root) |
| 表格模式 | 设置 columns + show="headings" |
| 树形模式 | 不设置 columns,使用 insert 的 parent |
| 混合模式 | columns + show="tree headings" |
| 滚动条 | 需要 command 和 yscrollcommand 双向绑定 |
| 数据量 | 大数据量时性能下降,需分批加载 |