杰瑞科技汇

Python Treeview控件如何创建与使用?

目录

  1. 第一部分:基础入门

    Python Treeview控件如何创建与使用?-图1
    (图片来源网络,侵删)
    • 1 创建一个简单的 Treeview
    • 2 添加列
    • 3 插入数据项
    • 4 运行第一个完整示例
  2. 第二部分:数据操作

    • 1 添加父节点和子节点
    • 2 更新和删除数据
    • 3 获取数据
  3. 第三部分:交互与事件

    • 1 绑定鼠标点击事件
    • 2 处理选择变化事件 (<<TreeviewSelect>>)
    • 3 实现双击展开/折叠功能
  4. 第四部分:高级用法

    • 1 使用 Treeview 作为表格
    • 2 自定义行样式
    • 3 添加滚动条
    • 4 搜索功能
  5. 第五部分:完整实战示例

    Python Treeview控件如何创建与使用?-图2
    (图片来源网络,侵删)

    1 一个简单的文件浏览器


基础入门

1 创建一个简单的 Treeview

你需要导入 tkinterttk 模块。ttk 模块提供了主题化的小部件,Treeview 就在其中。

import tkinter as tk
from tkinter import ttk
# 创建主窗口
root = tk.Tk()"Treeview 基础示例")
root.geometry("300x200")
# 创建 Treeview 组件
# 第一个参数是父窗口
tree = ttk.Treeview(root)
# 将 Treeview 放入主窗口
tree.pack(fill=tk.BOTH, expand=True)
# 启动主事件循环
root.mainloop()

这段代码会创建一个最简单的空树形视图。

2 添加列

默认情况下,Treeview 只有一列(通常称为 "#0"),用于显示树状结构,我们可以添加更多的列来显示其他信息。

Python Treeview控件如何创建与使用?-图3
(图片来源网络,侵删)
# 添加两列,分别命名为 "姓名" 和 "年龄"
tree["columns"] = ("name", "age")
# 设置列的标题
tree.heading("#0", text="ID")
tree.heading("name", text="姓名")
tree.heading("age", text="年龄")
# 设置列的宽度
tree.column("#0", width=100, anchor=tk.CENTER) # anchor=tk.CENTER 让文字居中
tree.column("name", width=100, anchor=tk.CENTER)
tree.column("age", width=100, anchor=tk.CENTER)

3 插入数据项

使用 insert() 方法来添加数据。

  • parent: 父节点的 ID,如果是根节点,则为空字符串 。
  • index: 插入的位置,可以是 'end' (末尾), '0' (开头), 或一个数字。
  • text: "#0" 列显示的文本。
  • values: 一个元组,包含其他列的值。
# 插入根节点
tree.insert("", "end", text="用户组1", values=("Group 1", ""))
# 插入子节点
# parent 参数是父节点的 ID,这里我们使用 text 属性作为简单标识
# 在实际应用中,最好使用返回的 ID
child_id = tree.insert("", "end", text="用户A", values=("Alice", 25))
tree.insert("", "end", text="用户B", values=("Bob", 30))
# 插入另一个根节点
tree.insert("", "end", text="用户组2", values=("Group 2", ""))
tree.insert("", "end", text="用户C", values=("Charlie", 35))

4 运行第一个完整示例

将以上代码片段组合起来,你将看到一个功能完整的树形视图。

