Compare commits

...

2 Commits

9 changed files with 170 additions and 116 deletions

View File

@ -217,3 +217,7 @@ Restructuring of storage of components
- [ ] in conversions from propgroups - [ ] in conversions from propgroups
component_name = definition["short_name"] component_name = definition["short_name"]
- Hashmap Support
- [ ] fix parsing of keys's type either on Bevy side (prefered, unlikely to be possible) or on the Blender side
- [ ] handle missing types in registry for keys & values

View File

@ -11,17 +11,6 @@ class ComponentDefinitionsList(bpy.types.PropertyGroup):
#print("add components to ui_list") #print("add components to ui_list")
items = [] items = []
type_infos = context.window_manager.components_registry.type_infos type_infos = context.window_manager.components_registry.type_infos
short_names = context.window_manager.components_registry.short_names_to_long_names
"""for short_name in sorted(short_names.keys()):
long_name = short_names[short_name]
definition = type_infos[long_name]
is_component = definition['isComponent'] if "isComponent" in definition else False
if self.filter in short_name and is_component:
if not 'Handle' in short_name and not "Cow" in short_name and not "AssetId" in short_name and short_name not in self.exclude: # FIXME: hard coded, seems wrong
items.append((long_name, short_name, long_name))"""
for long_name in type_infos.keys(): for long_name in type_infos.keys():
definition = type_infos[long_name] definition = type_infos[long_name]
short_name = definition["short_name"] short_name = definition["short_name"]

View File

@ -5,7 +5,7 @@ from bpy.props import (StringProperty, EnumProperty, PointerProperty, FloatVecto
class GENERIC_MAP_OT_actions(Operator): class GENERIC_MAP_OT_actions(Operator):
"""Move items up and down, add and remove""" """Move items up and down, add and remove"""
bl_idname = "generic_map.map_action" bl_idname = "generic_map.map_action"
bl_label = "List Actions" bl_label = "Map Actions"
bl_description = "Move items up and down, add and remove" bl_description = "Move items up and down, add and remove"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
@ -26,6 +26,8 @@ class GENERIC_MAP_OT_actions(Operator):
description="", description="",
) # type: ignore ) # type: ignore
target_index: IntProperty(name="target index", description="index of item to manipulate")# type: ignore
def invoke(self, context, event): def invoke(self, context, event):
object = context.object object = context.object
# information is stored in component meta # information is stored in component meta
@ -36,33 +38,74 @@ class GENERIC_MAP_OT_actions(Operator):
for path_item in json.loads(self.property_group_path): for path_item in json.loads(self.property_group_path):
propertyGroup = getattr(propertyGroup, path_item) propertyGroup = getattr(propertyGroup, path_item)
target_list = getattr(propertyGroup, "list") keys_list = getattr(propertyGroup, "list")
index = getattr(propertyGroup, "list_index") index = getattr(propertyGroup, "list_index")
values_list = getattr(propertyGroup, "values_list") values_list = getattr(propertyGroup, "values_list")
values_index = getattr(propertyGroup, "values_list_index") values_index = getattr(propertyGroup, "values_list_index")
key_setter = getattr(propertyGroup, "keys_setter")
value_setter = getattr(propertyGroup, "values_setter")
if self.action == 'DOWN' and index < len(target_list) - 1:
if self.action == 'DOWN' and index < len(keys_list) - 1:
#item_next = scn.rule_list[index + 1].name #item_next = scn.rule_list[index + 1].name
target_list.move(index, index + 1) keys_list.move(index, index + 1)
propertyGroup.list_index += 1 propertyGroup.list_index += 1
elif self.action == 'UP' and index >= 1: elif self.action == 'UP' and index >= 1:
#item_prev = scn.rule_list[index - 1].name #item_prev = scn.rule_list[index - 1].name
target_list.move(index, index - 1) keys_list.move(index, index - 1)
propertyGroup.list_index -= 1 propertyGroup.list_index -= 1
elif self.action == 'REMOVE': elif self.action == 'REMOVE':
target_list.remove(index) index = self.target_index
propertyGroup.list_index = min(max(0, index - 1), len(target_list) - 1) keys_list.remove(index)
values_list.remove(index)
propertyGroup.list_index = min(max(0, index - 1), len(keys_list) - 1)
propertyGroup.values_index = min(max(0, index - 1), len(keys_list) - 1)
if self.action == 'ADD': if self.action == 'ADD':
key = target_list.add() print("keys_list", keys_list)
value = values_list.add() hashmap = {}
for index, key in enumerate(keys_list):
key_entry = {}
for field_name in key.field_names:
print("field name", field_name, key)
key_entry[field_name] = key[field_name]
value_entry = {}
for field_name in values_list[index].field_names:
value_entry[field_name] = values_list[index][field_name]
propertyGroup.list_index = index + 1 # we use this to force the change detection hashmap[json.dumps(key_entry)] = index #{"value": json.dumps(value_entry), "index": index}
propertyGroup.values_index = index + 1 # we use this to force the change detection print("hashmap", hashmap )
# we need to find the index of a specific value
key_entry = {}
for field_name in key_setter.field_names:
key_entry[field_name] = key_setter[field_name]
key_to_add = json.dumps(key_entry)
existing_index = hashmap.get(key_to_add, None)
print("existing_index", existing_index)
if existing_index is None:
print("adding new value")
key = keys_list.add()
# copy the values over
for field_name in key_setter.field_names:
key[field_name] = key_setter[field_name]
value = values_list.add()
# copy the values over
for field_name in value_setter.field_names:
value[field_name] = value_setter[field_name]
propertyGroup.list_index = index + 1 # we use this to force the change detection
propertyGroup.values_index = index + 1 # we use this to force the change detection
else:
print("overriding value")
for field_name in value_setter.field_names:
values_list[existing_index][field_name] = value_setter[field_name]
#info = '"%s" added to list' % (item.name) #info = '"%s" added to list' % (item.name)

