└── addons
└── config_table_manager.daylily-zeleen
├── scripts
├── table_header.gd
├── log.gd
├── import_modifier.gd
├── generate_modifier.gd
└── preset.gd
├── scenes
├── meta_edit.gd
├── meta_edit.tscn
├── property_description_edit.gd
├── property_description_edit.tscn
├── gen_and_import.tscn
├── additional_property_edit.gd
├── settings.tscn
├── additional_property_edit.tscn
├── gen_and_import.gd
├── settings.gd
├── main_screen.gd
└── main_screen.tscn
├── icon.svg.import
├── plugin.cfg
├── localization
├── localize.gd
├── template.pot
└── en.po
├── config_table_manager.daylily-zeleen.gd
├── import_tools
├── import_tool.gd
├── gdscript_dictionary.gd
└── gdscript_default.gd
├── table_tools
├── table_tool.gd
├── py
│ └── xlsx_json.py
├── csv.gd
└── xlsx.gd
└── icon.svg
/addons/config_table_manager.daylily-zeleen/scripts/table_header.gd:
--------------------------------------------------------------------------------
1 | @tool
2 |
3 | var meta_list: PackedStringArray
4 | var descriptions: PackedStringArray
5 | var fields: PackedStringArray
6 | var types: PackedStringArray
7 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scripts/log.gd:
--------------------------------------------------------------------------------
1 | @tool
2 |
3 |
4 | static func error(texts: PackedStringArray) -> void:
5 | printerr("[TableManager]", "".join(texts))
6 |
7 |
8 | static func warning(texts: PackedStringArray) -> void:
9 | print_rich("[color=yellow][TableManager]%s[/color]" % "".join(texts))
10 |
11 |
12 | static func info(texts: PackedStringArray) -> void:
13 | print("[TableManager]", "".join(texts))
14 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends PanelContainer
3 |
4 | signal delete_request
5 |
6 | @onready var meta_line_edit: LineEdit = %MetaLineEdit
7 | @onready var delete_btn: Button = %DeleteBtn
8 |
9 |
10 | func _ready() -> void:
11 | delete_btn.pressed.connect(func() -> void: delete_request.emit())
12 |
13 |
14 | func setup(meta: String) -> void:
15 | if not is_node_ready():
16 | await ready
17 | meta_line_edit.text = meta
18 |
19 |
20 | func get_meta_text() -> String:
21 | return meta_line_edit.text
22 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://dnjoxwe0301f6"]
2 |
3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd" id="1_w8oww"]
4 |
5 | [node name="MetaEdit" type="PanelContainer"]
6 | offset_right = 1130.0
7 | offset_bottom = 44.0
8 | script = ExtResource("1_w8oww")
9 |
10 | [node name="HBoxContainer" type="HBoxContainer" parent="."]
11 | layout_mode = 2
12 |
13 | [node name="MetaLineEdit" type="LineEdit" parent="HBoxContainer"]
14 | unique_name_in_owner = true
15 | layout_mode = 2
16 | size_flags_horizontal = 3
17 | placeholder_text = "Meta"
18 |
19 | [node name="DeleteBtn" type="Button" parent="HBoxContainer"]
20 | unique_name_in_owner = true
21 | layout_mode = 2
22 | text = "X"
23 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends PanelContainer
3 |
4 | signal delete_request
5 |
6 | @onready var name_line_edit: LineEdit = %NameLineEdit
7 | @onready var description_edit: LineEdit = %DescriptionLineEdit
8 | @onready var delete_btn: Button = %DeleteBtn
9 |
10 |
11 | func _ready() -> void:
12 | delete_btn.pressed.connect(func() -> void: delete_request.emit())
13 | preload("../localization/localize.gd").localize_node(self)
14 |
15 | func setup(prop_name: String, desc: String) -> void:
16 | if not is_node_ready():
17 | await ready
18 | name_line_edit.text = prop_name
19 | description_edit.text = desc
20 |
21 |
22 | func get_property_name() -> String:
23 | return name_line_edit.text
24 |
25 |
26 | func get_description() -> String:
27 | return description_edit.text
28 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://itkw7iwcegvd"]
2 |
3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd" id="1_16vph"]
4 |
5 | [node name="PropertyDescriptionEdit" type="PanelContainer"]
6 | offset_right = 1130.0
7 | offset_bottom = 44.0
8 | script = ExtResource("1_16vph")
9 |
10 | [node name="HBoxContainer" type="HBoxContainer" parent="."]
11 | layout_mode = 2
12 |
13 | [node name="NameLineEdit" type="LineEdit" parent="HBoxContainer"]
14 | unique_name_in_owner = true
15 | layout_mode = 2
16 | size_flags_horizontal = 3
17 | size_flags_stretch_ratio = 0.25
18 | placeholder_text = "字段名称"
19 |
20 | [node name="DescriptionLineEdit" type="LineEdit" parent="HBoxContainer"]
21 | unique_name_in_owner = true
22 | layout_mode = 2
23 | size_flags_horizontal = 3
24 | placeholder_text = "描述"
25 |
26 | [node name="DeleteBtn" type="Button" parent="HBoxContainer"]
27 | unique_name_in_owner = true
28 | layout_mode = 2
29 | text = " - "
30 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/icon.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bkhw1qeqagwnl"
6 | path="res://.godot/imported/icon.svg-871927cf7bd84ef7791e78a3bd30c145.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/config_table_manager.daylily-zeleen/icon.svg"
14 | dest_files=["res://.godot/imported/icon.svg-871927cf7bd84ef7791e78a3bd30c145.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=1
24 | mipmaps/generate=true
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=false
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=0
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://bepjlsptadkrq"]
2 |
3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd" id="1_o1jft"]
4 |
5 | [node name="GenAndImport" type="VBoxContainer"]
6 | offset_right = 500.0
7 | offset_bottom = 647.0
8 | size_flags_horizontal = 3
9 | script = ExtResource("1_o1jft")
10 | title = "表格生成"
11 |
12 | [node name="TitleLabel" type="Label" parent="."]
13 | unique_name_in_owner = true
14 | layout_mode = 2
15 | text = "表格生成"
16 |
17 | [node name="Tree" type="Tree" parent="."]
18 | unique_name_in_owner = true
19 | layout_mode = 2
20 | size_flags_vertical = 3
21 | columns = 3
22 | column_titles_visible = true
23 | hide_root = true
24 |
25 | [node name="HBoxContainer2" type="HBoxContainer" parent="."]
26 | layout_mode = 2
27 |
28 | [node name="SelectOnlyBtn" type="Button" parent="HBoxContainer2"]
29 | unique_name_in_owner = true
30 | layout_mode = 2
31 | text = "对选中项进行生成"
32 |
33 | [node name="AllBtn" type="Button" parent="HBoxContainer2"]
34 | unique_name_in_owner = true
35 | layout_mode = 2
36 | text = "对所有项进行生成"
37 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends PanelContainer
3 |
4 | const _Localize = preload("../localization/localize.gd")
5 |
6 | signal delete_request
7 |
8 | @onready var name_line_edit: LineEdit = %NameLineEdit
9 | @onready var type_options: OptionButton = %TypeOptions
10 | @onready var setter_line_edit: LineEdit = %SetterLineEdit
11 | @onready var delete_btn: Button = %DeleteBtn
12 |
13 |
14 | func _ready() -> void:
15 | type_options.clear()
16 | for t in range(TYPE_MAX):
17 | if t in [TYPE_NIL, TYPE_OBJECT, TYPE_CALLABLE, TYPE_RID, TYPE_SIGNAL]:
18 | continue
19 | type_options.add_item(type_string(t).trim_prefix("Packed"), t)
20 | delete_btn.pressed.connect(func() -> void: delete_request.emit())
21 | _Localize.localize_node(self)
22 |
23 |
24 | func setup(prop_name: String, type: int, setter: String) -> void:
25 | if not is_node_ready():
26 | await ready
27 | name_line_edit.text = prop_name
28 | type_options.select(type_options.get_item_index(type))
29 | setter_line_edit.text = setter
30 |
31 |
32 | func get_property_name() -> String:
33 | return name_line_edit.text
34 |
35 |
36 | func get_type() -> int:
37 | return type_options.get_selected_id()
38 |
39 |
40 | func get_setter() -> String:
41 | return setter_line_edit.text
42 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="Config Table Manager"
4 | description="配置表格生成与导入工具。
5 | ## Features
6 |
7 | 1. Easy to use, generate table header by using a data class. Allow to add additional columns.
8 | 2. How to generate and import can be save as a preset, convenient to adjust repeatedly.
9 | 3. Support backup and merge when regenerating tables.
10 | 4. Highly customizable, you can add your **Table Tool** and **Import Tool** to generate table file and import as resource which are meet your needs. ( This plugin is provide **CSV**, **xlsx** table tools, and provide **GDScript** import tool.)
11 | 5. You can add Generate Modifier and Import Modifier to insert your logic for modify data in generating and importing workflow.
12 |
13 | ## 特性
14 |
15 | 1. 简单使用,根据数据类生成表头,允许添加附加列。
16 | 2. 生成与导入预设可保存,方便反复调节。
17 | 3. 支持重新生成表格时自动备份与合并。
18 | 4. 高度自定义,可添加自己的**表格工具**与**导入工具**,生成符合您需求的的表格文件或导入为你需要的资源格式。(该插件已提供**csv**,**xlsx**表格工具,以及**GDScript**导入工具。)
19 | 5. 可添加**生成修改器**与**导入修改器**,在表格生成与导入流程中插入你的自定义逻辑以修改要生成或导入的数据。
20 |
21 |
22 | others = \"诚接Godot游戏开发,欢迎邮件联系。/ Receive game development outsourcing, welcome to contact by email.\""
23 | author="忘忧の (Daylily Zeleen) - daylily-zeleen@foxmail.com"
24 | version="0.3.0"
25 | script="config_table_manager.daylily-zeleen.gd"
26 | support_link = "https://afdian.com/a/Daylily-Zeleen"
27 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/localization/localize.gd:
--------------------------------------------------------------------------------
1 | @tool
2 |
3 | static var _translations: Array[Translation] = []
4 |
5 |
6 | static func add_translation(translation: Translation) -> void:
7 | if translation in _translations:
8 | return
9 | _translations.push_front(translation)
10 |
11 |
12 | static func remote_translation(translation: Translation) -> void:
13 | _translations.erase(translation)
14 |
15 |
16 | static func clean_translations() -> void:
17 | _translations.clear()
18 |
19 |
20 | static func translate(msg: String) -> String:
21 | var local := TranslationServer.get_tool_locale()
22 | for translation: Translation in _translations.filter(func(t: Translation) -> bool: return t.locale == local):
23 | translation = translation as Translation
24 | var translated := translation.get_message(msg)
25 | if not translated.is_empty() and translated != msg:
26 | return translated
27 | return msg
28 |
29 |
30 | static func localize_node(node: Control) -> void:
31 | for n in node.get_children():
32 | if n is Control:
33 | localize_node(n)
34 | for p in node.get_property_list():
35 | if not p.type in [TYPE_STRING, TYPE_STRING_NAME]:
36 | continue
37 | var name := p.name as String
38 |
39 | var val := node.get(name) as StringName
40 | if val.is_empty():
41 | continue
42 |
43 | var translated := translate(val)
44 | if not translated.is_empty() and translated != val:
45 | node.set(name, translated)
46 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/config_table_manager.daylily-zeleen.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorPlugin
3 |
4 | const _Localize = preload("res://addons/config_table_manager.daylily-zeleen/localization/localize.gd")
5 | var plugin_control: Control
6 |
7 |
8 | func _enter_tree() -> void:
9 | const localization_dir = "res://addons/config_table_manager.daylily-zeleen/localization"
10 | for file in DirAccess.get_files_at(localization_dir):
11 | if file.get_file().get_extension().to_lower() != "po":
12 | continue
13 | _Localize.add_translation(ResourceLoader.load(localization_dir.path_join(file), "", ResourceLoader.CACHE_MODE_IGNORE))
14 |
15 | var path := _get_main_scene_path()
16 | plugin_control = ResourceLoader.load(path).instantiate()
17 | EditorInterface.get_editor_main_screen().add_child(plugin_control)
18 | EditorInterface.get_editor_main_screen().size_flags_vertical = Control.SIZE_EXPAND_FILL
19 | plugin_control.hide()
20 |
21 |
22 | func _exit_tree() -> void:
23 | _Localize.clean_translations()
24 |
25 |
26 | func _has_main_screen() -> bool:
27 | return true
28 |
29 |
30 | func _make_visible(visible: bool) -> void:
31 | plugin_control.visible = visible
32 |
33 |
34 | func _get_plugin_name() -> String:
35 | return "Config Table Manger"
36 |
37 |
38 | func _get_plugin_icon() -> Texture2D:
39 | return ResourceLoader.load(get_script().resource_path.get_base_dir().path_join("icon.svg"), "", ResourceLoader.CACHE_MODE_IGNORE)
40 |
41 |
42 | # =====================
43 | func _get_main_scene_path() -> String:
44 | return get_script().resource_path.get_base_dir().path_join("scenes/main_screen.tscn")
45 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd:
--------------------------------------------------------------------------------
1 | ## 用于在导入流场中修改数据,内部状态不会被复用,每次修改修改流程都会实例化一个修改器
2 | @tool
3 |
4 | const _Localize = preload("../localization/localize.gd")
5 |
6 |
7 | # 修改开始时调用, 典型应用是用于让继承者知道当前是为哪个数据类进行数据修改
8 | func _begin_modify(_table_name: String, _data_class_name: String, _data_class_script: String) -> void:
9 | pass
10 |
11 |
12 | func _modify_table_name(table_name: String) -> String:
13 | return table_name
14 |
15 |
16 | func _modify_custom_setters(custom_setters: Dictionary) -> Dictionary:
17 | return custom_setters
18 |
19 |
20 | func _modify_data(data: Array[Dictionary]) -> Array[Dictionary]:
21 | return data
22 |
23 |
24 | func _modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray:
25 | return meta_list
26 |
27 |
28 | func _modify_import_tool_options(options: PackedStringArray) -> PackedStringArray:
29 | return options
30 |
31 |
32 | # ----------------------
33 | func begin_modify(table_name: String, data_class_name: String, data_class_script: String) -> void:
34 | _begin_modify(table_name, data_class_name, data_class_script)
35 |
36 |
37 | func modify_table_name(table_name: String) -> String:
38 | return _modify_table_name(table_name)
39 |
40 |
41 | func modify_custom_setters(custom_setters: Dictionary) -> Dictionary:
42 | return _modify_custom_setters(custom_setters)
43 |
44 |
45 | func modify_data(data: Array[Dictionary]) -> Array[Dictionary]:
46 | return _modify_data(data)
47 |
48 |
49 | func modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray:
50 | if self.has_method("_modify_metas"):
51 | push_warning("'_modify_metas()' is deprecated, override '_modify_meta_list()' instead.")
52 | return self.call("_modify_metas", meta_list)
53 | else:
54 | return _modify_meta_list(meta_list)
55 |
56 |
57 | func modify_import_tool_options(options: PackedStringArray) -> PackedStringArray:
58 | return _modify_import_tool_options(options)
59 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd:
--------------------------------------------------------------------------------
1 | ## 表格导入工具基类,子类必须能够被无参构造
2 | ## 重写 _ 开头的方法已自定义您的导入逻辑
3 | @tool
4 |
5 | const _TableHeader = preload("../scripts/table_header.gd")
6 | const _Log = preload("../scripts/log.gd")
7 | const _Localize = preload("../localization/localize.gd")
8 |
9 | func _import(
10 | _import_path: String,
11 | _table_name: String,
12 | _header: _TableHeader,
13 | _data_class_name: String,
14 | _data_class_script: String,
15 | _instantiation: String,
16 | _custom_setters: Dictionary,
17 | _data_rows: Array[Dictionary],
18 | _options: PackedStringArray
19 | ) -> Error:
20 | assert(false, "Unimplemented")
21 | _Log.error(["Unimplemented: _import"])
22 | return FAILED
23 |
24 |
25 | func _get_import_file_extension() -> String:
26 | assert(false, "Unimplemented")
27 | _Log.error(["Unimplemented: _get_import_file_extension"])
28 | return ""
29 |
30 |
31 | func _get_tooltip_text() -> String:
32 | return ""
33 |
34 | #-----------------------
35 | func is_meta_filed(field_name: String) -> bool:
36 | return field_name.begins_with("#")
37 |
38 |
39 | func to_type_id(type_text: String) -> int:
40 | type_text = type_text.strip_edges()
41 | for i in range(TYPE_MAX):
42 | if type_text == type_string(i):
43 | return i
44 | return -1
45 |
46 |
47 | func import(
48 | import_path: String,
49 | table_name: String,
50 | header: _TableHeader,
51 | data_class_name: String,
52 | data_class_script: String,
53 | instantiation: String,
54 | custom_setters: Dictionary,
55 | data_rows: Array[Dictionary],
56 | options: PackedStringArray
57 | ) -> Error:
58 | return _import(import_path, table_name, header, data_class_name, data_class_script, instantiation, custom_setters, data_rows, options)
59 |
60 |
61 | func get_import_file_extension() -> String:
62 | return _get_import_file_extension()
63 |
64 |
65 | #region Tools
66 | func get_tooltip_text() -> String:
67 | return _get_tooltip_text()
68 |
69 |
70 | func get_kv_option(option: String) -> Dictionary:
71 | var splits := option.split("=", false)
72 | assert(splits.size() == 2, "\"%s\" is not a valid key-value option text." % option)
73 | var ret := {}
74 | ret[splits[0].strip_edges()] = splits[1].strip_edges()
75 | return ret
76 |
77 |
78 | func parse_options(options: PackedStringArray) -> Dictionary:
79 | var ret := {}
80 | for op in options:
81 | if "=" in op:
82 | ret.merge(get_kv_option(op))
83 | else:
84 | ret[op.strip_edges()] = null
85 | return ret
86 |
87 | #endregion Tools
88 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd:
--------------------------------------------------------------------------------
1 | ## 在生成表格流程中修改数据,内部状态不会被复用,每次修改都会实例化一个修改器,
2 | @tool
3 |
4 | const _Localize = preload("../localization/localize.gd")
5 |
6 |
7 | # 修改开始时调用, 典型应用是用于让继承者知道当前是为哪个数据类进行数据修改
8 | func _begin_modify(_table_name: String, _data_class_name: String, _data_class_script: String) -> void:
9 | pass
10 |
11 |
12 | ## 修改后的字段与类型数量必须匹配,且类型必须时表格工具支持的类型
13 | func _modify_fields_definitions(_in_out_fields: PackedStringArray, _in_out_types: PackedByteArray) -> void:
14 | pass
15 |
16 |
17 | func _modify_data(data: Array[Dictionary]) -> Array[Dictionary]:
18 | return data
19 |
20 |
21 | ## descriptions: Key 为字段名(String), Value 为描述(String)
22 | func _modify_descriptions(descriptions: Dictionary) -> Dictionary:
23 | return descriptions
24 |
25 |
26 | func _modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray:
27 | return meta_list
28 |
29 |
30 | func _modify_table_tool_options(options: PackedStringArray) -> PackedStringArray:
31 | return options
32 |
33 |
34 | # --------------------------
35 | func begin_modify(table_name: String, data_class_name: String, data_class_script: String) -> void:
36 | _begin_modify(table_name, data_class_name, data_class_script)
37 |
38 |
39 | func modify_fields_definitions(in_out_fields: PackedStringArray, in_out_types: PackedByteArray) -> bool:
40 | if self.has_method("_modify_fileds_definitions"):
41 | push_warning("'_modify_fileds_definitions()' is deprecated, override '_modify_fields_definitions()' instead.")
42 | self.call("_modify_fileds_definitions", in_out_fields, in_out_types)
43 | else:
44 | _modify_fields_definitions(in_out_fields, in_out_types)
45 | if in_out_fields.size() != in_out_types.size():
46 | preload("log.gd").error([_Localize.translate(&"修改后的字段数量与类型属性不对应。")])
47 | return false
48 | return true
49 |
50 |
51 | func modify_data(data: Array[Dictionary]) -> Array[Dictionary]:
52 | return _modify_data(data)
53 |
54 |
55 | ## descriptions: Key 为字段名(String), Value 为描述(String)
56 | func modify_descriptions(descriptions: Dictionary) -> Dictionary:
57 | return _modify_descriptions(descriptions)
58 |
59 |
60 | func modify_meta_list(meta_list: PackedStringArray) -> PackedStringArray:
61 | if self.has_method("_modify_metas"):
62 | push_warning("'_modify_metas()' is deprecated, override '_modify_meta_list()' instead.")
63 | return self.call("_modify_metas", meta_list)
64 | else:
65 | return _modify_meta_list(meta_list)
66 |
67 |
68 | func modify_table_tool_options(options: PackedStringArray) -> PackedStringArray:
69 | return _modify_table_tool_options(options)
70 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/settings.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://bwb7n8a3xx0bu"]
2 |
3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/settings.gd" id="1_klie4"]
4 |
5 | [node name="设置" type="PanelContainer"]
6 | anchors_preset = 15
7 | anchor_right = 1.0
8 | anchor_bottom = 1.0
9 | grow_horizontal = 2
10 | grow_vertical = 2
11 | size_flags_horizontal = 3
12 | size_flags_vertical = 3
13 | script = ExtResource("1_klie4")
14 | table_tools = {
15 | "CSV(,分隔)": "res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd"
16 | }
17 | import_tools = {
18 | "默认GDScript导入": "res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd"
19 | }
20 | metadata/_tab_index = 1
21 |
22 | [node name="MarginContainer" type="MarginContainer" parent="."]
23 | layout_mode = 2
24 | theme_override_constants/margin_left = 20
25 | theme_override_constants/margin_top = 20
26 | theme_override_constants/margin_right = 20
27 | theme_override_constants/margin_bottom = 20
28 |
29 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
30 | layout_mode = 2
31 |
32 | [node name="GridContainer" type="GridContainer" parent="MarginContainer/VBoxContainer"]
33 | layout_mode = 2
34 | columns = 3
35 |
36 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/GridContainer"]
37 | layout_mode = 2
38 | text = "预设保存路径:"
39 |
40 | [node name="PresetSaveDirLineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/GridContainer"]
41 | unique_name_in_owner = true
42 | layout_mode = 2
43 | size_flags_horizontal = 3
44 | text = "res://tables/presets/"
45 | caret_blink = true
46 | caret_blink_interval = 0.5
47 |
48 | [node name="PresetDirSelectBtn" type="Button" parent="MarginContainer/VBoxContainer/GridContainer"]
49 | unique_name_in_owner = true
50 | layout_mode = 2
51 | text = "选择"
52 |
53 | [node name="TableToolsTree" type="Tree" parent="MarginContainer/VBoxContainer"]
54 | unique_name_in_owner = true
55 | layout_mode = 2
56 | size_flags_vertical = 3
57 | columns = 3
58 | column_titles_visible = true
59 | hide_root = true
60 |
61 | [node name="ImportToolsTree" type="Tree" parent="MarginContainer/VBoxContainer"]
62 | unique_name_in_owner = true
63 | layout_mode = 2
64 | size_flags_vertical = 3
65 | columns = 3
66 | column_titles_visible = true
67 | hide_root = true
68 |
69 | [node name="BoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
70 | layout_mode = 2
71 | size_flags_vertical = 10
72 | alignment = 1
73 |
74 | [node name="SaveBtn" type="Button" parent="MarginContainer/VBoxContainer/BoxContainer"]
75 | unique_name_in_owner = true
76 | layout_mode = 2
77 | text = "保存设置"
78 |
79 | [node name="FileDialog" type="FileDialog" parent="."]
80 | unique_name_in_owner = true
81 | title = "Open a Directory"
82 | position = Vector2i(384, 216)
83 | size = Vector2i(1152, 648)
84 | ok_button_text = "选择当前文件夹"
85 | file_mode = 2
86 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://ckg7862ktuel8"]
2 |
3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd" id="1_nbwpx"]
4 |
5 | [node name="AdditionalPropertyEdit" type="PanelContainer"]
6 | offset_right = 1130.0
7 | offset_bottom = 44.0
8 | script = ExtResource("1_nbwpx")
9 |
10 | [node name="HBoxContainer" type="HBoxContainer" parent="."]
11 | layout_mode = 2
12 |
13 | [node name="NameLineEdit" type="LineEdit" parent="HBoxContainer"]
14 | unique_name_in_owner = true
15 | layout_mode = 2
16 | size_flags_horizontal = 3
17 | size_flags_stretch_ratio = 0.49
18 | placeholder_text = "要附加的属性名称"
19 |
20 | [node name="TypeOptions" type="OptionButton" parent="HBoxContainer"]
21 | unique_name_in_owner = true
22 | custom_minimum_size = Vector2(120, 0)
23 | layout_mode = 2
24 | item_count = 33
25 | popup/item_0/text = "bool"
26 | popup/item_0/id = 1
27 | popup/item_1/text = "int"
28 | popup/item_1/id = 2
29 | popup/item_2/text = "float"
30 | popup/item_2/id = 3
31 | popup/item_3/text = "String"
32 | popup/item_3/id = 4
33 | popup/item_4/text = "Vector2"
34 | popup/item_4/id = 5
35 | popup/item_5/text = "Vector2i"
36 | popup/item_5/id = 6
37 | popup/item_6/text = "Rect2"
38 | popup/item_6/id = 7
39 | popup/item_7/text = "Rect2i"
40 | popup/item_7/id = 8
41 | popup/item_8/text = "Vector3"
42 | popup/item_8/id = 9
43 | popup/item_9/text = "Vector3i"
44 | popup/item_9/id = 10
45 | popup/item_10/text = "Transform2D"
46 | popup/item_10/id = 11
47 | popup/item_11/text = "Vector4"
48 | popup/item_11/id = 12
49 | popup/item_12/text = "Vector4i"
50 | popup/item_12/id = 13
51 | popup/item_13/text = "Plane"
52 | popup/item_13/id = 14
53 | popup/item_14/text = "Quaternion"
54 | popup/item_14/id = 15
55 | popup/item_15/text = "AABB"
56 | popup/item_15/id = 16
57 | popup/item_16/text = "Basis"
58 | popup/item_16/id = 17
59 | popup/item_17/text = "Transform3D"
60 | popup/item_17/id = 18
61 | popup/item_18/text = "Projection"
62 | popup/item_18/id = 19
63 | popup/item_19/text = "Color"
64 | popup/item_19/id = 20
65 | popup/item_20/text = "StringName"
66 | popup/item_20/id = 21
67 | popup/item_21/text = "NodePath"
68 | popup/item_21/id = 22
69 | popup/item_22/text = "Dictionary"
70 | popup/item_22/id = 27
71 | popup/item_23/text = "Array"
72 | popup/item_23/id = 28
73 | popup/item_24/text = "ByteArray"
74 | popup/item_24/id = 29
75 | popup/item_25/text = "Int32Array"
76 | popup/item_25/id = 30
77 | popup/item_26/text = "Int64Array"
78 | popup/item_26/id = 31
79 | popup/item_27/text = "Float32Array"
80 | popup/item_27/id = 32
81 | popup/item_28/text = "Float64Array"
82 | popup/item_28/id = 33
83 | popup/item_29/text = "StringArray"
84 | popup/item_29/id = 34
85 | popup/item_30/text = "Vector2Array"
86 | popup/item_30/id = 35
87 | popup/item_31/text = "Vector3Array"
88 | popup/item_31/id = 36
89 | popup/item_32/text = "ColorArray"
90 | popup/item_32/id = 37
91 |
92 | [node name="SetterLineEdit" type="LineEdit" parent="HBoxContainer"]
93 | unique_name_in_owner = true
94 | layout_mode = 2
95 | size_flags_horizontal = 3
96 | placeholder_text = "非空将使用自定义Setter进行设置"
97 |
98 | [node name="DeleteBtn" type="Button" parent="HBoxContainer"]
99 | unique_name_in_owner = true
100 | layout_mode = 2
101 | text = " - "
102 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends VBoxContainer
3 |
4 | const _Preset = preload("../scripts/preset.gd")
5 | const _Log = _Preset._Log
6 | const _Localize = _Preset._Localize
7 |
8 | enum Mode {
9 | GENERATE_TABLE,
10 | IMPORT_TABLE,
11 | }
12 |
13 | @export var title: String:
14 | set(v):
15 | if not is_node_ready():
16 | await ready
17 | %TitleLabel.text = v
18 | get:
19 | return %TitleLabel.text
20 |
21 | @export var mode: Mode:
22 | set(v):
23 | mode = v
24 | if mode == Mode.GENERATE_TABLE:
25 | %SelectOnlyBtn.text = _Localize.translate("对选中项进行生成")
26 | %AllBtn.text = _Localize.translate("对所有项进行生成")
27 | else:
28 | %SelectOnlyBtn.text = _Localize.translate("对选中项执行导入")
29 | %AllBtn.text = _Localize.translate("对所有项执行导入")
30 |
31 | var save_path: String
32 |
33 | @onready var _tree: Tree = %Tree
34 |
35 |
36 | func _ready() -> void:
37 | %SelectOnlyBtn.pressed.connect(_on_select_only_btn_pressed)
38 | %AllBtn.pressed.connect(_on_all_btn_pressed)
39 |
40 | mode = mode
41 |
42 | _tree.clear()
43 | _tree.create_item()
44 | _tree.column_titles_visible = true
45 | _tree.set_column_title(0, _Localize.translate("选中"))
46 | _tree.set_column_title(1, _Localize.translate("排除"))
47 | _tree.set_column_title(2, _Localize.translate("预设"))
48 | _tree.set_column_expand(0, false)
49 | _tree.set_column_expand(1, false)
50 | _tree.item_edited.connect(_on_tree_item_edited)
51 |
52 |
53 | func setup(presets: Array[_Preset]) -> void:
54 | for item in _tree.get_root().get_children():
55 | _tree.get_root().remove_child(item)
56 | item.free()
57 |
58 | for p in presets:
59 | var item := _tree.get_root().create_child() as TreeItem
60 | item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK)
61 | item.set_cell_mode(1, TreeItem.CELL_MODE_CHECK)
62 | item.set_text(2, "%s (%s)" % [p.name, p.resource_path])
63 | item.set_editable(0, true)
64 | item.set_editable(1, true)
65 | item.set_metadata(2, p.resource_path)
66 |
67 | _load_archive()
68 |
69 |
70 | # -----------------------
71 | func _on_select_only_btn_pressed() -> void:
72 | _execute(true)
73 |
74 |
75 | func _on_all_btn_pressed() -> void:
76 | _execute(false)
77 |
78 |
79 | func _on_tree_item_edited() -> void:
80 | if save_path.is_empty():
81 | return
82 |
83 | var presets := {}
84 |
85 | for item in _tree.get_root().get_children():
86 | if not item.is_checked(0) and not item.is_checked(1):
87 | continue
88 | var preset_file := item.get_metadata(2) as String
89 | if not presets.has(preset_file):
90 | presets[preset_file] = {}
91 | if item.is_checked(0):
92 | presets[preset_file]["include"] = true
93 | if item.is_checked(1):
94 | presets[preset_file]["exclude"] = true
95 |
96 | if not DirAccess.dir_exists_absolute(save_path.get_base_dir()):
97 | DirAccess.make_dir_recursive_absolute(save_path.get_base_dir())
98 |
99 | var f := FileAccess.open(save_path, FileAccess.WRITE)
100 | if not is_instance_valid(f):
101 | printerr("BUG, Open File failed: ", error_string(FileAccess.get_open_error()))
102 | return
103 |
104 | f.store_var(presets)
105 | f.close()
106 |
107 |
108 | # -------------
109 | func _load_archive() -> void:
110 | if not FileAccess.file_exists(save_path):
111 | return
112 |
113 | var f := FileAccess.open(save_path, FileAccess.READ)
114 | if not is_instance_valid(f):
115 | printerr("BUG, open file failed: ", error_string(FileAccess.get_open_error()))
116 | return
117 |
118 | var presets: Variant = f.get_var()
119 | if typeof(presets) != TYPE_DICTIONARY:
120 | printerr("BUG, Parse error")
121 | return
122 |
123 | for item in _tree.get_root().get_children():
124 | var preset_file := item.get_metadata(2) as String
125 |
126 | if not presets.has(preset_file):
127 | continue
128 |
129 | item.set_checked(0, presets[preset_file].get("include", false))
130 | item.set_checked(1, presets[preset_file].get("exclude", false))
131 |
132 |
133 | func _execute(select_only: bool) -> void:
134 | for item in _tree.get_root().get_children():
135 | if select_only and not item.is_checked(0):
136 | # 仅选中项
137 | continue
138 | if item.is_checked(1):
139 | # 排除
140 | continue
141 | var path := item.get_metadata(2) as String
142 | var preset := ResourceLoader.load(path, "Resource", ResourceLoader.CACHE_MODE_IGNORE) as _Preset
143 | if not is_instance_valid(preset):
144 | _Log.error([_Localize.translate("表格预设不存在: "), path])
145 | continue
146 |
147 | if preset.resource_path.is_empty():
148 | preset.take_over_path(path)
149 |
150 | if mode == Mode.GENERATE_TABLE:
151 | preset.generate_table()
152 | else:
153 | preset.import_table()
154 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd:
--------------------------------------------------------------------------------
1 | ## 表格工具基类,子类必须能够被无参构造
2 | ## 重写 _ 开头的方法已自定义您的解析表格与生成表格逻辑
3 | @tool
4 |
5 | const _Log = preload("../scripts/log.gd")
6 | const _TableHeader = preload("../scripts/table_header.gd")
7 | const _Localize = preload("../localization/localize.gd")
8 |
9 |
10 | func _get_support_types() -> PackedByteArray:
11 | assert(false, "unimplemented")
12 | _Log.error(["Unimplemented: _get_support_types"])
13 | return []
14 |
15 |
16 | func _get_parse_error() -> Error:
17 | assert(false, "unimplemented")
18 | _Log.error(["Unimplemented: _get_parse_error"])
19 | return ERR_PARSE_ERROR
20 |
21 |
22 | func _parse_table_file(_table_file: String, _options: PackedStringArray) -> Error:
23 | assert(false, "unimplemented")
24 | _Log.error(["Unimplemented: _parse_table_file"])
25 | return ERR_PARSE_ERROR
26 |
27 |
28 | func _get_table_file_extension() -> String:
29 | assert(false, "unimplemented")
30 | _Log.error(["Unimplemented: _get_table_file_extension"])
31 | return ""
32 |
33 |
34 | func _generate_table_file(_save_path: String, _table_header: _TableHeader, _data_rows: Array[PackedStringArray], _options: PackedStringArray) -> Error:
35 | assert(false, "unimplemented")
36 | _Log.error(["Unimplemented: _generate_table_file"])
37 | return FAILED
38 |
39 |
40 | func _to_value_text(_value: Variant) -> String:
41 | assert(false, "unimplemented")
42 | _Log.error(["Unimplemented: _to_value_text"])
43 | return ""
44 |
45 |
46 | func _parse_value(_text: String, _type_id: int) -> Variant:
47 | assert(false, "unimplemented")
48 | _Log.error(["Unimplemented: _parse_value"])
49 | return null
50 |
51 |
52 | # 获取解析结果->表头
53 | func _get_header() -> _TableHeader:
54 | assert(false, "unimplemented")
55 | _Log.error(["Unimplemented: _get_header"])
56 | return null
57 |
58 |
59 | # 获取解析结果->数据
60 | func _get_data() -> Array[Dictionary]:
61 | assert(false, "unimplemented")
62 | _Log.error(["Unimplemented: _get_data"])
63 | return []
64 |
65 |
66 | func _get_tooltip_text() -> String:
67 | return ""
68 |
69 | # -----------
70 | func is_meta_filed(field_name: String) -> bool:
71 | return field_name.begins_with("#")
72 |
73 |
74 | func get_type_id(type_text: String) -> int:
75 | type_text = type_text.strip_edges()
76 | for t in _get_support_types():
77 | if type_string(t) == type_text:
78 | return t
79 | _Log.error([_Localize.translate("不支持的类型: "), type_text])
80 | return -1
81 |
82 |
83 | func to_data_rows(data: Array[Dictionary], fields: PackedStringArray, types: PackedByteArray) -> Array[PackedStringArray]:
84 | if fields.size() != types.size():
85 | _Log.error([_Localize.translate("无法转换,类型与字段不符")])
86 | return []
87 |
88 | for t in types:
89 | if not t in _get_support_types():
90 | _Log.error([_Localize.translate("无法转换,不支持的类型: "), type_string(t)])
91 | return []
92 |
93 | var ret: Array[PackedStringArray] = []
94 | for d in data:
95 | var r: PackedStringArray = []
96 | r.resize(types.size())
97 | for i in range(fields.size()):
98 | var f := fields[i].strip_edges()
99 | var t := types[i]
100 | if not f in d:
101 | continue
102 |
103 | r[i] = _to_value_text(type_convert(d[f], t))
104 | ret.push_back(r)
105 |
106 | return ret
107 |
108 |
109 | func get_support_types() -> PackedByteArray:
110 | return _get_support_types()
111 |
112 |
113 | func get_parse_error() -> Error:
114 | return _get_parse_error()
115 |
116 |
117 | func parse_table_file(file: String, options: PackedStringArray) -> Error:
118 | return _parse_table_file(file, options)
119 |
120 |
121 | func get_table_file_extension() -> String:
122 | return _get_table_file_extension()
123 |
124 |
125 | func generate_table_file(save_path: String, table_header: _TableHeader, data_rows: Array[PackedStringArray], options: PackedStringArray) -> Error:
126 | return _generate_table_file(save_path, table_header, data_rows, options)
127 |
128 |
129 | func to_value_text(value: Variant) -> String:
130 | return _to_value_text(value)
131 |
132 |
133 | func parse_value(text: String, type_id: int) -> Variant:
134 | return _parse_value(text, type_id)
135 |
136 |
137 | func get_header() -> _TableHeader:
138 | return _get_header()
139 |
140 |
141 | func get_data() -> Array[Dictionary]:
142 | return _get_data()
143 |
144 |
145 | #region Tools
146 | func get_tooltip_text() -> String:
147 | return _get_tooltip_text()
148 |
149 |
150 | func get_kv_option(option: String) -> Dictionary:
151 | var splits := option.split("=", false)
152 | assert(splits.size() == 2, "\"%s\" is not a valid key-value option text." % option)
153 | var ret := {}
154 | ret[splits[0].strip_edges()] = splits[1].strip_edges()
155 | return ret
156 |
157 |
158 | func parse_options(options: PackedStringArray) -> Dictionary:
159 | var ret := {}
160 | for op in options:
161 | if "=" in op:
162 | ret.merge(get_kv_option(op))
163 | else:
164 | ret[op.strip_edges()] = null
165 | return ret
166 |
167 | #endregion Tools
168 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
168 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/settings.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends PanelContainer
3 |
4 | const _Localize = preload("../localization/localize.gd")
5 |
6 | signal tools_updated
7 |
8 | var presets_dir: String:
9 | set(v):
10 | _preset_save_dir_line_edit.text = v
11 | get:
12 | return _preset_save_dir_line_edit.text
13 |
14 | @export var table_tools: Dictionary:
15 | set(v):
16 | table_tools = v
17 | tools_updated.emit()
18 | get:
19 | return _validate_tools(table_tools, true)
20 | @export var import_tools: Dictionary:
21 | set(v):
22 | import_tools = v
23 | tools_updated.emit()
24 | get:
25 | return _validate_tools(import_tools, false)
26 |
27 | @onready var _preset_save_dir_line_edit: LineEdit = %PresetSaveDirLineEdit
28 | @onready var _preset_dir_select_btn: Button = %PresetDirSelectBtn
29 |
30 | @onready var _table_tools_tree: Tree = %TableToolsTree
31 | @onready var _import_tools_tree: Tree = %ImportToolsTree
32 |
33 | @onready var _save_btn: Button = %SaveBtn
34 | @onready var _file_dialog: FileDialog = %FileDialog
35 |
36 | const DEFAULT_TABLE_TOOL = {
37 | "CSV(,分隔)": "res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd",
38 | "Excel(xlsx)": "res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd",
39 | }
40 |
41 | const DEFAULT_IMPORT_TOOL = {
42 | "GDScript(TypedArray风格)": "res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd",
43 | "GDScript(Dictionary风格)": "res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd",
44 | }
45 |
46 |
47 | func _ready() -> void:
48 | _preset_dir_select_btn.pressed.connect(func() -> void: _file_dialog.popup_centered_ratio(0.6))
49 | _file_dialog.dir_selected.connect(func(dir: String) -> void: _preset_save_dir_line_edit.text = dir)
50 | _save_btn.pressed.connect(_on_save_btn_pressed)
51 |
52 | _table_tools_tree.clear()
53 | _table_tools_tree.create_item()
54 | _table_tools_tree.set_column_title(0, _Localize.translate("表格工具"))
55 | _table_tools_tree.set_column_title(1, _Localize.translate("脚本路径"))
56 | _table_tools_tree.set_column_title(2, _Localize.translate("+"))
57 | _table_tools_tree.set_column_expand(1, true)
58 | _table_tools_tree.set_column_expand(2, false)
59 | _table_tools_tree.column_title_clicked.connect(_on_tree_column_title_clicked.bind(_table_tools_tree))
60 | _table_tools_tree.item_edited.connect(_on_tree_item_edited.bind(_table_tools_tree))
61 | _table_tools_tree.button_clicked.connect(_on_tree_button_clicked.bind(_table_tools_tree))
62 |
63 | _import_tools_tree.clear()
64 | _import_tools_tree.create_item()
65 | _import_tools_tree.set_column_title(0, _Localize.translate("导入工具"))
66 | _import_tools_tree.set_column_title(1, _Localize.translate("脚本路径"))
67 | _import_tools_tree.set_column_title(2, _Localize.translate("+"))
68 | _import_tools_tree.set_column_expand(1, true)
69 | _import_tools_tree.set_column_expand(2, false)
70 | _import_tools_tree.column_title_clicked.connect(_on_tree_column_title_clicked.bind(_import_tools_tree))
71 | _import_tools_tree.item_edited.connect(_on_tree_item_edited.bind(_import_tools_tree))
72 | _import_tools_tree.button_clicked.connect(_on_tree_button_clicked.bind(_import_tools_tree))
73 |
74 | _refresh()
75 |
76 |
77 | func _refresh() -> void:
78 | var root := _table_tools_tree.get_root()
79 | for item in root.get_children():
80 | root.remove_child(item)
81 | item.free()
82 | for k: String in DEFAULT_TABLE_TOOL:
83 | _add_tree_item(root, k, DEFAULT_TABLE_TOOL[k], false)
84 | for n: String in table_tools:
85 | if table_tools[n] in DEFAULT_TABLE_TOOL.values():
86 | continue
87 | _add_tree_item(root, n, table_tools[n])
88 | _refresh_tools(_table_tools_tree)
89 |
90 | root = _import_tools_tree.get_root()
91 | for item in root.get_children():
92 | root.remove_child(item)
93 | item.free()
94 | for k: String in DEFAULT_IMPORT_TOOL:
95 | _add_tree_item(root, k, DEFAULT_IMPORT_TOOL[k], false)
96 | for n: String in import_tools:
97 | if import_tools[n] in DEFAULT_IMPORT_TOOL.values():
98 | continue
99 | _add_tree_item(root, n, import_tools[n])
100 | _refresh_tools(_import_tools_tree)
101 |
102 |
103 | func _add_tree_item(parent: TreeItem, p_name: String, path: String, editable := true) -> void:
104 | var item := parent.create_child()
105 | item.set_text(0, _Localize.translate(p_name))
106 | item.set_text(1, path)
107 | item.set_editable(0, editable)
108 | item.set_editable(1, editable)
109 |
110 | if editable:
111 | item.add_button(2, EditorInterface.get_editor_theme().get_icon(&"Remove", &"EditorIcons"), 0)
112 | item.set_text(2, "-")
113 |
114 |
115 | func _refresh_tools(tree: Tree) -> void:
116 | var map := {}
117 | for item in tree.get_root().get_children():
118 | var n := item.get_text(0).strip_edges()
119 | if map.has(n):
120 | continue
121 | map[n] = item.get_text(1)
122 |
123 | if tree == _table_tools_tree:
124 | table_tools = map
125 | elif tree == _import_tools_tree:
126 | import_tools = map
127 |
128 |
129 | func _validate_tools(tools: Dictionary, for_table_tool: bool) -> Dictionary:
130 | var ret := {}
131 | var required_base: Script
132 | if for_table_tool:
133 | required_base = ResourceLoader.load("res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd", "Script", ResourceLoader.CACHE_MODE_IGNORE)
134 | else:
135 | required_base = ResourceLoader.load("res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd", "Script", ResourceLoader.CACHE_MODE_IGNORE)
136 |
137 | for n: String in tools:
138 | n = n.strip_edges()
139 | var path := tools[n] as String
140 | if n.is_empty():
141 | continue
142 | if not ResourceLoader.exists(path, "Script"):
143 | continue
144 | var s := ResourceLoader.load(path, "Script", ResourceLoader.CACHE_MODE_IGNORE) as Script
145 | if not is_instance_valid(s):
146 | continue
147 |
148 | var valid := false
149 | var base := s.get_base_script()
150 | while base:
151 | if base == required_base:
152 | valid = true
153 | break
154 | base = base.get_base_script()
155 |
156 | if valid:
157 | ret[n] = path
158 |
159 | return ret
160 |
161 |
162 | # --------
163 | func _on_tree_column_title_clicked(column: int, mouse_button_index: int, tree: Tree) -> void:
164 | if column != 2 or mouse_button_index != MOUSE_BUTTON_LEFT:
165 | return
166 | _add_tree_item(tree.get_root(), "tool name", "script path")
167 |
168 |
169 | func _on_tree_item_edited(tree: Tree) -> void:
170 | _refresh_tools(tree)
171 |
172 |
173 | func _on_tree_button_clicked(item: TreeItem, column: int, id: int, mouse_button_index: int, tree: Tree) -> void:
174 | if column != 2 or mouse_button_index != MOUSE_BUTTON_LEFT or id != 0:
175 | return
176 | if not item.is_editable(0):
177 | return
178 | item.get_parent().remove_child(item)
179 | item.free()
180 |
181 | _refresh_tools(tree)
182 |
183 |
184 | func _on_save_btn_pressed() -> void:
185 | var scene_path := (get_script().resource_path as String).trim_suffix("gd") + "tscn"
186 | var ps := PackedScene.new()
187 | var err := ps.pack(self)
188 | if err != OK:
189 | printerr(_Localize.translate("保存设置失败: "), error_string(err))
190 |
191 | err = ResourceSaver.save(ps, scene_path)
192 | if err != OK:
193 | printerr(_Localize.translate("保存设置失败: "), error_string(err))
194 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/table_tools/py/xlsx_json.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import logging
3 |
4 | # 检查
5 | _result = subprocess.run("pip show openpyxl", shell=True, capture_output=True)
6 | if _result.returncode != 0:
7 | logging.error("This tool is require 'openpyxl' package, please run: pip install openpyxl")
8 | exit(1)
9 |
10 |
11 | import sys
12 | import json
13 | import os
14 | import openpyxl
15 | from openpyxl import Workbook, cell
16 | from openpyxl.styles.colors import Color, RGB
17 | from openpyxl.styles.borders import Border, Side
18 |
19 |
20 | def dump_xlsx_to_json(xlsx_file_path: str, output_json_path: str):
21 | workbook: Workbook = openpyxl.load_workbook(xlsx_file_path)
22 |
23 | data: dict[str, dict] = {}
24 |
25 | for sheet_name in workbook.sheetnames:
26 | sheet = workbook[sheet_name]
27 | # Sheet
28 | sheet_data: list[list[dict[str, None]]] = []
29 | for row in range(sheet.max_row):
30 | row_data: list[dict] = []
31 | for column in range(sheet.max_column):
32 | cell = sheet.cell(row=row + 1, column=column + 1)
33 | row_data.append(
34 | {
35 | "value": cell.value,
36 | "fill": _to_dict(cell.fill),
37 | "border": _to_dict(cell.border),
38 | # TODO: 存储更多的单元格属性
39 | }
40 | )
41 | sheet_data.append(row_data)
42 |
43 | data[sheet_name] = {
44 | "data": sheet_data,
45 | "sheet_format": _to_dict(sheet.sheet_format),
46 | # TODO: 存储更多的Sheet属性
47 | }
48 |
49 | workbook.close()
50 | f = open(output_json_path, "w", encoding="utf8")
51 | json.dump(obj=data, fp=f, indent="\t")
52 | f.close()
53 | return True
54 |
55 |
56 | def override_xlsx_worksheets_from_json(json_file_path: str, output_xlsx_file_path: str):
57 | f = open(json_file_path, "r", encoding="utf8")
58 | data: dict[str, dict] = json.load(f)
59 |
60 | if len(data) <= 0:
61 | logging.error("The input json has not data.")
62 | exit(1)
63 |
64 | workbook: Workbook
65 | default_sheetnames: list[str] = []
66 | if os.path.exists(output_xlsx_file_path):
67 | workbook = openpyxl.load_workbook(output_xlsx_file_path)
68 | else:
69 | workbook = Workbook()
70 | default_sheetnames = workbook.sheetnames
71 |
72 | for sheet_name in data:
73 | if sheet_name in default_sheetnames:
74 | default_sheetnames.remove(sheet_name)
75 |
76 | # 覆盖
77 | if not sheet_name in workbook.sheetnames:
78 | workbook.create_sheet(sheet_name)
79 | sheet = workbook[sheet_name]
80 |
81 | if "sheet_format" in data[sheet_name]:
82 | sheet_format: dict = data[sheet_name]["sheet_format"]
83 | for k in sheet_format:
84 | setattr(sheet.sheet_format, k, sheet_format[k])
85 | # TODO 其他表格属性
86 |
87 | # 数据行
88 | sheet_data: list[list[dict[str, None]]] = data[sheet_name]["data"]
89 | for row in range(len(sheet_data)):
90 | for column in range(len(sheet_data[row])):
91 | cell_data: dict[str, None] = sheet_data[row][column]
92 | sheet.cell(row=row + 1, column=column + 1, value=cell_data["value"])
93 | cell = sheet.cell(row=row + 1, column=column + 1)
94 | if "fill" in cell_data:
95 | _set_cell_fill(cell, cell_data["fill"])
96 | if "border" in cell_data:
97 | _set_cell_border(cell, cell_data["border"])
98 | # TODO: 其他单元格属性(先检查是否有对应属性)
99 |
100 | # 清除右侧的多余格
101 | for column in range(len(sheet_data[row]), sheet.max_column):
102 | cell = sheet.cell(row=row + 1, column=column + 1)
103 | if not cell.value is None:
104 | cell.value = ""
105 |
106 | for sheet_name in default_sheetnames:
107 | if sheet_name in workbook:
108 | del workbook[sheet_name]
109 |
110 | workbook.save(output_xlsx_file_path)
111 | workbook.close()
112 |
113 |
114 | def _to_dict(obj: None) -> dict:
115 | if not hasattr(obj, "__dict__"):
116 | return obj
117 | ret = {}
118 | for k in vars(obj):
119 | if k.startswith("_"):
120 | continue
121 | v = getattr(obj, k)
122 | if hasattr(v, "__dict__"):
123 | v = _to_dict(v)
124 | ret[k] = v
125 | return ret
126 |
127 |
128 | def _set_cell_fill(cell: cell.Cell, dict: dict) -> None:
129 | import openpyxl.styles.fills as fills
130 |
131 | PatternFill_attrs = ["patternType", "fgColor", "bgColor"]
132 | GradientFill_attrs = ["type", "degree", "left", "right", "top", "bottom", "stop"]
133 |
134 | is_meet = lambda attrs: len([attr for attr in attrs if attr in dict]) == len(attrs)
135 |
136 | if is_meet(PatternFill_attrs):
137 | cell.fill = fills.PatternFill(
138 | patternType=dict["patternType"],
139 | fgColor=_get_color_from_dict(dict["fgColor"]),
140 | bgColor=_get_color_from_dict(dict["bgColor"]),
141 | )
142 | elif is_meet(GradientFill_attrs):
143 | sl = []
144 | for dict in dict["stop"]:
145 | stop = fills.Stop(color=_get_color_from_dict(dict=["color"]), position=dict["position"])
146 | sl.append(stop)
147 | cell.fill = fills.GradientFill(
148 | type=dict["type"],
149 | degree=dict["degree"],
150 | left=dict["left"],
151 | right=dict["right"],
152 | top=dict["top"],
153 | bottom=dict["bottom"],
154 | stop=sl,
155 | )
156 | else:
157 | logging("Parse error: is not a valid dict.", dict)
158 |
159 |
160 | def _set_cell_border(cell: cell.Cell, dict: dict) -> None:
161 | for k in ["start", "end", "left", "right", "top", "bottom", "diagonal", "vertical", "horizontal"]:
162 | if k not in dict:
163 | dict[k] = None
164 | else:
165 | side_dict = dict[k]
166 | if not "style" in side_dict:
167 | side_dict["style"] = None
168 | if not "color" in side_dict:
169 | side_dict["color"] = None
170 | dict[k] = Side(style=side_dict["style"], color=_get_color_from_dict(side_dict["color"]))
171 |
172 | for k in ["diagonalUp", "diagonalDown"]:
173 | if k not in dict:
174 | dict[k] = False
175 |
176 | if "outline" not in dict:
177 | dict["outline"] = True
178 |
179 | cell.border = Border(
180 | start=dict["start"],
181 | end=dict["end"],
182 | left=dict["left"],
183 | right=dict["right"],
184 | top=dict["top"],
185 | bottom=dict["bottom"],
186 | diagonal=dict["diagonal"],
187 | vertical=dict["vertical"],
188 | horizontal=dict["horizontal"],
189 | diagonalUp=dict["diagonalUp"],
190 | diagonalDown=dict["diagonalDown"],
191 | outline=dict["outline"],
192 | )
193 |
194 |
195 | def _get_color_from_dict(dict: dict) -> Color:
196 | if "index" in dict:
197 | dict["indexed"] = dict["index"]
198 | if not "tint" in dict:
199 | dict["tint"] = 0.0
200 | if "indexed" in dict:
201 | return Color(indexed=dict["indexed"], tint=dict["tint"])
202 | elif "theme" in dict:
203 | return Color(theme=dict["theme"], tint=dict["tint"])
204 | elif "auto" in dict:
205 | return Color(auto=dict["auto"])
206 | else:
207 | return Color(rgb=dict["rgb"])
208 |
209 |
210 | def main(argv):
211 | for arg in argv:
212 | if arg in ["-h", "--help"]:
213 | print("Valid command:")
214 | print("\tpython xlsx_json.py --dump_json path/to/excel.xlsx path/to/output.json")
215 | print("\tpython xlsx_json.py --override_xlsx path/to/data.json path/to/output.xlsx")
216 | exit(0)
217 |
218 | invalid_hint = "Invalid arguments for 'xlsx_json' tool. Please type '-h' or '--help' for more details."
219 | if len(argv) != 3:
220 |
221 | logging.error(invalid_hint)
222 | exit(1)
223 |
224 | if argv[0] == "--dump_json":
225 | dump_xlsx_to_json(argv[1], argv[2])
226 | elif argv[0] == "--override_xlsx":
227 | override_xlsx_worksheets_from_json(argv[1], argv[2])
228 | else:
229 |
230 | logging.error(invalid_hint)
231 | exit(1)
232 |
233 |
234 | if __name__ == "__main__":
235 | main(sys.argv[1:])
236 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/table_tools/csv.gd:
--------------------------------------------------------------------------------
1 | """csv 表格工具
2 | 可选参数:
3 | - arr_dict_with_brackets=true/false 生成表格时是否为所有的数组与字典类型将加上方/花括号, 默认为 false
4 | """
5 | @tool
6 | extends "table_tool.gd"
7 |
8 | const CSV_DELIM = ","
9 |
10 | var _last_parse_error: Error = ERR_PARSE_ERROR
11 | var _header: _TableHeader
12 | var _data: Array[Dictionary] = []
13 |
14 | var arr_dict_with_brackets := false
15 |
16 |
17 | func _get_support_types() -> PackedByteArray:
18 | return [
19 | TYPE_BOOL,
20 | TYPE_INT,
21 | TYPE_FLOAT,
22 | TYPE_STRING,
23 | TYPE_STRING_NAME,
24 | TYPE_NODE_PATH,
25 | TYPE_ARRAY,
26 | TYPE_DICTIONARY,
27 | TYPE_PACKED_BYTE_ARRAY,
28 | TYPE_PACKED_INT32_ARRAY,
29 | TYPE_PACKED_INT64_ARRAY,
30 | TYPE_PACKED_FLOAT32_ARRAY,
31 | TYPE_PACKED_FLOAT64_ARRAY,
32 | TYPE_PACKED_STRING_ARRAY,
33 | ]
34 |
35 |
36 | func _get_parse_error() -> Error:
37 | return _last_parse_error
38 |
39 |
40 | func _parse_table_file(csv_file: String, options: PackedStringArray) -> Error:
41 | var option_pairs := parse_options(options)
42 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false").to_lower() == "true"
43 |
44 | var fa := FileAccess.open(csv_file, FileAccess.READ)
45 | if not is_instance_valid(fa):
46 | _Log.error([_Localize.translate("无法读取csv文件: "), csv_file, " - ", error_string(FileAccess.get_open_error())])
47 | _last_parse_error = FileAccess.get_open_error()
48 | return _last_parse_error
49 |
50 | _header = _TableHeader.new()
51 | var meta_list := fa.get_csv_line(CSV_DELIM)
52 | var descs := fa.get_csv_line(CSV_DELIM)
53 | var fields := fa.get_csv_line(CSV_DELIM)
54 | var types := fa.get_csv_line(CSV_DELIM)
55 |
56 | # 移除尾随空项(由其他软件产生)
57 | for i in range(meta_list.size() - 1, -1, -1):
58 | if meta_list[i].is_empty():
59 | meta_list.remove_at(i)
60 | else:
61 | break
62 |
63 | for i in range(descs.size() - 1, -1, -1):
64 | if descs[i].is_empty():
65 | descs.remove_at(i)
66 | else:
67 | break
68 |
69 | if meta_list.size() == 1 and meta_list[0] in ["PlaceHolder Meta List", "PlaceHolder Metas"]: # 兼容旧格式
70 | meta_list.clear()
71 | if descs.size() == 1 and descs[0] == "PlaceHolder Descriptions":
72 | descs.clear()
73 |
74 | _header.meta_list = meta_list
75 | _header.descriptions = descs
76 | _header.fields = fields
77 | _header.types = types
78 |
79 | # 检查字段与类型是否匹配
80 | if fields.size() != types.size():
81 | _header = null
82 | _data.clear()
83 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file, " - ", _Localize.translate("请使用生成工具创建合法的表头。")])
84 | _last_parse_error = ERR_PARSE_ERROR
85 | return _last_parse_error
86 |
87 | # 检查字段名
88 | for f in fields:
89 | if not TextServerManager.get_primary_interface().is_valid_identifier(f) and not is_meta_filed(f):
90 | _header = null
91 | _data.clear()
92 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file, " - ", _Localize.translate("非法标识符: "), f])
93 | _last_parse_error = ERR_PARSE_ERROR
94 | return _last_parse_error
95 | # 检查类型
96 | for t in types:
97 | if get_type_id(t) <= 0:
98 | _header = null
99 | _data.clear()
100 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file, " - ", _Localize.translate("不支持的类型: "), t])
101 | _last_parse_error = ERR_PARSE_ERROR
102 | return _last_parse_error
103 |
104 | _data.clear()
105 | # 读取数据行
106 | while not fa.eof_reached():
107 | var row := fa.get_csv_line(CSV_DELIM)
108 | if _is_empty_csv_row(row, fields.size()):
109 | # 跳过空行
110 | continue
111 | var row_data := {}
112 | for i in range(min(types.size(), row.size())):
113 | var type := get_type_id(types[i].strip_edges())
114 | var field := fields[i].strip_edges()
115 | if type < 0:
116 | _header = null
117 | _data.clear()
118 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file])
119 | _last_parse_error = ERR_PARSE_ERROR
120 | return _last_parse_error
121 |
122 | if row[i].is_empty():
123 | # 跳过空数据,避免在此生成默认值
124 | continue
125 |
126 | var value: Variant = parse_value(row[i], type)
127 |
128 | if typeof(value) == TYPE_NIL:
129 | _header = null
130 | _data.clear()
131 | _Log.error([_Localize.translate("解析csv文件失败: "), csv_file])
132 | _last_parse_error = ERR_PARSE_ERROR
133 | return _last_parse_error
134 |
135 | row_data[field] = value
136 |
137 | _data.push_back(row_data)
138 |
139 | return OK
140 |
141 |
142 | func _get_table_file_extension() -> String:
143 | return "csv"
144 |
145 |
146 | func _generate_table_file(save_path: String, header: _TableHeader, data_rows: Array[PackedStringArray], options: PackedStringArray) -> Error:
147 | var option_pairs := parse_options(options)
148 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false").to_lower() == "true"
149 |
150 | if not is_instance_valid(header):
151 | _Log.error([_Localize.translate("生成表格失败: "), error_string(ERR_INVALID_PARAMETER)])
152 | return ERR_INVALID_PARAMETER
153 |
154 | # 生成用于跳过导入的.import
155 | var f := FileAccess.open(save_path + ".import", FileAccess.WRITE)
156 | if not is_instance_valid(f):
157 | _Log.error([_Localize.translate("生成表格失败,无法生成:"), save_path + ".import", " - ", error_string(FileAccess.get_open_error())])
158 | return FAILED
159 |
160 | var engine_version := Engine.get_version_info()
161 | if engine_version.major >= 4 and engine_version.minor >= 3:
162 | # 4.3 之后使用skip
163 | f.store_string('[remap]\n\nimporter="skip"\n')
164 | else:
165 | # 4.3 之前使用keep
166 | f.store_string('[remap]\n\nimporter="keep"\n')
167 |
168 | f.close()
169 |
170 | var fa := FileAccess.open(save_path, FileAccess.WRITE)
171 | if not is_instance_valid(fa):
172 | _Log.error([_Localize.translate("生成表格失败: "), error_string(FileAccess.get_open_error())])
173 | return FileAccess.get_open_error()
174 |
175 | # 确保非空行
176 | var meta_list := header.meta_list.duplicate()
177 | var descs := header.descriptions.duplicate()
178 | if meta_list.size() == 0:
179 | meta_list.push_back("PlaceHolder Meta List")
180 | if descs.size() <= 0:
181 | descs.push_back("PlaceHolder Descriptions")
182 | fa.store_csv_line(meta_list, CSV_DELIM)
183 | fa.store_csv_line(descs, CSV_DELIM)
184 | fa.store_csv_line(header.fields, CSV_DELIM)
185 | fa.store_csv_line(header.types, CSV_DELIM)
186 | for row in data_rows:
187 | fa.store_csv_line(row, CSV_DELIM)
188 | fa.close()
189 |
190 | return OK
191 |
192 |
193 | func _to_value_text(value: Variant) -> String:
194 | if not typeof(value) in get_support_types():
195 | _Log.error([_Localize.translate("转换为文本失败,不支持的类型: "), value, " - ", type_string(typeof(value))])
196 | return ""
197 | if typeof(value) in [TYPE_STRING, TYPE_NODE_PATH, TYPE_STRING_NAME]:
198 | return str(value)
199 | if typeof(value) > TYPE_ARRAY:
200 | # 转为数组给json
201 | value = type_convert(value, TYPE_ARRAY)
202 | # 不带两侧括号
203 | const fake_indent = "F`A@K*E&I#N|D-E+N/T"
204 | match typeof(value):
205 | TYPE_ARRAY:
206 | var text := JSON.stringify(value, fake_indent).replace("\n" + fake_indent, " ").trim_prefix("[ ").trim_suffix("\n]")
207 | if value.is_empty():
208 | text = ""
209 | if arr_dict_with_brackets:
210 | text = "[%s]" % text
211 | return text
212 | TYPE_DICTIONARY:
213 | var text := JSON.stringify(value, fake_indent).replace("\n" + fake_indent, " ").trim_prefix("{ ").trim_suffix("\n}")
214 | if value.is_empty():
215 | text = ""
216 | if arr_dict_with_brackets:
217 | text = "{%s}" % text
218 | return text
219 | _:
220 | return JSON.stringify(value)
221 |
222 |
223 | func _parse_value(text: String, type_id: int) -> Variant:
224 | if not type_id in get_support_types():
225 | _Log.error([_Localize.translate("不支持的类型: "), type_string(type_id)])
226 | return null
227 |
228 | var default := text.is_empty()
229 | if default:
230 | _Log.error([_Localize.translate("Bug: 为空值生成默认值,请提交issue并提供复现流程或MRP。")])
231 | return null
232 |
233 | match type_id:
234 | TYPE_BOOL:
235 | return false if default else ("t" in text.to_lower())
236 | TYPE_INT:
237 | return 0 if default else text.to_int()
238 | TYPE_FLOAT:
239 | return 0.0 if default else text.to_float()
240 | TYPE_STRING:
241 | return "" if default else text
242 | TYPE_STRING_NAME:
243 | return &"" if default else StringName(text)
244 | TYPE_NODE_PATH:
245 | return ^"" if default else NodePath(text)
246 | TYPE_ARRAY, TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY, TYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY, TYPE_PACKED_STRING_ARRAY:
247 | if default:
248 | return convert([], type_id)
249 | var value_text := text
250 | if not text.begins_with("[") and not text.ends_with("]"):
251 | value_text = "[%s]" % text
252 | var arr: Variant = JSON.parse_string(value_text)
253 | if typeof(arr) != TYPE_ARRAY:
254 | _Log.error([_Localize.translate("非法值文本: "), text])
255 | return null
256 | return convert(arr, type_id)
257 | TYPE_DICTIONARY:
258 | if default:
259 | return {}
260 | var value_text := text
261 | if not text.begins_with("{") and not text.ends_with("}"):
262 | value_text = "{%s}" % text
263 | var dict: Variant = JSON.parse_string(value_text)
264 | if typeof(dict) != TYPE_DICTIONARY:
265 | _Log.error([_Localize.translate("非法值文本: "), text])
266 | return null
267 | return dict
268 | return null
269 |
270 |
271 | func _get_header() -> _TableHeader:
272 | return _header
273 |
274 |
275 | func _get_data() -> Array[Dictionary]:
276 | return _data
277 |
278 |
279 | func _get_tooltip_text() -> String:
280 | return """csv 表格工具
281 | 可选参数:
282 | - arr_dict_with_brackets=true/false 生成表格时是否为所有的数组与字典类型将加上方/花括号, 默认为 false
283 | """
284 |
285 |
286 | # --------------
287 | func _is_empty_csv_row(row: PackedStringArray, fields_count: int) -> bool:
288 | for i in range(min(row.size(), fields_count)):
289 | if not row[i].strip_edges().is_empty():
290 | return false
291 | return true
292 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd:
--------------------------------------------------------------------------------
1 | ## 字典形式的 GDScript 导入,适合表格条目较多的情况
2 | ## Required Options:
3 | ## key=prop_name - 必选,指定用于查找的数据类属性,该属性的值在所有数据中不能重复,也不能留空
4 | ## Options:
5 | ## generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名
6 | ## pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true
7 | @tool
8 | extends "gdscript_default.gd"
9 |
10 |
11 | func _import(
12 | import_path: String,
13 | table_name: String,
14 | header: _TableHeader,
15 | data_class_name: String,
16 | data_class_script: String,
17 | instantiation: String,
18 | custom_setters: Dictionary,
19 | data_rows: Array[Dictionary],
20 | options: PackedStringArray
21 | ) -> Error:
22 | var option_pairs := parse_options(options)
23 |
24 | var priority_key := option_pairs.get("key", "") as String
25 | if priority_key.is_empty():
26 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("未指定作为key的数据类属性,请使用 key=prop_name 作为选项参数进行指定。")])
27 | return ERR_INVALID_PARAMETER
28 |
29 | var can_use_typed_dictionary := false
30 | var version_info := Engine.get_version_info()
31 | if version_info.major > 4 or (version_info.major == 4 and version_info.minor >= 4): # 4.4 later
32 | can_use_typed_dictionary = true
33 |
34 | var fa := FileAccess.open(import_path, FileAccess.WRITE)
35 | if not is_instance_valid(fa):
36 | _Log.error([_Localize.translate("导表失败: "), error_string(FileAccess.get_open_error())])
37 | return FileAccess.get_open_error()
38 |
39 | var pure_static := (option_pairs.get("pure_static", "true") as String).to_lower() == "true"
40 | var member_prefix := "static " if pure_static else ""
41 | if pure_static:
42 | fa.store_line("@static_unload")
43 |
44 | if TextServerManager.get_primary_interface().is_valid_identifier(table_name) and option_pairs.has("generate_class_name"):
45 | fa.store_line("class_name %s" % table_name)
46 | fa.store_line("")
47 |
48 | # TABLE_META_LIST
49 | var meta_list_text := ""
50 | for m in header.meta_list:
51 | if not meta_list_text.is_empty():
52 | meta_list_text += ", "
53 | meta_list_text += '"%s"' % m
54 | fa.store_line("const TABLE_META_LIST: PackedStringArray = [%s]" % meta_list_text)
55 | fa.store_line("")
56 |
57 | # DataClass
58 | var property_list: Array[Dictionary]
59 | var property_default_values := {}
60 | var data_class := ""
61 | if not data_class_script.is_empty():
62 | var internal_class := "" if data_class_name.is_empty() else ".%s" % data_class_name
63 | fa.store_line('const DataClass = preload("%s")%s' % [data_class_script, internal_class])
64 | fa.store_line("")
65 | data_class = "DataClass"
66 |
67 | # 获取属性列表
68 | var script := ResourceLoader.load(data_class_script, "Script", ResourceLoader.CACHE_MODE_IGNORE) as Script
69 | if not data_class_name.is_empty():
70 | var internal := data_class_name.split(".", false)
71 | while not internal.is_empty():
72 | script = script.get_script_constant_map()[internal[0]]
73 | internal.remove_at(0)
74 | var base := script
75 | while true:
76 | property_list.append_array(base.get_script_property_list())
77 | if not is_instance_valid(base.get_base_script()):
78 | property_list.append_array(ClassDB.class_get_property_list(base.get_instance_base_type()))
79 | var tmp_obj := ClassDB.instantiate(base.get_instance_base_type()) as Object
80 | for p in property_list:
81 | var n := p["name"] as String
82 | if n in tmp_obj:
83 | property_default_values[n] = tmp_obj.get(n)
84 | break
85 | base = base.get_base_script()
86 |
87 | for p in property_list:
88 | var n := p["name"] as String
89 | if n in property_default_values:
90 | continue
91 | property_default_values[n] = script.get_property_default_value(n)
92 | else:
93 | data_class = data_class_name
94 | property_list = ClassDB.class_get_property_list(data_class_name)
95 | var tmp_obj := ClassDB.instantiate(data_class_name) as Object
96 | for p in property_list:
97 | var n := p["name"] as String
98 | if n in tmp_obj:
99 | property_default_values[n] = tmp_obj.get(n)
100 |
101 | var existed_values := []
102 | for d in data_rows:
103 | var v: Variant = d.get(priority_key, null)
104 | if typeof(v) == TYPE_NIL:
105 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("存在未配置key (%s)的数据: %s。") % [priority_key, d]])
106 | return ERR_INVALID_PARAMETER
107 | if existed_values.has(v):
108 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("存在key (%s)重复的数据,重复的值为: %s。") % [priority_key, v]])
109 | return ERR_INVALID_PARAMETER
110 | existed_values.push_back(v)
111 |
112 | var fields := header.fields.duplicate()
113 | var types := Array(header.types).map(to_type_id) as PackedByteArray
114 |
115 | if not fields.has(priority_key):
116 | _Log.error([table_name, " ", _Localize.translate("导表失败: "), _Localize.translate("数据类不存在指定为key的属性 %s,请使用 key=prop_name 作为选项参数进行指定。") % [priority_key]])
117 | return ERR_INVALID_PARAMETER
118 |
119 | var priority_key_type_id: int = types[fields.find(priority_key)]
120 |
121 | var dictionary_type_text := "Dictionary"
122 | if can_use_typed_dictionary:
123 | dictionary_type_text = "Dictionary[%s, %s]" % [type_string(priority_key_type_id), data_class]
124 |
125 | # get_data
126 | fa.store_line(member_prefix + "func get_data() -> %s:" % dictionary_type_text)
127 | fa.store_line("\treturn _data")
128 | fa.store_line("")
129 | fa.store_line("")
130 |
131 | # get_record
132 | fa.store_line(member_prefix + "func get_record(key: %s) -> %s:" % [type_string(priority_key_type_id), data_class])
133 | fa.store_line("\treturn _data.get(key, null)")
134 | fa.store_line("")
135 | fa.store_line("")
136 |
137 | # find_by_property
138 | fa.store_line(member_prefix + "func find_by_property(prop_name: StringName, target_value: Variant) -> %s:" % data_class)
139 | fa.store_line("\tfor d: %s in _data.values():" % data_class)
140 | fa.store_line("\t\tif d.get(prop_name) == target_value:")
141 | fa.store_line("\t\t\treturn d")
142 | fa.store_line("\treturn null")
143 | fa.store_line("")
144 | fa.store_line("")
145 |
146 | # find_by_getter
147 | fa.store_line(member_prefix + "func find_by_getter(getter_name: StringName, target_value: Variant) -> %s:" % data_class)
148 | fa.store_line("\tfor d: %s in _data.values():" % data_class)
149 | fa.store_line("\t\tif d.call(getter_name) == target_value:")
150 | fa.store_line("\t\t\treturn d")
151 | fa.store_line("\treturn null")
152 | fa.store_line("")
153 | fa.store_line("")
154 |
155 | # find
156 | fa.store_line(member_prefix + "func find(indicate: Callable) -> %s:" % data_class)
157 | fa.store_line("\tfor d: %s in _data.values():" % data_class)
158 | fa.store_line("\t\tif indicate.call(d):")
159 | fa.store_line("\t\t\treturn d")
160 | fa.store_line("\treturn null")
161 | fa.store_line("")
162 | fa.store_line("")
163 |
164 | # filter
165 | fa.store_line(member_prefix + "func filter(indicate: Callable) -> Array[%s]:" % data_class)
166 | if data_class == "DataClass":
167 | fa.store_line("\treturn Array(_data.values().filter(indicate), TYPE_OBJECT, (DataClass as Script).get_instance_base_type(), DataClass)")
168 | else:
169 | fa.store_line("\treturn Array(_data.values().filter(indicate), TYPE_OBJECT, &\"%s\", null)" % data_class)
170 | fa.store_line("")
171 | fa.store_line("")
172 |
173 | fa.store_line("# -----------------------------------------------------------------------")
174 | # 过滤meta字段
175 | var idx := 0
176 | while idx < fields.size():
177 | if is_meta_filed(fields[idx]):
178 | fields.remove_at(idx)
179 | types.remove_at(idx)
180 | continue
181 | idx += 1
182 |
183 | # _make_data
184 | instantiation = instantiation.strip_edges()
185 | var args := (
186 | (
187 | Array(instantiation.split("(", false, 1)[1].split(")", false, 1)[0].split(",")).map(func(a: String) -> String: return a.strip_edges().trim_prefix("{").trim_suffix("}"))
188 | as PackedStringArray
189 | )
190 | if not instantiation.ends_with("()")
191 | else PackedStringArray()
192 | )
193 | var fields_with_type := fields.duplicate()
194 | for i in range(fields_with_type.size()):
195 | fields_with_type[i] = "%s: %s" % [fields_with_type[i], type_string(types[i])]
196 | fa.store_line(member_prefix + "func _make_data(%s) -> %s:" % [", ".join(fields_with_type), data_class])
197 | fa.store_line("\tvar ret := %s.%s(%s)" % [data_class, instantiation.split("(")[0], ", ".join(args)])
198 |
199 | var valid_properties :Dictionary = {}
200 | for prop in property_list:
201 | valid_properties[prop["name"]] = prop
202 |
203 | var hinted_fields: PackedStringArray = []
204 | for f: String in Array(fields).filter(func(f: String) -> bool: return not args.has(f)):
205 | if custom_setters.has(f):
206 | fa.store_line("\tret.%s(%s)" % [custom_setters[f], f])
207 | elif valid_properties.has(f):
208 | var prop := valid_properties[f] as Dictionary
209 | if prop.type == TYPE_ARRAY:
210 | fa.store_line("\tret.%s.assign(%s)" % [f, f])
211 | elif prop.type == TYPE_DICTIONARY and (Engine.get_version_info().major > 4 or Engine.get_version_info().minor >= 4):
212 | # 4.4 以上支持类型化字典
213 | fa.store_line("\tret.%s.assign(%s)" % [f, f])
214 | else:
215 | fa.store_line("\tret.%s = %s" % [f, f])
216 | elif not hinted_fields.has(f):
217 | # 只提示一次
218 | _Log.warning([_Localize.translate("无法被赋值的字段将被跳过: "), f])
219 | hinted_fields.push_back(f)
220 | fa.store_line("\treturn ret")
221 | fa.store_line("")
222 | fa.store_line("")
223 |
224 | # 数据行
225 | fa.store_line(member_prefix + "var _data: %s = {}" % dictionary_type_text)
226 | fa.store_line("")
227 | fa.store_line("")
228 |
229 | if member_prefix.is_empty():
230 | fa.store_line(member_prefix + "func _init() -> void:")
231 | else:
232 | fa.store_line(member_prefix + "func _static_init() -> void:")
233 | for row in data_rows:
234 | var args_text_list := PackedStringArray()
235 | for i in range(fields.size()):
236 | var f := fields[i]
237 | var t := types[i]
238 | args_text_list.push_back(_get_value_text(row, f, t, property_default_values))
239 | var priority_key_text := _get_value_text(row, priority_key, priority_key_type_id, property_default_values)
240 | fa.store_line("\t_data[%s] = _make_data(%s)" % [priority_key_text, ", ".join(args_text_list)])
241 | fa.store_line("\t_data.make_read_only()")
242 | if data_rows.is_empty():
243 | fa.store_line("\tpass")
244 | fa.store_line("")
245 | fa.close()
246 |
247 | return OK
248 |
249 |
250 | func _get_tooltip_text() -> String:
251 | return """字典形式的 GDScript 导入,适合表格条目较多的情况
252 | 必选参数:
253 | - key=prop_name - 必选,指定用于查找的数据类属性,该属性的值在所有数据中不能重复,也不能留空
254 | 可选参数:
255 | - generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名
256 | - pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true
257 | """
258 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd:
--------------------------------------------------------------------------------
1 | """Excel(xlsx) 表格工具
2 | 必要参数:
3 | - sheet=your_sheet_name 指定要解析的工作表,如果xlsx中存在多个工作表,则该参数必须指定。
4 | 可选参数:
5 | - parse_sheet_must_exists=true/false 为true时如果指定工作表不存在时将发生解析错误。默认 false
6 | - arr_dict_with_brackets=true/false 为true时生成表格时所有的数组与字典类型将加上方/花括号。默认为 false
7 | - colorize_header=true 是否对生成的表头单元格被赋予颜色。默认 true
8 | """
9 | @tool
10 | extends "csv.gd"
11 |
12 | const META_COLOR = Color.DARK_GRAY
13 | const DESC_COLOR = Color.AQUA
14 | const FIELD_COLOR = Color.DARK_SALMON
15 | const TYPE_COLOR = Color.LAWN_GREEN
16 | const META_FILED_COLOR = Color.ALICE_BLUE
17 |
18 | const _PY_TOOL_RELATIVE_PATH = "py/xlsx_json.py"
19 |
20 | var _py_tool_path: String = ProjectSettings.globalize_path((get_script().resource_path as String).get_base_dir().path_join(_PY_TOOL_RELATIVE_PATH))
21 | var _tmp_json_path: String = ProjectSettings.globalize_path(EditorInterface.get_editor_paths().get_project_settings_dir().path_join("_xlsx_tmp_.json"))
22 |
23 |
24 | func _parse_table_file(xlsx_file: String, options: PackedStringArray) -> Error:
25 | var sheet_name: String = ""
26 | var parse_sheet_must_exists := false
27 |
28 | var option_pairs := parse_options(options)
29 | sheet_name = option_pairs.get("sheet", "")
30 | parse_sheet_must_exists = option_pairs.get("parse_sheet_must_exists", "false") == "true"
31 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false") == "true"
32 |
33 | if sheet_name.is_empty():
34 | _Log.error([_Localize.translate("解析xlsx文件: "), xlsx_file, " - ", _Localize.translate("必须使用 sheet=your_sheet_name 选项指定工作表。")])
35 | _last_parse_error = ERR_INVALID_PARAMETER
36 | return _last_parse_error
37 |
38 | if not FileAccess.file_exists(xlsx_file):
39 | _Log.error([_Localize.translate("无法读取xlsx文件: "), xlsx_file, " - ", error_string(ERR_FILE_NOT_FOUND)])
40 | _last_parse_error = ERR_FILE_NOT_FOUND
41 | return _last_parse_error
42 |
43 | var output := []
44 | var err := OS.execute("python", [_py_tool_path, "--dump_json", ProjectSettings.globalize_path(xlsx_file), _tmp_json_path], output, true)
45 | if err != OK:
46 | _Log.error([_Localize.translate("无法解析xlsx文件: "), xlsx_file, " - ", "\n".join(output)])
47 | _last_parse_error = FAILED
48 | return _last_parse_error
49 |
50 | var json := FileAccess.get_file_as_string(_tmp_json_path)
51 | var sheets: Dictionary = JSON.parse_string(json) as Dictionary
52 |
53 | if not sheet_name in sheets:
54 | if parse_sheet_must_exists:
55 | _Log.error([_Localize.translate("解析xlsx文件: "), xlsx_file, " - ", _Localize.translate("不存在指定的工作表: "), sheet_name])
56 | _last_parse_error = ERR_PARSE_ERROR
57 | return _last_parse_error
58 | else:
59 | _last_parse_error = OK
60 | return _last_parse_error
61 |
62 | var sheet := Array(sheets[sheet_name]["data"], TYPE_ARRAY, &"", null) as Array[Array]
63 | if sheet.size() < 4:
64 | _Log.error([_Localize.translate("解析xlsx文件: "), xlsx_file, " - ", _Localize.translate("非法格式")])
65 | _last_parse_error = ERR_PARSE_ERROR
66 | return _last_parse_error
67 |
68 | _header = _TableHeader.new()
69 | var meta_list := _to_str_arr(sheet[0])
70 | var descs := _to_str_arr(sheet[1])
71 | var fields := _to_str_arr(sheet[2])
72 | var types := _to_str_arr(sheet[3])
73 |
74 | # 移除尾随空项(由其他软件产生)
75 | for i in range(meta_list.size() - 1, -1, -1):
76 | if meta_list[i].is_empty():
77 | meta_list.remove_at(i)
78 | else:
79 | break
80 |
81 | for i in range(descs.size() - 1, -1, -1):
82 | if descs[i].is_empty():
83 | descs.remove_at(i)
84 | else:
85 | break
86 |
87 | if meta_list.size() == 1 and meta_list[0] in ["PlaceHolder Meta List", "PlaceHolder Metas"]: # 兼容旧格式
88 | meta_list.clear()
89 | if descs.size() == 1 and descs[0] == "PlaceHolder Descriptions":
90 | descs.clear()
91 |
92 | _header.meta_list = meta_list
93 | _header.descriptions = descs
94 | _header.fields = fields
95 | _header.types = types
96 |
97 | # 检查字段与类型是否匹配
98 | if fields.size() != types.size():
99 | _header = null
100 | _data.clear()
101 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file, " - ", _Localize.translate("请使用生成工具创建合法的表头。")])
102 | _last_parse_error = ERR_PARSE_ERROR
103 | return _last_parse_error
104 |
105 | # 检查字段名
106 | for f in fields:
107 | if not TextServerManager.get_primary_interface().is_valid_identifier(f) and not is_meta_filed(f):
108 | _header = null
109 | _data.clear()
110 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file, " - ", _Localize.translate("非法标识符: "), f])
111 | _last_parse_error = ERR_PARSE_ERROR
112 | return _last_parse_error
113 | # 检查类型
114 | for t in types:
115 | if get_type_id(t) <= 0:
116 | _header = null
117 | _data.clear()
118 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file, " - ", _Localize.translate("不支持的类型: "), t])
119 | _last_parse_error = ERR_PARSE_ERROR
120 | return _last_parse_error
121 |
122 | _data.clear()
123 | # 读取数据行
124 | for row_idx in range(4, sheet.size()):
125 | var row := _to_str_arr(sheet[row_idx])
126 | if _is_empty_csv_row(row, fields.size()):
127 | # 跳过空行
128 | continue
129 | var row_data := {}
130 | for i in range(min(types.size(), row.size())):
131 | var type := get_type_id(types[i].strip_edges())
132 | var field := fields[i].strip_edges()
133 | if type < 0:
134 | _header = null
135 | _data.clear()
136 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file])
137 | _last_parse_error = ERR_PARSE_ERROR
138 | return _last_parse_error
139 |
140 | if row[i].is_empty():
141 | # 跳过空数据,避免在此生成默认值
142 | continue
143 |
144 | var value: Variant = parse_value(row[i], type)
145 |
146 | if typeof(value) == TYPE_NIL:
147 | _header = null
148 | _data.clear()
149 | _Log.error([_Localize.translate("解析xlsx文件失败: "), xlsx_file])
150 | _last_parse_error = ERR_PARSE_ERROR
151 | return _last_parse_error
152 |
153 | row_data[field] = value
154 |
155 | _data.push_back(row_data)
156 |
157 | return OK
158 |
159 |
160 | func _get_table_file_extension() -> String:
161 | return "xlsx"
162 |
163 |
164 | func _generate_table_file(save_path: String, header: _TableHeader, data_rows: Array[PackedStringArray], options: PackedStringArray) -> Error:
165 | save_path = ProjectSettings.globalize_path(save_path)
166 | var sheet_name: String = ""
167 | var colorize_header := true
168 |
169 | var option_pairs := parse_options(options)
170 | sheet_name = option_pairs.get("sheet", "")
171 | arr_dict_with_brackets = option_pairs.get("arr_dict_with_brackets", "false") == "true"
172 | colorize_header = option_pairs.get("colorize_header", "true") == "true"
173 |
174 | if sheet_name.is_empty():
175 | _Log.error([_Localize.translate("解析xlsx文件: "), save_path, " - ", _Localize.translate("必须使用 sheet=your_sheet_name 选项指定工作表。")])
176 | _last_parse_error = ERR_INVALID_PARAMETER
177 | return _last_parse_error
178 |
179 | if not is_instance_valid(header):
180 | _Log.error([_Localize.translate("生成表格失败: "), error_string(ERR_INVALID_PARAMETER)])
181 | return ERR_INVALID_PARAMETER
182 |
183 | var json_data := {}
184 | var sheet_data := []
185 | json_data[sheet_name] = {"data": sheet_data}
186 |
187 | # 确保非空行
188 | var meta_list := header.meta_list.duplicate()
189 | var descs := header.descriptions.duplicate()
190 | if meta_list.size() == 0:
191 | meta_list.push_back("PlaceHolder Meta List")
192 | if descs.size() <= 0:
193 | descs.push_back("PlaceHolder Descriptions")
194 |
195 | sheet_data.push_back(_to_row(meta_list))
196 | sheet_data.push_back(_to_row(descs))
197 | sheet_data.push_back(_to_row(header.fields))
198 | sheet_data.push_back(_to_row(header.types))
199 |
200 | if colorize_header:
201 | _colorize_header(sheet_data)
202 |
203 | for row in data_rows:
204 | var row_data := []
205 | row_data.resize(row.size())
206 | for i in range(row.size()):
207 | if row[i].is_empty():
208 | row_data[i] = ""
209 | else:
210 | var type_id := get_type_id(header.types[i])
211 | if type_id in [TYPE_INT, TYPE_FLOAT, TYPE_BOOL]:
212 | row_data[i] = parse_value(row[i], type_id)
213 | elif (
214 | not arr_dict_with_brackets
215 | and type_id in [TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY, TYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY]
216 | ):
217 | var converted: Variant = parse_value(row[i], type_id)
218 | if converted.size() == 1:
219 | row_data[i] = converted[0]
220 | else:
221 | row_data[i] = row[i]
222 | elif not arr_dict_with_brackets and type_id == TYPE_ARRAY:
223 | var converted: Variant = parse_value(row[i], type_id)
224 | if converted.size() == 1 and typeof(converted[0]) in [TYPE_INT, TYPE_FLOAT, TYPE_BOOL]:
225 | row_data[i] = converted[0]
226 | else:
227 | row_data[i] = row[i]
228 | else:
229 | row_data[i] = row[i]
230 | sheet_data.push_back(_to_row(row_data))
231 |
232 | var fa := FileAccess.open(_tmp_json_path, FileAccess.WRITE)
233 | fa.store_string(JSON.stringify(json_data))
234 | fa.close()
235 |
236 | var output := []
237 | var err := OS.execute("python", [_py_tool_path, "--override_xlsx", _tmp_json_path, ProjectSettings.globalize_path(save_path)], output, true)
238 | if err != OK:
239 | _Log.error([_Localize.translate("无法覆盖xlsx文件: "), save_path, " - ", "\n".join(output)])
240 | _last_parse_error = FAILED
241 | return _last_parse_error
242 |
243 | return OK
244 |
245 |
246 | func _get_tooltip_text() -> String:
247 | return """Excel(xlsx) 表格工具
248 | 必要参数:
249 | - sheet=your_sheet_name 指定要解析的工作表,如果xlsx中存在多个工作表,则该参数必须指定。
250 | 可选参数:
251 | - parse_sheet_must_exists=true/false 为true时如果指定工作表不存在时将发生解析错误。默认 false
252 | - arr_dict_with_brackets=true/false 为true时生成表格时所有的数组与字典类型将加上方/花括号。默认为 false
253 | - colorize_header=true 是否对生成的表头单元格被赋予颜色。默认 true
254 | """
255 |
256 |
257 | # -----------------
258 | func _colorize_header(sheet_data: Array) -> void:
259 | assert(sheet_data.size() >= 4)
260 | # meta
261 | for cell: Dictionary in sheet_data[0]:
262 | cell["fill"] = _make_pattern_fill(META_COLOR)
263 | cell["border"] = _make_border()
264 | # desc
265 | for cell: Dictionary in sheet_data[1]:
266 | cell["fill"] = _make_pattern_fill(DESC_COLOR)
267 | cell["border"] = _make_border()
268 | # field & type
269 | for i in range(sheet_data[2].size()):
270 | if is_meta_filed(sheet_data[2][i]["value"]):
271 | sheet_data[2][i]["fill"] = _make_pattern_fill(META_FILED_COLOR)
272 | sheet_data[3][i]["fill"] = _make_pattern_fill(META_FILED_COLOR)
273 | if sheet_data[1].size() > i:
274 | sheet_data[1][i]["fill"] = _make_pattern_fill(META_FILED_COLOR)
275 | else:
276 | sheet_data[2][i]["fill"] = _make_pattern_fill(FIELD_COLOR)
277 | sheet_data[3][i]["fill"] = _make_pattern_fill(TYPE_COLOR)
278 | sheet_data[2][i]["border"] = _make_border()
279 | sheet_data[3][i]["border"] = _make_border()
280 |
281 |
282 | func _make_pattern_fill(fgColor: Color, bgColor: Color = Color(0.0, 0.0, 0.0, 0.0)) -> Dictionary:
283 | return {
284 | patternType = "solid",
285 | fgColor = {rgb = fgColor.to_html(false)},
286 | bgColor = {rgb = bgColor.to_html(false)},
287 | }
288 |
289 |
290 | func _make_border() -> Dictionary:
291 | return {
292 | left = {style = "thin", color = {rgb = Color.BLACK.to_html(false)}, outline = true},
293 | right = {style = "thin", color = {rgb = Color.BLACK.to_html(false)}, outline = true},
294 | top = {style = "thin", color = {rgb = Color.BLACK.to_html(false)}, outline = true},
295 | bottom = {style = "thin", color = {rgb = Color.BLACK.to_html(false), outline = true}},
296 | }
297 |
298 |
299 | func _strip_right_null_cell(row: Array) -> Array[Dictionary]:
300 | # row:Array[Dictionary]
301 | var row_data := Array(row.duplicate(), TYPE_DICTIONARY, &"", null) as Array[Dictionary]
302 | for i in range(row_data.size() - 1, -1, -1):
303 | if row_data[i]["value"] == null:
304 | row_data.pop_back()
305 | else:
306 | break
307 | return row_data
308 |
309 |
310 | func _to_str_arr(row: Array) -> PackedStringArray:
311 | # roe: Array[Dictionary]
312 | return _strip_right_null_cell(row).map(func(e: Dictionary) -> String: return "" if e["value"] == null else str(e["value"]))
313 |
314 |
315 | func _to_row(str_arr: Array) -> Array[Dictionary]:
316 | return Array(str_arr.map(func(e: Variant) -> Dictionary: return {value = e}), TYPE_DICTIONARY, &"", null)
317 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd:
--------------------------------------------------------------------------------
1 | ## 默认的 GDScript 导入 (TypedArray) 适合表格条目较少的情况。
2 | ## 可选配置参数:
3 | ## generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名
4 | ## pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true
5 | @tool
6 | extends "import_tool.gd"
7 |
8 |
9 | func _import(
10 | import_path: String,
11 | table_name: String,
12 | header: _TableHeader,
13 | data_class_name: String,
14 | data_class_script: String,
15 | instantiation: String,
16 | custom_setters: Dictionary,
17 | data_rows: Array[Dictionary],
18 | options: PackedStringArray
19 | ) -> Error:
20 | var fa := FileAccess.open(import_path, FileAccess.WRITE)
21 | if not is_instance_valid(fa):
22 | _Log.error([_Localize.translate("导表失败: "), error_string(FileAccess.get_open_error())])
23 | return FileAccess.get_open_error()
24 |
25 | var option_pairs := parse_options(options)
26 |
27 | var pure_static := (option_pairs.get("pure_static", "true") as String).to_lower() == "true"
28 | var member_prefix := "static " if pure_static else ""
29 |
30 | if pure_static:
31 | fa.store_line("@static_unload")
32 |
33 | if TextServerManager.get_primary_interface().is_valid_identifier(table_name) and option_pairs.has("generate_class_name"):
34 | fa.store_line("class_name %s" % table_name)
35 | fa.store_line("")
36 |
37 | # TABLE_META_LIST
38 | var meta_list_text := ""
39 | for m in header.meta_list:
40 | if not meta_list_text.is_empty():
41 | meta_list_text += ", "
42 | meta_list_text += '"%s"' % m
43 | fa.store_line("const TABLE_META_LIST: PackedStringArray = [%s]" % meta_list_text)
44 | fa.store_line("")
45 |
46 | # DataClass
47 | var property_list: Array[Dictionary]
48 | var property_default_values: Dictionary = {}
49 | var data_class := ""
50 | if not data_class_script.is_empty():
51 | var internal_class := "" if data_class_name.is_empty() else ".%s" % data_class_name
52 | fa.store_line('const DataClass = preload("%s")%s' % [data_class_script, internal_class])
53 | fa.store_line("")
54 | data_class = "DataClass"
55 |
56 | # 获取属性列表
57 | var script := ResourceLoader.load(data_class_script, "Script", ResourceLoader.CACHE_MODE_IGNORE) as Script
58 | if not data_class_name.is_empty():
59 | var internal := data_class_name.split(".", false)
60 | while not internal.is_empty():
61 | script = script.get_script_constant_map()[internal[0]]
62 | internal.remove_at(0)
63 | var base := script
64 | while true:
65 | property_list.append_array(base.get_script_property_list())
66 | if not is_instance_valid(base.get_base_script()):
67 | property_list.append_array(ClassDB.class_get_property_list(base.get_instance_base_type()))
68 | var tmp_obj := ClassDB.instantiate(base.get_instance_base_type()) as Object
69 | for p in property_list:
70 | var n := p["name"] as String
71 | if n in tmp_obj:
72 | property_default_values[n] = tmp_obj.get(n)
73 | break
74 | base = base.get_base_script()
75 |
76 | for p in property_list:
77 | var n := p["name"] as String
78 | if n in property_default_values:
79 | continue
80 | property_default_values[n] = script.get_property_default_value(n)
81 | else:
82 | data_class = data_class_name
83 | property_list = ClassDB.class_get_property_list(data_class_name)
84 | var tmp_obj := ClassDB.instantiate(data_class_name) as Object
85 | for p in property_list:
86 | var n := p["name"] as String
87 | if n in tmp_obj:
88 | property_default_values[n] = tmp_obj.get(n)
89 |
90 | # get_data
91 | fa.store_line(member_prefix + "func get_data() -> Array[%s]:" % data_class)
92 | fa.store_line("\treturn _data")
93 | fa.store_line("")
94 | fa.store_line("")
95 |
96 | # find_by_property
97 | fa.store_line(member_prefix + "func find_by_property(prop_name: StringName, target_value: Variant) -> %s:" % data_class)
98 | fa.store_line("\tfor d in _data:")
99 | fa.store_line("\t\tif d.get(prop_name) == target_value:")
100 | fa.store_line("\t\t\treturn d")
101 | fa.store_line("\treturn null")
102 | fa.store_line("")
103 | fa.store_line("")
104 |
105 | # find_by_getter
106 | fa.store_line(member_prefix + "func find_by_getter(getter_name: StringName, target_value: Variant) -> %s:" % data_class)
107 | fa.store_line("\tfor d in _data:")
108 | fa.store_line("\t\tif d.call(getter_name) == target_value:")
109 | fa.store_line("\t\t\treturn d")
110 | fa.store_line("\treturn null")
111 | fa.store_line("")
112 | fa.store_line("")
113 |
114 | # find
115 | fa.store_line(member_prefix + "func find(indicate: Callable) -> %s:" % data_class)
116 | fa.store_line("\tfor d in _data:")
117 | fa.store_line("\t\tif indicate.call(d):")
118 | fa.store_line("\t\t\treturn d")
119 | fa.store_line("\treturn null")
120 | fa.store_line("")
121 | fa.store_line("")
122 |
123 | # filter
124 | fa.store_line(member_prefix + "func filter(indicate: Callable) -> Array[%s]:" % data_class)
125 | fa.store_line("\treturn _data.filter(indicate)")
126 | fa.store_line("")
127 | fa.store_line("")
128 |
129 | fa.store_line("# -----------------------------------------------------------------------")
130 | # 过滤meta字段
131 | var fields := header.fields.duplicate()
132 | var types := Array(header.types).map(to_type_id) as PackedByteArray
133 | var idx := 0
134 | while idx < fields.size():
135 | if is_meta_filed(fields[idx]):
136 | fields.remove_at(idx)
137 | types.remove_at(idx)
138 | continue
139 | idx += 1
140 |
141 | # _make_data
142 | instantiation = instantiation.strip_edges()
143 | var args := (
144 | (
145 | Array(instantiation.split("(", false, 1)[1].split(")", false, 1)[0].split(",")).map(func(a: String) -> String: return a.strip_edges().trim_prefix("{").trim_suffix("}"))
146 | as PackedStringArray
147 | )
148 | if not instantiation.ends_with("()")
149 | else PackedStringArray()
150 | )
151 | var fields_with_type := fields.duplicate()
152 | for i in range(fields_with_type.size()):
153 | fields_with_type[i] = "%s: %s" % [fields_with_type[i], type_string(types[i])]
154 | fa.store_line(member_prefix + "func _make_data(%s) -> %s:" % [", ".join(fields_with_type), data_class])
155 | fa.store_line("\tvar ret := %s.%s(%s)" % [data_class, instantiation.split("(")[0], ", ".join(args)])
156 | var hinted_fields: PackedStringArray = []
157 |
158 | var valid_properties :Dictionary = {}
159 | for prop in property_list:
160 | valid_properties[prop["name"]] = prop
161 |
162 | for f: String in Array(fields).filter(func(f: String) -> bool: return not args.has(f)):
163 | if custom_setters.has(f):
164 | fa.store_line("\tret.%s(%s)" % [custom_setters[f], f])
165 | elif valid_properties.has(f):
166 | var prop := valid_properties[f] as Dictionary
167 | if prop.type == TYPE_ARRAY:
168 | fa.store_line("\tret.%s.assign(%s)" % [f, f])
169 | elif prop.type == TYPE_DICTIONARY and (Engine.get_version_info().major > 4 or Engine.get_version_info().minor >= 4):
170 | # 4.4 以上支持类型化字典
171 | fa.store_line("\tret.%s.assign(%s)" % [f, f])
172 | else:
173 | fa.store_line("\tret.%s = %s" % [f, f])
174 | elif not hinted_fields.has(f):
175 | # 只提示一次
176 | _Log.warning([_Localize.translate("无法被赋值的字段将被跳过: "), f])
177 | hinted_fields.push_back(f)
178 | fa.store_line("\treturn ret")
179 | fa.store_line("")
180 | fa.store_line("")
181 |
182 | # 数据行
183 | fa.store_line(member_prefix + "var _data:Array[%s] = [" % data_class)
184 | for row in data_rows:
185 | var args_text_list := PackedStringArray()
186 | for i in range(fields.size()):
187 | var f := fields[i]
188 | var t := types[i]
189 | args_text_list.push_back(_get_value_text(row, f, t, property_default_values))
190 | fa.store_line("\t_make_data(%s)," % [", ".join(args_text_list)])
191 | fa.store_line("]")
192 | fa.store_line("")
193 | fa.store_line("")
194 |
195 | # 构造,使数据只读
196 | if member_prefix.is_empty():
197 | fa.store_line(member_prefix + "func _init() -> void:")
198 | else:
199 | fa.store_line(member_prefix + "func _static_init() -> void:")
200 | fa.store_line("\t_data.make_read_only()")
201 | fa.store_line("")
202 | fa.close()
203 |
204 | return OK
205 |
206 |
207 | func _get_import_file_extension() -> String:
208 | return "gd"
209 |
210 |
211 | func _get_value_text(row: Dictionary, field: String, type_id: int, default_values: Dictionary) -> String:
212 | var value: Variant = row.get(field, default_values.get(field, null))
213 |
214 | var default := typeof(value) == TYPE_NIL
215 | var converted_value: Variant = type_convert(value, type_id)
216 | return __get_value_text(converted_value, default)
217 |
218 |
219 | func __get_value_text(converted_value: Variant, default := false) -> String:
220 | var type_id: Variant = typeof(converted_value)
221 | var type := type_string(type_id)
222 | match type_id:
223 | TYPE_NIL:
224 | return "null"
225 | TYPE_BOOL:
226 | return str(false) if default else str(converted_value)
227 | TYPE_INT:
228 | return str(0) if default else str(converted_value)
229 | TYPE_FLOAT:
230 | return str(0.0) if default else str(converted_value)
231 | TYPE_STRING:
232 | return '""' if default else '"%s"' % converted_value
233 | TYPE_VECTOR2, TYPE_VECTOR2I:
234 | return (type + "()") if default else (type + "(%s, %s)" % [converted_value.x, converted_value.y])
235 | TYPE_VECTOR3, TYPE_VECTOR3I:
236 | return (type + "()") if default else (type + "(%s, %s, %s)" % [converted_value.x, converted_value.y, converted_value.z])
237 | TYPE_RECT2, TYPE_RECT2I:
238 | if default:
239 | return type + "()"
240 | else:
241 | return type + "(%s, %s, %s, %s)" % [converted_value.position.x, converted_value.position.y, converted_value.size.x, converted_value.size.y]
242 | TYPE_TRANSFORM2D:
243 | if default:
244 | return type + "()"
245 | else:
246 | var tran := converted_value as Transform2D
247 | return type + "(Vector2(%s, %s), Vector2(%s, %s), Vector2(%s, %s))" % [tran.x.x, tran.x.y, tran.y.x, tran.y.y, tran.origin.x, tran.origin.y]
248 | TYPE_VECTOR4, TYPE_VECTOR4I:
249 | if default:
250 | return type + "()"
251 | else:
252 | return type + "(%s, %s, %s, %s)" % [converted_value.x, converted_value.y, converted_value.z, converted_value.w]
253 | TYPE_PLANE:
254 | if default:
255 | return type + "()"
256 | else:
257 | var plane := converted_value as Plane
258 | return type + "(Vector3(%s, %s, %s), %s)" % [plane.normal.x, plane.normal.y, plane.normal.z, plane.d]
259 | TYPE_QUATERNION:
260 | if default:
261 | return type + "()"
262 | else:
263 | return type + "(%s, %s, %s, %s)" % [converted_value.x, converted_value.y, converted_value.z, converted_value.w]
264 | TYPE_AABB:
265 | if default:
266 | return type + "()"
267 | else:
268 | var aabb := converted_value as AABB
269 | return type + "(Vector3(%s, %s, %s), Vector3(%s, %s, %s))" % [aabb.position.x, aabb.position.y, aabb.position.z, aabb.size.x, aabb.size.y, aabb.size.z]
270 | TYPE_BASIS:
271 | if default:
272 | return type + "()"
273 | else:
274 | var basis := converted_value as Basis
275 | return (
276 | type
277 | + (
278 | "(Vector3(%s, %s, %s), Vector3(%s, %s, %s), Vector3(%s, %s, %s))"
279 | % [basis.x.x, basis.x.y, basis.x.z, basis.y.x, basis.y.y, basis.y.z, basis.z.x, basis.z.y, basis.z.z]
280 | )
281 | )
282 | TYPE_TRANSFORM3D:
283 | if default:
284 | return type + "()"
285 | else:
286 | var trans := converted_value as Transform3D
287 | var basis := trans.basis
288 | var origin := trans.origin
289 | return (
290 | type
291 | + (
292 | "(Vector3(%s, %s, %s), Vector3(%s, %s, %s), Vector3(%s, %s, %s), Vector3(%s, %s, %s))"
293 | % [basis.x.x, basis.x.y, basis.x.z, basis.y.x, basis.y.y, basis.y.z, basis.z.x, basis.z.y, basis.z.z, origin.x, origin.y, origin.z]
294 | )
295 | )
296 | TYPE_PROJECTION:
297 | if default:
298 | return type + "()"
299 | else:
300 | var proj := converted_value as Projection
301 | return (
302 | type
303 | + (
304 | "(Vector4(%s, %s, %s, %s), Vector4(%s, %s, %s, %s), Vector4(%s, %s, %s, %s), Vector4(%s, %s, %s, %s))"
305 | % [
306 | proj.x.x,
307 | proj.x.y,
308 | proj.x.z,
309 | proj.x.w,
310 | proj.y.x,
311 | proj.y.y,
312 | proj.y.z,
313 | proj.y.w,
314 | proj.z.x,
315 | proj.z.y,
316 | proj.z.z,
317 | proj.z.w,
318 | proj.w.x,
319 | proj.w.y,
320 | proj.w.z,
321 | proj.w.w,
322 | ]
323 | )
324 | )
325 | TYPE_COLOR:
326 | if default:
327 | return type + "()"
328 | else:
329 | var color := converted_value as Color
330 | return type + "(%s, %s, %s, %s)" % [color.r, color.g, color.b, color.a]
331 | TYPE_STRING_NAME:
332 | return '&""' if default else '&"%s"' % String(converted_value)
333 | TYPE_NODE_PATH:
334 | return '^""' if default else '^"%s"' % String(converted_value)
335 | TYPE_DICTIONARY:
336 | var elements: PackedStringArray = []
337 | for k: Variant in converted_value:
338 | var v: Variant = converted_value[k]
339 | elements.push_back("%s: %s" % [__get_value_text(k), __get_value_text(v)])
340 | return "{}" if default else "{%s}" % ", ".join(elements)
341 | _:
342 | if type_id >= TYPE_ARRAY and type_id < TYPE_MAX:
343 | var elements: PackedStringArray = []
344 | for e: Variant in converted_value:
345 | elements.push_back(__get_value_text(e))
346 | return ("%s([])" % type) if default else ("%s([%s])" % [type, ", ".join(elements)])
347 |
348 | _Log.error([_Localize.translate("转换失败,不支持的类型: "), type, " - ", converted_value])
349 | return ""
350 |
351 |
352 | func _get_tooltip_text() -> String:
353 | return """默认的 GDScript 导入 (TypedArray) 适合表格条目较少的情况。
354 | 可选配置参数:
355 | - generate_class_name - 如果 table_name 非空且是合法的标识符,将使用 table_name 生成全局类名
356 | - pure_static=true/false - 是否以纯静态成员的形式进行生成, 默认为 true
357 | """
358 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends PanelContainer
3 |
4 | const _Settings = preload("settings.gd")
5 | const _Preset = preload("../scripts/preset.gd")
6 | const _Log = _Preset._Log
7 | const _Localize = _Preset._Localize
8 |
9 | const _AdditionalPropertyEdit = preload("additional_property_edit.gd")
10 | const _PropertyDescriptionEdit = preload("property_description_edit.gd")
11 | const _MetaEdit = preload("meta_edit.gd")
12 |
13 | #
14 | @onready var _file_dialog: FileDialog = %FileDialog
15 | @onready var _confirmation_dialog: ConfirmationDialog = %ConfirmationDialog
16 |
17 | # 预设
18 | @onready var _preset_name_line_edit: LineEdit = %PresetNameLineEdit
19 |
20 | # 操作
21 | @onready var _preset_options: OptionButton = %PresetOption
22 | @onready var _save_btn: Button = %SaveButton
23 | @onready var _delete_btn: Button = %DeleteButton
24 | @onready var _script_select_btn: Button = %ScriptSelectBtn
25 | @onready var _add_ap_btn: Button = %AddAPBtn
26 | @onready var _add_desc_btn: Button = %AddDescBtn
27 | @onready var _add_meta_btn: Button = %AddMetaBtn
28 | @onready var _output_select_btn: Button = %OutputSelectBtn
29 | # @onready var _table_import_select_btn: Button = %TableImportSelectBtn
30 |
31 | # 预设参数
32 | @onready var _class_name_line_edit: LineEdit = %ClassNameLineEdit
33 | @onready var _script_line_edit: LineEdit = %ScriptLineEdit
34 | @onready var _only_storage_check_box: CheckBox = %OnlyStorageCheckBox
35 | @onready var _no_inheritance_check_box: CheckBox = %NoInheritanceCheckBox
36 | @onready var _ascending_check_box: CheckBox = %AscendingCheckBox
37 | @onready var _instantiation_line_edit: LineEdit = %InstantiationLineEdit
38 | @onready var _priority_line_edit: LineEdit = %PriorityLineEdit
39 | @onready var _output_line_edit: LineEdit = %OutputLineEdit
40 | @onready var _table_import_line_edit: LineEdit = %TableImportLineEdit
41 |
42 | @onready var _tab_container: TabContainer = %TabContainer
43 | @onready var _settings: _Settings
44 | @onready var _ap_container: VBoxContainer = %APContainer
45 | @onready var _desc_container: VBoxContainer = %DescContainer
46 | @onready var _meta_container: VBoxContainer = %MetaContainer
47 |
48 | @onready var _table_tool_option_line_edit: LineEdit = %TableOptionsLineEdit
49 | @onready var _import_tool_option_line_edit: LineEdit = %ImportOptionsLineEdit
50 |
51 | @onready var _table_tool_options: OptionButton = %TableToolOptions
52 | @onready var _import_tool_options: OptionButton = %ImportToolOptions
53 |
54 | # @onready var _preset_manager_tab := find_child("预设管理") as Node
55 | @onready var _gen_and_import_tab := find_child("生成与导入") as Node
56 |
57 | var _ap_edit_scene: PackedScene = _load((get_script().resource_path as String).get_base_dir().path_join("additional_property_edit.tscn"))
58 | var _pd_edit_scene: PackedScene = _load((get_script().resource_path as String).get_base_dir().path_join("property_description_edit.tscn"))
59 | var _meta_edit_scene: PackedScene = _load((get_script().resource_path as String).get_base_dir().path_join("meta_edit.tscn"))
60 |
61 |
62 | func _ready() -> void:
63 | _settings = _load((get_script().resource_path as String).get_base_dir().path_join("settings.tscn")).instantiate()
64 | _settings.tools_updated.connect(_on_settings_tools_updated)
65 | _tab_container.add_child(_settings)
66 |
67 | _confirmation_dialog.confirmed.connect(func() -> void: _confirmed.emit(true))
68 | _confirmation_dialog.canceled.connect(func() -> void: _confirmed.emit(false))
69 |
70 | _file_dialog.canceled.connect(func() -> void: _path_selected.emit(false, ""))
71 | _file_dialog.dir_selected.connect(func(dir: String) -> void: _path_selected.emit(true, dir))
72 | _file_dialog.file_selected.connect(func(file: String) -> void: _path_selected.emit(true, file))
73 |
74 | _preset_options.item_selected.connect(_on_preset_options_selected)
75 | _save_btn.pressed.connect(_on_save_btn_pressed)
76 | _delete_btn.pressed.connect(_on_delete_btn_pressed)
77 | _script_select_btn.pressed.connect(_on_script_select_btn_pressed)
78 | _add_ap_btn.pressed.connect(_on_add_additional_property_btn_pressed)
79 | _add_desc_btn.pressed.connect(_on_add_desc_btn_pressed)
80 | _add_meta_btn.pressed.connect(_on_add_meta_btn_pressed)
81 | _output_select_btn.pressed.connect(_on_output_select_btn_pressed)
82 | %GenerateModifierSelectBtn.pressed.connect(_on_generate_modifier_select_btn_pressed)
83 | %ImportModifierSelectBtn.pressed.connect(_on_import_modifier_select_btn_pressed)
84 |
85 | _gen_and_import_tab.visibility_changed.connect(_on_gen_and_import_tab_visibility_changed)
86 |
87 | _table_tool_options.item_selected.connect(_on_table_tool_options_item_selected)
88 | _import_tool_options.item_selected.connect(_on_import_tool_options_item_selected)
89 |
90 | _preset_options.pressed.connect(_on_preset_options_pressed)
91 |
92 | _preset_options.clear()
93 | _preset_options.add_item(_Localize.translate("- None -"))
94 | _preset_options.set_item_metadata(0, null)
95 |
96 | _Localize.localize_node(self)
97 | _on_settings_tools_updated()
98 |
99 |
100 | # ---------------
101 | signal _path_selected(confirmed: bool, path: String)
102 | signal _confirmed(confirm: bool)
103 |
104 |
105 | #region 内部方法
106 | func _pop_file_dialog(title: String, mode: FileDialog.FileMode, filters: PackedStringArray, path: String) -> void:
107 | _file_dialog.title = title
108 | _file_dialog.file_mode = mode
109 | _file_dialog.filters = filters
110 | _file_dialog.current_path = path
111 | _file_dialog.popup_centered_ratio(0.6)
112 |
113 |
114 | func _on_preset_options_pressed() -> void:
115 | if _preset_options.item_count <= 1:
116 | _refresh_preset_options()
117 |
118 |
119 | func _refresh_preset_options(preset_name: String = "") -> void:
120 | _preset_options.clear()
121 | _preset_options.add_item(_Localize.translate("- None -"))
122 | _preset_options.set_item_metadata(0, null)
123 | if not DirAccess.dir_exists_absolute(_settings.presets_dir):
124 | return
125 | var fs := EditorInterface.get_resource_filesystem() as EditorFileSystem
126 | var dir := fs.get_filesystem_path(_settings.presets_dir)
127 | if not is_instance_valid(dir):
128 | _Log.error([_Localize.translate('无效的预设保存路径"{save_dir}",必须是资源目录下的合法路径。').format({save_dir = _settings.presets_dir})])
129 | return
130 |
131 | var idx := -1
132 | for i in range(dir.get_file_count()):
133 | if dir.get_file_type(i) != &"Resource":
134 | continue
135 | var preset := _load(dir.get_file_path(i)) as _Preset
136 | if not is_instance_valid(preset):
137 | continue
138 |
139 | _preset_options.add_item(preset.name)
140 | _preset_options.set_item_metadata(_preset_options.item_count - 1, preset)
141 | if preset.name == preset_name:
142 | idx = _preset_options.item_count - 1
143 |
144 | if idx >= 0:
145 | _on_preset_options_selected(idx)
146 |
147 |
148 | func _save_preset() -> void:
149 | var preset_name: String = _preset_name_line_edit.text
150 | if preset_name.is_empty():
151 | _Log.error([_Localize.translate("预设名称不能为空。")])
152 | return
153 | if not preset_name.is_valid_filename():
154 | _Log.error([_Localize.translate('预设名称"{preset_name}"无法作为文件名').format({preset_name = preset_name})])
155 | return
156 | var presets_dir := _settings.presets_dir
157 | if not DirAccess.dir_exists_absolute(presets_dir):
158 | var make_dir_err := DirAccess.make_dir_recursive_absolute(presets_dir)
159 | if make_dir_err != OK:
160 | _Log.error([_Localize.translate('创建预设路径"{presets_dir}"失败: ').format({presets_dir = presets_dir}), error_string(make_dir_err)])
161 | return
162 |
163 | var preset := _Preset.new()
164 | _set_to_preset(preset)
165 |
166 | var file_name := preset_name.capitalize().replace(" ", "_").to_lower().trim_suffix(".tres") + ".tres"
167 | var fp := presets_dir.path_join(file_name)
168 | var err := ResourceSaver.save(preset, fp, ResourceSaver.FLAG_NONE)
169 | if err != OK:
170 | _Log.error([_Localize.translate('保存预设失败"{file_name}"失败: ').format({file_name = file_name}), error_string(err)])
171 | return
172 |
173 | EditorInterface.get_resource_filesystem().update_file(file_name)
174 | _Log.info([_Localize.translate('保存预设"{preset_name}"成功: ').format({preset_name = preset_name})])
175 | _refresh_preset_options(preset.name)
176 |
177 |
178 | func _delete_preset(preset: _Preset) -> void:
179 | var file_path := preset.resource_path
180 | if not FileAccess.file_exists(file_path):
181 | _Log.error([_Localize.translate("删除失败,预设”{file_path}“不存在").format({file_path = file_path})])
182 | return
183 |
184 | var err := DirAccess.remove_absolute(file_path)
185 | if not err == OK:
186 | _Log.error([_Localize.translate("删除预设失败: "), error_string(err)])
187 | return
188 |
189 | _Log.info([_Localize.translate("删除预设成功: "), preset.name])
190 | EditorInterface.get_resource_filesystem().update_file(file_path)
191 | _refresh_preset_options()
192 |
193 |
194 | func _remove_and_queue_free_children(node: Node) -> void:
195 | for c in node.get_children():
196 | node.remove_child(c)
197 | c.queue_free()
198 |
199 |
200 | func _load_preset(preset: _Preset) -> void:
201 | _preset_name_line_edit.text = preset.name
202 | _class_name_line_edit.text = preset.data_class
203 | _script_line_edit.text = preset.data_class_script
204 | %TableNameLineEdit.text = preset.table_name.strip_edges()
205 | %SkipPrefixUnderscoreCheckBox.set_pressed_no_signal(preset.skip_prefix_underscore_properties)
206 | _only_storage_check_box.set_pressed_no_signal(preset.only_storage_properties)
207 | _no_inheritance_check_box.set_pressed_no_signal(preset.no_inheritance)
208 | _ascending_check_box.set_pressed_no_signal(preset.ascending_order)
209 | _instantiation_line_edit.text = preset.instantiation
210 | _priority_line_edit.text = ", ".join(preset.priority_properties)
211 | %IgnoreLineEdit.text = ", ".join(preset.ignored_properties)
212 | %MetaPriorityLineEdit.text = ", ".join(preset.need_meta_properties)
213 | _output_line_edit.text = preset.table_output_path
214 | %BackupCheckBox.set_pressed_no_signal(preset.auto_backup)
215 | %MergeCheckBox.set_pressed_no_signal(preset.auto_merge)
216 | _table_import_line_edit.text = preset.import_path
217 | _table_tool_option_line_edit.text = ", ".join(preset.table_tool_options)
218 | _import_tool_option_line_edit.text = ", ".join(preset.import_tool_options)
219 | %GenerateModifierLineEdit.text = preset.generate_modifier_file
220 | %ImportModifierLineEdit.text = preset.import_modifier_file
221 |
222 | _remove_and_queue_free_children(_ap_container)
223 | for ap: Dictionary in preset.additional_properties:
224 | var ape := _ap_edit_scene.instantiate() as _AdditionalPropertyEdit
225 | ape.setup(ap.get("name", ""), ap.get("type", TYPE_BOOL), ap.get("setter", ""))
226 | ape.delete_request.connect(_on_additional_property_delete_request.bind(ape))
227 | _ap_container.add_child(ape)
228 |
229 | _remove_and_queue_free_children(_desc_container)
230 | for f: String in preset.descriptions:
231 | var pde := _pd_edit_scene.instantiate() as _PropertyDescriptionEdit
232 | pde.setup(f, preset.descriptions[f])
233 | pde.delete_request.connect(_on_property_description_delete_request.bind(pde))
234 | _desc_container.add_child(pde)
235 |
236 | _remove_and_queue_free_children(_meta_container)
237 | for m: String in preset.meta_list:
238 | var me := _meta_edit_scene.instantiate() as _MetaEdit
239 | me.setup(m)
240 | me.delete_request.connect(_on_meta_edit_delete_request.bind(me))
241 | _meta_container.add_child(me)
242 |
243 | for i in range(_table_tool_options.item_count):
244 | if _table_tool_options.get_item_metadata(i) == preset.table_tool_script_file:
245 | _table_tool_options.select(i)
246 | break
247 |
248 | for i in range(_import_tool_options.item_count):
249 | if _import_tool_options.get_item_metadata(i) == preset.import_tool_script_file:
250 | _import_tool_options.select(i)
251 | _import_tool_options.item_selected.emit(i)
252 | break
253 |
254 |
255 | func _set_to_preset(preset: _Preset) -> void:
256 | preset.name = _preset_name_line_edit.text
257 | preset.data_class = _class_name_line_edit.text
258 | preset.data_class_script = _script_line_edit.text
259 | preset.table_name = %TableNameLineEdit.text.strip_edges()
260 | preset.skip_prefix_underscore_properties = %SkipPrefixUnderscoreCheckBox.button_pressed
261 | preset.only_storage_properties = _only_storage_check_box.button_pressed
262 | preset.no_inheritance = _no_inheritance_check_box.button_pressed
263 | preset.ascending_order = _ascending_check_box.button_pressed
264 | preset.instantiation = _instantiation_line_edit.text
265 | preset.priority_properties = Array(_priority_line_edit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges())
266 | preset.ignored_properties = Array(%IgnoreLineEdit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges())
267 | preset.table_output_path = _output_line_edit.text
268 | preset.need_meta_properties = Array(%MetaPriorityLineEdit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges())
269 | preset.auto_backup = %BackupCheckBox.button_pressed
270 | preset.auto_merge = %MergeCheckBox.button_pressed
271 | preset.import_path = _table_import_line_edit.text
272 | preset.table_tool_options = Array(_table_tool_option_line_edit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges())
273 | preset.import_tool_options = Array(_import_tool_option_line_edit.text.split(",", false)).map(func(text: String) -> String: return text.strip_edges())
274 | preset.generate_modifier_file = %GenerateModifierLineEdit.text
275 | preset.import_modifier_file = %ImportModifierLineEdit.text
276 |
277 | preset.additional_properties.clear()
278 | for ape in _ap_container.get_children():
279 | ape = ape as _AdditionalPropertyEdit
280 | if not is_instance_valid(ape):
281 | continue
282 | var ap := {} # _Preset._AdditionalProperty.new()
283 | ap["name"] = ape.get_property_name()
284 | ap["type"] = ape.get_type()
285 | ap["setter"] = ape.get_setter()
286 | preset.additional_properties.push_back(ap)
287 |
288 | preset.descriptions.clear()
289 | for pde in _desc_container.get_children():
290 | pde = pde as _PropertyDescriptionEdit
291 | if not is_instance_valid(pde):
292 | continue
293 | if preset.descriptions.has(pde.get_property_name()):
294 | _Log.warning([_Localize.translate("重复的字段描述将被跳过: "), pde.get_property_name(), " - ", pde.get_description()])
295 | continue
296 | preset.descriptions[pde.get_property_name()] = pde.get_description()
297 |
298 | preset.meta_list.clear()
299 | for me in _meta_container.get_children():
300 | me = me as _MetaEdit
301 | if not is_instance_valid(me):
302 | continue
303 | preset.meta_list.push_back(me.get_meta_text())
304 |
305 | var m: Variant = _table_tool_options.get_selected_metadata()
306 | if typeof(m) == TYPE_STRING:
307 | preset.table_tool_script_file = m
308 | else:
309 | preset.table_tool_script_file = ""
310 |
311 | m = _import_tool_options.get_selected_metadata()
312 | if typeof(m) == TYPE_STRING:
313 | preset.import_tool_script_file = m
314 | else:
315 | preset.import_tool_script_file = ""
316 |
317 |
318 | #endregion
319 |
320 |
321 | #--------------------------------------
322 | #region 回调
323 | func _on_preset_options_selected(idx: int) -> void:
324 | var preset: _Preset
325 | if idx == 0:
326 | preset = _Preset.new()
327 | else:
328 | preset = _preset_options.get_item_metadata(idx)
329 | if not is_instance_valid(preset):
330 | _Log.error([_Localize.translate("Bug,请提交issue并提供复现步骤")])
331 | return
332 | _load_preset(preset)
333 |
334 |
335 | func _on_save_btn_pressed() -> void:
336 | _save_preset()
337 |
338 |
339 | func _on_delete_btn_pressed() -> void:
340 | if _preset_options.selected < 0:
341 | _Log.error([_Localize.translate("删除预设失败,未选中预设")])
342 | return
343 |
344 | _confirmation_dialog.popup_centered()
345 | if not await _confirmed:
346 | return
347 |
348 | var preset := _preset_options.get_item_metadata(_preset_options.selected) as _Preset
349 | if not is_instance_valid(preset):
350 | _Log.error([_Localize.translate("Bug,请提交issue并提供复现步骤")])
351 | return
352 |
353 | _delete_preset(preset)
354 |
355 |
356 | func _on_script_select_btn_pressed() -> void:
357 | var path := _script_line_edit.text
358 | _pop_file_dialog(_Localize.translate("选择脚本"), FileDialog.FILE_MODE_OPEN_FILE, _get_script_filters(), path)
359 | var result := await _path_selected as Array
360 | if not result[0]:
361 | return
362 | _script_line_edit.text = result[1]
363 |
364 |
365 | func _on_generate_modifier_select_btn_pressed() -> void:
366 | var path := %GenerateModifierLineEdit.text as String
367 | _pop_file_dialog(_Localize.translate("选择脚本"), FileDialog.FILE_MODE_OPEN_FILE, _get_script_filters(), path)
368 | var result := await _path_selected as Array
369 | if not result[0]:
370 | return
371 | %GenerateModifierLineEdit.text = result[1]
372 |
373 |
374 | func _on_import_modifier_select_btn_pressed() -> void:
375 | var path := %ImportModifierLineEdit.text as String
376 | _pop_file_dialog(_Localize.translate("选择脚本"), FileDialog.FILE_MODE_OPEN_FILE, _get_script_filters(), path)
377 | var result := await _path_selected as Array
378 | if not result[0]:
379 | return
380 | %ImportModifierLineEdit.text = result[1]
381 |
382 |
383 | func _on_add_additional_property_btn_pressed() -> void:
384 | var ape := _ap_edit_scene.instantiate() as _AdditionalPropertyEdit
385 | ape.delete_request.connect(_on_additional_property_delete_request.bind(ape))
386 | _ap_container.add_child(ape)
387 |
388 |
389 | func _on_add_desc_btn_pressed() -> void:
390 | var pde := _pd_edit_scene.instantiate() as _PropertyDescriptionEdit
391 | pde.delete_request.connect(_on_property_description_delete_request.bind(pde))
392 | _desc_container.add_child(pde)
393 |
394 |
395 | func _on_add_meta_btn_pressed() -> void:
396 | var me := _meta_edit_scene.instantiate() as _MetaEdit
397 | me.delete_request.connect(_on_meta_edit_delete_request.bind(me))
398 | _meta_container.add_child(me)
399 |
400 |
401 | func _on_output_select_btn_pressed() -> void:
402 | var path := _output_line_edit.text as String
403 | # "*.csv;CSV表格"
404 | _pop_file_dialog(_Localize.translate("输出文件"), FileDialog.FILE_MODE_OPEN_FILE, [], path)
405 | var result := await _path_selected as Array
406 | if not result[0]:
407 | return
408 | _output_line_edit.text = result[1]
409 |
410 |
411 | func _on_additional_property_delete_request(ape: _AdditionalPropertyEdit) -> void:
412 | _ap_container.remove_child(ape)
413 | ape.queue_free()
414 |
415 |
416 | func _on_property_description_delete_request(pde: _PropertyDescriptionEdit) -> void:
417 | _desc_container.remove_child(pde)
418 | pde.queue_free()
419 |
420 |
421 | func _on_meta_edit_delete_request(me: _MetaEdit) -> void:
422 | _meta_container.remove_child(me)
423 | me.queue_free()
424 |
425 |
426 | func _on_settings_tools_updated() -> void:
427 | var selecting_table_tool: Variant = _table_tool_options.get_selected_metadata()
428 | _table_tool_options.clear()
429 | var table_tools := _settings.table_tools
430 | var table_option_idx := -1
431 | for tool_name: String in table_tools:
432 | var tool_path := table_tools[tool_name] as String
433 | var tool_script := load(tool_path) as Script
434 | _table_tool_options.add_item("%s: %s" % [_Localize.translate(tool_name), tool_path])
435 | _table_tool_options.set_item_metadata(_table_tool_options.item_count - 1, tool_path)
436 | _table_tool_options.set_item_tooltip(_table_tool_options.item_count - 1, tool_script.new().get_tooltip_text())
437 | if selecting_table_tool == tool_path:
438 | table_option_idx = _table_tool_options.item_count - 1
439 | if table_option_idx >= 0:
440 | _table_tool_options.select(table_option_idx)
441 | _table_tool_options.item_selected.emit(table_option_idx)
442 |
443 | var selecting_import_tool: Variant = _import_tool_options.get_selected_metadata()
444 | _import_tool_options.clear()
445 | var import_option_idx := -1
446 | var import_tools := _settings.import_tools
447 | for tool_name: String in import_tools:
448 | var tool_path := import_tools[tool_name] as String
449 | var tool_script := load(tool_path) as Script
450 | _import_tool_options.add_item("%s: %s" % [_Localize.translate(tool_name), tool_path])
451 | _import_tool_options.set_item_metadata(_import_tool_options.item_count - 1, tool_path)
452 | _import_tool_options.set_item_tooltip(_import_tool_options.item_count - 1, tool_script.new().get_tooltip_text())
453 | if selecting_import_tool == tool_path:
454 | import_option_idx = _import_tool_options.item_count - 1
455 | if import_option_idx >= 0:
456 | _import_tool_options.select(import_option_idx)
457 | _import_tool_options.item_selected.emit(import_option_idx)
458 |
459 |
460 | func _on_table_tool_options_item_selected(idx: int) -> void:
461 | var tooltip := ""
462 | var meta: Variant = _table_tool_options.get_item_metadata(idx)
463 | if meta == null or not meta is String:
464 | tooltip = ""
465 | else:
466 | var script := load(meta) as Script
467 | tooltip = script.new().get_tooltip_text()
468 | _table_tool_options.tooltip_text = tooltip
469 | _table_tool_option_line_edit.tooltip_text = tooltip
470 |
471 |
472 | func _on_import_tool_options_item_selected(idx: int) -> void:
473 | var tooltip := ""
474 | var meta: Variant = _import_tool_options.get_item_metadata(idx)
475 | if meta == null or not meta is String:
476 | tooltip = ""
477 | else:
478 | var script := load(meta) as Script
479 | tooltip = script.new().get_tooltip_text()
480 | _import_tool_options.tooltip_text = tooltip
481 | _import_tool_option_line_edit.tooltip_text = tooltip
482 |
483 |
484 | func _on_gen_and_import_tab_visibility_changed() -> void:
485 | if not _gen_and_import_tab.visible:
486 | return
487 |
488 | var presets: Array[_Preset] = []
489 | var dir := EditorInterface.get_resource_filesystem().get_filesystem_path(_settings.presets_dir)
490 | if is_instance_valid(dir):
491 | for i in range(dir.get_file_count()):
492 | if dir.get_file_type(i) != &"Resource":
493 | continue
494 | var preset := _load(dir.get_file_path(i)) as _Preset
495 | if not is_instance_valid(preset):
496 | continue
497 |
498 | presets.append(preset)
499 |
500 | %GenerateTables.save_path = EditorInterface.get_editor_paths().get_project_settings_dir().path_join(".ctm_generate_settings")
501 | %ImportTables.save_path = EditorInterface.get_editor_paths().get_project_settings_dir().path_join(".ctm_import_settings")
502 |
503 | %GenerateTables.setup(presets)
504 | %ImportTables.setup(presets)
505 |
506 |
507 | #endregion
508 |
509 |
510 | #--------------------------------------
511 | func _load(path: String) -> Resource:
512 | var ret := ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE)
513 | if is_instance_valid(ret) and ret.resource_path.is_empty():
514 | # 兼容 4.2
515 | ret.take_over_path(path)
516 | #ret.resource_path = path
517 | return ret
518 |
519 |
520 | func _get_script_filters() -> PackedStringArray:
521 | var ret: PackedStringArray = ["*.gd;GDScript"]
522 | if ClassDB.class_exists(&"CSharpScript"):
523 | ret.push_back("*.cs;CSharpScript")
524 | for klass in ClassDB.get_inheriters_from_class(&"ScriptLanguageExtension"):
525 | if not ClassDB.can_instantiate(klass):
526 | continue
527 | var script := ClassDB.instantiate(klass) as ScriptExtension
528 | if not is_instance_valid(script):
529 | continue
530 | var language := script._get_language() as ScriptLanguageExtension
531 | if not is_instance_valid(language):
532 | continue
533 | var extension := language._get_extension() as String
534 | var script_name := language._get_name() as String
535 | if extension.is_empty() or script_name.is_empty():
536 | continue
537 | ret.push_back("*.%s;%s" % [extension, script_name])
538 | return ret
539 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=3 format=3 uid="uid://cl4vm3gw0ltsi"]
2 |
3 | [ext_resource type="Script" path="res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd" id="1_bsud7"]
4 | [ext_resource type="PackedScene" uid="uid://bepjlsptadkrq" path="res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn" id="2_sf0g2"]
5 |
6 | [node name="MainScreen" type="PanelContainer"]
7 | custom_minimum_size = Vector2(500, 300)
8 | anchors_preset = 15
9 | anchor_right = 1.0
10 | anchor_bottom = 1.0
11 | grow_horizontal = 2
12 | grow_vertical = 2
13 | size_flags_horizontal = 3
14 | size_flags_vertical = 3
15 | script = ExtResource("1_bsud7")
16 |
17 | [node name="TabContainer" type="TabContainer" parent="."]
18 | unique_name_in_owner = true
19 | layout_mode = 2
20 | drag_to_rearrange_enabled = true
21 |
22 | [node name="预设管理" type="PanelContainer" parent="TabContainer"]
23 | layout_mode = 2
24 | metadata/_tab_index = 0
25 |
26 | [node name="MarginContainer" type="MarginContainer" parent="TabContainer/预设管理"]
27 | layout_mode = 2
28 | theme_override_constants/margin_left = 5
29 | theme_override_constants/margin_top = 5
30 | theme_override_constants/margin_right = 5
31 | theme_override_constants/margin_bottom = 5
32 |
33 | [node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer"]
34 | layout_mode = 2
35 | theme_override_constants/separation = 23
36 |
37 | [node name="GridContainer" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer"]
38 | layout_mode = 2
39 | size_flags_vertical = 0
40 |
41 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"]
42 | layout_mode = 2
43 | text = "预设:"
44 |
45 | [node name="PresetOption" type="OptionButton" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"]
46 | unique_name_in_owner = true
47 | layout_mode = 2
48 | size_flags_horizontal = 3
49 | item_count = 1
50 | selected = 0
51 | popup/item_0/text = "- None -"
52 | popup/item_0/id = 0
53 |
54 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"]
55 | layout_mode = 2
56 | text = "预设名称: "
57 |
58 | [node name="PresetNameLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"]
59 | unique_name_in_owner = true
60 | layout_mode = 2
61 | size_flags_horizontal = 3
62 | placeholder_text = "要保存的预设名称"
63 |
64 | [node name="SaveButton" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"]
65 | unique_name_in_owner = true
66 | layout_mode = 2
67 | text = "保存"
68 |
69 | [node name="DeleteButton" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/GridContainer"]
70 | unique_name_in_owner = true
71 | layout_mode = 2
72 | text = "删除"
73 |
74 | [node name="ScrollContainer2" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer"]
75 | layout_mode = 2
76 | size_flags_vertical = 3
77 |
78 | [node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2"]
79 | layout_mode = 2
80 | size_flags_horizontal = 3
81 | size_flags_vertical = 3
82 |
83 | [node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"]
84 | layout_mode = 2
85 |
86 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer"]
87 | layout_mode = 2
88 | text = "数据类名:"
89 |
90 | [node name="ClassNameLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer"]
91 | unique_name_in_owner = true
92 | layout_mode = 2
93 | size_flags_horizontal = 3
94 | placeholder_text = "原生类名或脚本的内部类名,留空时脚本不能为空"
95 |
96 | [node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"]
97 | layout_mode = 2
98 |
99 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer2"]
100 | layout_mode = 2
101 | text = "数据类脚本:"
102 |
103 | [node name="ScriptLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer2"]
104 | unique_name_in_owner = true
105 | layout_mode = 2
106 | size_flags_horizontal = 3
107 | placeholder_text = "可选项"
108 |
109 | [node name="ScriptSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer2"]
110 | unique_name_in_owner = true
111 | layout_mode = 2
112 | text = "选择"
113 |
114 | [node name="HBoxContainer6" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"]
115 | layout_mode = 2
116 |
117 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer6"]
118 | layout_mode = 2
119 | text = "表格名:"
120 |
121 | [node name="TableNameLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/HBoxContainer6"]
122 | unique_name_in_owner = true
123 | layout_mode = 2
124 | size_flags_horizontal = 3
125 | placeholder_text = "建议使用大驼峰命名"
126 |
127 | [node name="PanelContainer" type="PanelContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer"]
128 | layout_mode = 2
129 | size_flags_vertical = 3
130 |
131 | [node name="TabContainer" type="TabContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer"]
132 | layout_mode = 2
133 | size_flags_vertical = 3
134 | tab_alignment = 2
135 |
136 | [node name="表格选项" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"]
137 | layout_mode = 2
138 | metadata/_tab_index = 0
139 |
140 | [node name="HBoxContainer5" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
141 | layout_mode = 2
142 |
143 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer5"]
144 | layout_mode = 2
145 | text = "优先字段:"
146 |
147 | [node name="PriorityLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer5"]
148 | unique_name_in_owner = true
149 | layout_mode = 2
150 | size_flags_horizontal = 3
151 | placeholder_text = "用\",\"分隔,未列入的字段将排在其后。例:id, name, description"
152 |
153 | [node name="HBoxContainer8" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
154 | layout_mode = 2
155 |
156 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer8"]
157 | layout_mode = 2
158 | text = "忽略字段:"
159 |
160 | [node name="IgnoreLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer8"]
161 | unique_name_in_owner = true
162 | layout_mode = 2
163 | size_flags_horizontal = 3
164 | placeholder_text = "用\",\"分隔,列入的字段将不被生成(但优先度低于附加字段)。例:internal_id, changeable_data"
165 |
166 | [node name="HBoxContainer6" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
167 | layout_mode = 2
168 |
169 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer6"]
170 | layout_mode = 2
171 | text = "需要元字段的字段: "
172 |
173 | [node name="MetaPriorityLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer6"]
174 | unique_name_in_owner = true
175 | layout_mode = 2
176 | size_flags_horizontal = 3
177 | placeholder_text = "用\",\"分隔,生成的元字段列不会被导入,仅用于表格编辑。"
178 |
179 | [node name="HBoxContainer3" type="HFlowContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
180 | layout_mode = 2
181 |
182 | [node name="SkipPrefixUnderscoreCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"]
183 | unique_name_in_owner = true
184 | layout_mode = 2
185 | button_pressed = true
186 | text = "跳过\"_\"开头的属性"
187 |
188 | [node name="OnlyStorageCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"]
189 | unique_name_in_owner = true
190 | layout_mode = 2
191 | tooltip_text = "即含有PROPERTY_USAGE_STORAGE的属性"
192 | text = "仅需要存储的属性"
193 |
194 | [node name="NoInheritanceCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"]
195 | unique_name_in_owner = true
196 | layout_mode = 2
197 | button_pressed = true
198 | text = "不含父类属性"
199 |
200 | [node name="AscendingCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"]
201 | unique_name_in_owner = true
202 | layout_mode = 2
203 | text = "按字母升序"
204 |
205 | [node name="BackupCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"]
206 | unique_name_in_owner = true
207 | layout_mode = 2
208 | tooltip_text = "如果表格生成路径已存在文件,则将生成备份(于同目录下的.backup文件夹中),备份文件名称将加上备份时间作为前缀。"
209 | text = "覆盖前备份"
210 |
211 | [node name="MergeCheckBox" type="CheckBox" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer3"]
212 | unique_name_in_owner = true
213 | layout_mode = 2
214 | tooltip_text = "如果生成路径已存在目标文件,将对数据行的兼容字段进行合并"
215 | button_pressed = true
216 | text = "自动合并"
217 |
218 | [node name="GridContainer2" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
219 | layout_mode = 2
220 | size_flags_vertical = 10
221 | columns = 3
222 |
223 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer2"]
224 | layout_mode = 2
225 | text = "表格生成路径:"
226 |
227 | [node name="OutputLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer2"]
228 | unique_name_in_owner = true
229 | layout_mode = 2
230 | size_flags_horizontal = 3
231 | text = "res://tables/{table_name}.csv"
232 |
233 | [node name="OutputSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer2"]
234 | unique_name_in_owner = true
235 | layout_mode = 2
236 | text = "选择"
237 |
238 | [node name="HBoxContainer7" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
239 | layout_mode = 2
240 | size_flags_vertical = 8
241 | alignment = 2
242 |
243 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer7"]
244 | layout_mode = 2
245 | text = "表格选项参数:"
246 |
247 | [node name="TableOptionsLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer7"]
248 | unique_name_in_owner = true
249 | layout_mode = 2
250 | size_flags_horizontal = 3
251 | placeholder_text = "特定于表格工具的选项参数,用\",\"分隔。"
252 |
253 | [node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
254 | layout_mode = 2
255 |
256 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer"]
257 | layout_mode = 2
258 | text = "表格工具:"
259 |
260 | [node name="TableToolOptions" type="OptionButton" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/HBoxContainer"]
261 | unique_name_in_owner = true
262 | layout_mode = 2
263 | size_flags_horizontal = 3
264 | item_count = 2
265 | selected = 0
266 | popup/item_0/text = "CSV(,分隔): res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd"
267 | popup/item_0/id = 0
268 | popup/item_1/text = "Excel(xlsx): res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd"
269 | popup/item_1/id = 1
270 |
271 | [node name="GridContainer3" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项"]
272 | layout_mode = 2
273 | size_flags_vertical = 8
274 | columns = 3
275 |
276 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer3"]
277 | layout_mode = 2
278 | text = "表格生成修改器:"
279 |
280 | [node name="GenerateModifierLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer3"]
281 | unique_name_in_owner = true
282 | layout_mode = 2
283 | size_flags_horizontal = 3
284 | placeholder_text = "使用修改器在表格生成流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd"
285 |
286 | [node name="GenerateModifierSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/表格选项/GridContainer3"]
287 | unique_name_in_owner = true
288 | layout_mode = 2
289 | text = "选择"
290 |
291 | [node name="导入选项" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"]
292 | visible = false
293 | layout_mode = 2
294 | metadata/_tab_index = 1
295 |
296 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"]
297 | layout_mode = 2
298 |
299 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer4"]
300 | layout_mode = 2
301 | text = "数据对象实例化:"
302 |
303 | [node name="InstantiationLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer4"]
304 | unique_name_in_owner = true
305 | layout_mode = 2
306 | size_flags_horizontal = 3
307 | placeholder_text = "例:create({id}, {name}), 作为参数的属性将在赋值阶段时被跳过。默认无参构造"
308 |
309 | [node name="GridContainer2" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"]
310 | layout_mode = 2
311 | size_flags_vertical = 10
312 | columns = 3
313 |
314 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer2"]
315 | layout_mode = 2
316 | text = "导入路径:"
317 |
318 | [node name="TableImportLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer2"]
319 | unique_name_in_owner = true
320 | layout_mode = 2
321 | size_flags_horizontal = 3
322 | text = "res://tables/imported/{table_name}.gd"
323 |
324 | [node name="TableImportSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer2"]
325 | unique_name_in_owner = true
326 | layout_mode = 2
327 | text = "选择"
328 |
329 | [node name="HBoxContainer7" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"]
330 | layout_mode = 2
331 | size_flags_vertical = 8
332 | alignment = 2
333 |
334 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer7"]
335 | layout_mode = 2
336 | text = "导入选项参数:"
337 |
338 | [node name="ImportOptionsLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer7"]
339 | unique_name_in_owner = true
340 | layout_mode = 2
341 | size_flags_horizontal = 3
342 | tooltip_text = "特定于导入工具的选项参数,用\",\"分隔。
343 | 例如默认GDScript导入可提供generate_class_name参数来为生成的脚本添加全局类名(如果表格名是合法的标识符)。"
344 | placeholder_text = "特定于导入工具的选项参数,用\",\"分隔。如默认GDScript导入可使用generate_class_name"
345 |
346 | [node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"]
347 | layout_mode = 2
348 |
349 | [node name="Label2" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer2"]
350 | layout_mode = 2
351 | text = "导入工具:"
352 |
353 | [node name="ImportToolOptions" type="OptionButton" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/HBoxContainer2"]
354 | unique_name_in_owner = true
355 | layout_mode = 2
356 | size_flags_horizontal = 3
357 | item_count = 2
358 | selected = 0
359 | allow_reselect = true
360 | popup/item_0/text = "GDScript(TypedArray风格): res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd"
361 | popup/item_0/id = 0
362 | popup/item_1/text = "GDScript(Dictionary风格): res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd"
363 | popup/item_1/id = 1
364 |
365 | [node name="GridContainer3" type="GridContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项"]
366 | layout_mode = 2
367 | size_flags_vertical = 8
368 | columns = 3
369 |
370 | [node name="Label" type="Label" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer3"]
371 | layout_mode = 2
372 | text = "导入修改器:"
373 |
374 | [node name="ImportModifierLineEdit" type="LineEdit" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer3"]
375 | unique_name_in_owner = true
376 | layout_mode = 2
377 | size_flags_horizontal = 3
378 | placeholder_text = "使用修改器在表格导入流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd"
379 |
380 | [node name="ImportModifierSelectBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/导入选项/GridContainer3"]
381 | unique_name_in_owner = true
382 | layout_mode = 2
383 | text = "选择"
384 |
385 | [node name="附加字段" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"]
386 | visible = false
387 | layout_mode = 2
388 | metadata/_tab_index = 2
389 |
390 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段"]
391 | layout_mode = 2
392 | alignment = 2
393 |
394 | [node name="AddAPBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段/HBoxContainer4"]
395 | unique_name_in_owner = true
396 | layout_mode = 2
397 | text = "+"
398 |
399 | [node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段"]
400 | layout_mode = 2
401 | size_flags_vertical = 3
402 |
403 | [node name="APContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/附加字段/ScrollContainer"]
404 | unique_name_in_owner = true
405 | layout_mode = 2
406 | size_flags_horizontal = 3
407 | size_flags_vertical = 3
408 |
409 | [node name="字段描述" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"]
410 | visible = false
411 | layout_mode = 2
412 | metadata/_tab_index = 3
413 |
414 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述"]
415 | layout_mode = 2
416 | alignment = 2
417 |
418 | [node name="AddDescBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述/HBoxContainer4"]
419 | unique_name_in_owner = true
420 | layout_mode = 2
421 | text = "+"
422 |
423 | [node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述"]
424 | layout_mode = 2
425 | size_flags_vertical = 3
426 |
427 | [node name="DescContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/字段描述/ScrollContainer"]
428 | unique_name_in_owner = true
429 | layout_mode = 2
430 | size_flags_horizontal = 3
431 | size_flags_vertical = 3
432 |
433 | [node name="自定义元数据" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer"]
434 | visible = false
435 | layout_mode = 2
436 | metadata/_tab_index = 4
437 |
438 | [node name="HBoxContainer4" type="HBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据"]
439 | layout_mode = 2
440 | alignment = 2
441 |
442 | [node name="AddMetaBtn" type="Button" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据/HBoxContainer4"]
443 | unique_name_in_owner = true
444 | layout_mode = 2
445 | text = "+"
446 |
447 | [node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据"]
448 | layout_mode = 2
449 | size_flags_vertical = 3
450 |
451 | [node name="MetaContainer" type="VBoxContainer" parent="TabContainer/预设管理/MarginContainer/VBoxContainer/ScrollContainer2/VBoxContainer/PanelContainer/TabContainer/自定义元数据/ScrollContainer"]
452 | unique_name_in_owner = true
453 | layout_mode = 2
454 | size_flags_horizontal = 3
455 | size_flags_vertical = 3
456 |
457 | [node name="生成与导入" type="MarginContainer" parent="TabContainer"]
458 | unique_name_in_owner = true
459 | visible = false
460 | layout_mode = 2
461 | theme_override_constants/margin_left = 10
462 | theme_override_constants/margin_top = 10
463 | theme_override_constants/margin_right = 10
464 | theme_override_constants/margin_bottom = 10
465 | metadata/_tab_index = 1
466 |
467 | [node name="HBoxContainer" type="HSplitContainer" parent="TabContainer/生成与导入"]
468 | layout_mode = 2
469 |
470 | [node name="GenerateTables" parent="TabContainer/生成与导入/HBoxContainer" instance=ExtResource("2_sf0g2")]
471 | unique_name_in_owner = true
472 | layout_mode = 2
473 |
474 | [node name="ImportTables" parent="TabContainer/生成与导入/HBoxContainer" instance=ExtResource("2_sf0g2")]
475 | unique_name_in_owner = true
476 | layout_mode = 2
477 | title = "表格导入"
478 | mode = 1
479 |
480 | [node name="FileDialog" type="FileDialog" parent="."]
481 | unique_name_in_owner = true
482 | size = Vector2i(384, 210)
483 | root_subfolder = "res://"
484 |
485 | [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
486 | unique_name_in_owner = true
487 | size = Vector2i(200, 106)
488 | dialog_text = "确认操作?"
489 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/localization/template.pot:
--------------------------------------------------------------------------------
1 | # LANGUAGE translation for Config Table Manager for the following files:
2 | # res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
3 | # res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd
4 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd
5 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
6 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
7 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
8 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
9 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
10 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd
11 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn
12 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd
13 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
14 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.gd
15 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
16 | # res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd
17 | # res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd
18 | # res://addons/config_table_manager.daylily-zeleen/scripts/log.gd
19 | # res://addons/config_table_manager.daylily-zeleen/scripts/preset.gd
20 | # res://addons/config_table_manager.daylily-zeleen/scripts/table_header.gd
21 | # res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
22 | # res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
23 | # res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
24 | # res://addons/config_table_manager.daylily-zeleen/config_table_manager.daylily-zeleen.gd
25 | #
26 | # FIRST AUTHOR , YEAR.
27 | #
28 | #, fuzzy
29 | msgid ""
30 | msgstr ""
31 | "Project-Id-Version: Config Table Manager\n"
32 | "MIME-Version: 1.0\n"
33 | "Content-Type: text/plain; charset=UTF-8\n"
34 | "Content-Transfer-Encoding: 8-bit\n"
35 |
36 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
37 | msgid "导表失败: "
38 | msgstr ""
39 |
40 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
41 | msgid "无法被赋值的字段将被跳过: "
42 | msgstr ""
43 |
44 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
45 | msgid "转换失败,不支持的类型: "
46 | msgstr ""
47 |
48 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
49 | msgid "未指定作为key的数据类属性,请使用 key=prop_name 作为选项参数进行指定。"
50 | msgstr ""
51 |
52 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
53 | msgid "数据类不存在指定为key的属性 %s,请使用 key=prop_name 作为选项参数进行指定。"
54 | msgstr ""
55 |
56 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
57 | msgid "存在未配置key (%s)的数据: %s。"
58 | msgstr ""
59 |
60 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
61 | msgid "存在key (%s)重复的数据,重复的值为: %s。"
62 | msgstr ""
63 |
64 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
65 | msgid "要附加的属性名称"
66 | msgstr ""
67 |
68 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
69 | msgid "非空将使用自定义Setter进行设置"
70 | msgstr ""
71 |
72 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
73 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
74 | msgid " - "
75 | msgstr ""
76 |
77 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
78 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
79 | msgid "选中"
80 | msgstr ""
81 |
82 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
83 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
84 | msgid "排除"
85 | msgstr ""
86 |
87 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
88 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
89 | msgid "预设"
90 | msgstr ""
91 |
92 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
93 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
94 | msgid "表格预设不存在: "
95 | msgstr ""
96 |
97 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
98 | msgid "表格生成"
99 | msgstr ""
100 |
101 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
102 | msgid "对选中项进行生成"
103 | msgstr ""
104 |
105 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
106 | msgid "对所有项进行生成"
107 | msgstr ""
108 |
109 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
110 | msgid "对选中项执行导入"
111 | msgstr ""
112 |
113 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
114 | msgid "对所有项执行导入"
115 | msgstr ""
116 |
117 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
118 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
119 | msgid "无效的预设保存路径\"{save_dir}\",必须是资源目录下的合法路径。"
120 | msgstr ""
121 |
122 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
123 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
124 | msgid "预设名称不能为空。"
125 | msgstr ""
126 |
127 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
128 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
129 | msgid "预设名称\"{preset_name}\"无法作为文件名"
130 | msgstr ""
131 |
132 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
133 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
134 | msgid "创建预设路径\"{presets_dir}\"失败: "
135 | msgstr ""
136 |
137 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
138 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
139 | msgid "保存预设失败\"{file_name}\"失败: "
140 | msgstr ""
141 |
142 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
143 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
144 | msgid "保存预设\"{preset_name}\"成功: "
145 | msgstr ""
146 |
147 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
148 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
149 | msgid "删除失败,预设”{file_path}“不存在"
150 | msgstr ""
151 |
152 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
153 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
154 | msgid "删除预设失败: "
155 | msgstr ""
156 |
157 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
158 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
159 | msgid "删除预设成功: "
160 | msgstr ""
161 |
162 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
163 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
164 | msgid "重复的字段描述将被跳过: "
165 | msgstr ""
166 |
167 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
168 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
169 | msgid "Bug,请提交issue并提供复现步骤"
170 | msgstr ""
171 |
172 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
173 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
174 | msgid "删除预设失败,未选中预设"
175 | msgstr ""
176 |
177 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
178 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
179 | msgid "选择脚本"
180 | msgstr ""
181 |
182 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
183 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
184 | msgid "输出文件"
185 | msgstr ""
186 |
187 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
188 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
189 | msgid "- None -"
190 | msgstr ""
191 |
192 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
193 | msgid "预设:"
194 | msgstr ""
195 |
196 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
197 | msgid "预设名称: "
198 | msgstr ""
199 |
200 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
201 | msgid "要保存的预设名称"
202 | msgstr ""
203 |
204 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
205 | msgid "保存"
206 | msgstr ""
207 |
208 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
209 | msgid "删除"
210 | msgstr ""
211 |
212 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
213 | msgid "数据类名:"
214 | msgstr ""
215 |
216 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
217 | msgid "原生类名或脚本的内部类名,留空时脚本不能为空"
218 | msgstr ""
219 |
220 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
221 | msgid "数据类脚本:"
222 | msgstr ""
223 |
224 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
225 | msgid "可选项"
226 | msgstr ""
227 |
228 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
229 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
230 | msgid "选择"
231 | msgstr ""
232 |
233 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
234 | msgid "表格名:"
235 | msgstr ""
236 |
237 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
238 | msgid "建议使用大驼峰命名"
239 | msgstr ""
240 |
241 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
242 | msgid "优先字段:"
243 | msgstr ""
244 |
245 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
246 | msgid "用\",\"分隔,未列入的字段将排在其后。例:id, name, description"
247 | msgstr ""
248 |
249 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
250 | msgid "忽略字段:"
251 | msgstr ""
252 |
253 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
254 | msgid "用\",\"分隔,列入的字段将不被生成(但优先度低于附加字段)。例:internal_id, changeable_data"
255 | msgstr ""
256 |
257 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
258 | msgid "需要元字段的字段: "
259 | msgstr ""
260 |
261 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
262 | msgid "用\",\"分隔,生成的元字段列不会被导入,仅用于表格编辑。"
263 | msgstr ""
264 |
265 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
266 | msgid "跳过\"_\"开头的属性"
267 | msgstr ""
268 |
269 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
270 | msgid "即含有PROPERTY_USAGE_STORAGE的属性"
271 | msgstr ""
272 |
273 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
274 | msgid "仅需要存储的属性"
275 | msgstr ""
276 |
277 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
278 | msgid "不含父类属性"
279 | msgstr ""
280 |
281 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
282 | msgid "按字母升序"
283 | msgstr ""
284 |
285 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
286 | msgid "如果表格生成路径已存在文件,则将生成备份(于同目录下的.backup文件夹中),备份文件名称将加上备份时间作为前缀。"
287 | msgstr ""
288 |
289 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
290 | msgid "覆盖前备份"
291 | msgstr ""
292 |
293 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
294 | msgid "如果生成路径已存在目标文件,将对数据行的兼容字段进行合并"
295 | msgstr ""
296 |
297 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
298 | msgid "自动合并"
299 | msgstr ""
300 |
301 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
302 | msgid "表格生成路径:"
303 | msgstr ""
304 |
305 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
306 | msgid "表格选项参数:"
307 | msgstr ""
308 |
309 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
310 | msgid "特定于表格工具的选项参数,用\",\"分隔。"
311 | msgstr ""
312 |
313 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
314 | msgid "表格工具:"
315 | msgstr ""
316 |
317 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
318 | msgid "表格生成修改器:"
319 | msgstr ""
320 |
321 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
322 | msgid "使用修改器在表格生成流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd"
323 | msgstr ""
324 |
325 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
326 | msgid "数据对象实例化:"
327 | msgstr ""
328 |
329 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
330 | msgid "例:create({id}, {name}), 作为参数的属性将在赋值阶段时被跳过。默认无参构造"
331 | msgstr ""
332 |
333 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
334 | msgid "导入路径:"
335 | msgstr ""
336 |
337 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
338 | msgid "导入选项参数:"
339 | msgstr ""
340 |
341 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
342 | msgid ""
343 | "特定于导入工具的选项参数,用\",\"分隔。\n"
344 | "例如默认GDScript导入可提供generate_class_name参数来为生成的脚本添加全局类名(如果表格名是合法的标识符)。"
345 | msgstr ""
346 |
347 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
348 | msgid "特定于导入工具的选项参数,用\",\"分隔。如默认GDScript导入可使用generate_class_name"
349 | msgstr ""
350 |
351 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
352 | msgid "导入工具:"
353 | msgstr ""
354 |
355 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
356 | msgid "导入修改器:"
357 | msgstr ""
358 |
359 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
360 | msgid "使用修改器在表格导入流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd"
361 | msgstr ""
362 |
363 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
364 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
365 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
366 | msgid "+"
367 | msgstr ""
368 |
369 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
370 | msgid "确认操作?"
371 | msgstr ""
372 |
373 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
374 | msgid "预设管理"
375 | msgstr ""
376 |
377 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
378 | msgid "生成与导入"
379 | msgstr ""
380 |
381 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
382 | msgid "设置"
383 | msgstr ""
384 |
385 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
386 | msgid "表格选项"
387 | msgstr ""
388 |
389 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
390 | msgid "导入选项"
391 | msgstr ""
392 |
393 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
394 | msgid "附加字段"
395 | msgstr ""
396 |
397 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
398 | msgid "字段描述"
399 | msgstr ""
400 |
401 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
402 | msgid "自定义元数据"
403 | msgstr ""
404 |
405 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
406 | msgid "表格导入"
407 | msgstr ""
408 |
409 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn
410 | msgid "Meta"
411 | msgstr ""
412 |
413 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn
414 | msgid "X"
415 | msgstr ""
416 |
417 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
418 | msgid "字段名称"
419 | msgstr ""
420 |
421 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
422 | msgid "描述"
423 | msgstr ""
424 |
425 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
426 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
427 | msgid "表格工具"
428 | msgstr ""
429 |
430 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
431 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
432 | msgid "脚本路径"
433 | msgstr ""
434 |
435 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
436 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
437 | msgid "导入工具"
438 | msgstr ""
439 |
440 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
441 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
442 | msgid "保存设置失败: "
443 | msgstr ""
444 |
445 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
446 | msgid "预设保存路径:"
447 | msgstr ""
448 |
449 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
450 | msgid "保存设置"
451 | msgstr ""
452 |
453 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
454 | msgid "Open a Directory"
455 | msgstr ""
456 |
457 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
458 | msgid "CSV(,分隔)"
459 | msgstr ""
460 |
461 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
462 | msgid "Excel(xlsx)"
463 | msgstr ""
464 |
465 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
466 | msgid "GDScript(TypedArray风格)"
467 | msgstr ""
468 |
469 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
470 | msgid "GDScript(Dictionary风格)"
471 | msgstr ""
472 |
473 | #: addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd
474 | msgid "修改后的字段数量与类型属性不对应。"
475 | msgstr ""
476 |
477 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
478 | msgid "生成表格失败:"
479 | msgstr ""
480 |
481 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
482 | msgid "表格名不能为空"
483 | msgstr ""
484 |
485 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
486 | msgid "非法脚本文件"
487 | msgstr ""
488 |
489 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
490 | msgid "非法内部类"
491 | msgstr ""
492 |
493 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
494 | msgid "原生类不存在需要的静态实例化方法"
495 | msgstr ""
496 |
497 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
498 | msgid "表格工具脚本不存在: "
499 | msgstr ""
500 |
501 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
502 | msgid "表格工具无法被实例化: "
503 | msgstr ""
504 |
505 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
506 | msgid "脚本不是继承自合法的表格工具: "
507 | msgstr ""
508 |
509 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
510 | msgid "请查阅: "
511 | msgstr ""
512 |
513 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
514 | msgid "表格工具不支持该扩展名: "
515 | msgstr ""
516 |
517 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
518 | msgid "重复的附加字段将被跳过:"
519 | msgstr ""
520 |
521 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
522 | msgid "被生成的属性中缺少需要的实例化参数: "
523 | msgstr ""
524 |
525 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
526 | msgid "不存在的优先排序字段将被跳过: "
527 | msgstr ""
528 |
529 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
530 | msgid "无法创建备份路径: "
531 | msgstr ""
532 |
533 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
534 | msgid "无法创建备份: "
535 | msgstr ""
536 |
537 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
538 | msgid "指定的表格工具无法解析已有的表格: "
539 | msgstr ""
540 |
541 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
542 | msgid "无效的生成修改器脚本: "
543 | msgstr ""
544 |
545 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
546 | msgid "生成修改器无法被实例化: "
547 | msgstr ""
548 |
549 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
550 | msgid "脚本不是继承自合法的合法的生成修改器: "
551 | msgstr ""
552 |
553 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
554 | msgid "不被导入"
555 | msgstr ""
556 |
557 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
558 | msgid "生成表格成功:"
559 | msgstr ""
560 |
561 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
562 | msgid "导入失败:"
563 | msgstr ""
564 |
565 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
566 | msgid "表格工具脚本不存在:"
567 | msgstr ""
568 |
569 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
570 | msgid "表格工具无法被实例化:"
571 | msgstr ""
572 |
573 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
574 | msgid "脚本不是继承自合法的表格工具:"
575 | msgstr ""
576 |
577 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
578 | msgid "表格工具不支持该扩展名:"
579 | msgstr ""
580 |
581 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
582 | msgid "表格不存在:"
583 | msgstr ""
584 |
585 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
586 | msgid "指定表格工具无法解析表格:"
587 | msgstr ""
588 |
589 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
590 | msgid "指定导入工具不存在:"
591 | msgstr ""
592 |
593 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
594 | msgid "指定导入工具无法实例化:"
595 | msgstr ""
596 |
597 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
598 | msgid "导入表格失败:"
599 | msgstr ""
600 |
601 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
602 | msgid "无效的导入修改器脚本: "
603 | msgstr ""
604 |
605 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
606 | msgid "导入修改器无法被实例化: "
607 | msgstr ""
608 |
609 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
610 | msgid "脚本不是继承自合法的合法的导入修改器: "
611 | msgstr ""
612 |
613 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
614 | msgid "类无法被实例化"
615 | msgstr ""
616 |
617 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
618 | msgid "脚本类不存在需要的静态实例化方法"
619 | msgstr ""
620 |
621 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
622 | msgid "原生类不存在或者不能被实例化"
623 | msgstr ""
624 |
625 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
626 | msgid "指定脚本不是继承自导入工具:"
627 | msgstr ""
628 |
629 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
630 | msgid "导入工具不支持该扩展名:"
631 | msgstr ""
632 |
633 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
634 | msgid "导入失败,无法创建导入路径:"
635 | msgstr ""
636 |
637 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
638 | msgid "导入表格成功:"
639 | msgstr ""
640 |
641 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
642 | msgid "不支持的属性类型将被跳过:"
643 | msgstr ""
644 |
645 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
646 | msgid "如果是脚本类型可以安全忽略。"
647 | msgstr ""
648 |
649 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
650 | msgid "非法属性名称: "
651 | msgstr ""
652 |
653 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
654 | msgid "非法属性类型: "
655 | msgstr ""
656 |
657 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
658 | msgid "无法读取csv文件: "
659 | msgstr ""
660 |
661 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
662 | msgid "解析csv文件失败: "
663 | msgstr ""
664 |
665 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
666 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
667 | msgid "请使用生成工具创建合法的表头。"
668 | msgstr ""
669 |
670 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
671 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
672 | msgid "非法标识符: "
673 | msgstr ""
674 |
675 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
676 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
677 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
678 | msgid "不支持的类型: "
679 | msgstr ""
680 |
681 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
682 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
683 | msgid "生成表格失败: "
684 | msgstr ""
685 |
686 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
687 | msgid "生成表格失败,无法生成:"
688 | msgstr ""
689 |
690 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
691 | msgid "转换为文本失败,不支持的类型: "
692 | msgstr ""
693 |
694 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
695 | msgid "Bug: 为空值生成默认值,请提交issue并提供复现流程或MRP。"
696 | msgstr ""
697 |
698 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
699 | msgid "非法值文本: "
700 | msgstr ""
701 |
702 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
703 | msgid "无法转换,类型与字段不符"
704 | msgstr ""
705 |
706 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
707 | msgid "无法转换,不支持的类型: "
708 | msgstr ""
709 |
710 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
711 | msgid "解析xlsx文件: "
712 | msgstr ""
713 |
714 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
715 | msgid "必须使用 sheet=your_sheet_name 选项指定工作表。"
716 | msgstr ""
717 |
718 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
719 | msgid "无法读取xlsx文件: "
720 | msgstr ""
721 |
722 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
723 | msgid "无法解析xlsx文件: "
724 | msgstr ""
725 |
726 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
727 | msgid "不存在指定的工作表: "
728 | msgstr ""
729 |
730 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
731 | msgid "非法格式"
732 | msgstr ""
733 |
734 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
735 | msgid "解析xlsx文件失败: "
736 | msgstr ""
737 |
738 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
739 | msgid "无法覆盖xlsx文件: "
740 | msgstr ""
741 |
742 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/scripts/preset.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Resource
3 |
4 | const _Log = preload("log.gd")
5 | const _TableHeader = preload("table_header.gd")
6 | const _TableTool = preload("../table_tools/table_tool.gd")
7 | const _ImportTool = preload("../import_tools/import_tool.gd")
8 | const _GenerateModifier = preload("generate_modifier.gd")
9 | const _ImportModifier = preload("import_modifier.gd")
10 | const _Localize = preload("../localization/localize.gd")
11 |
12 | const _DEFAULT_GENERATE_MODIFIER_FILE := "res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd"
13 | const _DEFAULT_IMPORT_MODIFIER_FILE := "res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd"
14 |
15 | @export_group("Editor Tool")
16 | # 触发生成
17 | @export var trigger_generate_table: bool = false:
18 | set(v):
19 | if v:
20 | generate_table()
21 | # 触发导入
22 | @export var trigger_import_table: bool = false:
23 | set(v):
24 | if v:
25 | import_table()
26 |
27 | @export_group("Basic")
28 | @export var name: String
29 | # 数据类
30 | @export var data_class: String
31 | var data_class_script: String:
32 | set(v):
33 | _data_class_script = _get_script_by_path(v)
34 | get:
35 | return _get_script_file_path(_data_class_script)
36 | @export var _data_class_script: Script # 为保兼容性,仅作为保存用的脚本引用
37 | @export var table_name: String
38 |
39 | # 表格生成选项
40 | @export var skip_prefix_underscore_properties: bool = false
41 | @export var only_storage_properties: bool = false
42 | @export var no_inheritance: bool = true
43 | @export var ascending_order: bool = false
44 | @export var auto_backup: bool = false #
45 | @export var auto_merge: bool = true #
46 | @export var priority_properties: PackedStringArray:
47 | get:
48 | return _strip_str_arr_elements_edges(priority_properties)
49 | @export var ignored_properties: PackedStringArray:
50 | get:
51 | return _strip_str_arr_elements_edges(ignored_properties)
52 |
53 | @export_group("Table")
54 | @export var table_tool_options: PackedStringArray:
55 | get:
56 | return _strip_str_arr_elements_edges(table_tool_options)
57 | # Deprecated
58 | var table_tool_script_file: String:
59 | set(v):
60 | table_tool_script = _get_script_by_path(v)
61 | get:
62 | return _get_script_file_path(table_tool_script)
63 | @export var table_tool_script: Script = preload("res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd")
64 | @export_file() var table_output_path: String = "res://tables/{table_name}.csv"
65 | # Deprecated
66 | var generate_modifier_file: String:
67 | set(v):
68 | generate_modifier = _get_script_by_path(v)
69 | get:
70 | return _get_script_file_path(generate_modifier)
71 | @export var generate_modifier: Script = preload(_DEFAULT_GENERATE_MODIFIER_FILE)
72 |
73 | @export_group("Import")
74 | # 表格导入选项
75 | @export var instantiation: String
76 | @export var import_tool_options: PackedStringArray:
77 | get:
78 | return _strip_str_arr_elements_edges(import_tool_options)
79 | var import_tool_script_file: String:
80 | set(v):
81 | import_tool_script = _get_script_by_path(v)
82 | get:
83 | return _get_script_file_path(import_tool_script)
84 | @export var import_tool_script: Script = preload("res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd")
85 | @export_file() var import_path: String = "res://tables/imported/{table_name}.gd"
86 | # Deprecated
87 | var import_modifier_file: String:
88 | set(v):
89 | import_modifier = _get_script_by_path(v)
90 | get:
91 | return _get_script_file_path(import_modifier)
92 | @export var import_modifier: Script = preload(_DEFAULT_IMPORT_MODIFIER_FILE)
93 |
94 | @export_group("Addtional")
95 | # 附加
96 | @export var additional_properties: Array[Dictionary]
97 | @export var descriptions: Dictionary
98 | @export var meta_list: PackedStringArray
99 | # 需要meta的字段,生成时将在对应的字段下一列插入一列#开头的字段,仅用于编辑时,不会被导入
100 | @export var need_meta_properties: PackedStringArray:
101 | get:
102 | return _strip_str_arr_elements_edges(need_meta_properties)
103 |
104 | #region 旧版兼容
105 | func _set(property: StringName, value: Variant) -> bool:
106 | match property:
107 | &"only_strage_properties":
108 | only_storage_properties = value
109 | &"table_ouput_path":
110 | table_output_path = value
111 | &"metas":
112 | meta_list = value
113 | _:
114 | return false
115 | return true
116 |
117 |
118 | func _get(property: StringName) -> Variant:
119 | match property:
120 | &"only_strage_properties":
121 | return only_storage_properties
122 | &"table_ouput_path":
123 | return table_output_path
124 | &"metas":
125 | return meta_list
126 | return null
127 | #endregion 旧版兼容
128 |
129 |
130 | ## enable_modifier: 是否启用修改器
131 | ## func_modify_data: Callable 修改要生成的数据行,参数为 Array[Dictionary], 返回修改后的数据 Array[Dictionary]
132 | func generate_table(enable_modifier: bool = true, func_modify_data: Callable = Callable()) -> Error:
133 | if table_name.is_empty():
134 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格名不能为空")])
135 | return ERR_INVALID_PARAMETER
136 |
137 | instantiation = instantiation.strip_edges()
138 | var instantiation_method := "new" if instantiation.is_empty() else instantiation.split("(")[0]
139 | var instantiation_args := Array([] if instantiation.is_empty() or instantiation.ends_with("()") else Array(instantiation.trim_suffix(")").split("(")).back().split(",", false)).map(
140 | func(a: String) -> String: return a.strip_edges().trim_prefix("{").trim_suffix("}")
141 | ) as PackedStringArray
142 |
143 | var script: Script = _data_class_script
144 | var property_list: Array[Dictionary]
145 |
146 | if is_instance_valid(script):
147 | if not data_class.is_empty():
148 | # 脚本内部类
149 | script = script[data_class]
150 | if not is_instance_valid(script):
151 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("非法内部类"), " - ", data_class])
152 | return ERR_INVALID_PARAMETER
153 |
154 | _append_base_property_list_recursively_script(no_inheritance, script, property_list)
155 | else:
156 | if instantiation_method != "new":
157 | if not ClassDB.class_has_method(data_class, instantiation_method):
158 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("原生类不存在需要的静态实例化方法"), " - ", data_class, " - ", instantiation_method])
159 | return ERR_INVALID_PARAMETER
160 | _append_base_property_list_recursively(no_inheritance, data_class, property_list)
161 |
162 | # 排除忽略的属性
163 | property_list = property_list.filter(func(p: Dictionary) -> bool: return not p["name"] in ignored_properties)
164 |
165 | # 附加属性检查
166 | for ap in additional_properties:
167 | if not _is_ap_valid(ap):
168 | return ERR_INVALID_PARAMETER
169 |
170 | if skip_prefix_underscore_properties:
171 | property_list = property_list.filter(func(p: Dictionary) -> bool: return not p["name"].begins_with("_"))
172 |
173 | # 仅 PROPERTY_USAGE_STORAGE
174 | if only_storage_properties:
175 | property_list = property_list.filter(func(p: Dictionary) -> bool: return p["usage"] & PROPERTY_USAGE_STORAGE)
176 |
177 | # 表格工具实例化
178 | if not is_instance_valid(table_tool_script):
179 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具脚本不存在: "), table_tool_script.resource_path])
180 | return ERR_INVALID_PARAMETER
181 |
182 | if table_tool_script.reload() != OK:
183 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具脚本不存在: "), table_tool_script.resource_path])
184 | return ERR_INVALID_PARAMETER
185 |
186 | if not table_tool_script.can_instantiate():
187 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具无法被实例化: "), table_tool_script.resource_path])
188 | return ERR_INVALID_PARAMETER
189 |
190 | var table_tool := table_tool_script.new() as _TableTool
191 | if not is_instance_valid(table_tool):
192 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("脚本不是继承自合法的表格工具: "), table_tool_script.resource_path])
193 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd"])
194 | return ERR_INVALID_PARAMETER
195 |
196 | var table_file := table_output_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower())
197 |
198 | if table_tool.get_table_file_extension().to_lower() != table_file.get_extension().to_lower():
199 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("表格工具不支持该扩展名: "), table_output_path])
200 | return ERR_INVALID_PARAMETER
201 |
202 | # 过滤不支持的类型
203 | property_list = property_list.filter(_exclude_unsupported_type_filter.bind(table_tool.get_support_types()))
204 |
205 | # 添加附加属性
206 | for ap in additional_properties:
207 | var ap_name: String = ap.name.strip_edges()
208 |
209 | if property_list.filter(func(p: Dictionary) -> bool: return p["name"] == ap_name).size():
210 | _Log.warning([name, " - ", _Localize.translate("重复的附加字段将被跳过:"), ap_name])
211 | continue
212 |
213 | property_list.append({"name": ap_name, "type": ap.type})
214 |
215 | # 检查实例化需要的参数
216 | for arg in instantiation_args:
217 | if property_list.filter(func(p: Dictionary) -> bool: return p["name"] == arg).is_empty():
218 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("被生成的属性中缺少需要的实例化参数: "), arg])
219 | return ERR_INVALID_PARAMETER
220 |
221 | # 升序
222 | if ascending_order:
223 | property_list.sort_custom(_sort_ascending)
224 |
225 | # 优先排序
226 | for i in range(priority_properties.size() - 1, -1, -1):
227 | var prop_name := priority_properties[i].strip_edges()
228 | var filtered := property_list.filter(func(p: Dictionary) -> bool: return p["name"] == prop_name)
229 | if filtered.size() <= 0:
230 | _Log.warning([name, " - ", _Localize.translate("不存在的优先排序字段将被跳过: "), prop_name])
231 | continue
232 | # 将其调到最前端
233 | var first := filtered.front() as Dictionary
234 | property_list.erase(first)
235 | property_list.push_front(first)
236 |
237 | # 将meta字段插入
238 | var need_meta_props := Array(need_meta_properties).map(func(e: String) -> String: return e.strip_edges()) as PackedStringArray
239 | if need_meta_props.size():
240 | var idx := 0
241 | while idx < property_list.size():
242 | var p := property_list[idx] as Dictionary
243 | if need_meta_props.has(p["name"]):
244 | idx += 1
245 | property_list.insert(idx, {"name": "#" + p["name"], "type": TYPE_STRING})
246 | idx += 1
247 |
248 | var backup_file := _converted_to_backup_file_path(table_file)
249 |
250 | var data: Array[Dictionary] = []
251 | if FileAccess.file_exists(table_file):
252 | if not DirAccess.dir_exists_absolute(backup_file.get_base_dir()):
253 | var make_dir_err := DirAccess.make_dir_recursive_absolute(backup_file.get_base_dir())
254 | if make_dir_err != OK:
255 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("无法创建备份路径: "), backup_file.get_base_dir()])
256 | return make_dir_err
257 |
258 | var copy_err := DirAccess.copy_absolute(table_file, backup_file)
259 | if copy_err != OK:
260 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("无法创建备份: "), backup_file])
261 | return copy_err
262 |
263 | # 自动合并
264 | if auto_merge:
265 | var parse_err := table_tool.parse_table_file(table_file, table_tool_options)
266 | if parse_err != OK:
267 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("指定的表格工具无法解析已有的表格: "), table_file])
268 | return parse_err
269 | data = table_tool.get_data().duplicate(true)
270 |
271 | # 修改器
272 | var modified_fields := property_list.map(func(p: Dictionary) -> String: return p["name"]) as PackedStringArray
273 | var modified_types := property_list.map(func(p: Dictionary) -> int: return p["type"]) as PackedByteArray
274 | var modified_data := data
275 | var modified_options := table_tool_options.duplicate()
276 | var modified_meta_list := meta_list.duplicate()
277 | var modified_desc := descriptions.duplicate()
278 | if enable_modifier:
279 | if not is_instance_valid(generate_modifier):
280 | generate_modifier = ResourceLoader.load(_DEFAULT_GENERATE_MODIFIER_FILE, "Script", ResourceLoader.CACHE_MODE_IGNORE)
281 |
282 | if generate_modifier.reload(false) != OK:
283 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("无效的生成修改器脚本: "), generate_modifier.resource_path])
284 | return ERR_INVALID_PARAMETER
285 |
286 | if not generate_modifier.can_instantiate():
287 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("生成修改器无法被实例化: "), generate_modifier.resource_path])
288 | return ERR_INVALID_PARAMETER
289 |
290 | var modifier := generate_modifier.new() as _GenerateModifier
291 | if not is_instance_valid(modifier):
292 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("脚本不是继承自合法的合法的生成修改器: "), generate_modifier.resource_path])
293 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd"])
294 | return ERR_INVALID_PARAMETER
295 |
296 | # 修改
297 | modifier.begin_modify(table_name, data_class.strip_edges(), data_class_script.strip_edges())
298 | modifier.modify_fields_definitions(modified_fields, modified_types)
299 | modified_data = modifier.modify_data(modified_data)
300 | modified_desc = modifier.modify_descriptions(modified_desc)
301 | modified_meta_list = modifier.modify_meta_list(modified_meta_list)
302 | modified_options = modifier.modify_table_tool_options(modified_options)
303 |
304 | # 通过数据修改方法修改数据
305 | if func_modify_data.is_valid():
306 | modified_data = func_modify_data.call(modified_data)
307 |
308 | var data_rows: Array[PackedStringArray]
309 | if not modified_data.is_empty():
310 | data_rows = table_tool.to_data_rows(modified_data, modified_fields, modified_types)
311 |
312 | # 准备表头
313 | var header := _TableHeader.new()
314 | header.meta_list = modified_meta_list
315 | header.fields = modified_fields
316 | header.types = Array(modified_types).map(func(t: int) -> String: return type_string(t)) as PackedStringArray
317 | header.descriptions.resize(header.fields.size())
318 | for i in range(header.fields.size()):
319 | var f := header.fields[i]
320 | if f.begins_with("#"):
321 | header.descriptions[i] = _Localize.translate("不被导入")
322 | elif modified_desc.has(f):
323 | header.descriptions[i] = modified_desc[f]
324 |
325 | var err := table_tool.generate_table_file(table_file, header, data_rows, table_tool_options)
326 | if not auto_backup:
327 | # 不需要备份,删除
328 | DirAccess.remove_absolute(backup_file)
329 |
330 | if err != OK:
331 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), error_string(err)])
332 | return err
333 |
334 | _Log.info([name, " - ", _Localize.translate("生成表格成功:"), table_file])
335 | _update_file_change(table_file)
336 | return OK
337 |
338 |
339 | ## enable_modifier: 是否启用修改器
340 | func import_table(enable_modifier: bool = true) -> Error:
341 | # 表格工具
342 | if not is_instance_valid(table_tool_script):
343 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具脚本不存在:"), table_tool_script.resource_path])
344 | return ERR_INVALID_PARAMETER
345 |
346 | if table_tool_script.reload(false) != OK:
347 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具脚本不存在:"), table_tool_script.resource_path])
348 | return ERR_INVALID_PARAMETER
349 |
350 | if not table_tool_script.can_instantiate():
351 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具无法被实例化:"), table_tool_script.resource_path])
352 | return ERR_INVALID_PARAMETER
353 |
354 | var table_tool := table_tool_script.new() as _TableTool
355 | if not is_instance_valid(table_tool):
356 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("脚本不是继承自合法的表格工具:"), table_tool_script.resource_path])
357 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd"])
358 | return ERR_INVALID_PARAMETER
359 |
360 | var table_file := table_output_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower())
361 |
362 | if table_tool.get_table_file_extension().to_lower() != table_file.get_extension().to_lower():
363 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格工具不支持该扩展名:"), table_output_path])
364 | return ERR_INVALID_PARAMETER
365 | if not FileAccess.file_exists(table_file):
366 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("表格不存在:"), table_file])
367 | return ERR_FILE_NOT_FOUND
368 |
369 | var err := table_tool.parse_table_file(table_output_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower()), table_tool_options)
370 | if err != OK:
371 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定表格工具无法解析表格:"), error_string(err)])
372 | return err
373 |
374 | # 导入工具
375 | if not is_instance_valid(import_tool_script):
376 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定导入工具不存在:"), import_tool_script.resource_path])
377 | return ERR_INVALID_PARAMETER
378 |
379 | if not import_tool_script.can_instantiate():
380 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定导入工具无法实例化:"), import_tool_script.resource_path])
381 | return ERR_INVALID_PARAMETER
382 |
383 | var header := table_tool.get_header()
384 | if not is_instance_valid(header):
385 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定表格工具无法解析表格:"), error_string(ERR_PARSE_ERROR)])
386 | return ERR_PARSE_ERROR
387 |
388 | var custom_setters := {}
389 | for ap in additional_properties:
390 | if not ap.setter.is_empty():
391 | custom_setters[ap.name] = ap.setter
392 |
393 | # 修改器
394 | var modified_table_name := table_name
395 | var modified_custom_setter := custom_setters
396 | var modified_data := table_tool.get_data().duplicate(true)
397 | var modified_options := import_tool_options.duplicate()
398 | if enable_modifier:
399 | if not is_instance_valid(import_modifier):
400 | import_modifier = ResourceLoader.load(_DEFAULT_IMPORT_MODIFIER_FILE, "Script", ResourceLoader.CACHE_MODE_IGNORE)
401 |
402 | if import_modifier.reload(false) != OK:
403 | _Log.error([name, " - ", _Localize.translate("导入表格失败:"), _Localize.translate("无效的导入修改器脚本: "), import_modifier.resource_path])
404 | return ERR_INVALID_PARAMETER
405 |
406 | if not import_modifier.can_instantiate():
407 | _Log.error([name, " - ", _Localize.translate("导入表格失败:"), _Localize.translate("导入修改器无法被实例化: "), import_modifier.resource_path])
408 | return ERR_INVALID_PARAMETER
409 |
410 | var modifier := import_modifier.new() as _ImportModifier
411 | if not is_instance_valid(modifier):
412 | _Log.error([name, " - ", _Localize.translate("导入表格失败:"), _Localize.translate("脚本不是继承自合法的合法的导入修改器: "), import_modifier.resource_path])
413 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd"])
414 | return ERR_INVALID_PARAMETER
415 |
416 | # 修改
417 | modifier.begin_modify(table_name, data_class.strip_edges(), data_class_script.strip_edges())
418 | modified_table_name = modifier.modify_table_name(modified_table_name)
419 | modified_custom_setter = modifier.modify_custom_setters(modified_custom_setter)
420 | modified_data = modifier.modify_data(modified_data)
421 | header.meta_list = modifier.modify_meta_list(header.meta_list)
422 | modified_options = modifier.modify_import_tool_options(modified_options)
423 |
424 | var inst := instantiation.strip_edges()
425 | if inst.is_empty():
426 | inst = "new()"
427 |
428 | var script: Script = _data_class_script
429 | if is_instance_valid(script):
430 | if not data_class.is_empty():
431 | # 脚本内部类
432 | script = script[data_class]
433 | if not is_instance_valid(script):
434 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("非法内部类"), " - ", data_class])
435 | return ERR_INVALID_PARAMETER
436 |
437 | if not Engine.is_editor_hint() and not script.can_instantiate():
438 | # 只在非编辑器下进行检查
439 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("类无法被实例化"), " - ", data_class_script, " - ", data_class])
440 | return ERR_INVALID_PARAMETER
441 |
442 | if not inst.begins_with("new"):
443 | if script.get_script_method_list().filter(func(m: Dictionary) -> bool: return m["name"] == inst.split("(")[0] and m["flags"] & METHOD_FLAG_STATIC).is_empty():
444 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("脚本类不存在需要的静态实例化方法"), " - ", data_class_script, " - ", inst])
445 | return ERR_INVALID_PARAMETER
446 | else:
447 | if not ClassDB.class_exists(data_class) or not ClassDB.can_instantiate(data_class):
448 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("原生类不存在或者不能被实例化"), " - ", data_class])
449 | return ERR_INVALID_PARAMETER
450 |
451 | if not inst.begins_with("new"):
452 | if not ClassDB.class_has_method(data_class, inst.split("(")[0]):
453 | _Log.error([name, " - ", _Localize.translate("生成表格失败:"), _Localize.translate("原生类不存在需要的静态实例化方法"), " - ", data_class, " - ", inst])
454 | return ERR_INVALID_PARAMETER
455 |
456 | var import_tool := import_tool_script.new() as _ImportTool
457 | if not is_instance_valid(import_tool):
458 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("指定脚本不是继承自导入工具:"), import_tool_script.resource_path])
459 | _Log.error(["\t- ", _Localize.translate("请查阅: "), "res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd"])
460 | return ERR_INVALID_PARAMETER
461 |
462 | var import_file_path := import_path.replace("{table_name}", table_name.capitalize().replace(" ", "_").to_lower())
463 | if import_tool.get_import_file_extension().to_lower() != import_file_path.get_extension().to_lower():
464 | _Log.error([name, " - ", _Localize.translate("导入失败:"), _Localize.translate("导入工具不支持该扩展名:"), import_file_path])
465 | return ERR_INVALID_PARAMETER
466 |
467 | if not DirAccess.dir_exists_absolute(import_file_path.get_base_dir()):
468 | err = DirAccess.make_dir_recursive_absolute(import_file_path.get_base_dir())
469 | if err != OK:
470 | _Log.error([name, " - ", _Localize.translate("导入失败,无法创建导入路径:"), import_file_path.get_base_dir(), " - ", error_string(err)])
471 | return err
472 |
473 | err = import_tool.import(import_file_path, modified_table_name, header, data_class, data_class_script, inst, modified_custom_setter, modified_data, modified_options)
474 | if err != OK:
475 | _Log.error([name, " - ", _Localize.translate("导入失败:"), error_string(err)])
476 | return err
477 |
478 | _Log.info([name, " - ", _Localize.translate("导入表格成功:"), import_file_path])
479 | _update_file_change(import_file_path)
480 | return OK
481 |
482 |
483 | #---------------------
484 | func _get_script_by_path(path: String) -> Script:
485 | if FileAccess.file_exists(path) and ResourceLoader.exists(path, &"Script"):
486 | return load(path)
487 | return null
488 |
489 |
490 | func _get_script_file_path(script: Script) -> String:
491 | if is_instance_valid(script):
492 | return script.resource_path
493 | return ""
494 |
495 |
496 | func _strip_str_arr_elements_edges(str_arr: PackedStringArray) -> PackedStringArray:
497 | for i in range(str_arr.size()):
498 | str_arr[i] = str_arr[i].strip_edges()
499 | return str_arr
500 |
501 |
502 | func _update_file_change(file: String) -> void:
503 | if not Engine.is_editor_hint():
504 | return
505 | EditorInterface.get_resource_filesystem().update_file(file)
506 | var file_type := EditorInterface.get_resource_filesystem().get_file_type(file)
507 | if ClassDB.is_parent_class(file_type, &"Script"):
508 | if Engine.get_main_loop().has_meta(&"__HACK_TIMER__"):
509 | return
510 | var timer := (Engine.get_main_loop() as SceneTree).create_timer(1.0)
511 | timer.timeout.connect(func() -> void:
512 | # Hack
513 | EditorInterface.get_script_editor().notification(Node.NOTIFICATION_APPLICATION_FOCUS_IN)
514 | Engine.get_main_loop().remove_meta(&"__HACK_TIMER__")
515 | )
516 | Engine.get_main_loop().set_meta(&"__HACK_TIMER__", timer)
517 | elif file_type == &"Resource" or ClassDB.is_parent_class(file_type, &"Resource"):
518 | EditorInterface.get_resource_filesystem().reimport_files([file])
519 |
520 |
521 | func _converted_to_backup_file_path(origin_path: String) -> String:
522 | var base_dir := origin_path.get_base_dir().path_join(".backup")
523 | var prefix := ".%s_" % (Time.get_datetime_string_from_system().replace(":", "-") + str(Time.get_unix_time_from_system()))
524 | return base_dir.path_join(prefix + origin_path.get_file())
525 |
526 |
527 | func _get_custom_setter(prop_name: String) -> String:
528 | var filtered := additional_properties.filter(func(ap: Dictionary) -> bool: return ap.get("name", "").strip_edges() == prop_name)
529 | if filtered.size():
530 | return filtered.front().get("setter", "")
531 | return ""
532 |
533 |
534 | func _exclude_unsupported_type_filter(p: Dictionary, support_types: PackedByteArray) -> bool:
535 | var t := p["type"] as int
536 | if not t in support_types:
537 | _Log.warning([name, " - ", _Localize.translate("不支持的属性类型将被跳过:"), p["name"], " - ", type_string(t), " ", _Localize.translate("如果是脚本类型可以安全忽略。")])
538 | return false
539 | return true
540 |
541 |
542 | func _sort_ascending(a: Dictionary, b: Dictionary) -> bool:
543 | var an := a["name"] as String
544 | var bn := b["name"] as String
545 | return an > bn
546 |
547 |
548 | func _append_base_property_list_recursively_script(p_no_inheritance: bool, script: Script, r_property_list: Array[Dictionary]) -> void:
549 | if not is_instance_valid(script):
550 | return
551 | r_property_list.append_array(script.get_script_property_list())
552 |
553 | if p_no_inheritance:
554 | return
555 |
556 | var base := script.get_base_script()
557 | if is_instance_valid(base):
558 | _append_base_property_list_recursively_script(p_no_inheritance, script, r_property_list)
559 | else:
560 | _append_base_property_list_recursively(p_no_inheritance, script.get_instance_base_type(), r_property_list)
561 |
562 |
563 | func _append_base_property_list_recursively(p_no_inheritance: bool, klass: StringName, r_property_list: Array[Dictionary]) -> void:
564 | if klass.is_empty():
565 | return
566 |
567 | if klass in [&"Object", &"RefCounted", &"Resource", &"Node"]:
568 | # 跳过基础类
569 | return
570 |
571 | r_property_list.append_array(ClassDB.class_get_property_list(klass, true))
572 |
573 | if p_no_inheritance:
574 | return
575 |
576 | _append_base_property_list_recursively(p_no_inheritance, ClassDB.get_parent_class(klass), r_property_list)
577 |
578 |
579 | func _is_ap_valid(ap: Dictionary) -> bool:
580 | ap["name"] = ap.get("name", "").strip_edges()
581 | if not TextServerManager.get_primary_interface().is_valid_identifier(ap["name"]):
582 | _Log.error([_Localize.translate("非法属性名称: "), ap["name"]])
583 | return false
584 |
585 | var type := ap["type"] as int
586 | if type in [TYPE_NIL, TYPE_OBJECT, TYPE_CALLABLE, TYPE_SIGNAL, TYPE_RID]:
587 | _Log.error([_Localize.translate("非法属性类型: "), name, " - ", type_string(type)])
588 | return false
589 |
590 | return true
591 |
--------------------------------------------------------------------------------
/addons/config_table_manager.daylily-zeleen/localization/en.po:
--------------------------------------------------------------------------------
1 | # LANGUAGE translation for Config Table Manager for the following files:
2 | # res://addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
3 | # res://addons/config_table_manager.daylily-zeleen/import_tools/import_tool.gd
4 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.gd
5 | # res://addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
6 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
7 | # res://addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
8 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
9 | # res://addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
10 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.gd
11 | # res://addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn
12 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.gd
13 | # res://addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
14 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.gd
15 | # res://addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
16 | # res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd
17 | # res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd
18 | # res://addons/config_table_manager.daylily-zeleen/scripts/log.gd
19 | # res://addons/config_table_manager.daylily-zeleen/scripts/preset.gd
20 | # res://addons/config_table_manager.daylily-zeleen/scripts/table_header.gd
21 | # res://addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
22 | # res://addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
23 | # res://addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
24 | # res://addons/config_table_manager.daylily-zeleen/config_table_manager.daylily-zeleen.gd
25 | #
26 | # FIRST AUTHOR , YEAR.
27 | #
28 | #, fuzzy
29 | msgid ""
30 | msgstr ""
31 | "Project-Id-Version: Config Table Manager\n"
32 | "MIME-Version: 1.0\n"
33 | "Content-Type: text/plain; charset=UTF-8\n"
34 | "Content-Transfer-Encoding: 8-bit\n"
35 |
36 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
37 | msgid "导表失败: "
38 | msgstr "Import table failed: "
39 |
40 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
41 | msgid "无法被赋值的字段将被跳过: "
42 | msgstr "An unassignable filed will be skiped: "
43 |
44 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_default.gd
45 | msgid "转换失败,不支持的类型: "
46 | msgstr "Unsupported type is converted failed: "
47 |
48 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
49 | msgid "未指定作为key的数据类属性,请使用 key=prop_name 作为选项参数进行指定。"
50 | msgstr "Has not specify the key of data class. Please use \"key=prop_name\" to specify as import option."
51 |
52 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
53 | msgid "数据类不存在指定为key的属性 %s,请使用 key=prop_name 作为选项参数进行指定。"
54 | msgstr "The data class has not this property (%s) to be the key. Please use \"key=prop_name\" to specify as import option."
55 |
56 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
57 | msgid "存在未配置key (%s)的数据: %s。"
58 | msgstr "Has a data without config key (%s): %s"
59 |
60 | #: addons/config_table_manager.daylily-zeleen/import_tools/gdscript_dictionary.gd
61 | msgid "存在key (%s)重复的数据,重复的值为: %s。"
62 | msgstr "Duplicate key (%s), the value is: %s"
63 |
64 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
65 | msgid "要附加的属性名称"
66 | msgstr "The name of additional property"
67 |
68 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
69 | msgid "非空将使用自定义Setter进行设置"
70 | msgstr "Custom setter for assigning value. Keep empty will use \"=\" directly."
71 |
72 | #: addons/config_table_manager.daylily-zeleen/scenes/additional_property_edit.tscn
73 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
74 | msgid " - "
75 | msgstr " - "
76 |
77 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
78 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
79 | msgid "选中"
80 | msgstr "Select"
81 |
82 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
83 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
84 | msgid "排除"
85 | msgstr "Exclude"
86 |
87 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
88 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
89 | msgid "预设"
90 | msgstr "Preset"
91 |
92 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.gd
93 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
94 | msgid "表格预设不存在: "
95 | msgstr "A non-existent table preset: "
96 |
97 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
98 | msgid "表格生成"
99 | msgstr "Table Generate"
100 |
101 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
102 | msgid "对选中项进行生成"
103 | msgstr "Generate Selected"
104 |
105 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
106 | msgid "对所有项进行生成"
107 | msgstr "Generate All"
108 |
109 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
110 | msgid "对选中项执行导入"
111 | msgstr "Import Selected"
112 |
113 | #: addons/config_table_manager.daylily-zeleen/scenes/gen_and_import.tscn
114 | msgid "对所有项执行导入"
115 | msgstr "Import All"
116 |
117 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
118 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
119 | msgid "无效的预设保存路径\"{save_dir}\",必须是资源目录下的合法路径。"
120 | msgstr "Invalid presets save directory \"{save_dir}\", it require a valid directory of resource file system."
121 |
122 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
123 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
124 | msgid "预设名称不能为空。"
125 | msgstr "Prset name must not empty."
126 |
127 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
128 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
129 | msgid "预设名称\"{preset_name}\"无法作为文件名"
130 | msgstr "Preset name \"{preset_name}\" can't be a file name."
131 |
132 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
133 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
134 | msgid "创建预设路径\"{presets_dir}\"失败: "
135 | msgstr "Create presets directory \"{presets_dir}\" failed: "
136 |
137 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
138 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
139 | msgid "保存预设失败\"{file_name}\"失败: "
140 | msgstr "Save preset \"{file_name}\" failed: "
141 |
142 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
143 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
144 | msgid "保存预设\"{preset_name}\"成功: "
145 | msgstr "Save preset \"{preset_name}\" success: "
146 |
147 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
148 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
149 | msgid "删除失败,预设”{file_path}“不存在"
150 | msgstr "Delete failed, preset "{file_path}" is not exists."
151 |
152 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
153 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
154 | msgid "删除预设失败: "
155 | msgstr "Delete preset failed: "
156 |
157 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
158 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
159 | msgid "删除预设成功: "
160 | msgstr "Delete preset success: "
161 |
162 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
163 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
164 | msgid "重复的字段描述将被跳过: "
165 | msgstr "A duplicate filed description will be skiped: "
166 |
167 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
168 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
169 | msgid "Bug,请提交issue并提供复现步骤"
170 | msgstr "But, please submit an issue and provide reproduce stps."
171 |
172 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
173 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
174 | msgid "删除预设失败,未选中预设"
175 | msgstr "Delete preset failed, has not seleting preset."
176 |
177 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
178 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
179 | msgid "选择脚本"
180 | msgstr "Select Script"
181 |
182 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
183 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
184 | msgid "输出文件"
185 | msgstr "Output File"
186 |
187 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.gd
188 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
189 | msgid "- None -"
190 | msgstr "- None -"
191 |
192 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
193 | msgid "预设:"
194 | msgstr "Preset:"
195 |
196 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
197 | msgid "预设名称: "
198 | msgstr "Preset Name: "
199 |
200 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
201 | msgid "要保存的预设名称"
202 | msgstr "The name of preset."
203 |
204 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
205 | msgid "保存"
206 | msgstr "Save"
207 |
208 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
209 | msgid "删除"
210 | msgstr "Delete"
211 |
212 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
213 | msgid "数据类名:"
214 | msgstr "Data Class Name: "
215 |
216 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
217 | msgid "原生类名或脚本的内部类名,留空时脚本不能为空"
218 | msgstr "Native class name or internal class of a script. If has not selete a script, this options must be specified."
219 |
220 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
221 | msgid "数据类脚本:"
222 | msgstr "Data Class Script: "
223 |
224 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
225 | msgid "可选项"
226 | msgstr "Options"
227 |
228 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
229 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
230 | msgid "选择"
231 | msgstr "Select"
232 |
233 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
234 | msgid "表格名:"
235 | msgstr "Table Name: "
236 |
237 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
238 | msgid "建议使用大驼峰命名"
239 | msgstr "Recommend to use PascalCase."
240 |
241 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
242 | msgid "优先字段:"
243 | msgstr "Priority Fields:"
244 |
245 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
246 | msgid "用\",\"分隔,未列入的字段将排在其后。例:id, name, description"
247 | msgstr "Seperate by \",\". e.g. id, name, description"
248 |
249 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
250 | msgid "忽略字段:"
251 | msgstr "Ignore Fileds: "
252 |
253 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
254 | msgid "用\",\"分隔,列入的字段将不被生成(但优先度低于附加字段)。例:internal_id, changeable_data"
255 | msgstr "Seperate by \",\". e.g. internal_id, changeable_data"
256 |
257 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
258 | msgid "需要元字段的字段: "
259 | msgstr "Need Meta Fields:"
260 |
261 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
262 | msgid "用\",\"分隔,生成的元字段列不会被导入,仅用于表格编辑。"
263 | msgstr "Seperate by \",\". Generate meta fields will not be imported, only use for editing tables."
264 |
265 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
266 | msgid "跳过\"_\"开头的属性"
267 | msgstr "Skip properties starting with \"_\""
268 |
269 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
270 | msgid "即含有PROPERTY_USAGE_STORAGE的属性"
271 | msgstr "Which have "PROPERTY_USAGE_STORAGE" flag."
272 |
273 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
274 | msgid "仅需要存储的属性"
275 | msgstr "Only Storage Properties"
276 |
277 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
278 | msgid "不含父类属性"
279 | msgstr "Exclude Parent Properties"
280 |
281 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
282 | msgid "按字母升序"
283 | msgstr "In Ascending Alphabetical Order"
284 |
285 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
286 | msgid "如果表格生成路径已存在文件,则将生成备份(于同目录下的.backup文件夹中),备份文件名称将加上备份时间作为前缀。"
287 | msgstr ""
288 |
289 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
290 | msgid "覆盖前备份"
291 | msgstr "Backup Before Overwrite"
292 |
293 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
294 | msgid "如果生成路径已存在目标文件,将对数据行的兼容字段进行合并"
295 | msgstr ""
296 |
297 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
298 | msgid "自动合并"
299 | msgstr "Auto Merge Data"
300 |
301 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
302 | msgid "表格生成路径:"
303 | msgstr "Table Generate Path:"
304 |
305 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
306 | msgid "表格选项参数:"
307 | msgstr "Table Tool Options: "
308 |
309 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
310 | msgid "特定于表格工具的选项参数,用\",\"分隔。"
311 | msgstr "Option parameters specific to table tool, separated by \",\"."
312 |
313 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
314 | msgid "表格工具:"
315 | msgstr "Table Tool: "
316 |
317 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
318 | msgid "表格生成修改器:"
319 | msgstr "Table Generate Modifier: "
320 |
321 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
322 | msgid "使用修改器在表格生成流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd"
323 | msgstr "Modify data in table generate workflow. Should extends from \"res://addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd\""
324 |
325 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
326 | msgid "数据对象实例化:"
327 | msgstr "Instantiation of Data Object: "
328 |
329 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
330 | msgid "例:create({id}, {name}), 作为参数的属性将在赋值阶段时被跳过。默认无参构造"
331 | msgstr "e.g. create({id}, {name}). Default is non-parameters constuctor."
332 |
333 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
334 | msgid "导入路径:"
335 | msgstr "Import Path: "
336 |
337 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
338 | msgid "导入选项参数:"
339 | msgstr "Import Tool Options: "
340 |
341 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
342 | msgid ""
343 | "特定于导入工具的选项参数,用\",\"分隔。\n"
344 | "例如默认GDScript导入可提供generate_class_name参数来为生成的脚本添加全局类名(如果表格名是合法的标识符)。"
345 | msgstr "Option parameters specific to import tool, separated by \",\"."
346 |
347 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
348 | msgid "特定于导入工具的选项参数,用\",\"分隔。如默认GDScript导入可使用generate_class_name"
349 | msgstr "Option parameters specific to import tool, separated by \",\". e.g. \"generate_class_name\" for Default GDScript Import."
350 |
351 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
352 | msgid "导入工具:"
353 | msgstr "Import Tool: "
354 |
355 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
356 | msgid "导入修改器:"
357 | msgstr "Import Modifier: "
358 |
359 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
360 | msgid "使用修改器在表格导入流程中修改数据,修改器应扩展自 res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd"
361 | msgstr "Modify data in table import workflow. Should extends from \"res://addons/config_table_manager.daylily-zeleen/scripts/import_modifier.gd\""
362 |
363 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
364 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
365 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
366 | msgid "+"
367 | msgstr "+"
368 |
369 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
370 | msgid "确认操作?"
371 | msgstr "Confirm?"
372 |
373 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
374 | msgid "预设管理"
375 | msgstr "Presets"
376 |
377 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
378 | msgid "生成与导入"
379 | msgstr "Generat & Import"
380 |
381 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
382 | msgid "设置"
383 | msgstr "Settings"
384 |
385 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
386 | msgid "表格选项"
387 | msgstr "Table Options"
388 |
389 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
390 | msgid "导入选项"
391 | msgstr "Import Options"
392 |
393 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
394 | msgid "附加字段"
395 | msgstr "Additnial Fields"
396 |
397 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
398 | msgid "字段描述"
399 | msgstr "Fields Descriptions"
400 |
401 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
402 | msgid "自定义元数据"
403 | msgstr "Custom Meta Data"
404 |
405 | #: addons/config_table_manager.daylily-zeleen/scenes/main_screen.tscn
406 | msgid "表格导入"
407 | msgstr "Table Import"
408 |
409 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn
410 | msgid "Meta"
411 | msgstr "Meta"
412 |
413 | #: addons/config_table_manager.daylily-zeleen/scenes/meta_edit.tscn
414 | msgid "X"
415 | msgstr "X"
416 |
417 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
418 | msgid "字段名称"
419 | msgstr "Filed Name"
420 |
421 | #: addons/config_table_manager.daylily-zeleen/scenes/property_description_edit.tscn
422 | msgid "描述"
423 | msgstr "Description"
424 |
425 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
426 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
427 | msgid "表格工具"
428 | msgstr "Table Tool"
429 |
430 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
431 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
432 | msgid "脚本路径"
433 | msgstr "Script Path"
434 |
435 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
436 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
437 | msgid "导入工具"
438 | msgstr "Import Tool"
439 |
440 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.gd
441 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
442 | msgid "保存设置失败: "
443 | msgstr "Save Setting Failed: "
444 |
445 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
446 | msgid "预设保存路径:"
447 | msgstr "Presets Save Path: "
448 |
449 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
450 | msgid "保存设置"
451 | msgstr "Save Settings"
452 |
453 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
454 | msgid "Open a Directory"
455 | msgstr ""
456 |
457 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
458 | msgid "CSV(,分隔)"
459 | msgstr "CSV(\",\" delimiter)"
460 |
461 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
462 | msgid "Excel(xlsx)"
463 | msgstr ""
464 |
465 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
466 | msgid "GDScript(TypedArray风格)"
467 | msgstr "GDScript(TypedArray Style)"
468 |
469 | #: addons/config_table_manager.daylily-zeleen/scenes/settings.tscn
470 | msgid "GDScript(Dictionary风格)"
471 | msgstr "GDScript(Dictionary风格 Style)"
472 |
473 | #: addons/config_table_manager.daylily-zeleen/scripts/generate_modifier.gd
474 | msgid "修改后的字段数量与类型属性不对应。"
475 | msgstr "The number of modified fields does not match the types."
476 |
477 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
478 | msgid "生成表格失败:"
479 | msgstr "Generate table failed: "
480 |
481 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
482 | msgid "表格名不能为空"
483 | msgstr "Table name can not be empty."
484 |
485 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
486 | msgid "非法脚本文件"
487 | msgstr "Invalid script file"
488 |
489 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
490 | msgid "非法内部类"
491 | msgstr "Invalid internal class"
492 |
493 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
494 | msgid "原生类不存在需要的静态实例化方法"
495 | msgstr "The native class has not the static instantiation method"
496 |
497 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
498 | msgid "表格工具脚本不存在: "
499 | msgstr "Table Tool is not exists: "
500 |
501 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
502 | msgid "表格工具无法被实例化: "
503 | msgstr "The table tool can't be instantiated: "
504 |
505 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
506 | msgid "脚本不是继承自合法的表格工具: "
507 | msgstr "The script is not a valid table tool: "
508 |
509 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
510 | msgid "请查阅: "
511 | msgstr "Please refer to: "
512 |
513 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
514 | msgid "表格工具不支持该扩展名: "
515 | msgstr "The table tool is unsupport this extension: "
516 |
517 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
518 | msgid "重复的附加字段将被跳过:"
519 | msgstr "Duplicate additional filed will be skiped: "
520 |
521 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
522 | msgid "被生成的属性中缺少需要的实例化参数: "
523 | msgstr "Missing the required instantiation parameter:"
524 |
525 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
526 | msgid "不存在的优先排序字段将被跳过: "
527 | msgstr "Priority sorting fields that do not exist will be skipped: "
528 |
529 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
530 | msgid "无法创建备份路径: "
531 | msgstr "Can not create backup directory: "
532 |
533 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
534 | msgid "无法创建备份: "
535 | msgstr "Can not create backup: "
536 |
537 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
538 | msgid "指定的表格工具无法解析已有的表格: "
539 | msgstr "The table tool can't parsing exists table: "
540 |
541 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
542 | msgid "无效的生成修改器脚本: "
543 | msgstr "Invalid generate modifier script: "
544 |
545 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
546 | msgid "生成修改器无法被实例化: "
547 | msgstr "Can't instantiate generate modifier: "
548 |
549 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
550 | msgid "脚本不是继承自合法的合法的生成修改器: "
551 | msgstr "The script is not a valid generate modifier: "
552 |
553 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
554 | msgid "不被导入"
555 | msgstr "Not imported."
556 |
557 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
558 | msgid "生成表格成功:"
559 | msgstr "Generate table success: "
560 |
561 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
562 | msgid "导入失败:"
563 | msgstr "Import table failed: "
564 |
565 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
566 | msgid "表格工具脚本不存在:"
567 | msgstr "Not exsits table tool script: "
568 |
569 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
570 | msgid "表格工具无法被实例化:"
571 | msgstr "Can't instantiate table tool: "
572 |
573 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
574 | msgid "脚本不是继承自合法的表格工具:"
575 | msgstr "The script is not a valid table tool: "
576 |
577 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
578 | msgid "表格工具不支持该扩展名:"
579 | msgstr "The table tool can't support this extension: "
580 |
581 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
582 | msgid "表格不存在:"
583 | msgstr "Table is not exists: "
584 |
585 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
586 | msgid "指定表格工具无法解析表格:"
587 | msgstr "The table tool can't parse table: "
588 |
589 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
590 | msgid "指定导入工具不存在:"
591 | msgstr "The import tool is not exists: "
592 |
593 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
594 | msgid "指定导入工具无法实例化:"
595 | msgstr "Can't not instantiate import tool: "
596 |
597 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
598 | msgid "导入表格失败:"
599 | msgstr "Import table failed: "
600 |
601 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
602 | msgid "无效的导入修改器脚本: "
603 | msgstr "The import modifier script is invalid: "
604 |
605 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
606 | msgid "导入修改器无法被实例化: "
607 | msgstr "Can't instantiate import modifier: "
608 |
609 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
610 | msgid "脚本不是继承自合法的合法的导入修改器: "
611 | msgstr "The script is not extends from a valid import modifer: "
612 |
613 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
614 | msgid "类无法被实例化"
615 | msgstr "Class can't be instantiated"
616 |
617 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
618 | msgid "脚本类不存在需要的静态实例化方法"
619 | msgstr "Script class has not the require static instantiation method"
620 |
621 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
622 | msgid "原生类不存在或者不能被实例化"
623 | msgstr "The native class is not exists or can't be instantiated."
624 |
625 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
626 | msgid "指定脚本不是继承自导入工具:"
627 | msgstr "The Script is not a valid import tool: "
628 |
629 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
630 | msgid "导入工具不支持该扩展名:"
631 | msgstr "The import tool is not support extension: "
632 |
633 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
634 | msgid "导入失败,无法创建导入路径:"
635 | msgstr "Import failed, can't create import directory."
636 |
637 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
638 | msgid "导入表格成功:"
639 | msgstr "Import table success: "
640 |
641 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
642 | msgid "不支持的属性类型将被跳过:"
643 | msgstr "Unsupport type will be skiped: "
644 |
645 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
646 | msgid "如果是脚本类型可以安全忽略。"
647 | msgstr "Can be ignore if it is script type."
648 |
649 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
650 | msgid "非法属性名称: "
651 | msgstr "Invlid property name: "
652 |
653 | #: addons/config_table_manager.daylily-zeleen/scripts/preset.gd
654 | msgid "非法属性类型: "
655 | msgstr "Invalid property type: "
656 |
657 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
658 | msgid "无法读取csv文件: "
659 | msgstr "Can not read CSV file: "
660 |
661 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
662 | msgid "解析csv文件失败: "
663 | msgstr "Parse CSV failed: "
664 |
665 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
666 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
667 | msgid "请使用生成工具创建合法的表头。"
668 | msgstr "Please use table tool to generate a valid table header."
669 |
670 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
671 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
672 | msgid "非法标识符: "
673 | msgstr "Illegal identifier: "
674 |
675 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
676 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
677 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
678 | msgid "不支持的类型: "
679 | msgstr "Unsupport type: "
680 |
681 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
682 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
683 | msgid "生成表格失败: "
684 | msgstr "Generate table failed: "
685 |
686 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
687 | msgid "生成表格失败,无法生成:"
688 | msgstr "Generate table failed: "
689 |
690 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
691 | msgid "转换为文本失败,不支持的类型: "
692 | msgstr "Convert text failed, unsupport type: "
693 |
694 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
695 | msgid "Bug: 为空值生成默认值,请提交issue并提供复现流程或MRP。"
696 | msgstr "Bug: generate default value for empty value. Please submit an issue and provide reproduce steps or MRP."
697 |
698 | #: addons/config_table_manager.daylily-zeleen/table_tools/csv.gd
699 | msgid "非法值文本: "
700 | msgstr "Illegal value text: "
701 |
702 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
703 | msgid "无法转换,类型与字段不符"
704 | msgstr "Conver failed, types and files are not match"
705 |
706 | #: addons/config_table_manager.daylily-zeleen/table_tools/table_tool.gd
707 | msgid "无法转换,不支持的类型: "
708 | msgstr "Conver failed, unsupport type: "
709 |
710 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
711 | msgid "解析xlsx文件: "
712 | msgstr "Paese xlsx file: "
713 |
714 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
715 | msgid "必须使用 sheet=your_sheet_name 选项指定工作表。"
716 | msgstr "Must use "sheet=your_sheet_name" option to specific the worksheet."
717 |
718 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
719 | msgid "无法读取xlsx文件: "
720 | msgstr "Can't read xlsx file: "
721 |
722 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
723 | msgid "无法解析xlsx文件: "
724 | msgstr "Can't parse xlsx file: "
725 |
726 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
727 | msgid "不存在指定的工作表: "
728 | msgstr "The worksheet is not exists: "
729 |
730 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
731 | msgid "非法格式"
732 | msgstr "Illegal format"
733 |
734 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
735 | msgid "解析xlsx文件失败: "
736 | msgstr "Parse xlsx file failed: "
737 |
738 | #: addons/config_table_manager.daylily-zeleen/table_tools/xlsx.gd
739 | msgid "无法覆盖xlsx文件: "
740 | msgstr "Can't Overwrite xlsx file:"
741 |
--------------------------------------------------------------------------------