import tkinter as tk
from tkinter import ttk
# --- 创建主窗口 ---
root = tk.Tk()"我的第一个 Treeview")
root.geometry("400x300")
# --- 创建 Treeview ---
tree = ttk.Treeview(root, columns=("name", "age"), show="headings") # show="headings" 隐藏默认的 #0 列
# --- 配置列 ---
tree.heading("#0", text="ID")
tree.heading("name", text="姓名")
tree.heading("age", text="年龄")
tree.column("#0", width=100, anchor=tk.W) # anchor=tk.W 让文字左对齐
tree.column("name", width=150, anchor=tk.W)
tree.column("age", width=80, anchor=tk.CENTER)
# --- 插入数据 ---
# 插入一个顶级节点
group1 = tree.insert("", "end", text="组别 A", values=("Group A", ""))
# 在顶级节点下插入子节点
tree.insert(group1, "end", text="成员 1", values=("张三", 28))
tree.insert(group1, "end", text="成员 2", values=("李四", 32))
# 插入另一个顶级节点
group2 = tree.insert("", "end", text="组别 B", values=("Group B", ""))
tree.insert(group2, "end", text="成员 3", values=("王五", 45))
# --- 滚动条 ---超出窗口大小时可以滚动,我们需要添加一个滚动条
scrollbar = ttk.Scrollbar(root, orient="vertical", command=tree.yview)
tree.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side='right', fill='y')
# --- 布局 ---
tree.pack(fill=tk.BOTH, expand=True)
# --- 启动主循环 ---
root.mainloop()

注意: show="headings" 会隐藏默认的 #0 列,只显示我们定义的列,如果不使用这个参数,则会同时显示 #0 列和我们定义的列。


数据操作

1 添加父节点和子节点

如上例所示,通过设置 parent 参数来创建层级关系。parent 参数是父节点的唯一标识符(ID),当你使用 insert() 时,它会返回该节点的 ID,这个 ID 可以用于后续操作。

# 获取父节点的 ID
parent_id = tree.insert("", "end", text="父节点", values=("Parent", "N/A"))
# 使用父节点的 ID 添加子节点
child_id = tree.insert(parent_id, "end", text="子节点", values=("Child", "N/A"))
# 可以继续添加孙子节点
grandchild_id = tree.insert(child_id, "end", text="孙子节点", values=("Grandchild", "N/A"))

2 更新和删除数据

  • 更新: item(item_id, **options)
    • text: 更新 "#0" 列的文本。
    • values: 更新其他列的值。
# 假设 child_id 是我们之前获取的子节点ID
tree.item(child_id, text="更新后的子节点", values=("Updated Child", "New Value"))
  • 删除: delete(item_id)

    删除指定节点及其所有子节点。

# 删除 "子节点" 及其所有后代
tree.delete(child_id)
  • 清空整个树: delete(*tree.get_children())
    • get_children() 获取所有顶级节点的 ID。
tree.delete(*tree.get_children())

3 获取数据

  • 获取选中项的 ID: selection()

    返回一个包含所有选中项 ID 的元组,如果没有选中项,返回空元组。

selected_ids = tree.selection()
if selected_ids:
    print("选中的ID:", selected_ids)
    # 通常我们只关心第一个选中的项
    selected_id = selected_ids[0]
    print("第一个选中项的ID:", selected_id)
  • 获取节点的详细信息: item(item_id)
    • 返回一个包含节点信息的字典,包括 text, image, values 等。
if selected_id:
    node_info = tree.item(selected_id)
    print("节点信息:", node_info)
    # 获取 values
    values = node_info['values']
    print("节点的值:", values)
  • 获取节点的文本: item(item_id)['text']

交互与事件

Treeview 支持多种事件,让程序能够响应用户的操作。

1 绑定鼠标点击事件

<<TreeviewSelect>> 事件在用户选择或取消选择一个或多个节点时触发。

def on_select(event):
    """处理节点选择事件"""
    selected_items = tree.selection()
    print(f"当前选中的节点ID: {selected_items}")
    for item_id in selected_items:
        item_data = tree.item(item_id)
        print(f"  - ID: {item_id}, Text: {item_data['text']}, Values: {item_data['values']}")
# 绑定选择事件到 on_select 函数
tree.bind("<<TreeviewSelect>>", on_select)

2 处理选择变化事件

上面的 <<TreeviewSelect>> 已经是最常用的选择事件,这里再展示一个双击事件的例子。

3 实现双击展开/折叠功能

<<TreeviewOpen>><<TreeviewClose>> 事件在节点被展开或折叠时触发,我们可以通过绑定双击事件来实现手动切换。