View File

@ -225,7 +225,7 @@ def upsert_component_in_object(object, long_name, registry):
# now deal with property groups details # now deal with property groups details
if propertyGroup != None: if propertyGroup != None:
if short_name in registry.invalid_components: if long_name in registry.invalid_components:
component_meta.enabled = False component_meta.enabled = False
component_meta.invalid = True component_meta.invalid = True
component_meta.invalid_details = "component contains fields that are not in the schema, disabling" component_meta.invalid_details = "component contains fields that are not in the schema, disabling"

View File

@ -85,55 +85,65 @@ def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
op.property_group_path = json.dumps(nesting) op.property_group_path = json.dumps(nesting)
elif is_map: elif is_map:
keys_list = getattr(propertyGroup, "list") root = layout.row().column()
values_list = getattr(propertyGroup, "values_list") if hasattr(propertyGroup, "list"): # TODO: improve handling of non drawable UI
box = layout.box() keys_list = getattr(propertyGroup, "list")
row = box.row() values_list = getattr(propertyGroup, "values_list")
row.label(text="key") box = root.box()
row.label(text="value") row = box.row()
#values_setter = getattr(propertyGroup, "values_setter") row.label(text="Add entry:")
# draw_propertyGroup(values_setter, row, nesting, rootName) keys_setter = getattr(propertyGroup, "keys_setter")
draw_propertyGroup(keys_setter, row, nesting, rootName)
split = box.split(factor=0.9) values_setter = getattr(propertyGroup, "values_setter")
list_column, buttons_column = (split.column(),split.column()) draw_propertyGroup(values_setter, row, nesting, rootName)
list_column = list_column.box()
for index, item in enumerate(keys_list): op = row.operator('generic_map.map_action', icon='ADD', text="")
row = list_column.row() op.action = 'ADD'
#row.label(text=str(index)) op.component_name = rootName
draw_propertyGroup(item, row, nesting, rootName) op.property_group_path = json.dumps(nesting)
value = values_list[index] box = root.box()
draw_propertyGroup(value, row, nesting, rootName) split = box.split(factor=0.9)
list_column, buttons_column = (split.column(),split.column())
list_column = list_column.box()
for index, item in enumerate(keys_list):
row = list_column.row()
draw_propertyGroup(item, row, nesting, rootName)
value = values_list[index]
draw_propertyGroup(value, row, nesting, rootName)
op = row.operator('generic_map.map_action', icon='REMOVE', text="")
op.action = 'REMOVE'
op.component_name = rootName
op.property_group_path = json.dumps(nesting)
op.target_index = index
#various control buttons #various control buttons
buttons_column.separator() buttons_column.separator()
row = buttons_column.row() row = buttons_column.row()
op = row.operator('generic_map.map_action', icon='ADD', text="")
op.action = 'ADD'
op.component_name = rootName
op.property_group_path = json.dumps(nesting)
row = buttons_column.row()
op = row.operator('generic_map.map_action', icon='REMOVE', text="")
op.action = 'REMOVE'
op.component_name = rootName
op.property_group_path = json.dumps(nesting)
else: else:
for fname in field_names: for fname in field_names:
subrow = layout.row() #subrow = layout.row()
nestedPropertyGroup = getattr(propertyGroup, fname) nestedPropertyGroup = getattr(propertyGroup, fname)
nested = getattr(nestedPropertyGroup, "nested", False) nested = getattr(nestedPropertyGroup, "nested", False)
display_name = fname if propertyGroup.tupple_or_struct == "struct" else "" display_name = fname if propertyGroup.tupple_or_struct == "struct" else ""
if nested: if nested:
layout.separator() layout.separator()
layout.separator()
layout.label(text=display_name) # this is the name of the field/sub field layout.label(text=display_name) # this is the name of the field/sub field
layout.separator() layout.separator()
draw_propertyGroup(nestedPropertyGroup, subrow.column(), nesting + [fname], rootName ) subrow = layout.row()
draw_propertyGroup(nestedPropertyGroup, subrow, nesting + [fname], rootName )
else: else:
subrow = layout.row()
subrow.prop(propertyGroup, fname, text=display_name) subrow.prop(propertyGroup, fname, text=display_name)
subrow.separator() subrow.separator()
@ -216,8 +226,8 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
# we fetch the matching ui property group # we fetch the matching ui property group
root_propertyGroup_name = registry.get_propertyGroupName_from_longName(component_name) root_propertyGroup_name = registry.get_propertyGroupName_from_longName(component_name)
"""print("root_propertyGroup_name", root_propertyGroup_name) """print("root_propertyGroup_name", root_propertyGroup_name)"""
print("component_meta", component_meta, component_invalid)""" print("component_meta", component_meta, component_invalid)
if root_propertyGroup_name: if root_propertyGroup_name:
propertyGroup = getattr(component_meta, root_propertyGroup_name, None) propertyGroup = getattr(component_meta, root_propertyGroup_name, None)
@ -226,8 +236,8 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
# if the component has only 0 or 1 field names, display inline, otherwise change layout # if the component has only 0 or 1 field names, display inline, otherwise change layout
single_field = len(propertyGroup.field_names) < 2 single_field = len(propertyGroup.field_names) < 2
prop_group_location = box.row(align=True).column() prop_group_location = box.row(align=True).column()
if single_field: """if single_field:
prop_group_location = row.column(align=True)#.split(factor=0.9)#layout.row(align=False) prop_group_location = row.column(align=True)#.split(factor=0.9)#layout.row(align=False)"""
if component_visible: if component_visible:
if component_invalid: if component_invalid:

