【Godot4.2】MLTag类:HTML、XML通用标签类
概述
HTML和XML采用类似的标签形式。
之前在Godot中以函数库形式实现了网页标签和内容生成。能用,但是缺点也很明显。函数之间没有从属关系,但是多有依赖,而且没有划分出各种对象和类型。
如果以完全的面向对象形式来设计标签类或者元素类,将可以更贴近HTML或XML的本来面目。也更容易生成。
整体思路是设计如下的类继承结构:

实现之后,将可以充分的定义和生成HTML、XML和SVG标签,并用于内容生成或文档解析。
通用标签类
因为HTML和XML标签的语法格式和要素是类似的,因此可以通过创建一个通用的标签类,来生成HTML或XML标签。
MLtag就是这个通用的标签类,它定义了几个核心的属性:
tag_name:标签名称attrs:包含标签所有属性的字典is_single:是否为单标签,默认为falsecontent:标签的子内容,可以直接赋值,也可以使用append()方法追加。单标签(is_single为true)时被忽略get_end_tag():获取结束标签,单标签时返回空字符串append():追加当前标签的子内容,可以是字符串形式,也可以是SVG标签实例(会自动调用to_string()方法转化为字符串)
# =============================================
# 名称:MLtag
# 类型:类
# 描述:HTML、XML通用标签类,用于定义和生成HTML、XML标签字符串
# 作者:巽星石
# 创建时间:2024年7月16日17:48:01
# 最后修改时间:2024年7月16日22:27:53
# =============================================
class_name MLTag# ====================== 属性 ======================
var tag_name = "" # 标签名称
var attrs:Dictionary = {} # 属性字典
var is_single = false # 是否单标签
var content = "" # 子内容 # ====================== 方法 ======================
# 获取结束标签
func get_end_tag() -> String:return "" if is_single else "</%s>" % tag_name# 追加子内容
func append(new_content) -> void:if new_content is String:content += "\n" + new_contentelse:content += "\n" + new_content.to_string()# ====================== 虚函数 ======================
# 转化为字符串
func _to_string() -> String:var tag_str:String# 获取属性字典的字符串var attr = ""for key in attrs.keys():if attrs[key] != "":attr += "%s=\"%s\" " % [key,attrs[key]]tag_str = "<%s%s/>" % [tag_name," " + attr] if is_single else "<%s%s>%s</%s>" % [tag_name," " + attr,content,tag_name]return tag_str
测试:
@tool
extends EditorScriptfunc _run() -> void:var tag = MLTag.new() # 创建一个标签tag.name = "a" # 名称tag.attrs["id"] = "a1" # 设定属性tag.attrs["href"] = "https://www.runoob.com/"tag.content = "菜鸟教程"print(tag)
默认是双标签,也就是带有起始标签和结束标签的标签对。
<a id="a1" href="https://www.runoob.com/" >菜鸟教程</a>
is_single设为true后,就被认为是单标签:
@tool
extends EditorScriptfunc _run() -> void:var tag = MLTag.new() # 创建一个标签tag.name = "a" # 名称tag.attrs["id"] = "a1" # 设定属性tag.attrs["href"] = "1.com"tag.innerHTML = "hahah"tag.is_single = true # 设为标签print(tag)
打印输出的结果也就变了:
<a id="a1" href="https://www.runoob.com/" />
可以看到没有了结束标签,子内容部分也自动省略。
HTML标签基类
# =============================================
# 名称:HTMLtag
# 类型:类
# 描述:HTML标签基类
# 作者:巽星石
# 创建时间:2024年7月16日19:05:46
# 最后修改时间:2024年7月16日19:29:34
# =============================================
class_name HTMLTag extends MLTag# ====================== 属性 ======================
# ID
var id:String:set(val):attrs["id"] = valget:return attrs["id"]
# name
var name:String:set(val):attrs["name"] = valget:return attrs["name"]
# css类名
var className:String:set(val):attrs["class"] = valget:return attrs["class"]
# 行内CSS样式
var style:String:set(val):attrs["style"] = valget:return attrs["style"]
# ====================== 初始化 ======================
func _init() -> void:# HTML标签通用属性attrs = {"id" = "", # ID"name" = "", # name属性"class" = "", # css类名"style" = "", # 行内样式}
测试代码:
@tool
extends EditorScriptfunc _run() -> void:var tag = HTMLTag.new() # 创建一个标签tag.tag_name = "a" # 名称# 设定HTML标签通用属性tag.id = "a2"tag.name = "a2"tag.className = "url"tag.style = "font-size:16px;"tag.innerHTML = "一个超链接"print(tag)
输出:
<a id="a2" name="a2" class="url" style="font-size:16px;" >一个超链接</a>
HTML<a>标签
# =============================================
# 名称:HTMLtagA
# 类型:类
# 描述:HTML的<a>标签类
# 作者:巽星石
# 创建时间:2024年7月16日19:32:47
# 最后修改时间:2024年7月16日22:30:57
# =============================================
class_name HTMLTagA extends HTMLTag# ====================== 属性 ======================
# 链接地址
var href:String:set(val):attrs["href"] = valget:return attrs["href"]
# 指定链接如何在浏览器中打开
var target:String:set(val):attrs["target"] = valget:return attrs["target"]
# 鼠标提示文本
var title:String:set(val):attrs["title"] = valget:return attrs["title"]
# 指定与链接目标的关系,如 nofollow、noopener 等
var rel:String:set(val):attrs["rel"] = valget:return attrs["rel"]# ====================== 初始化 ======================
func _init() -> void:super() # 初始化父类,否则无法继承相关的默认属性# 确定标签名称以及是否单标签tag_name = "a"is_single = false# <a>标签可用属性attrs["href"] = "" # 链接地址attrs["target"] = "" # 指定链接如何在浏览器中打开attrs["title"] = "" # 鼠标提示文本attrs["rel"] = "" # 指定与链接目标的关系,如 nofollow、noopener 等
测试代码:
@tool
extends EditorScriptfunc _run() -> void:var link = HTMLTagA.new() # 创建一个标签# 设定<a>标签属性link.href = "https://www.runoob.com/"link.innerHTML = "菜鸟教程"print(link)
输出:
<a href="https://www.runoob.com/" >菜鸟教程</a>
SVG标签类