def on_double_click(event):
    """处理双击事件,切换节点的展开/折叠状态"""
    # 获取点击位置的项
    item_id = tree.identify('item', event.x, event.y)
    if item_id:
        # 如果节点有子节点,则切换其展开状态
        if tree.item(item_id)['children']:
            tree.item(item_id, open=not tree.item(item_id)['open'])
# 绑定双击事件
tree.bind("<Double-1>", on_double_click) # <Double-1> 是鼠标左键双击

高级用法

1 使用 Treeview 作为表格

如果你不需要树状结构,只想用它来显示表格数据,可以这样做:

  1. 隐藏树列: 使用 show="tree"show="headings"
  2. 不设置 text: 在 insert 时,不提供 text 参数。
# 创建一个表格视图
table_tree = ttk.Treeview(root, columns=("Name", "Score", "Grade"), show="headings")
table_tree.heading("Name", text="姓名")
table_tree.heading("Score", text="分数")
table_tree.heading("Grade", text="等级")
# 插入数据(不提供 text)
table_tree.insert("", "end", values=("赵六", 95, "A"))
table_tree.insert("", "end", values=("钱七", 88, "B"))
table_tree.insert("", "end", values=("孙八", 76, "C"))
table_tree.pack(fill=tk.BOTH, expand=True)

2 自定义行样式

你可以为不同的行设置不同的背景色,以增强可读性。

  1. 定义样式: 使用 ttk.Style()
  2. 应用样式: 在 insert 时,使用 tags 参数。
# 创建样式对象
style = ttk.Style(root)
# 定义一个名为 'odd_row' 的样式
style.configure("odd_row", background="#E8E8E8")
# 定义一个名为 'even_row' 的样式
style.configure("even_row", background="#F0F0F0")
# 插入数据并应用标签
for i in range(10):
    # 使用模运算判断奇偶行
    tag = "odd_row" if i % 2 == 0 else "even_row"
    tree.insert("", "end", text=f"项目 {i}", values=(f"Data {i}", i), tags=(tag,))
# 配置 Treeview 以使用这些标签
# 这一步很重要,告诉 Treeview 如何处理这些标签
tree.tag_configure("odd_row", background="#E8E8E8")
tree.tag_configure("even_row", background="#F0F0F0")

3 添加滚动条

这是一个非常常见的需求。Treeview 本身不包含滚动条,需要手动创建并关联。

# 创建 Treeview 和 Scrollbar
tree = ttk.Treeview(root)
scrollbar = ttk.Scrollbar(root, orient="vertical", command=tree.yview)
# 将滚动条的命令绑定到 Treeview 的 yview
tree.configure(yscrollcommand=scrollbar.set)
# 使用 Frame 来布局,或者 pack/grid 两者
tree.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")

4 搜索功能

实现一个搜索框,用于高亮或定位包含特定文本的节点。

def search_item():
    search_term = entry.get()
    # 先清除所有高亮
    tree.tag_remove("highlight", "0", "end")
    if not search_term:
        return
    # 遍历所有节点
    for item_id in tree.get_children():
        # 检查节点的 text 和 values 是否包含搜索词
        item_text = tree.item(item_id)['text'].lower()
        item_values = ' '.join(tree.item(item_id)['values']).lower()
        if search_term.lower() in item_text or search_term.lower() in item_values:
            # 如果找到,则高亮该节点
            tree.selection_set(item_id) # 选中该节点
            tree.see(item_id) # 确保该节点可见
            tree.tag_add("highlight", item_id) # 添加高亮标签
            break # 找到第一个后停止
# 定义高亮样式
style = ttk.Style(root)
style.configure("highlight", background="yellow")
# 创建搜索框和按钮
entry = ttk.Entry(root)
entry.pack(side="top", fill="x", padx=5, pady=5)
search_button = ttk.Button(root, text="搜索", command=search_item)
search_button.pack(side="top", padx=5)
# 创建 Treeview
tree = ttk.Treeview(root, columns=("value1",), show="tree headings")
tree.heading("#0", text="名称")
tree.heading("value1", text="值")
tree.pack(fill="both", expand=True)
# 插入一些测试数据
tree.insert("", "end", text="文件夹 A", values=("内容 A1",))
tree.insert("文件夹 A", "end", text="文件 A-1.txt", values=("数据 A-1",))
tree.insert("文件夹 A", "end", text="文件 A-2.txt", values=("数据 A-2",))
tree.insert("", "end", text="文件夹 B", values=("内容 B1",))
tree.insert("文件夹 B", "end", text="文件 B-1.txt", values=("数据 B-1",))

