【Godot 4.2】Tree控件与TreeItem完全解析

概述

本篇是控件完全解析系列之一,主要总结一下Tree控件与TreeItem的使用。

Tree控件是一个非常强大的控件,尤其是在编写一些相关的程序或编辑器插件时,非常适合展示树形组织的节点型数据。

本篇将从简单的添加根节点,根节点子节点,设置文本和图标等等基础开始学习,尽量涵盖和串联TreeTreeItem所提供的API。

为Tree添加节点

这里的“节点”只是对TreeItem的一个通俗(但有歧义的)称呼,Tree控件的TreeItem并不算是一个真正意义上的Godot节点,行为方法也很节点不一样。因为它是直接继承自Object类型的,而不是Control,所以无法直接在场景面板中查看到,也无法直接用检视器面板添加和编辑。删除的时候也要使用free()方法。

为了尽量减少歧义,又符合通俗描述,我会在本文中尽量少的地方用“节点”的称呼,而是直接改用TreeItem

创建根节点

  • 我们可以使用Tree控件的create_item()方法创TreeItem
  • create_item()有一个参数,用于传入作为父节点的TreeItem,如果为null,则创建的TreeItem作为Tree根节点被添加。
  • create_item()返回对新建的TreeItem的引用,我们可以用一个变量接收,并通过这个变量继续设定TreeItem的属性或调用其方法。
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")   # 设定根节点文本
  • 这里我们使用TreeItemset_text()方法设定根节点root的文本
  • Tree控件可以显示多列信息,显示列数由其columns属性设定,默认为1,也就是只显示一列。
  • set_text()第一个参数是所在列的索引,默认为0

上面代码的效果:

image.png

创建根节点的子节点

  • 在添加根节点后,可以继续使用Tree控件的create_item()方法创建根节点的子节点,只是需要将根节点引用root作为父节点传入。
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点文本# 创建根节点的子节点
var itm:TreeItem = tree.create_item(root)
itm.set_text(0,"子节点1")

上面代码的效果:

image.png

  • 除了直接用Tree控件的create_item()方法外,也可以直接利用已经创建的TreeItem(比如根节点)的create_child()方法来创建子节点。
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点文本# 创建根节点的子节点
var itm:TreeItem = root.create_child()
itm.set_text(0,"子节点1")

两种方式得到的效果是一致的。

设定图标

使用TreeItemset_icon()方法,可以设定图标。同样第一个参数指定所在列。

var icon = preload("res://icon.svg")
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")   # 设定根节点文本
root.set_icon(0,icon)     # 设定根节点图标

上面代码效果:

image.png

设定图标最大宽度

  • 可以看到,单纯用set_icon()方法,只能在TreeItem上显示图标
  • 但是很多情况下我们会遇到图标尺寸不合适的情况,这时就需要用set_icon_max_width方法设定图标最大宽度了。
var icon = preload("res://icon.svg")
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点文本
root.set_icon(0,icon)               # 设定根节点图标
root.set_icon_max_width(0,30)       # 设定根节点图标宽度
  • Godot项目默认图标icon.svg尺寸为128×128像素,通过设定最大宽度为30,则图标将以30×30像素进行显示。
    image.png

使用SpriteSheet或打包纹理作为图标来源

  • SpriteSheet打包纹理都是一种常见的2D游戏或UI美术素材存储形式。SpriteSheet采用尺寸一致的行列划分,每一帧大小尺寸一致。而打包纹理,则是更自由的压缩存储多个大小不一的小图片。

  • 这里我使用如下的一个SpriteSheet来作为一个图标库,用来给不同的TreeItem显示不同的图标。

roller_sprites.png

  • 原图尺寸是900×150像素,单个图标的尺寸就是150×150像素。
  • 而我们将最终显示在TreeItem上的图标宽度限定为18像素,这样最终呈现的大小就是18×18像素。
