mirror of
https://github.com/Relintai/godot-resources-as-sheets-plugin.git
synced 2025-04-08 17:41:50 +02:00
Implement direct editing of csv
This commit is contained in:
parent
6ab9bcb96e
commit
58e410898f
@ -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()
|
||||
|
||||
|
||||
|
@ -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"]
|
||||
|
@ -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.
|
||||
|
@ -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
|
@ -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"
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user