完整实战示例:一个简单的文件浏览器

这个示例将结合前面学到的知识,创建一个可以浏览本地文件系统目录的简单应用。

import tkinter as tk
from tkinter import ttk
import os
class FileBrowser(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Python 文件浏览器")
        self.geometry("600x400")
        # --- 布局 ---
        self.tree_frame = ttk.Frame(self)
        self.tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        # --- 创建 Treeview 和滚动条 ---
        self.tree = ttk.Treeview(self.tree_frame, columns=("size",), show="tree headings")
        self.tree.heading("#0", text="文件/文件夹")
        self.tree.heading("size", text="大小 (字节)")
        self.tree.column("#0", width=400)
        self.tree.column("size", width=150, anchor=tk.E)
        scrollbar = ttk.Scrollbar(self.tree_frame, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)
        self.tree.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        # --- 绑定事件 ---
        self.tree.bind("<<TreeviewSelect>>", self.on_item_select)
        self.tree.bind("<Double-1>", self.on_double_click)
        # --- 初始加载 ---
        self.load_directory(os.path.expanduser("~")) # 加载用户主目录
    def load_directory(self, path):
        """加载指定路径下的内容到 Treeview"""
        # 清空当前 Treeview
        self.tree.delete(*self.tree.get_children())
        try:
            # 获取目录内容
            items = os.listdir(path)
            for item in items:
                full_path = os.path.join(path, item)
                # 获取文件大小
                size = os.path.getsize(full_path) if os.path.isfile(full_path) else ""
                # 插入节点
                node_id = self.tree.insert("", "end", text=item, values=(size,))
                # 如果是目录,递归加载其内容
                if os.path.isdir(full_path):
                    self.load_directory_recursive(full_path, node_id)
        except PermissionError:
            self.tree.insert("", "end", text="[无权限访问]", values=("",))
    def load_directory_recursive(self, path, parent_id):
        """递归加载目录内容"""
        try:
            items = os.listdir(path)
            for item in items:
                full_path = os.path.join(path, item)
                size = os.path.getsize(full_path) if os.path.isfile(full_path) else ""
                node_id = self.tree.insert(parent_id, "end", text=item, values=(size,))
                if os.path.isdir(full_path):
                    self.load_directory_recursive(full_path, node_id)
        except PermissionError:
            self.tree.insert(parent_id, "end", text="[无权限访问]", values=(""))
    def on_item_select(self, event):
        """处理选择事件,在状态栏显示路径"""
        selected_id = self.tree.selection()
        if selected_id:
            item_text = self.tree.item(selected_id[0])['text']
            print(f"已选择: {item_text}")
    def on_double_click(self, event):
        """处理双击事件,如果是目录则展开/折叠,如果是文件则可以打开(这里只打印)"""
        item_id = self.tree.identify('item', event.x, event.y)
        if not item_id:
            return
        item_text = self.tree.item(item_id)['text']
        # 注意:这里我们没有存储完整的路径,这是一个简化的例子
        # 一个更健壮的实现会存储完整路径在 item 的字典中
        print(f"双击了: {item_text}")
        # 如果是目录,切换展开状态
        if self.tree.item(item_id)['children']:
            self.tree.item(item_id, open=not self.tree.item(item_id)['open'])
if __name__ == "__main__":
    app = FileBrowser()
    app.mainloop()

tkinter.ttk.Treeview 是一个非常灵活和强大的组件,通过掌握其核心方法(insert, item, delete)和事件(<<TreeviewSelect>>),你可以创建出各种复杂的树形和表格界面,希望这份详细的教程能帮助你快速上手并应用到自己的项目中。

分享:
扫描分享到社交APP
上一篇
下一篇