我发现专门做一个XML标签基类是没有必要的,XML本身就没有固定的标签,所以由MLTag定义完全可以解决XML标签定义和生成的功能。
而<svg>本身既可以算HTML标签,也可以看做是XML标签。这里为了简化,我创建SVG类,代表<svg>标签,并直接继承自MLTag类型。
# =============================================
# 名称:SVG
# 类型:类
# 描述:SVG的<svg>标签类
# 作者:巽星石
# 创建时间:2024年7月16日21:37:24
# 最后修改时间:2024年7月16日21:44:04
# =============================================
class_name SVG extends MLTag# ====================== 属性 ======================
# SVG画布宽度
var width:String:set(val):attrs["width"] = valget:return attrs["width"]
# SVG画布高度
var height:String:set(val):attrs["height"] = valget:return attrs["height"]# ====================== 初始化 ======================
func _init() -> void:tag_name = "svg"# HTML标签通用属性attrs = {"version"="1.1","baseProfile"="full","width"="200","height"="200","xmlns"="http://www.w3.org/2000/svg",}
SVGShape
SVG的形状和路径有许多共同的属性,所以抽象出一个SVGShape类型,用来承载共同属性。
# =============================================
# 名称:SVGShape
# 类型:类
# 描述:SVG所有形状和路径标签类的基类,承载共同属性
# 作者:巽星石
# 创建时间:2024年7月16日21:51:00
# 最后修改时间:2024年7月16日23:07:46
# =============================================
class_name SVGShape extends MLTag# ====================== 属性 ======================
# 填充颜色
var fill:String:set(val):attrs["fill"] = valget:return attrs["fill"]# 描边颜色
var stroke:String:set(val):attrs["stroke"] = valget:return attrs["stroke"]# 描边宽度
var stroke_width:int:set(val):attrs["stroke-width"] = str(val)get:return int(attrs["stroke-width"])# ====================== 初始化 ======================
func _init() -> void:tag_name = "svg"# svg形状和路径标签通用属性attrs["fill"] = "#fff"attrs["stroke"] = "#000"attrs["stroke-width"] = "1"
矩形
基于SVGShape类型,我们可以创建SVG具体的形状和路径类型。SVGRect就是SVG矩形
# =============================================
# 名称:SVGRect
# 类型:类
# 描述:SVG的<rect>标签类
# 作者:巽星石
# 创建时间:2024年7月16日21:47:43
# 最后修改时间:2024年7月16日22:16:54
# =============================================
class_name SVGRect extends SVGShape# ====================== 属性 ======================# ---------------------- 位置 ----------------------
# 矩形左上角x坐标
var x:String:set(val):attrs["x"] = valget:return attrs["x"]# 矩形左上角y坐标
var y:String:set(val):attrs["y"] = valget:return attrs["y"]
# ---------------------- 尺寸 ----------------------
# 矩形宽度
var width:String:set(val):attrs["width"] = valget:return attrs["width"]# 矩形高度
var height:String:set(val):attrs["height"] = valget:return attrs["height"]
# ---------------------- 圆角 ----------------------
# 矩形圆角半径(水平方向)
var rx:String:set(val):attrs["rx"] = valget:return attrs["rx"]# 矩形圆角半径(垂直方向)
var ry:String:set(val):attrs["ry"] = valget:return attrs["ry"]# ====================== 初始化 ======================
func _init() -> void:super() # 初始化父类,否则无法继承相关的默认属性tag_name = "rect"# HTML标签通用属性attrs["x"] = "0"attrs["y"] = "0"attrs["width"] = "100"attrs["height"] = "100"attrs["rx"] = "0"attrs["ry"] = "0"
测试代码:
@tool
extends EditorScriptfunc _run() -> void:var svg = SVG.new() # 创建SVG标签var rect = SVGRect.new() # 创建矩形svg.append(rect)print(svg)
获得的SVG代码:
<svg version="1.1" baseProfile="full" width="200" height="200" xmlns="http://www.w3.org/2000/svg" >
<rect fill="#fff" stroke="#000" stroke-width="1" x="0" y="0" width="100" height="100" rx="0" ry="0" ></rect></svg>
SVG预览如下:

