【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的神秘领域,学习如…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