var icon = preload("res://items.png") # 使用SpriteSheet或打包纹理
var cell_size = Vector2(150,150)      # 单元格尺寸# ------ 创建根节点 ------ 
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点文本
root.set_icon(0,icon)               # 设定根节点图标
root.set_icon_max_width(0,18)       # 设定根节点图标宽度
var cell_pos = Vector2(0,0)         # 帧的位置
# 显示图片局部区域作为图标
root.set_icon_region(0,Rect2(cell_pos * cell_size,cell_size))# ------ 为根节点创建5个子节点 ------ 
for i in range(5):var itm:TreeItem = root.create_child()itm.set_text(0,"item%d" % i)itm.set_icon(0,icon)               # 设定根节点图标itm.set_icon_max_width(0,18)       # 设定根节点图标宽度cell_pos = Vector2(i+1,0)         # 帧的位置itm.set_icon_region(0,Rect2(cell_pos * cell_size,cell_size))

效果:

image.png

对图标颜色进行调制

我们连接并处理Tree控件的item_selected信号。

image.png
信号处理函数代码如下:

# 处理Tree控件的item_selected信号
func _on_tree_item_selected():# 获取选中节点var selected_item:TreeItem = tree.get_selected()# 恢复其他节点的图标颜色var root:TreeItem = tree.get_root()root.set_icon_modulate(0,Color.WHITE)for itm in root.get_children():itm.set_icon_modulate(0,Color.WHITE)# 修改选中节点的图标颜色selected_item.set_icon_modulate(0,Color.BLUE)

运行后可以看到,选中项的图标颜色用蓝色调制的效果,没有使用额外的图标,而是通过颜色调制,做出了选中状态与非选中状态的图标差异效果。

调制图标颜色.gif

创建多列

启用和添加多列

Tree控件支持多列视图,其columns属性控制列的数目。我们可以在检视器面板直接修改,或者使用代码形式设定。

image.png

以下是代码形式:

# 设定显示两列信息
tree.columns = 2
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点第1列文本
root.set_text(1,"第2列")             # 设定第2列文本

我们通过设定第2列文本,就可以看到如下的效果:

image.png

每一列都可以像第1列一样设置图标或其他。

const icon = preload("res://icon.svg")
# 设定显示两列信息
tree.columns = 2
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点第1列文本
root.set_icon(0,icon)               # 设定第1列图标
root.set_icon_max_width(0,16)       # 设定第1列图标最大宽度
root.set_text(1,"第2列")             # 设定第2列文本
root.set_icon(1,icon)               # 设定第1列图标
root.set_icon_max_width(1,16)       # 设定第1列图标最大宽度

上面代码,我们为两列都设定的图标和图标最大宽度。

image.png

显示列标题

和一些其他可视化程序开发语言或数据表格软件的表格控件一样,Tree控件也可以显示列标题(或者通俗叫“字段”)。

你需要用代码或在检视器面板手动开启column_titles_visible

image.png

启用后就会在每列上方显示两个按钮,目前因为还没有设置其文本,所以是不完全的形态:

image.png

通过Treeset_column_title()方法,我们可以设定所有列标题按钮的文本:

# 设定和显示列标题
tree.column_titles_visible = true    # 显示列标题
tree.set_column_title(0,"第1列")     # 设定第1列列标题
tree.set_column_title(1,"第2列")     # 设定第2列列标题

上面代码设置后的效果:

image.png

是树型列表也是二维表格

我们为根节点添加一个子节点:

# 添加子节点
var itm:TreeItem = tree.create_item(root)
itm.set_text(0,"itm1")             # 设定根节点第1列文本
itm.set_icon(0,icon)               # 设定第1列图标
itm.set_icon_max_width(0,16)       # 设定第1列图标最大宽度
itm.set_text(1,"第2列")             # 设定第2列文本
itm.set_icon(1,icon)               # 设定第1列图标
itm.set_icon_max_width(1,16)       # 设定第1列图标最大宽度

此时的效果如下:

image.png

可以看到Tree的第一列保持了树形列表的特征,同时每一列的单元格都可以单独选中和交互。节点的折叠与展开也不受影响,表现出一种树形结构+二维表格的特性。

设定列标题文本对齐方式

  • 对列标题的,也可以设定其文本对齐方式,宽度等细节。

  • set_column_title_alignment设定列标题文本的对齐方式:

tree.set_column_title_alignment(0,HORIZONTAL_ALIGNMENT_LEFT)   # 居左显示

上面的代码设定第一列的列标题居左显示:

