ttk.Treeview使用指南

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 双向绑定
数据量 大数据量时性能下降,需分批加载
相关推荐
工业互联网专业15 小时前
国潮男装微博评论数据分析系统的设计与实现 _flask+spider
python·flask·毕业设计·源码·课程设计·spider
BUG研究员_15 小时前
python之数据分析-pandas部分
python·信息可视化·数据分析
覆东流15 小时前
Python变量与数值类型
开发语言·后端·python
Cthy_hy16 小时前
Python 算法竞赛:快速IO+字符串常用方法一站式整理
开发语言·python·算法
技术钱16 小时前
字符分割器组件的使用
android·python
码界筑梦坊16 小时前
146-基于Python的智能手表数据可视化分析系统
python·信息可视化·智能手表
Wang ruoxi16 小时前
Pygame 小游戏——打砖块
开发语言·python·pygame
薛定谔的猫喵喵16 小时前
【从 HTTP 到 HTTPS】Flask 多项目迁移到 Nginx 子路径完整实战
python·nginx·http·https·flask·ssl
lunzi_082616 小时前
【学习笔记】《Python编程 从入门到实践》第1章:Python环境搭建与Hello World(完整版)
笔记·python·学习