【Godot4.2】EasyTreeData通用解析

概述

之前在《【Godot4.2】Tree控件自定义树形数据ETD及其解析》一文中,实现了对带缩进的层级结构文本的解析,并将其用于Tree控件的列表项构造。

不过当时并没有实现专门的类,今天花了一点时间实现了一下。现在可以更方便的构造和解析ETD数据,并转化为其他格式。

并且EasyTreeData类,可以被理解为一个树型数据结构,可以基于它构造数据结构中的树。

本篇就介绍一下这个类的使用方法。

代码

swift 复制代码
# ========================================================
# 名称:EasyTreeData
# 类型:类
# 简介:专用于构造树形结构数据的类
# 作者:巽星石
# Godot版本:v4.2.2.stable.official [15073afe3]
# 创建时间:2024年4月29日23:20:02
# 最后修改时间:2024年4月30日00:57:15
# ========================================================
class_name EasyTreeData

var _root:EasyTreeItem

# ============================ 内部类 ============================
# 单项数据
class EasyTreeItem:
	var data:String
	var deep:int
	var parent:EasyTreeItem
	var children:Array[EasyTreeItem]
	
	func _init(_data:String,_deep:int) -> void:
		data = _data
		deep = _deep
		children = []
	
	# 输出ETD多行文本格式
	func _to_string() -> String:
		var sttr:= "%s%s\n" % ["\t".repeat(deep),data]
		for child in children:
			sttr += child.to_string()
		return sttr
	
	# 输出MarkDown带缩进无序列表字符串
	func to_MD_ul() -> String:
		var sttr:= "%s- %s\n" % ["\t".repeat(deep),data]
		for child in children:
			sttr += child.to_MD_ul()
		return sttr
		
	# 输出可识别的LaTex总结字符串
	func to_LaTex() -> String:
		var list_str = "\n/begin{cases}\n%s/end{cases}\n"
		var sttr:= "%s%s"
		if children.size()!=0:
			var subs = ""
			for child in children:
				subs += child.to_LaTex() + "//\n"
			sttr =  sttr % [data,list_str % subs]
		else:
			sttr =  sttr % [data,""]
		return sttr.replace("/","\\")


# ============================ 虚函数 ============================
# 转化为字符串
func _to_string() -> String:
	return "" if !_root else _root.to_string()

# 返回ETD多行文本
func get_data() -> String:
	return _to_string()

# 输出MarkDown带缩进无序列表字符串
func to_MD_ul() -> String:
	return "" if !_root else _root.to_MD_ul()

func to_LaTex() -> String:
	return "" if !_root else _root.to_LaTex()
# ============================ 方法 ============================
# 创建并返回一个EasyTreeItem实例
func create_item(text:String,p_node:EasyTreeItem = null) -> EasyTreeItem:
	var itm = EasyTreeItem.new(text,0)
	if _root:
		if p_node:
			itm.deep = p_node.deep + 1
			itm.parent = p_node
			p_node.children.append(itm)
		else:
			itm.deep = _root.deep + 1
			itm.parent = _root
			_root.children.append(itm)
	else:
		_root = itm
	return itm

# 由多行文本创建
static func new_with_etd_str(etd_str:String) ->EasyTreeData:
	var edt = EasyTreeData.new()

	var items = etd_str.split("\n",false)    # 将ETD字符串按行切分为字符串数组
	var pre_itm:EasyTreeItem                 # 记录前一项
	var p_itm:EasyTreeItem = null            # 记录父节点

	# 遍历每行数据
	for i in range(items.size()):
		# 第1行直接添加为Tree控件的根节点(跳过下面if部分)
		# 从第2行开始比较当前行与前一行的缩进深度(也就是\t的数目)
		if i > 0: 
			var d_deep = deep(items[i-1]) - deep(items[i])  # 与前一行数据的缩进差值
			match d_deep:
				-1:                                # 缩进比前一项深:
					p_itm = pre_itm                # 将前一项作为父节点
				0:                                 # 缩进深度与前一项一样:
					p_itm = pre_itm.parent   # 父节点与前一项父节点一样
				_:                                 
					if d_deep>0:                   # 缩进比前一项浅
												   # 通过缩进差值计算获得合适的父节点
						p_itm = pre_itm            
						for j in range(d_deep+1):
							p_itm = p_itm.parent
		
		# 实际创建和添加TreeItem到Tree控件
		var itm:EasyTreeItem = edt.create_item(items[i].replace("\t",""),p_itm)
		pre_itm = itm                              # 将当前项记录为前一项
	return edt

# 返回字符串的Tab缩进值
static func deep(sttr:String):
	return sttr.rstrip(" ").count("\t")

使用方法

基础使用

EasyTreeData在使用和API名称上模仿了Godot的Tree控件和TreeItem类。所以熟悉Tree控件和TreeItem的Godoter可以直接零难度上手。