image.png

设定最小列宽

tree.set_column_custom_minimum_width(0,100)  # 设定第一列最小宽度

上面的代码设定第一列的最小宽度为100像素。
image.png

处理列标题点击

我们可以连接和处理Tree控件的column_title_clicked信号,来处理列标题的点击。

# 列标题被点击
func _on_tree_column_title_clicked(column, mouse_button_index):tree.select_mode = Tree.SELECT_MULTI # 设置多选模式# 获取Tree的所有节点var root:TreeItem = tree.get_root()var items = [root]items.append_array(root.get_children())# 选中指定列所有单元格for itm in items:for i in range(tree.columns):if i == column:itm.select(column)else:itm.deselect(i)

等价写法:

# 列标题被点击
func _on_tree_column_title_clicked(column, mouse_button_index):tree.select_mode = Tree.SELECT_MULTI # 设置多选模式# 获取Tree的所有节点var root:TreeItem = tree.get_root()var items = [root]items.append_array(root.get_children())tree.deselect_all()  # 清除所有选择# 选中指定列所有单元格for itm in items:itm.select(column)

上面的代码实现点击列标题时,选中当前列的所有单元格:

处理列标题按钮点击.gif

设定选择模式

Tree控件有三种选择模式,不同模式下选中TreeItem的效果哈方式不同,并且可能会影响某些信号的触发和处理。

检视器面板设定:

image.png

代码方式设定:

# 设定选择模式
tree.select_mode = Tree.SELECT_SINGLE # 单个单元格选择
tree.select_mode = Tree.SELECT_ROW    # 按行选择
tree.select_mode = Tree.SELECT_MULTI  # 多选

效果:
在这里插入图片描述

select_mode属性值效果
SELECT_SINGLE如果有多列,则一次只能单独选中某项的某列,也就是某个单元格
SELECT_ROW每次只能选中一行,也就是选中某项的所有列
SELECT_MULTI可以使用Ctrl或Shift键进行多选,一次可以选中多个单元格

创建按钮

在每列的单元格中都可以创建1个或多个按钮。按钮显示在单元格的最右侧,并且只显示一个图标,不显示文本。

但是可以设定其鼠标提示文本等。

这种按钮可以作为树控件单元格或行数据交互的一种形式。

const DIR = preload("res://custom_nodes/icons/dir.png")
# 创建根节点
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 设定根节点第1列文本
root.set_icon(0,icon)               # 设定第1列图标
root.set_icon_max_width(0,16)       # 设定第1列图标最大宽度
root.add_button(0,DIR)   # 为根节点0列的单元格创建按钮
tree.set_column_custom_minimum_width(0,200)

上面的代码为两项的两列都添加了一个按钮。

image.png

处理按钮的点击

我们可以连接Treebutton_clicked信号,来处理单元格中添加的按钮的信号。
其参数返回:

  • item:按钮所在的TreeItem
  • column:按钮所在的列
  • id:按钮在所在单元格按钮中的ID或索引
  • mouse_button_index:点击按钮的鼠标按键,鼠标左键、中键、右键还是其他额外按键。
func _on_tree_button_clicked(item, column, id, mouse_button_index):if mouse_button_index == MOUSE_BUTTON_LEFT: # 鼠标左键match column:  # 某一列0,1:match id:   # 某按钮0:item.set_text(column,"")

上面的信号处理代码,可以让我们清空所在单元格的文本:

处理按钮点击.gif

快速编辑单元格文本

单元格可以进入文本编辑状态,显示一个可以编辑的文本框,从而可以快速修改单元格的文本内容,回车后完成编辑状态。

双击进入编辑状态

Treeitem_activated()信号在TreeItem或其某列单元格双击时触发。

我们通过连接此信号,创建信号处理函数,并设定如下的处理代码:

# 双击单元格
func _on_tree_item_activated():tree.edit_selected(true)  # 选中单元格进入编辑状态

此时,我们就可以通过双击单元格,进入单元格文本的编辑状态,修改完成后,回车确认,退出编辑状态:

双击编辑单元格文本.gif

