feat(bevy_components):
* added tests * fixed a few issues * added small "attempt to fix" button for unit struct uis in case they are invalid * changed order of panels for clarity
This commit is contained in:
parent
50a7e138ec
commit
cf3c647afb
|
@ -16,7 +16,7 @@ from bpy.props import (StringProperty)
|
|||
|
||||
from .helpers import load_settings
|
||||
from .blueprints import CreateBlueprintOperator
|
||||
from .components.operators import CopyComponentOperator, OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, RenameHelper, Toggle_ComponentVisibility
|
||||
from .components.operators import CopyComponentOperator, Fix_Component_Operator, OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, RenameHelper, Toggle_ComponentVisibility
|
||||
|
||||
from .registry.registry import ComponentsRegistry,MissingBevyType
|
||||
from .registry.operators import (COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, OT_select_component_name_to_replace, OT_select_object, ReloadRegistryOperator, OT_OpenFilebrowser)
|
||||
|
@ -88,6 +88,7 @@ classes = [
|
|||
PasteComponentOperator,
|
||||
RemoveComponentOperator,
|
||||
RemoveComponentFromAllObjectsOperator,
|
||||
Fix_Component_Operator,
|
||||
OT_rename_component,
|
||||
RenameHelper,
|
||||
GenerateComponent_From_custom_property_Operator,
|
||||
|
@ -114,8 +115,8 @@ classes = [
|
|||
|
||||
BEVY_COMPONENTS_PT_MainPanel,
|
||||
BEVY_COMPONENTS_PT_ComponentsPanel,
|
||||
BEVY_COMPONENTS_PT_Configuration,
|
||||
BEVY_COMPONENTS_PT_AdvancedToolsPanel,
|
||||
BEVY_COMPONENTS_PT_Configuration,
|
||||
MISSING_TYPES_UL_List,
|
||||
BEVY_COMPONENTS_PT_MissingTypesPanel,
|
||||
|
||||
|
|
|
@ -237,6 +237,23 @@ def apply_propertyGroup_values_to_object_customProperties(object):
|
|||
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||
object[component_name] = value
|
||||
|
||||
# apply component value(s) to custom property of a single component
|
||||
def apply_propertyGroup_values_to_object_customProperties_for_component(object, component_name):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
print("yallah", component_name)
|
||||
(_, propertyGroup) = upsert_component_in_object(object, component_name, registry)
|
||||
component_definition = find_component_definition_from_short_name(component_name)
|
||||
if component_definition != None:
|
||||
print("merde")
|
||||
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||
object[component_name] = value
|
||||
|
||||
components_metadata = object.components_meta.components
|
||||
componentMeta = next(filter(lambda component: component["name"] == component_name, components_metadata), None)
|
||||
if componentMeta:
|
||||
print("here")
|
||||
componentMeta.invalid = False
|
||||
componentMeta.invalid_details = ""
|
||||
|
||||
|
||||
def apply_customProperty_values_to_object_propertyGroups(object):
|
||||
|
@ -258,6 +275,8 @@ def apply_customProperty_values_to_object_propertyGroups(object):
|
|||
object["__disable__update"] = True # disable update callback while we set the values of the propertyGroup "tree" (as a propertyGroup can contain other propertyGroups)
|
||||
property_group_value_from_custom_property_value(propertyGroup, component_definition, registry, customProperty_value)
|
||||
del object["__disable__update"]
|
||||
source_componentMeta.invalid = False
|
||||
source_componentMeta.invalid_details = ""
|
||||
|
||||
# removes the given component from the object: removes both the custom property and the matching metadata from the object
|
||||
def remove_component_from_object(object, component_name):
|
||||
|
|
|
@ -3,7 +3,7 @@ import json
|
|||
import bpy
|
||||
from bpy_types import Operator
|
||||
from bpy.props import (StringProperty)
|
||||
from .metadata import add_component_to_object, add_metadata_to_components_without_metadata, apply_customProperty_values_to_object_propertyGroups, copy_propertyGroup_values_to_another_object, find_component_definition_from_short_name, remove_component_from_object
|
||||
from .metadata import add_component_to_object, add_metadata_to_components_without_metadata, apply_customProperty_values_to_object_propertyGroups, apply_propertyGroup_values_to_object_customProperties_for_component, copy_propertyGroup_values_to_another_object, find_component_definition_from_short_name, remove_component_from_object
|
||||
|
||||
class AddComponentOperator(Operator):
|
||||
"""Add component to blueprint"""
|
||||
|
@ -173,7 +173,7 @@ class RenameHelper(bpy.types.PropertyGroup):
|
|||
|
||||
class OT_rename_component(Operator):
|
||||
"""Rename component"""
|
||||
bl_idname = "object.rename_component"
|
||||
bl_idname = "object.rename_bevy_component"
|
||||
bl_label = "rename component"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
|
@ -210,12 +210,7 @@ class OT_rename_component(Operator):
|
|||
for index, object_name in enumerate(target_objects):
|
||||
object = bpy.data.objects[object_name]
|
||||
if object and original_name in object:
|
||||
# get metadata
|
||||
components_metadata = getattr(object, "components_meta", None)
|
||||
component_meta = None
|
||||
if components_metadata:
|
||||
components_metadata = components_metadata.components
|
||||
component_meta = next(filter(lambda component: component["name"] == new_name, components_metadata), None)
|
||||
|
||||
# copy data to new component, remove the old one
|
||||
try:
|
||||
object[new_name] = object[original_name]
|
||||
|
@ -223,9 +218,14 @@ class OT_rename_component(Operator):
|
|||
except Exception as error:
|
||||
if '__disable__update' in object:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
if component_meta:
|
||||
component_meta.invalid = True
|
||||
component_meta.invalid_details = "unknow issue when renaming/transforming component, please remove it & add it back again"
|
||||
# get metadata
|
||||
components_metadata = getattr(object, "components_meta", None)
|
||||
if components_metadata:
|
||||
components_metadata = components_metadata.components
|
||||
component_meta = next(filter(lambda component: component["name"] == new_name, components_metadata), None)
|
||||
if component_meta:
|
||||
component_meta.invalid = True
|
||||
component_meta.invalid_details = "unknow issue when renaming/transforming component, please remove it & add it back again"
|
||||
|
||||
errors.append( "failed to copy old component value to new component: object: '" + object.name + "', error: " + str(error))
|
||||
|
||||
|
@ -237,16 +237,23 @@ class OT_rename_component(Operator):
|
|||
except Exception as error:
|
||||
if '__disable__update' in object:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
if component_meta:
|
||||
component_meta.invalid = True
|
||||
component_meta.invalid_details = "wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate ('Update UI from ...button')"
|
||||
components_metadata = getattr(object, "components_meta", None)
|
||||
if components_metadata:
|
||||
components_metadata = components_metadata.components
|
||||
component_meta = next(filter(lambda component: component["name"] == new_name, components_metadata), None)
|
||||
if component_meta:
|
||||
component_meta.invalid = True
|
||||
component_meta.invalid_details = "wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate"
|
||||
|
||||
errors.append( "wrong custom property values to generate target component: object: '" + object.name + "', error: " + str(error))
|
||||
|
||||
progress = index / total
|
||||
context.window_manager.components_rename_progress = progress
|
||||
# now force refresh the ui
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
|
||||
try:
|
||||
# now force refresh the ui
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
except: pass # this is to allow this to run in cli/headless mode
|
||||
|
||||
if len(errors) > 0:
|
||||
self.report({'ERROR'}, "Failed to rename component: Errors:" + str(errors))
|
||||
|
@ -289,6 +296,31 @@ class GenerateComponent_From_custom_property_Operator(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class Fix_Component_Operator(Operator):
|
||||
"""attempt to fix component"""
|
||||
bl_idname = "object.fix_bevy_component"
|
||||
bl_label = "Fix component (attempts to)"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
component_name: StringProperty(
|
||||
name="component name",
|
||||
description="component to fix",
|
||||
) # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
object = context.object
|
||||
error = False
|
||||
try:
|
||||
apply_propertyGroup_values_to_object_customProperties_for_component(object, self.component_name)
|
||||
except Exception as error:
|
||||
if "__disable__update" in object:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
error = True
|
||||
self.report({'ERROR'}, "Failed to fix component: Error:" + str(error))
|
||||
if not error:
|
||||
self.report({'INFO'}, "Sucessfully fixed component (please double check component & its custom property value)")
|
||||
return {'FINISHED'}
|
||||
|
||||
class Toggle_ComponentVisibility(Operator):
|
||||
"""toggles components visibility"""
|
||||
bl_idname = "object.toggle_bevy_component_visibility"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import json
|
||||
import bpy
|
||||
|
||||
from ..registry.operators import COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT
|
||||
from .metadata import do_object_custom_properties_have_missing_metadata
|
||||
from .operators import AddComponentOperator, CopyComponentOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, Toggle_ComponentVisibility
|
||||
from .operators import AddComponentOperator, CopyComponentOperator, Fix_Component_Operator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, Toggle_ComponentVisibility
|
||||
|
||||
def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
||||
is_enum = getattr(propertyGroup, "with_enum")
|
||||
|
@ -193,6 +195,16 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
|||
row.label(text=error_message)
|
||||
|
||||
# "footer" with additional controls
|
||||
if component_invalid:
|
||||
if root_propertyGroup_name:
|
||||
propertyGroup = getattr(component_meta, root_propertyGroup_name, None)
|
||||
if propertyGroup:
|
||||
unit_struct = len(propertyGroup.field_names) == 0
|
||||
if unit_struct:
|
||||
op = row.operator(Fix_Component_Operator.bl_idname, text="", icon="SHADERFX")
|
||||
op.component_name = component_name
|
||||
row.separator()
|
||||
|
||||
op = row.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
||||
op.component_name = component_name
|
||||
row.separator()
|
||||
|
|
|
@ -113,8 +113,10 @@ class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT(Operator):
|
|||
apply_customProperty_values_to_object_propertyGroups(object)
|
||||
progress = 0.5
|
||||
context.window_manager.components_from_custom_properties_progress = progress
|
||||
# now force refresh the ui
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
try:
|
||||
# now force refresh the ui
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
except:pass # ony run in ui
|
||||
|
||||
except Exception as error:
|
||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
import json
|
||||
import re
|
||||
import bpy
|
||||
import pprint
|
||||
import pytest
|
||||
|
||||
from .setup_data import setup_data
|
||||
|
||||
# small helpers
|
||||
def get_component_metadata(object, component_name):
|
||||
target_components_metadata = object.components_meta.components
|
||||
component_meta = next(filter(lambda component: component["name"] == component_name, target_components_metadata), None)
|
||||
return component_meta
|
||||
|
||||
def get_component_propGroup(registry, component_name, component_meta):
|
||||
# component_type = registry.short_names_to_long_names[component_name]
|
||||
# add_component_operator = bpy.ops.object.add_bevy_component
|
||||
property_group_name = registry.get_propertyGroupName_from_shortName(component_name)
|
||||
propertyGroup = getattr(component_meta, property_group_name, None)
|
||||
return propertyGroup
|
||||
|
||||
|
||||
def test_rename_component_single_unit_struct(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = setup_data["schema_path"]
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||
object = bpy.context.object
|
||||
|
||||
|
||||
source_component_name = "SomeOldUnitStruct"
|
||||
target_component_name = "UnitTest"
|
||||
object[source_component_name] = '()'
|
||||
|
||||
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||
|
||||
is_old_component_in_object = source_component_name in object
|
||||
is_new_component_in_object = target_component_name in object
|
||||
assert is_old_component_in_object == False
|
||||
assert is_new_component_in_object == True
|
||||
assert object[target_component_name] == '()'
|
||||
assert get_component_propGroup(registry, target_component_name, get_component_metadata(object, target_component_name)) != None
|
||||
|
||||
|
||||
def test_rename_component_single_complex_struct(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = setup_data["schema_path"]
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||
object = bpy.context.object
|
||||
|
||||
|
||||
source_component_name = "ProxyCollider"
|
||||
target_component_name = "Collider"
|
||||
object[source_component_name] = 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||
|
||||
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||
|
||||
is_old_component_in_object = source_component_name in object
|
||||
is_new_component_in_object = target_component_name in object
|
||||
assert is_old_component_in_object == False
|
||||
assert is_new_component_in_object == True
|
||||
assert object[target_component_name] == 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||
assert get_component_propGroup(registry, target_component_name, get_component_metadata(object, target_component_name)) != None
|
||||
|
||||
|
||||
def test_rename_component_single_error_handling(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = setup_data["schema_path"]
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||
object = bpy.context.object
|
||||
|
||||
|
||||
source_component_name = "SomeOldUnitStruct"
|
||||
target_component_name = "UnitTest"
|
||||
object[source_component_name] = 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||
|
||||
expected_error = 'Error: Failed to rename component: Errors:["wrong custom property values to generate target component: object: \'Cube\', error: input string too big for a unit struct"]\n'
|
||||
expected_error = re.escape(expected_error)
|
||||
with pytest.raises(Exception, match=expected_error):
|
||||
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||
|
||||
target_component_metadata = get_component_metadata(object, target_component_name)
|
||||
|
||||
is_old_component_in_object = source_component_name in object
|
||||
is_new_component_in_object = target_component_name in object
|
||||
assert is_old_component_in_object == False
|
||||
assert is_new_component_in_object == True
|
||||
assert object[target_component_name] == 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||
assert get_component_propGroup(registry, target_component_name, target_component_metadata) != None
|
||||
assert target_component_metadata.invalid == True
|
||||
|
||||
assert target_component_metadata.invalid_details == 'wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate'
|
||||
|
||||
|
||||
def test_rename_component_bulk(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = setup_data["schema_path"]
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||
|
||||
source_component_name = "SomeOldUnitStruct"
|
||||
target_component_name = "UnitTest"
|
||||
objects_names = []
|
||||
for object in bpy.data.objects:
|
||||
object[source_component_name] = '()'
|
||||
objects_names.append(object.name)
|
||||
|
||||
# bulk rename
|
||||
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps(objects_names))
|
||||
|
||||
for object in bpy.data.objects:
|
||||
is_old_component_in_object = source_component_name in object
|
||||
is_new_component_in_object = target_component_name in object
|
||||
assert is_old_component_in_object == False
|
||||
assert is_new_component_in_object == True
|
||||
assert object[target_component_name] == '()'
|
||||
assert get_component_propGroup(registry, target_component_name, get_component_metadata(object, target_component_name)) != None
|
||||
|
||||
|
||||
def test_rename_component_single_error_handling_clean_errors(setup_data):
|
||||
registry = bpy.context.window_manager.components_registry
|
||||
registry.schemaPath = setup_data["schema_path"]
|
||||
bpy.ops.object.reload_registry()
|
||||
|
||||
rename_component_operator = bpy.ops.object.rename_bevy_component
|
||||
object = bpy.context.object
|
||||
|
||||
|
||||
source_component_name = "SomeOldUnitStruct"
|
||||
target_component_name = "UnitTest"
|
||||
object[source_component_name] = 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||
|
||||
expected_error = 'Error: Failed to rename component: Errors:["wrong custom property values to generate target component: object: \'Cube\', error: input string too big for a unit struct"]\n'
|
||||
expected_error = re.escape(expected_error)
|
||||
with pytest.raises(Exception, match=expected_error):
|
||||
rename_component_operator(original_name=source_component_name, new_name=target_component_name, target_objects=json.dumps([object.name]))
|
||||
|
||||
target_component_metadata = get_component_metadata(object, target_component_name)
|
||||
|
||||
is_old_component_in_object = source_component_name in object
|
||||
is_new_component_in_object = target_component_name in object
|
||||
assert is_old_component_in_object == False
|
||||
assert is_new_component_in_object == True
|
||||
assert object[target_component_name] == 'Capsule(Vec3(x:1.0, y:2.0, z:0.0), Vec3(x:0.0, y:0.0, z:0.0), 3.0)'
|
||||
assert get_component_propGroup(registry, target_component_name, target_component_metadata) != None
|
||||
assert target_component_metadata.invalid == True
|
||||
|
||||
assert target_component_metadata.invalid_details == 'wrong custom property value, overwrite them by changing the values in the ui or change them & regenerate'
|
||||
|
||||
# if we fix the custom property value & regen the ui, it should be all good
|
||||
regen_component_operator = bpy.ops.object.refresh_ui_from_custom_properties_current
|
||||
object[target_component_name] = ''
|
||||
regen_component_operator()
|
||||
|
||||
assert target_component_metadata.invalid == False
|
Loading…
Reference in New Issue