View File

@ -33,17 +33,15 @@ def property_group_value_to_custom_property_value(property_group, definition, re
type_def = definition["type"] if "type" in definition else None type_def = definition["type"] if "type" in definition else None
type_name = definition["title"] type_name = definition["title"]
is_value_type = type_name in conversion_tables is_value_type = type_name in conversion_tables
print("computing custom property: component name:", component_name, "type_info", type_info, "type_def", type_def, "type_name", type_name) # print("computing custom property: component name:", component_name, "type_info", type_info, "type_def", type_def, "type_name", type_name)
if is_value_type: if is_value_type:
value = conversion_tables[type_name](value) value = conversion_tables[type_name](value)
elif type_info == "Struct": elif type_info == "Struct":
print("generating string for struct")
values = {} values = {}
if len(property_group.field_names) ==0: if len(property_group.field_names) ==0:
value = '()' value = '()'
else: else:
print("toto", type_def, definition, property_group)
for index, field_name in enumerate(property_group.field_names): for index, field_name in enumerate(property_group.field_names):
item_type_name = definition["properties"][field_name]["type"]["$ref"].replace("#/$defs/", "") item_type_name = definition["properties"][field_name]["type"]["$ref"].replace("#/$defs/", "")
item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None
@ -141,7 +139,6 @@ def property_group_value_to_custom_property_value(property_group, definition, re
value.append(item_value) value.append(item_value)
elif type_info == "Map": elif type_info == "Map":
print("MAAAAAP", property_group)
keys_list = getattr(property_group, "list", {}) keys_list = getattr(property_group, "list", {})
values_list = getattr(property_group, "values_list") values_list = getattr(property_group, "values_list")
value = {} value = {}
@ -159,7 +156,6 @@ def property_group_value_to_custom_property_value(property_group, definition, re
val = values_list[index] val = values_list[index]
value_type_name = getattr(val, "type_name") value_type_name = getattr(val, "type_name")
definition = registry.type_infos[value_type_name] if value_type_name in registry.type_infos else None definition = registry.type_infos[value_type_name] if value_type_name in registry.type_infos else None
print("value definition", definition)
if definition != None: if definition != None:
val_value = property_group_value_to_custom_property_value(val, definition, registry, component_name, None) val_value = property_group_value_to_custom_property_value(val, definition, registry, component_name, None)
if value_type_name.startswith("wrapper_"): #if we have a "fake" tupple for aka for value types, we need to remove one nested level if value_type_name.startswith("wrapper_"): #if we have a "fake" tupple for aka for value types, we need to remove one nested level
@ -167,10 +163,8 @@ def property_group_value_to_custom_property_value(property_group, definition, re
else: else:
val_value = '""' val_value = '""'
value[key_value] = val_value value[key_value] = val_value
print("MAP VALUES", value) value = str(value).replace('{','@').replace('}','²') # FIXME: eeek !!
else: else:
value = conversion_tables[type_name](value) if is_value_type else value value = conversion_tables[type_name](value) if is_value_type else value
value = '""' if isinstance(value, PropertyGroup) else value value = '""' if isinstance(value, PropertyGroup) else value
@ -180,9 +174,11 @@ def property_group_value_to_custom_property_value(property_group, definition, re
value = value.replace("'", "") value = value.replace("'", "")
if parent == None: if parent == None:
print("transforming value", value, definition)
value = str(value).replace("'", "") value = str(value).replace("'", "")
value = value.replace(",)",")") value = value.replace(",)",")")
value = value.replace("{", "(").replace("}", ")") # FIXME: deal with hashmaps value = value.replace("{", "(").replace("}", ")") # FIXME: deal with hashmaps
value = value.replace("True", "true").replace("False", "false") value = value.replace("True", "true").replace("False", "false")
value = value.replace('@', '{').replace('²', '}')
return value return value

View File

@ -1,9 +1,8 @@
from bpy.props import (StringProperty, IntProperty, CollectionProperty) from bpy.props import (StringProperty, IntProperty, CollectionProperty, PointerProperty)
from .utils import generate_wrapper_propertyGroup from .utils import generate_wrapper_propertyGroup
from . import process_component from . import process_component
def process_map(registry, definition, update, nesting=[], nesting_long_names=[]): def process_map(registry, definition, update, nesting=[], nesting_long_names=[]):
print("IS HASHMAP")
value_types_defaults = registry.value_types_defaults value_types_defaults = registry.value_types_defaults
type_infos = registry.type_infos type_infos = registry.type_infos
@ -13,59 +12,74 @@ def process_map(registry, definition, update, nesting=[], nesting_long_names=[])
nesting = nesting + [short_name] nesting = nesting + [short_name]
nesting_long_names = nesting_long_names + [long_name] nesting_long_names = nesting_long_names + [long_name]
#ref_name = definition["items"]["type"]["$ref"].replace("#/$defs/", "")
value_ref_name = definition["additionalProperties"]["type"]["$ref"].replace("#/$defs/", "") value_ref_name = definition["additionalProperties"]["type"]["$ref"].replace("#/$defs/", "")
key_ref_name = long_name.split(',')[0].split('<')[1]# FIXME: hack !!! key_ref_name = long_name.split(',')[0].split('<')[1]# FIXME: hack !!!
key_type = ''
value_type = ''
print("infos", short_name, "long name", long_name)
print("value ref", value_ref_name, "key ref", key_ref_name)
#print("definition", definition) #print("definition", definition)
__annotations__ = {}
if value_ref_name in type_infos:
value_definition = type_infos[value_ref_name]
original_type_name = value_definition["title"]
original_short_name = value_definition["short_name"]
is_value_value_type = original_type_name in value_types_defaults
definition_link = definition["additionalProperties"]["type"]["$ref"]#f"#/$defs/{value_ref_name}"
print("hashmap VALUE type", original_type_name)
#if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName !
if is_value_value_type:
values_property_group_class = generate_wrapper_propertyGroup(long_name, original_type_name, definition_link, registry, update)
else:
(_, list_content_group_class) = process_component.process_component(registry, value_definition, update, {"nested": True, "type_name": original_type_name}, nesting)
values_property_group_class = list_content_group_class
values_collection = CollectionProperty(type=values_property_group_class)
if key_ref_name in type_infos: if key_ref_name in type_infos:
key_definition = type_infos[key_ref_name] key_definition = type_infos[key_ref_name]
original_type_name = key_definition["title"] original_type_name = key_definition["title"]
original_short_name = key_definition["short_name"]
is_key_value_type = original_type_name in value_types_defaults is_key_value_type = original_type_name in value_types_defaults
definition_link = f"#/$defs/{key_ref_name}" definition_link = f"#/$defs/{key_ref_name}"
#if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName ! #if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName !
if is_key_value_type: if is_key_value_type:
keys_property_group_class = generate_wrapper_propertyGroup(long_name+'_values', original_type_name, definition_link, registry, update) keys_property_group_class = generate_wrapper_propertyGroup(f"{long_name}_values", original_type_name, definition_link, registry, update)
else: else:
(_, list_content_group_class) = process_component.process_component(registry, key_definition, update, {"nested": True, "type_name": original_type_name}, nesting) (_, list_content_group_class) = process_component.process_component(registry, key_definition, update, {"nested": True, "type_name": original_type_name}, nesting, nesting_long_names)
keys_property_group_class = list_content_group_class keys_property_group_class = list_content_group_class
keys_collection = CollectionProperty(type=keys_property_group_class) keys_collection = CollectionProperty(type=keys_property_group_class)
keys_property_group_pointer = PointerProperty(type=keys_property_group_class)
else:
__annotations__["list"] = StringProperty(default="N/A")
registry.add_missing_typeInfo(key_ref_name)
# the root component also becomes invalid (in practice it is not always a component, but good enough)
registry.add_invalid_component(nesting_long_names[0])
__annotations__ = { if value_ref_name in type_infos:
"list": keys_collection, value_definition = type_infos[value_ref_name]
"list_index": IntProperty(name = "Index for keys", default = 0, update=update), original_type_name = value_definition["title"]
"values_list": values_collection, is_value_value_type = original_type_name in value_types_defaults
"values_list_index": IntProperty(name = "Index for values", default = 0, update=update), definition_link = definition["additionalProperties"]["type"]["$ref"]#f"#/$defs/{value_ref_name}"
}
''' #if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName !
"values_setter": values_setter, if is_value_value_type:
"values_list": values_collection, values_property_group_class = generate_wrapper_propertyGroup(f"{long_name}_keys", original_type_name, definition_link, registry, update)
"values_list_index": IntProperty(name = "Index for values", default = 0, update=update), else:
''' (_, list_content_group_class) = process_component.process_component(registry, value_definition, update, {"nested": True, "type_name": original_type_name}, nesting, nesting_long_names)
values_property_group_class = list_content_group_class
values_collection = CollectionProperty(type=values_property_group_class)
values_property_group_pointer = PointerProperty(type=values_property_group_class)
else:
#__annotations__["list"] = StringProperty(default="N/A")
registry.add_missing_typeInfo(value_ref_name)
# the root component also becomes invalid (in practice it is not always a component, but good enough)
registry.add_invalid_component(nesting_long_names[0])
if key_ref_name in type_infos and value_ref_name in type_infos:
__annotations__ = {
"list": keys_collection,
"list_index": IntProperty(name = "Index for keys", default = 0, update=update),
"keys_setter":keys_property_group_pointer,
"values_list": values_collection,
"values_list_index": IntProperty(name = "Index for values", default = 0, update=update),
"values_setter":values_property_group_pointer,
}
"""__annotations__["list"] = StringProperty(default="N/A")
__annotations__["values_list"] = StringProperty(default="N/A")
__annotations__["keys_setter"] = StringProperty(default="N/A")"""
"""registry.add_missing_typeInfo(key_ref_name)
registry.add_missing_typeInfo(value_ref_name)
# the root component also becomes invalid (in practice it is not always a component, but good enough)
registry.add_invalid_component(nesting_long_names[0])
print("setting invalid flag for", nesting_long_names[0])"""
return __annotations__ return __annotations__

View File

@ -14,10 +14,7 @@ def generate_wrapper_propertyGroup(wrapped_type_long_name_name, item_long_name,
is_item_value_type = item_long_name in value_types_defaults is_item_value_type = item_long_name in value_types_defaults
wrapper_name = "wrapper_" + wrapped_type_long_name_name wrapper_name = "wrapper_" + wrapped_type_long_name_name
print("WRAPPER NAME", wrapper_name)
# FIXME: this is not a correct generic value for hashmaps !!?
wrapper_definition = { wrapper_definition = {
"isComponent": False, "isComponent": False,
"isResource": False, "isResource": False,

View File

@ -132,17 +132,18 @@ class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel):
comp_names = [] comp_names = []
for index, component_meta in enumerate(components_metadata): for index, component_meta in enumerate(components_metadata):
short_name = component_meta.name short_name = component_meta.name
long_name = component_meta.long_name
if component_meta.invalid: if component_meta.invalid:
self.draw_invalid_or_unregistered(layout, "Invalid", short_name, object) self.draw_invalid_or_unregistered(layout, "Invalid", long_name, object)
if not object.name in objects_with_invalid_components: if not object.name in objects_with_invalid_components:
objects_with_invalid_components.append(object.name) objects_with_invalid_components.append(object.name)
if not short_name in invalid_component_names: if not long_name in invalid_component_names:
invalid_component_names.append(short_name) invalid_component_names.append(long_name)
comp_names.append(short_name) comp_names.append(long_name)
for custom_property in object.keys(): for custom_property in object.keys():
if custom_property != 'components_meta' and custom_property not in comp_names: if custom_property != 'components_meta' and custom_property not in comp_names:
@ -150,7 +151,7 @@ class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel):
if not object.name in objects_with_invalid_components: if not object.name in objects_with_invalid_components:
objects_with_invalid_components.append(object.name) objects_with_invalid_components.append(object.name)
if not short_name in invalid_component_names: if not long_name in invalid_component_names:
invalid_component_names.append(custom_property) invalid_component_names.append(custom_property)
layout.separator() layout.separator()
layout.separator() layout.separator()