EXT核心API详解(十二)_Ext.data.Tree/Ext.data.Node

本文详细介绍了Ext.data.Tree类及其核心方法与事件,同时深入探讨了Ext.data.Node类的各种属性与方法,包括节点的创建、操作及遍历等。
[size=small][color=red][b]Ext.data.Tree[/b][/color]
继承自Observable,用于存放树装的数据结构
[color=red]方法[/color]
[b]Tree( [Node root] )[/b]
以root为根构造Ext.data.Tree对象
[b]getNodeById( String id ) : Node[/b]
由指定id得到节点
[b]getRootNode() : Node[/b]
得到根节点,由属性root得到更方便
[b]setRootNode( Node node ) : Node[/b]
设置根节点
[color=red]事件[/color]有
append : ( Tree tree, Node parent, Node node, Number index )
beforeappend : ( Tree tree, Node parent, Node node )
beforeinsert : ( Tree tree, Node parent, Node node, Node refNode )
beforemove : ( Tree tree, Node node, Node oldParent, Node newParent, Number index )
beforeremove : ( Tree tree, Node parent, Node node )
insert : ( Tree tree, Node parent, Node node, Node refNode )
move : ( Tree tree, Node node, Node oldParent, Node newParent, Number index )
remove : ( Tree tree, Node parent, Node node )