编辑后的处理

  • Treeitem_edited()信号,在编辑完某个单元格并按回车确认后触发
  • Treeget_edited()方法可以获取当前编辑的TreeItem
  • 通过Treeget_edited_column()方法可以获取刚刚被编辑的TreeItem的列。

这里我只简单的打印输出单元格被修改后的文本:

# 单元格编辑完成后
func _on_tree_item_edited():var edt:TreeItem = tree.get_edited()    # 获取被编辑的TreeItemvar col:int = tree.get_edited_column()  # 被编辑的列索引print(edt.get_text(col))                # 打印修改后的单元格文本

处理TreeItem或单元格选中

  • item_selected()信号,在TreeItem选中时触发,但仅在select_modeSELECT_ROWSELECT_SINGLE 时有效。
# TreeItem被选中或单击
func _on_tree_item_selected():print(tree.get_selected())pass
  • select_modeSELECT_MULTISELECT_SINGLE时,则可以响应cell_selected()信号。
# TreeItem的单元格被选中或点击
func _on_tree_cell_selected():var sel:TreeItem = tree.get_selected()    # 选中的单元格所在TreeItemvar col:int = tree.get_selected_column()  # 选中的单元格列索引print(sel.get_text(col))

总结一下就是:

select_mode属性值item_selected()信号cell_selected()信号
SELECT_SINGLE
SELECT_ROW×
SELECT_MULTI×

用按钮控制Tree控件项的增、删、移动

我们创建一个如下的用户界面场景,创建一个Tree以及多个Button

通过这些按钮,我们实现为Tree控件添加、删除好移动项。

image.png

为场景根节点添加如下代码:

extends Control@onready var tree = $HBoxContainer/Tree@onready var add_btn = $HBoxContainer/VBoxContainer/addBtn
@onready var del_btn = $HBoxContainer/VBoxContainer/delBtn
@onready var moveup_btn = $HBoxContainer/VBoxContainer/moveupBtn
@onready var movedown_btn = $HBoxContainer/VBoxContainer/movedownBtn
@onready var clear_btn = $HBoxContainer/VBoxContainer/clearBtn# 添加
func _on_add_btn_pressed():var parent:TreeItem = nullif tree.get_root(): # 树不是空的if tree.get_selected(): # 有选中项parent = tree.get_selected()else:parent = tree.get_root()var itm = tree.create_item(parent)var idx:int = parent.get_child_count() if parent else 0itm.set_text(0,"新节点%d" % idx)# 删除
func _on_del_btn_pressed():if tree.get_root(): # 树不是空的if tree.get_selected(): # 有选中项tree.get_selected().free()pass# 上移
func _on_moveup_btn_pressed():var sel:TreeItem = tree.get_selected()if sel and sel.get_prev(): # 有选中项sel.move_before(sel.get_prev())# 下移
func _on_movedown_btn_pressed():var sel:TreeItem = tree.get_selected()if sel and sel.get_next(): # 有选中项sel.move_after(sel.get_next())# 清空
func _on_clear_btn_pressed():tree.clear()pass

可以看到其中主要是对按钮信号的处理代码,实现增、删,上下移动以及清空,效果如下动图所示:

按钮操控-添加.gif按钮操控-删除.gif
按钮操控-上移和下移.gif按钮操控-清空.gif

自由拖放

通过为Tree控件添加基本的控件拖放逻辑,就可以实现用鼠标对TreeItem的移动和放置。