@tool
extends EditorScriptfunc _run() -> void:var svg = SVG.new(100,200) # 创建SVG标签var rect = SVGRect.new(0,0,100,200,12,12) # 创建矩形# 设定矩形属性rect.stroke = "red"rect.stroke_width = 2# 将矩形追加到svg内容的末尾svg.append(rect)print(svg) # 打印SVG代码
获得的SVG代码:
<svg version="1.1" baseProfile="full" width="100" height="200" xmlns="http://www.w3.org/2000/svg" >
<rect fill="#fff" stroke="red" stroke-width="2" x="0" y="0" width="100" height="200" rx="12" ry="12" ></rect></svg>
预览如下:

总结
- 纯面向对象的设计,尤其是基于继承的多个类组成的体系在某些方面还是很有用的。比如HTML、XML、SVG这样本身就很明显的继承设计。
- 基于继承或许会写出一堆类,但是辅以类图和文档,还是能让别人比较轻松的理解和掌握的。
- 静态函数库则完全是一个很差的设计形式,很有可能会被一堆函数搞得特别臃肿。
- 适当的用类和类的继承形式取代静态函数库形式是我正在做的尝试
相关文章:
【Godot4.2】MLTag类:HTML、XML通用标签类
概述 HTML和XML采用类似的标签形式。 之前在Godot中以函数库形式实现了网页标签和内容生成。能用,但是缺点也很明显。函数之间没有从属关系,但是多有依赖,而且没有划分出各种对象和类型。 如果以完全的面向对象形式来设计标签类或者元素类…...
美式键盘 QWERTY 布局的起源
注:机翻,未校对。 The QWERTY Keyboard Is Tech’s Biggest Unsolved Mystery QWERTY 键盘是科技界最大的未解之谜 It’s on your computer keyboard and your smartphone screen: QWERTY, the first six letters of the top row of the standard keybo…...
【JavaEE】HTTP(2)
🤡🤡🤡个人主页🤡🤡🤡 🤡🤡🤡JavaEE专栏🤡🤡🤡 🤡🤡🤡下一篇文章:【JavaEE】HTTP协议(…...
LinuxShell编程2——shell搭建Discuzz论坛网站
目录 一、环境准备 ①准备一台虚拟机 ②初始化虚拟机 1、关闭防火墙 2、关闭selinux 3、配置yum源 4、修改主机名 二、搭建LAMP环境 ①安装httpd(阿帕奇apache)服务器 查看是否安装过httpd 启动httpd 设置开机启动 查看状态 安装网络工具 测试 ②安装…...
.NET MAUI开源架构_1.学习资源分享
最近需要开发Android的App,想预研下使用.NET开源架构.NET MAUI来开发App程序。因此网上搜索了下相关资料,现在把我查询的结果记录下,方便后面学习。 1.官方文档 1.1MAUI官方学习网站 .NET Multi-Platform App UI 文档 - .NET MAUI | Micro…...
Unsloth 微调 Llama 3
本文参考: https://colab.research.google.com/drive/135ced7oHytdxu3N2DNe1Z0kqjyYIkDXp 改编自:https://blog.csdn.net/qq_38628046/article/details/138906504 文章目录 一、项目说明安装相关依赖下载模型和数据 二、训练1、加载 model、tokenizer2、…...
热修复的原理
热修复的原理 水一篇哈,完事儿后删掉热修复的原理 水一篇哈,完事儿后删掉 热修复的原理 Java虚拟机 —— JVM 是加载类的class文件的,而Android虚拟机——Dalvik/ART VM 是加载类的dex文件,而他们加载类的时候都需要ClassLoader,…...
【对顶堆 优先队列】2102. 序列顺序查询
本文涉及知识点 对顶堆 优先队列 LeetCode 2102. 序列顺序查询 一个观光景点由它的名字 name 和景点评分 score 组成,其中 name 是所有观光景点中 唯一 的字符串,score 是一个整数。景点按照最好到最坏排序。景点评分 越高 ,这个景点越好。…...
Go 语言中的互斥锁 Mutex
Mutex 是一种互斥锁,名称来自 mutual exclusion,是一种用于控制多线程对共享资源的竞争访问的同步机制。在有的编程语言中,也将其称为锁(lock)。当一个线程获取互斥锁时,它将阻止其他线程对该资源的访问,直到该线程释放锁。这可以防止多个线程对共享资源进行冲突访问,从而…...
CSS 中的 ::before 和 ::after 伪元素
目录 一、CSS 伪元素 二、::before ::after 介绍 1、::before 2、::after 3、content 常用属性值 三、::before ::after 应用场景 1、设置统一字符 2、通过背景添加图片 3、添加装饰线 4、右侧展开箭头 5、对话框小三角 6、插入icon图标 一、CSS 伪元素 CSS伪元…...
JuiceFS缓存特性
缓存 对于一个由对象存储和数据库组合驱动的文件系统,缓存是本地客户端与远端服务之间高效交互的重要纽带。读写的数据可以提前或者异步载入缓存,再由客户端在后台与远端服务交互执行异步上传或预取数据。相比直接与远端服务交互,采用缓存技…...
R语言实现SVM算法——分类与回归
### 11.6 基于支持向量机进行类别预测 ### # 构建数据子集 X <- iris[iris$Species! virginica,2:3] # 自变量:Sepal.Width, Petal.Length y <- iris[iris$Species ! virginica,Species] # 因变量 plot(X,col y,pch as.numeric(y)15,cex 1.5) # 绘制散点图…...
React@16.x(57)Redux@4.x(6)- 实现 bindActionCreators
目录 1,分析1,直接传入函数2,传入对象 2,实现 1,分析 一般情况下,action 并不是一个写死的对象,而是通过函数来获取。 而 bindActionCreators 的作用:为了更方便的使用创建 action…...
【深度学习入门篇 ⑦】PyTorch池化层
【🍊易编橙:一个帮助编程小伙伴少走弯路的终身成长社群🍊】 大家好,我是小森( ﹡ˆoˆ﹡ ) ! 易编橙终身成长社群创始团队嘉宾,橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…...
【Pytorch】数据集的加载和处理(一)
Pytorch torchvision 包提供了很多常用数据集 数据按照用途一般分为三组:训练(train)、验证(validation)和测试(test)。使用训练数据集来训练模型,使用验证数据集跟踪模型在训练期间…...
论文翻译:Explainability for Large Language Models: A Survey
https://arxiv.org/pdf/2309.01029 目录 可解释性在大型语言模型中:一项调查摘要1 引言2 LLMs的训练范式2.1 传统微调范式2.2 提示范式 3 传统微调范式的解释3.1 局部解释3.1.1 基于特征归因的解释3.1.2 基于注意力的解释3.1.3 基于示例的解释 3.2 全局解释3.2.1 基…...
38 IRF+链路聚合+ACL+NAT组网架构
38 IRF+链路聚合+ACL+NAT组网架构 参考文献 34 IRF的实例-CSDN博客 35 解决单条链路故障问题-华三链路聚合-CSDN博客 36 最经典的ACL控制-CSDN博客 37 公私网转换技术-NAT基础-CSDN博客 32 华三vlan案例+STP-CSDN博客 一 网络架构...
【昇思学习打卡营打卡-第二十八天】MindNLP ChatGLM-6B StreamChat
MindNLP ChatGLM-6B StreamChat 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。 安装mindnlp pip install mindnlp安装mdtex2html pip install mdtex2html配置网络线路 export HF_ENDPOINThttps://hf-mirror.com代码开发 下载权重大约需要10分钟 from mindnlp.transf…...
前端打包部署后源码安全问题总结
随着现代Web应用越来越依赖于客户端技术,前端安全问题也随之突显。源码泄露是一个严重的安全问题,它不仅暴露了应用的内部逻辑和业务关键信息,还可能导致更广泛的安全风险。本文将详细介绍源码泄露的潜在风险,并提供一系列策略和工…...
扩展你的App:Xcode中App Extensions的深度指南
扩展你的App:Xcode中App Extensions的深度指南 在iOS开发的世界中,App Extensions提供了一种强大的方式,允许你的应用程序与系统和其他应用更紧密地集成。从今天起,我们将探索Xcode中App Extensions的神秘领域,学习如…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space
问题:IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案:将编译的堆内存增加一点 位置:设置setting-》构建菜单build-》编译器Complier...