swift 复制代码
var edt = EasyTreeData.new()             # 创建实例
# 创建根节点
var root = edt.create_item("根节点")  
var itm = edt.create_item("节点1",root)  # 创建根节点的子节点
print(edt)   # 打印输出相应的EDT字符串

输出:

swift 复制代码
根节点
	节点1

用ETD多行文本数据创建

你可以用多行文本形式构造一个ETD数据字符串,也就是一个带层级关系和缩进关系的多行文本:

swift 复制代码
var etd = """
条目1
	条目1.1
	条目1.2
		条目1.2.1
		条目1.2.2
	条目1.3
"""

通过调用new_with_etd_str()静态方法,并传入上面的ETD数据字符串,可以构造一个新的EasyTreeData实例。

swift 复制代码
var edt = EasyTreeData.new_with_etd_str(etd)
print(edt)

输出:

swift 复制代码
条目1
	条目1.1
	条目1.2
		条目1.2.1
		条目1.2.2
	条目1.3

可以看到与传入的ETD数据字符串一致,但是你可以在此基础上继续添加新的数据项。

获取MarkDown的无序列表字符串

swift 复制代码
print(edt.to_MD_ul())  

输出:

swift 复制代码
- 条目1
	- 条目1.1
	- 条目1.2
		- 条目1.2.1
		- 条目1.2.2
	- 条目1.3

这段代码可以直接粘贴到MarkDown编辑器中,变为带层级的无序列表样式,效果如下:


  • 条目1
    • 条目1.1
    • 条目1.2
      • 条目1.2.1
      • 条目1.2.2
    • 条目1.3

将ETD解析为LaTex总结笔记

swift 复制代码
print(edt.to_LaTex())

输出:

swift 复制代码
条目1
\begin{cases}
条目1.1\\
条目1.2
\begin{cases}
条目1.2.1\\
条目1.2.2\\
\end{cases}
\\
条目1.3\\
\end{cases}

粘贴到MarkDown编辑器的LaTex公式中,显示为:
条 目 1 { 条 目 1.1 条 目 1.2 { 条 目 1.2.1 条 目 1.2.2 条 目 1.3 条目1 \begin{cases} 条目1.1\\ 条目1.2 \begin{cases} 条目1.2.1\\ 条目1.2.2\\ \end{cases} \\ 条目1.3\\ \end{cases} 条目1⎩⎪⎪⎪⎨⎪⎪⎪⎧条目1.1条目1.2{条目1.2.1条目1.2.2条目1.3

基于此,我们可以很方便的通过简单的层级缩进文本,或者Markdown中带层级缩进的无序列表,实现比较复杂的Latex公式总结笔记的转化。

比如,我们构造如下的ETD字符串:

swift 复制代码
数据结构
	线性结构
		栈
		队列
		双端列表
		列表
	非线性结构
		图
		树

生成的总结笔记:
数 据 结 构 { 线 性 结 构 { 栈 队 列 双 端 列 表 列 表 非 线 性 结 构 { 图 树 数据结构 \begin{cases} 线性结构 \begin{cases} 栈\\ 队列\\ 双端列表\\ 列表\\ \end{cases} \\ 非线性结构 \begin{cases} 图\\ 树\\ \end{cases} \\ \end{cases} 数据结构⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧线性结构⎩⎪⎪⎪⎨⎪⎪⎪⎧栈队列双端列表列表非线性结构{图树

展望

紧缩字串(暂未实现)

可以将ETD多行文本形式转化为如下的括号嵌套结构的单行字符串。这样更容易压缩存储。

此外,也可以编写相应函数来解析为树形结构数据。

swift 复制代码
条目1[条目1.1,条目1.2[条目1.2.1,条目1.2.2]条目1.3]

获取字典与JSON形式

在上面基础上,我们可以获取对应的字典以及JSON字符串。

用于树控件

  • 通过提供静态方法,可以用于任意Tree控件的数据项构造。

用于构造菜单

  • 菜单是多个树结构
相关推荐
代码盗圣15 天前
GODOT 4 不用scons编译cpp扩展的方法
游戏引擎·godot
ormcc17 天前
layui tree customSelet选中的内容重写,查找父级
前端·javascript·layui·tree·customselect
知兀1 个月前
单例模式(自动加载)
笔记·游戏引擎·godot
知兀1 个月前
Godot中的信号
笔记·游戏引擎·godot
知兀1 个月前
【godot游戏引擎学习笔记】初识界面
笔记·游戏引擎·godot
知兀1 个月前
Godot中类和静态类型
笔记·游戏引擎·godot
知兀1 个月前
编辑器、节点树、基础设置
笔记·游戏引擎·godot
巽星石2 个月前
【Godot4.3】匀速和匀变速直线运动粒子
godot·物理·加速度·速度曲线
巽星石2 个月前
【Godot4.3】基于中心点连线的矩形重叠检测
godot·gdscript·碰撞·图形
巽星石2 个月前
【Godot4.3】自定义数列类NumList
godot·gdscript·斐波那契·数列