extends Treefunc _get_drag_data(at_position):if get_tree(): # 不为空var sel:TreeItem = get_item_at_position(at_position)if sel: # 有选中项set_drag_preview(make_drag_preview(sel))return [sel]func _can_drop_data(at_position, data):# 如果拖放数据是一个TreeItem就可以放置# 拖放完毕恢复拖动标志设定drop_mode_flags = DROP_MODE_ON_ITEM | DROP_MODE_INBETWEENreturn data.size() > 0 and (data[0] is TreeItem)func _drop_data(at_position, data):# 获取目标拖放位置,-1,0,1分别代表在某项之前、之上和之后var target_pos = get_drop_section_at_position(at_position)# 获取鼠标位置处的TreeItemvar target_itm:TreeItem = get_item_at_position(at_position)# 如果目标位置处TreeItem是data[0]的子孙节点if target_itm in get_items(data[0]):return # 禁止移动match target_pos:-1: # 拖放到了某个TreeItem之前# 根据是否同级进行区别处理if data[0].get_parent() == target_itm.get_parent(): # 如果同级data[0].move_before(target_itm)else:data[0].get_parent().remove_child(data[0])         # 先从原来的父节点删除target_itm.add_child(data[0])                      # 添加到目标位置的TreeItemdata[0].move_before(target_itm)0:  # 拖放到了某个TreeItem上data[0].get_parent().remove_child(data[0])         # 先从原来的父节点删除target_itm.add_child(data[0])                      # 添加到目标位置的TreeItem1: # 拖放到了某个TreeItem之后# 根据是否同级进行区别处理if data[0].get_parent() == target_itm.get_parent(): # 如果同级data[0].move_after(target_itm)else:data[0].get_parent().remove_child(data[0])         # 先从原来的父节点删除target_itm.add_child(data[0])                      # 添加到目标位置的TreeItemdata[0].move_after(target_itm)# 返回某个TreeItem下所有子孙节点的集合
func get_items(item:TreeItem) -> Array[TreeItem]:var arr:Array[TreeItem]if item.get_child_count()>0:arr.append_array(item.get_children())for chd in item.get_children():arr.append_array(get_items(chd))return arr# 创建拖动预览
func make_drag_preview(itm:TreeItem) -> Button:var btn = Button.new()#btn.flat = truebtn.text = itm.get_text(0)btn.icon = itm.get_icon(0)return btn

实现效果:

自由拖放.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/718094.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp和vue项目配置多语言,实现前端切换语言

在uniapp中配置多语言功能,实现前端切换语言,可以按照以下步骤进行: 1. 创建语言包 首先,创建一个名为 lang 的目录,并在该目录下为每种支持的语言创建对应的JSON或JS文件。例如: lang/en.js&#xff08…

Microsoft PyRIT能自动化完成AI红队的任务

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

《异常检测——从经典算法到深度学习》26 Time-LLM:基于大语言模型的时间序列预测

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …

Windows10 安装Neo4j流程

1、下载并安装ava运行环境 官网链接(需要注册Oracle账号):https://www.oracle.com/java/technologies/downloads/ 根据自己Neo4j版本确认需要的JDK版本 百度网盘链接: 链接:链接:https://pan.baidu.com/s/…

Sodinokibi勒索病毒最新变种,解密工具更新到2.0版本

Sodinokibi勒索病毒 Sodinokibi勒索病毒又称REvil,自从2019年6月1日,GandCrab勒索病毒运营团伙宣布停止运营之后,Sodinokibi勒索病毒马上接管了GandCrab的大部分传播渠道,同时它也被称为是GandCrab勒索病毒的“接班人”&#xff…

VMware 虚拟机安装windows 10操作系统

先提前准备好镜像文件 1.创建新的虚拟机 2.选择自定义,然后下一步 v Windows 建议选择2G以上,下一步 选择网络地址转换(NAT),下一步 这里可按自己的需求来分区,也可以安装好后再分区 选择立即重启&#xff…

Vue组件置底方法,ElementPlus布局

问题描述 在开发网页时使用了elementplus的el-container组件 组件里分成了main和footer两块&#xff0c;但是想要将两个按钮置底在容器底部遇到了困难 如下图所示&#xff0c;在网页开发者工具可见两个按钮与左侧的图片没有底部对齐 此时我的代码是这样 <el-footer>&…

文件底层的深入理解之文件输入输出重定向

目录 一、文件fd的分配规则 二、对输出重定向现象的理解 三、输出输入重定向的简单实现 1、输出重定向 2、输入重定向 一、文件fd的分配规则 最小的没有被使用的数组下标&#xff0c;会被分配给最新打开的文件。 二、对输出重定向现象的理解 正如上面这段代码所示&#xff0…

从零开始学习Netty - 学习笔记 -Netty入门【半包,黏包】

