Implement direct editing of csv

This commit is contained in:
don-tnowe 2022-10-22 23:12:22 +03:00
parent 6ab9bcb96e
commit 58e410898f
8 changed files with 224 additions and 75 deletions

View File

@ -40,7 +40,7 @@ var edited_cells_text := []
var edit_cursor_positions := []
var inspector_resource : Resource
var search_cond : Reference
var io := SpreadsheetEditFormatTres.new()
var io : Reference
var hidden_columns := {}
var first_row := 0
@ -56,8 +56,6 @@ func _ready():
get_node(path_hide_columns_button).get_popup()\
.connect("id_pressed", self, "_on_VisibleCols_id_pressed")
io.editor_view = self
# Load saved recent paths
var file := File.new()
if file.file_exists(save_data_path):
@ -95,9 +93,9 @@ func _on_filesystem_changed():
func display_folder(folderpath : String, sort_by : String = "", sort_reverse : bool = false, force_rebuild : bool = false):
if folderpath == "": return # Root folder resources tend to have MANY properties.
if folderpath == "": return # Root folder resources tend to have MANY properties.W
$"HeaderContentSplit/MarginContainer/FooterContentSplit/Panel/Label".visible = false
if folderpath.ends_with(".tres"):
if folderpath.ends_with(".tres") && !folderpath.ends_with(SpreadsheetImport.SUFFIX):
folderpath = folderpath.get_base_dir() + "/"
if search_cond == null:
@ -118,7 +116,7 @@ func display_folder(folderpath : String, sort_by : String = "", sort_reverse : b
_update_hidden_columns()
_update_column_sizes()
yield(get_tree(), "idle_frame")
yield(get_tree().create_timer(0.5), "timeout")
if get_node(path_table_root).get_child_count() == 0:
display_folder(folderpath, sort_by, sort_reverse, force_rebuild)
@ -130,8 +128,35 @@ func refresh(force_rebuild : bool = true):
display_folder(current_path, sorting_by, sorting_reverse, force_rebuild)
func _load_resources_from_folder(folderpath : String, sort_by : String, sort_reverse : bool):
rows = io.import_from_path(folderpath, funcref(self, "insert_row_sorted"), sort_by, sort_reverse)
func _load_resources_from_folder(path : String, sort_by : String, sort_reverse : bool):
if path.ends_with("/"):
io = SpreadsheetEditFormatTres.new()
else:
io = load(path).view_script.new()
io.editor_view = self
rows = io.import_from_path(path, funcref(self, "insert_row_sorted"), sort_by, sort_reverse)
func fill_property_data(res):
columns.clear()
column_types.clear()
column_hints.clear()
column_hint_strings.clear()
column_editors.clear()
var column_index = -1
for x in res.get_property_list():
if x["usage"] & PROPERTY_USAGE_EDITOR != 0 and x["name"] != "script":
column_index += 1
columns.append(x["name"])
column_types.append(x["type"])
column_hints.append(x["hint"])
column_hint_strings.append(x["hint_string"].split(","))
for y in all_cell_editors:
if y.can_edit_value(io.get_value(res, x["name"]), x["type"], x["hint"], column_index):
column_editors.append(y)
break
func insert_row_sorted(res : Resource, rows : Array, sort_by : String, sort_reverse : bool):
@ -348,10 +373,10 @@ func _on_RecentPaths_item_selected(index : int):
display_folder(current_path)
func _on_FileDialog_dir_selected(dir : String):
get_node(path_folder_path).text = dir
add_path_to_recent(dir)
display_folder(dir)
func _on_FileDialog_dir_selected(path : String):
get_node(path_folder_path).text = path
add_path_to_recent(path)
display_folder(path)
func deselect_all_cells():
@ -687,7 +712,11 @@ func _move_selection_on_grid(move_h : int, move_v : int):
func _update_resources(update_rows : Array, update_cells : Array, update_column : int, values : Array):
var saved_indices = []
saved_indices.resize(update_rows.size())
for i in update_rows.size():
var row = _get_cell_row(update_cells[i])
saved_indices[i] = row
column_editors[update_column].set_value(update_cells[i], values[i])
values[i] = _try_convert(values[i], column_types[update_column])
if values[i] == null:
@ -696,9 +725,9 @@ func _update_resources(update_rows : Array, update_cells : Array, update_column
io.set_value(
update_rows[i],
columns[update_column],
convert(values[i], column_types[update_column])
values[i],
row
)
io.save_entry(rows, i)
if column_types[update_column] == TYPE_COLOR:
for j in columns.size() - update_column:
if j != 0 and column_types[j + update_column] == TYPE_COLOR:
@ -711,6 +740,7 @@ func _update_resources(update_rows : Array, update_cells : Array, update_column
values[i]
)
io.save_entries(rows, saved_indices)
_update_column_sizes()

View File

@ -535,7 +535,8 @@ margin_bottom = 192.0
rect_min_size = Vector2( 150, 52.5 )
window_title = "Open a Folder"
mode_overrides_title = false
mode = 2
mode = 3
filters = PoolStringArray( "*_spreadsheet_import.tres" )
[node name="FileDialogText" type="FileDialog" parent="Control"]
anchor_left = 0.5
@ -753,4 +754,5 @@ text = "Enable"
[connection signal="text_entered" from="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/Search/SearchCond" to="." method="_on_SearchCond_text_entered"]
[connection signal="text_entered" from="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/Search/ProcessExpr" to="." method="_on_ProcessExpr_text_entered"]
[connection signal="dir_selected" from="Control/FileDialog" to="." method="_on_FileDialog_dir_selected"]
[connection signal="file_selected" from="Control/FileDialog" to="." method="_on_FileDialog_dir_selected"]
[connection signal="file_selected" from="Control/FileDialogText" to="Control/ImportExport" method="_on_FileDialogText_file_selected"]

View File

@ -7,12 +7,12 @@ var editor_view : Control
func get_value(entry, key : String):
pass
## Override to define writing behaviour. This is NOT supposed to save - use `save_entry`.
func set_value(entry, key : String, value):
## Override to define writing behaviour. This is NOT supposed to save - use `save_entries`.
func set_value(entry, key : String, value, index : int):
pass
## Override to define how the data gets saved.
func save_entry(all_entries : Array, index : int):
func save_entries(all_entries : Array, indices : Array):
pass
## Override to allow editing rows from the Inspector.

View File

@ -0,0 +1,71 @@
class_name SpreadsheetEditFormatCsv
extends SpreadsheetEditFormatTres
var import_data
var csv_rows = []
var resource_original_positions = {}
var timer : SceneTreeTimer
func get_value(entry, key : String):
return entry.get(key)
func set_value(entry, key : String, value, index : int):
entry.set(key, value)
csv_rows[resource_original_positions[entry]] = import_data.resource_to_strings(entry)
func save_entries(all_entries : Array, indices : Array, repeat : bool = true):
if timer == null || timer.time_left <= 0.0:
var file = File.new()
file.open(import_data.edited_path, File.WRITE)
if import_data.remove_first_row:
var names = []
names.resize(import_data.prop_names.size())
for i in names.size():
names[i] = TextEditingUtils.string_snake_to_naming_case(import_data.prop_names[i])
file.store_csv_line(names, import_data.delimeter)
for x in csv_rows:
file.store_csv_line(x, import_data.delimeter)
file.close()
if repeat:
timer = editor_view.get_tree().create_timer(5.0)
timer.connect("timeout", self, "save_entries", [all_entries, indices, false])
func create_resource(entry) -> Resource:
return entry
func import_from_path(path : String, insert_func : FuncRef, sort_by : String, sort_reverse : bool = false) -> Array:
import_data = load(path)
var file = File.new()
file.open(import_data.edited_path, File.READ)
var line
var first = true
csv_rows = []
while !file.eof_reached():
line = file.get_csv_line(import_data.delimeter)
if first && import_data.remove_first_row:
line = " "
first = false
continue
if csv_rows.size() == 0 || line.size() == csv_rows[0].size():
csv_rows.append(line)
var rows := []
var res : Resource
resource_original_positions.clear()
for i in csv_rows.size():
res = import_data.strings_to_resource(csv_rows[i])
insert_func.call_func(res, rows, sort_by, sort_reverse)
resource_original_positions[res] = i
editor_view.fill_property_data(rows[0])
return rows

View File

@ -6,12 +6,13 @@ func get_value(entry, key : String):
return entry.get(key)
func set_value(entry, key : String, value):
func set_value(entry, key : String, value, index : int):
entry.set(key, value)
func save_entry(all_entries : Array, index : int):
ResourceSaver.save(all_entries[index].resource_path, all_entries[index])
func save_entries(all_entries : Array, indices : Array):
for x in indices:
ResourceSaver.save(all_entries[x].resource_path, all_entries[x])
func create_resource(entry) -> Resource:
@ -35,24 +36,7 @@ func import_from_path(folderpath : String, insert_func : FuncRef, sort_by : Stri
filepath = folderpath + filepath
res = load(filepath)
if !is_instance_valid(cur_dir_script):
editor_view.columns.clear()
editor_view.column_types.clear()
editor_view.column_hints.clear()
editor_view.column_hint_strings.clear()
editor_view.column_editors.clear()
var column_index = -1
for x in res.get_property_list():
if x["usage"] & PROPERTY_USAGE_EDITOR != 0 and x["name"] != "script":
column_index += 1
editor_view.columns.append(x["name"])
editor_view.column_types.append(x["type"])
editor_view.column_hints.append(x["hint"])
editor_view.column_hint_strings.append(x["hint_string"].split(","))
for y in editor_view.all_cell_editors:
if y.can_edit_value(get_value(res, x["name"]), x["type"], x["hint"], column_index):
editor_view.column_editors.append(y)
break
editor_view.fill_property_data(res)
cur_dir_script = res.get_script()
if !(sort_by in res):
sort_by = "resource_path"

View File

@ -10,25 +10,25 @@ onready var node_filename_props := $"TabContainer/Import/MarginContainer/ScrollC
onready var prop_list := $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer"
var entries := []
var uniques := {}
var property_used_as_filename := 0
var import_data : SpreadsheetImport
var delimeter := ","
func _on_FileDialogText_file_selected(path : String):
import_data = SpreadsheetImport.new()
import_data.path = path
import_data.initialize(path)
_open_dialog()
popup_centered()
func _open_dialog():
node_classname_field.text = TextEditingUtils\
.string_snake_to_naming_case(import_data.path.get_file().get_basename())\
.string_snake_to_naming_case(import_data.edited_path.get_file().get_basename())\
.replace(" ", "")
import_data.script_classname = node_classname_field.text
_load_entries()
_load_property_names()
_create_prop_editors()
@ -36,17 +36,17 @@ func _open_dialog():
func _load_entries():
var file = File.new()
file.open(import_data.path, File.READ)
file.open(import_data.edited_path, File.READ)
delimeter = ";"
var text_lines := [file.get_line().split(delimeter)]
import_data.delimeter = ";"
var text_lines := [file.get_line().split(import_data.delimeter)]
var line = text_lines[0]
if line.size() == 1:
delimeter = ","
text_lines[0] = text_lines[0][0].split(delimeter)
import_data.delimeter = ","
text_lines[0] = text_lines[0][0].split(import_data.delimeter)
while !file.eof_reached():
line = file.get_csv_line(delimeter)
line = file.get_csv_line(import_data.delimeter)
if line.size() == text_lines[0].size():
text_lines.append(line)
@ -99,7 +99,8 @@ func _generate_class():
new_script.source_code = "extends Resource\n\n"
# Enums
uniques = {}
var uniques = {}
import_data.uniques = uniques
for i in import_data.prop_types.size():
if import_data.prop_types[i] == SpreadsheetImport.PropType.ENUM:
var cur_value := ""
@ -114,21 +115,22 @@ func _generate_class():
if !uniques[i].has(cur_value):
uniques[i][cur_value] = uniques[i].size()
new_script.source_code += import_data.create_enum_for_prop(i, uniques)
new_script.source_code += import_data.create_enum_for_prop(i)
# Properties
for i in import_data.prop_names.size():
new_script.source_code += import_data.create_property_line_for_prop(i)
ResourceSaver.save(import_data.path.get_basename() + ".gd", new_script)
ResourceSaver.save(import_data.edited_path.get_basename() + ".gd", new_script)
new_script.reload()
new_script = load(import_data.path.get_basename() + ".gd") # Because when instanced, objects have a copy of the script
import_data.new_script = new_script
# Because when instanced, objects have a copy of the script
import_data.new_script = load(import_data.edited_path.get_basename() + ".gd")
func _export_tres_folder():
var dir = Directory.new()
dir.make_dir_recursive(import_data.path.get_basename())
dir.make_dir_recursive(import_data.edited_path.get_basename())
import_data.prop_used_as_filename = import_data.prop_names[property_used_as_filename]
var new_res : Resource
@ -136,7 +138,7 @@ func _export_tres_folder():
if import_data.remove_first_row && i == 0:
continue
new_res = import_data.strings_to_resource(entries[i], uniques)
new_res = import_data.strings_to_resource(entries[i])
ResourceSaver.save(new_res.resource_path, new_res)
@ -145,7 +147,7 @@ func _on_Ok_pressed():
_generate_class()
_export_tres_folder()
yield(get_tree(), "idle_frame")
editor_view.display_folder(import_data.path.get_basename())
editor_view.display_folder(import_data.edited_path.get_basename() + "/")
yield(get_tree(), "idle_frame")
editor_view.refresh()
@ -155,7 +157,7 @@ func _on_OkSafe_pressed():
_generate_class()
import_data.save()
yield(get_tree(), "idle_frame")
editor_view.display_folder(import_data.path.get_basename())
editor_view.display_folder(import_data.resource_path)
yield(get_tree(), "idle_frame")
editor_view.refresh()

View File

@ -1,6 +1,9 @@
tool
class_name SpreadsheetImport
extends Resource
const SUFFIX := "_spreadsheet_import.tres"
enum PropType {
BOOL,
INT,
@ -16,23 +19,33 @@ enum PropType {
MAX,
}
var prop_types := []
var prop_names := []
export var prop_types : Array
export var prop_names : Array
var path := "res://"
var prop_used_as_filename := "name"
var script_classname := ""
var remove_first_row := true
export var edited_path := "res://"
export var prop_used_as_filename := ""
export var script_classname := ""
export var remove_first_row := true
var new_script : GDScript
export var new_script : GDScript
export var view_script : Script = SpreadsheetEditFormatCsv
export var delimeter := ";"
export var uniques : Dictionary
func initialize(path):
resource_path = path.get_basename() + SUFFIX
edited_path = path
prop_types = []
prop_names = []
func save():
resource_path = path.get_basename() + "_spreadsheet_import.tres"
ResourceSaver.save(resource_path, self)
ResourceSaver.call_deferred("save", edited_path.get_basename() + SUFFIX, self)
func string_to_property(string : String, col_index : int, uniques : Dictionary):
func string_to_property(string : String, col_index : int):
match prop_types[col_index]:
PropType.STRING:
return string
@ -61,6 +74,30 @@ func string_to_property(string : String, col_index : int, uniques : Dictionary):
return int(uniques[col_index][string.to_upper().replace(" ", "_")])
func property_to_string(value, col_index : int):
match prop_types[col_index]:
PropType.STRING:
return value
PropType.BOOL:
return str(value) # TODO: make this actually persist
PropType.REAL, PropType.INT:
return str(value)
PropType.COLOR:
return value.to_html()
PropType.OBJECT:
return value.resource_path
PropType.ENUM:
var dict = uniques[col_index]
for k in dict:
if dict[k] == value:
return TextEditingUtils.string_snake_to_naming_case(k)
func create_property_line_for_prop(col_index : int):
var result = "export var " + prop_names[col_index] + " :"
match prop_types[col_index]:
@ -93,7 +130,7 @@ func create_property_line_for_prop(col_index : int):
) + "= 0\n"
func create_enum_for_prop(col_index, uniques):
func create_enum_for_prop(col_index):
var result := (
"enum "
+ TextEditingUtils.string_snake_to_naming_case(prop_names[col_index]).replace(" ", "")
@ -111,10 +148,21 @@ func create_enum_for_prop(col_index, uniques):
return result
func strings_to_resource(strings : Array, uniques : Dictionary):
func strings_to_resource(strings : Array):
var new_res = new_script.new()
for j in prop_names.size():
new_res.set(prop_names[j], string_to_property(strings[j], j, uniques))
new_res.set(prop_names[j], string_to_property(strings[j], j))
if prop_used_as_filename != "":
new_res.resource_path = edited_path.get_basename() + "/" + new_res.get(prop_used_as_filename) + ".tres"
new_res.resource_path = path.get_basename() + "/" + new_res.get(prop_used_as_filename) + ".tres"
return new_res
func resource_to_strings(res : Resource):
var strings := []
strings.resize(prop_names.size())
for i in prop_names.size():
strings[i] = property_to_string(res.get(prop_names[i]), i)
return PoolStringArray(strings)

View File

@ -34,6 +34,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/resources_speadsheet_view/import_export/formats_edit/edit_base.gd"
}, {
"base": "SpreadsheetEditFormatTres",
"class": "SpreadsheetEditFormatCsv",
"language": "GDScript",
"path": "res://addons/resources_speadsheet_view/import_export/formats_edit/edit_csv.gd"
}, {
"base": "SpreadsheetEditFormat",
"class": "SpreadsheetEditFormatTres",
"language": "GDScript",
@ -58,6 +63,11 @@ _global_script_classes=[ {
"class": "ThemeStylebox",
"language": "GDScript",
"path": "res://addons/resources_speadsheet_view/editor_stylebox_overrider.gd"
}, {
"base": "Resource",
"class": "TrainBigmartsalesprediction",
"language": "GDScript",
"path": "res://aa/train_BigMartSalesPrediction.gd"
} ]
_global_script_class_icons={
"CellEditor": "",
@ -65,11 +75,13 @@ _global_script_class_icons={
"SettingsGrid": "",
"SheetsDockEditor": "",
"SpreadsheetEditFormat": "",
"SpreadsheetEditFormatCsv": "",
"SpreadsheetEditFormatTres": "",
"SpreadsheetImport": "",
"TextEditingUtils": "",
"ThemeIconButton": "",
"ThemeStylebox": ""
"ThemeStylebox": "",
"TrainBigmartsalesprediction": ""
}
color_rows=false
color_arrays=true