[color=red][b]Ext.data.Node[/b][/color]
节点
[color=red]属性[/color]
[b]attributes : Object[/b]
节点属性集
[b]childNodes : Array[/b]
子节点
[b]firstChild : Node[/b]
第一个子节点
[b]id : String[/b]
id
[b]lastChild : Node[/b]
最后一个子节点
[b]nextSibling : Node[/b]
下一个兄弟节点
[b]parentNode : Node[/b]
父节点
[b]previousSibling : Node[/b]
前一个兄弟节点
[b]Node( Object attributes )[/b]
[color=red]构造节点[/color]
[b]appendChild( Node/Array node ) : Node[/b]
将node做为附加在当前节点的lastChild之后
[b]bubble( Function fn, [Object scope], [Array args] ) : void[/b]
由当前节点开始一直上溯到根节点,对于每个节点应用fn,直到有一个fn返回假为止
[b]cascade( Function fn, [Object scope], [Array args] ) : void[/b]
由当前节点开始一下对每个[color=red]子孙节点[/color]应用fn.直到返回false为止
[u][b]contains( Node node ) : Boolean[/[/u]b]
当前节点是node的祖先节点?
[b]eachChild( Function fn, [Object scope], [Array args] ) : void[/b]
基本同cascade,但只针对[color=red]子节点[/color]应用fn
[b]findChild( String attribute, Mixed value ) : Node[/b]
在子节点中找到[color=red]第一个[/color]有属性attribute值为value的节点
[b]findChildBy( Function fn, [Object scope] ) : Node[/b]
在子节点中找到[color=red]第一个应用fn返回真[/color]的节点
[b]getDepth() : Number[/b]
得到当前节点深度,根节点深度为0
[b]getOwnerTree() : Tree[/b]
得到当前节点的Tree对象
[b]getPath( [String attr] ) : String[/b]
得到当前节点的路径,默认attr为id
[b]indexOf( Node node ) : Number[/b]
node在当前节点的子节点中的位置
[b]insertBefore( Node node, Node refNode ) : Node[/b]
在参考节点refNode之前插入node节点
[u][b]isAncestor( Node node ) : Boolean[/b][/u]
当前节点是node的祖先节点?
[b]isFirst() : Boolean
isLast() : Boolean[/b]
当前节点是父节点的第一/最后一个节点
[b]isLeaf() : Boolean[/b]
是叶节点?指不含子节点
[b]item( Number index ) : Node[/b]
第index个子节点
[b]removeChild( Node node ) : Node[/b]
移除node子节点
[b]replaceChild( Node newChild, Node oldChild ) : Node[/b]
用newchild替换oldchild子节点
[b]sort( Function fn, [Object scope] ) : void[/b]
用指定的fn排序子节点[/size]
bl_info = { "name": "Geometry Nodes to Script", "author": "Your Name", "version": (1, 0), "blender": (3, 0, 0), "location": "View3D > N Panel > Geometry Nodes Tools", "description": "Export Geometry Nodes setups to reusable Python scripts.", "category": "Object", } import bpy import json from pathlib import Path def serialize_node_tree(node_tree): """递归序列化节点树为字典结构""" data = { "name": node_tree.name, "type": node_tree.bl_idname, "nodes": [], "links": [] } # 序列化每个节点 for node in node_tree.nodes: node_data = { "name": node.name, "type": node.bl_idname, "label": node.label, "location": [node.location.x, node.location.y], "width": node.width, "height": node.height, "hide": node.hide, "parent": node.parent.name if node.parent else None, "inputs": {}, "outputs": {}, "attributes": {} } # 特殊属性处理(如值、文本框等) if hasattr(node, "inputs"): for i, inp in enumerate(node.inputs): if inp.is_linked: continue try: value = inp.default_value if isinstance(value, (bpy.types.bpy_prop_array, tuple)): value = list(value) elif isinstance(value, str) or isinstance(value, float) or isinstance(value, int) or value is None: pass else: value = str(value) node_data["inputs"][i] = value except AttributeError: pass if hasattr(node, "outputs"): for i, out in enumerate(node.outputs): if out.is_linked: try: value = out.default_value if isinstance(value, (bpy.types.bpy_prop_array, tuple)): value = list(value) else: value = str(value) if not isinstance(value, (float, int, str, type(None))) else value node_data["outputs"][i] = value except: pass # 存储特定类型节点的额外参数 for attr in dir(node): if attr in {'name', 'type', 'inputs', 'outputs', 'location'}: continue if attr.startswith("_") or callable(getattr(node, attr)): continue try: val = getattr(node, attr) if isinstance(val, (str, int, float, bool)) or val is None: node_data["attributes"][attr] = val except: pass data["nodes"].append(node_data) # 序列化链接 for link in node_tree.links: data["links"].append({ "from_node": link.from_node.name, "from_socket": link.from_socket.identifier, "to_node": link.to_node.name, "to_socket": link.to_socket.identifier }) return data def generate_python_code(serialized_data): """根据序列化的数据生成可执行的 Python 脚本字符串""" import_lines = ''' import bpy def create_geometry_nodes_tree(name="{tree_name}"): # 创建新的几何节点树 node_tree = bpy.data.node_groups.new(name=name, type='GeometryNodeTree') nodes = node_tree.nodes links = node_tree.links # 清除默认节点 for node in nodes: nodes.remove(node) ''' tree_name = serialized_data["name"].replace('"', '\\"') code = import_lines.format(tree_name=tree_name) # 添加节点创建代码 for node_datum in serialized_data["nodes"]: node_type = node_datum["type"] node_name = node_datum["name"].replace('"', '\\"') label = node_datum["label"].replace('"', '\\"') if node_datum["label"] else "" x, y = node_datum["location"] code += f''' # 创建节点: {node_name} node_{node_name} = nodes.new(type="{node_type}") node_{node_name}.name = "{node_name}" node_{node_name}.label = "{label}" node_{node_name}.location[0] = {x} node_{node_name}.location[1] = {y} node_{node_name}.width = {node_datum["width"]} node_{node_name}.height = {node_datum["height"]} node_{node_name}.hide = {node_datum["hide"]} ''' if node_datum["parent"]: code += f' node_{node_name}.parent = nodes.get("{node_datum["parent"]}")\n' # 设置输入端口值 for idx, val in node_datum["inputs"].items(): if isinstance(val, str): val_str = f'"{val}"' elif isinstance(val, list): val_str = str(val) else: val_str = str(val) code += f' node_{node_name}.inputs[{idx}].default_value = {val_str}\n' # 恢复自定义属性 for attr, val in node_datum["attributes"].items(): if isinstance(val, str): val = f'"{val}"' else: val = str(val) code += f' node_{node_name}.{attr} = {val}\n' # 建立连接 for link in serialized_data["links"]: from_node = link["from_node"].replace('"', '\\"') to_node = link["to_node"].replace('"', '\\"') code += f''' # 连接: {from_node} -> {to_node} if node_{from_node} and node_{to_node}: links.new(node_{from_node}.outputs["{link["from_socket"]}"], node_{to_node}.inputs["{link["to_socket"]}"])''' code += '\n\n return node_tree\n\n' # 示例使用方式 code += f''' # --- 使用示例 --- if __name__ == "__main__": # 删除同名旧节点树避免冲突 old_tree = bpy.data.node_groups.get("{tree_name}") if old_tree: bpy.data.node_groups.remove(old_tree) new_tree = create_geometry_nodes_tree() print("成功创建节点树:", new_tree.name) ''' return code class OBJECT_OT_export_geometry_nodes(bpy.types.Operator): bl_idname = "object.export_geometry_nodes" bl_label = "Export Geometry Nodes as Script" bl_description = "将选中对象的几何节点导出为Python脚本" bl_options = {'REGISTER'} filepath: bpy.props.StringProperty(subtype="FILE_PATH") filter_glob: bpy.props.StringProperty(default="*.py", options={'HIDDEN'}) def execute(self, context): obj = context.active_object if not obj or not obj.modifiers: self.report({'ERROR'}, "请选择一个带有几何节点修改器的对象") return {'CANCELLED'} mod = None for m in obj.modifiers: if m.type == 'NODES': mod = m break if not mod or not mod.node_group: self.report({'ERROR'}, "未找到有效的几何节点组") return {'CANCELLED'} node_tree = mod.node_group serialized = serialize_node_tree(node_tree) # 确保路径是 .py 文件 if not self.filepath.endswith(".py"): self.filepath += ".py" # 生成 Python 脚本 script_content = generate_python_code(serialized) # 写入文件 try: with open(self.filepath, "w", encoding="utf-8") as f: f.write(script_content) self.report({'INFO'}, f"节点已成功导出至: {self.filepath}") except Exception as e: self.report({'ERROR'}, f"保存失败: {str(e)}") return {'CANCELLED'} return {'FINISHED'} def invoke(self, context, event): # 弹出文件选择对话框 wm = context.window_manager wm.fileselect_add(self) return {'RUNNING_MODAL'} class VIEW3D_PT_geometry_nodes_exporter(bpy.types.Panel): bl_label = "Geometry Nodes Exporter" bl_idname = "VIEW3D_PT_geometry_nodes_exporter" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Tool' bl_context = "" def draw(self, context): layout = self.layout layout.label(text="导出几何节点为脚本") layout.operator(OBJECT_OT_export_geometry_nodes.bl_idname) # 注册与注销函数 classes = ( OBJECT_OT_export_geometry_nodes, VIEW3D_PT_geometry_nodes_exporter, ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) if __name__ == "__main__": register() 给以上脚本添加批量导出多个对象的节点 支持导入脚本重建节点树 GUI 设置导出选项(是否包含样式、是否压缩) 导出为 JSON 格式便于编辑和版本控制 支持资产库集成(Asset Library) 自动检测并打包依赖的节点组功能,给我完整无误的代码。
最新发布
11-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值