Netty进阶 1.黏包半包 1.1.黏包 服务端代码 public class HelloWorldServer {private static final Logger logger LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());public static void main(String[] args) {NioEventLoopGroup bossGroup new NioEventL…

Ubuntu上Jenkins自动化部署Gitee上VUE项目

文章目录 1.安装NodeJS插件2.配置全局工具配置-NodeJS环境变量3.新建自由风格的软件项目任务4.配置General配置丢弃旧的构建配置参数化构建过程 5.配置源码管理6.构建触发器7.设置构建环境8.配置构建步骤9.配置构建后操作10测试构建 前文链接&#xff1a; Ubuntu上Jenkins自动…

java常用应用程序编程接口(API)——Instant,DateTimeFormatter,Period,Duration概述

前言&#xff1a; 整理下学习心得。打好基础&#xff0c;daydayup&#xff01; Instant Instant是时间线上的某个时刻/时间戳&#xff0c;通过获取Instant的对象可以拿到此刻的时间&#xff0c;该时间由两部分组成&#xff1a;1&#xff0c;从1970年1月1日00:00:00开始走到此刻…

前端开发 VSCode 插件推荐

1、Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code VS Code 的中文&#xff08;简体&#xff09;语言包&#xff0c;此中文&#xff08;简体&#xff09;语言包为 VS Code 提供本地化界面。 下载地址&#xff1a;Chinese (Simplified) (简体中文) La…

D*算法超详解 (D星算法 / Dynamic A*算法/ Dstar算法)(死循环解决--跟其他资料不一样奥)

所需先验知识&#xff08;没有先验知识可能会有大碍&#xff0c;了解的话会对D*的理解有帮助&#xff09;&#xff1a;A*算法/ Dijkstra算法 何为D*算法 Dijkstra算法是无启发的寻找图中两节点的最短连接路径的算法&#xff0c;A*算法则是在Dijkstra算法的基础上加入了启发函数…

[JavaWeb玩耍日记]HTML+CSS+JS快速使用

目录 一.标签 二.指定css 三.css选择器 四.超链接 五.视频与排版 六.布局测试 七.布局居中 八.表格 九.表单 十.表单项 十一.JS引入与输出 十二.JS变量&#xff0c;循环&#xff0c;函数 十三.Array与字符串方法 十四.自定义对象与JSON 十五.BOM对象 十六.获取…

Network LSA 结构简述

Network LSA主要用于描述一个区域内的网络拓扑结构&#xff0c;包括网络中的路由器和连接到这些路由器的网络。它记录了每个路由器的邻居关系、连接状态以及连接的度量值&#xff08;如带宽、延迟等&#xff09;&#xff0c;以便计算最短路径和构建路由表。display ospf lsdb n…

网关kong记录接口处理请求和响应插件 tcp-log-with-body的安装

tcp-log-with-body 介绍 Kong的tcp-log-with-body插件是一个高效的工具&#xff0c;它能够转发Kong处理的请求和响应。这个插件非常适用于需要详细记录API请求和响应信息的情景&#xff0c;尤其是在调试和排查问题时。 软件环境说明 kong version 2.1.4 - 2.8.3 [可用亲测]C…

二、数据结构——单链表,双链表,栈,队列,单调栈,单调队列,KMP,Trie,并查集,堆,哈希表等内容。

对于链表来说&#xff0c;由于new操作时间太长&#xff0c;因此&#xff0c;算法题中一般使用静态链表。 1.单链表 采用数组实现单链表&#xff0c;可以直接开两个数据&#xff0c;一个数组存放数值&#xff0c;另外一个数据存放下一个元素&#xff08;指针&#xff09;。 示…

一个教材上的CMS网站源码在Linux服务器上登录时验证码正常,但在windows下不能正常显示

一个教材上的CMS网站源码在Linux服务器上登录时验证码正常&#xff0c;但在windows下不能正常显示。 在linux服务器上能正常显示。显示界面如下所示&#xff1a;

蜻蜓FM语音下载(mediadown)

一、介绍 蜻蜓FM语音下载&#xff08;mediadown&#xff09;&#xff0c;能够帮助你下载蜻蜓FM音频节目。如果你是蜻蜓FM会员&#xff0c;它还能帮你下载会员节目。 二、下载地址 本站下载&#xff1a;蜻蜓FM语音下载&#xff08;mediadown&#xff09; 百度网盘下载&#…