如何在GTK4(≥4.10)中构建类TreeView的树形组件?
用GTK4的ColumnView + TreeExpander实现树形结构(替代废弃的TreeView)
GTK4.10+废弃TreeView后,官方推荐用ColumnView + TreeExpander + TreeListModel的组合实现树形结构,核心逻辑是用TreeListModel把树形数据源转换成ColumnView能识别的扁平列表,再通过TreeExpander处理节点的展开/折叠交互。
核心组件作用
- TreeListModel:将嵌套的树形数据包装成ColumnView可直接使用的扁平
Gio.ListModel,自动管理节点的展开/折叠状态。 - TreeExpander:作为ColumnView的单元格组件,负责显示展开/折叠箭头,并关联TreeListModel的节点数据。
- ColumnView:负责列布局,将TreeExpander放在首列实现树形缩进,其他列展示业务数据。
完整示例(PyGObject)
下面是一个可运行的Python示例,实现多级树形结构:
import gi gi.require_version('Gtk', '4.0') from gi.repository import Gtk, Gio # 自定义树形数据类(PyGObject中可直接用普通类,C语言需实现GObject子类) class TreeNode: def __init__(self, name, children=None): self.name = name self.children = children or [] # 生成子节点ListModel的回调函数 def create_child_model(item): child_store = Gio.ListStore.new(TreeNode) for child in item.children: child_store.append(child) return child_store def on_app_activate(app): # 创建主窗口 window = Gtk.ApplicationWindow(application=app, title="树形ColumnView示例") window.set_default_size(450, 600) # 构建树形数据源 root_node = TreeNode("根目录", [ TreeNode("文档", [ TreeNode("工作文档"), TreeNode("个人笔记", [ TreeNode("旅行计划"), TreeNode("技术总结") ]) ]), TreeNode("图片", [ TreeNode("风景照"), TreeNode("截图") ]), TreeNode("下载") ]) # 创建根节点的ListModel root_store = Gio.ListStore.new(TreeNode) root_store.append(root_node) # 初始化TreeListModel:参数依次为根model、是否透传顶层节点、是否自动展开 tree_list_model = Gtk.TreeListModel.new(root_store, False, False, create_child_model) # 创建ColumnView column_view = Gtk.ColumnView(model=tree_list_model) column_view.set_show_column_separators(True) # 第一列:树形展开列 expander_column = Gtk.ColumnViewColumn(title="名称") expander_factory = Gtk.SignalListItemFactory() # 初始化单元格UI def setup_expander(_, list_item): expander = Gtk.TreeExpander() label = Gtk.Label(xalign=0) # 左对齐 expander.set_child(label) list_item.set_child(expander) # 绑定数据到单元格 def bind_expander(_, list_item): expander = list_item.get_child() label = expander.get_child() # 获取TreeListModel的包装节点 tree_item = list_item.get_item() # 绑定TreeExpander到ListItem,让它自动处理展开状态 expander.set_list_item(list_item) # 获取实际业务数据 node = tree_item.get_item() label.set_label(node.name) expander_factory.connect("setup", setup_expander) expander_factory.connect("bind", bind_expander) expander_column.set_factory(expander_factory) column_view.append_column(expander_column) # 第二列:示例附加信息列 info_column = Gtk.ColumnViewColumn(title="类型") info_factory = Gtk.SignalListItemFactory() def setup_info(_, list_item): label = Gtk.Label(xalign=0) list_item.set_child(label) def bind_info(_, list_item): label = list_item.get_child() tree_item = list_item.get_item() node = tree_item.get_item() label.set_label("目录" if node.children else "文件") info_factory.connect("setup", setup_info) info_factory.connect("bind", bind_info) info_column.set_factory(info_factory) column_view.append_column(info_column) # 将ColumnView放入滚动窗口 scrolled_window = Gtk.ScrolledWindow() scrolled_window.set_child(column_view) window.set_child(scrolled_window) window.present() if __name__ == "__main__": app = Gtk.Application(application_id="com.example.TreeColumnViewDemo") app.connect("activate", on_app_activate) app.run(None)
关键细节说明
- TreeListModel初始化参数:
- 第二个参数
passthrough:设为False时,顶层节点会被包装成Gtk.TreeListItem;设为True则直接透传原始数据(适合顶层是多个根节点的场景)。 - 第三个参数
autoexpand:设为True会默认展开所有节点,False则默认折叠。
- 第二个参数
- TreeExpander绑定:必须调用
set_list_item()将其关联到ColumnView的ListItem,否则无法识别节点的展开状态和子节点。 - 数据绑定逻辑:通过
Gtk.TreeListItem.get_item()获取原始业务数据,再将数据渲染到UI组件上。 - C语言适配:逻辑完全一致,需将自定义数据实现为GObject子类,并实现
GtkTreeListModelCreateModelFunc回调函数返回子节点的GListModel。
内容的提问来源于stack exchange,提问作者Dorbmon
相关产品推荐
相关产品推荐

