From 1353e148024fa9c7332ba16feabf2c1342ee1035 Mon Sep 17 00:00:00 2001 From: Mark Moissette Date: Thu, 7 Mar 2024 16:29:04 +0100 Subject: [PATCH] feat(bevy_components): qol improvements (#164) * closes #163 * closes #153 * closes #154 * feat(bevy_components): added tools for diagnostics/ finding & replacing invalid & unregistered components * added ui for listing invalid & unregistered components * added boilerplate & functionality for component renaming/replacing * injection of invalid status & message in case the conversion did not work well * added deletion of components individual & bulk * added handling of wrong string for unit structs : allows detection of more wrong values for components * added progress bars for bulk operators * added docs for new features * added tests * added small "attempt to fix" button for unit struct uis in case they are invalid * feat(bevy_components): added progress indicators for from/to custom properties * various other minor ui tweaks for workflow improvement --- tools/bevy_components/README.md | 73 +++++- tools/bevy_components/__init__.py | 16 +- tools/bevy_components/components/metadata.py | 19 ++ tools/bevy_components/components/operators.py | 194 +++++++++++++++- tools/bevy_components/components/ui.py | 14 +- .../docs/component_remove_single.png | Bin 0 -> 2560 bytes .../docs/component_rename_object_select.png | Bin 0 -> 26721 bytes .../docs/component_rename_overview2.png | Bin 0 -> 18086 bytes .../docs/component_rename_remove_bulk.png | Bin 0 -> 2508 bytes .../docs/component_rename_remove_bulk2.png | Bin 0 -> 16120 bytes .../docs/component_rename_single.png | Bin 0 -> 2529 bytes tools/bevy_components/docs/other_options.png | Bin 8575 -> 14883 bytes .../propGroups/conversions_to_prop_group.py | 5 +- tools/bevy_components/registry/operators.py | 109 ++++++++- tools/bevy_components/registry/registry.py | 5 +- tools/bevy_components/registry/ui.py | 208 +++++++++++++++++- .../tests/test_rename_components.py | 159 +++++++++++++ 17 files changed, 759 insertions(+), 43 deletions(-) create mode 100644 tools/bevy_components/docs/component_remove_single.png create mode 100644 tools/bevy_components/docs/component_rename_object_select.png create mode 100644 tools/bevy_components/docs/component_rename_overview2.png create mode 100644 tools/bevy_components/docs/component_rename_remove_bulk.png create mode 100644 tools/bevy_components/docs/component_rename_remove_bulk2.png create mode 100644 tools/bevy_components/docs/component_rename_single.png create mode 100644 tools/bevy_components/tests/test_rename_components.py diff --git a/tools/bevy_components/README.md b/tools/bevy_components/README.md index d871271a..623712ff 100644 --- a/tools/bevy_components/README.md +++ b/tools/bevy_components/README.md @@ -57,6 +57,14 @@ Before you can use the add-on you need to configure it ![configuration 3](./docs/configuration3.png) + #### registry file polling + + + * by default, the add-on will check for changes in your registry file every second, and refresh the UI accordingly + * you can set the polling frequency or turn it off if you do not want auto-refresh + + ![registry file polling](./docs/registry_polling.png) + ## Use @@ -173,8 +181,8 @@ It will add the component to the select object ![invalid component](./docs/invalid_components.png) - > important ! ```gltf_auto_export``` currently has no way of filtering out components, so you need to delete invalid components like these before exporting - this will be adress in the future + > see [here](#invalidunregistered-type-renaming--conversion) for ways to convert invalid / unregistered components to other types. + - if you are encountering this type of view: don't panic your component data is not gone ! It just means you need to reload the registry data by clicking on the relevant button @@ -182,18 +190,65 @@ It will add the component to the select object -## advanced configuration +## Advanced Tools - ### registry file polling +In this section you will find various additional more advanced tooling +### Invalid/unregistered type renaming / conversion - * by default, the add-on will check for changes in your registry file every second, and refresh the UI accordingly - * you can set the polling frequency or turn it off if you do not want auto-refresh +If you have components that are + * invalid : ie some error was diagnosed + * unregistered: a custom property is present on the object, but there is no matching type in the registry + +Here you will get an overview, of ALL invalid and unregistered components in your Blender project, so you can find them, rename/convert them, +or delete them, also in bulk + +![component rename overview](./docs/component_rename_overview2.png) + +* you can click on the button to select the object in your outliner (this also works across scenes, so you will be taken to the scene where the +given object is located) + +![update custom properties](./docs/component_rename_object_select.png) + + +#### Single object component renaming/ conversion + + - to rename/convert a single component for a single object: + + * go to the row of the object you want to convert the component of + * in the dropdown menu, choose the target component + * click on the button with the magic wand to convert the component + + ![single rename](./docs/component_rename_single.png) + + > the tool will attempt to automatically convert the source component, including the field names/values, if the target component has the same ones + If it fails to do the conversion, you will get an error message, and you will either have to change the custom property yourself, or you can simply + change the values in the UI, which will automatically generate the custom property value + + - to delete a single component for a single object: + + * go to the row of the object you want to remove the component from + * click on the button with the "x" to remove the component + + ![single delete](./docs/component_remove_single.png) + +#### Bulk component renaming/ conversion + + - use this method if you want to convert ALL components of a given type of ALL objects + + * click on this button to pick your source component + + ![bulk convert remove](./docs/component_rename_remove_bulk.png) + + * for conversion: in the dropdown menu, choose the target component & click apply to convert all matching components + * for deletion: clic on the "x" to remove all matching components + + ![bulk convert remove](./docs/component_rename_remove_bulk2.png) - ![registry file polling](./docs/registry_polling.png) + ### For conversion between custom properties & components & vice-versa - ### regenerate custom property values + #### regenerate custom property values - "update custom properties of current object" : will go over **all components** that you have defined for the **currently selected object**, and re-generate the @@ -212,7 +267,7 @@ It will add the component to the select object You should also re-export your gltf files , otherwise you might run into issues - ### regenerate UI values + #### regenerate component/ UI values - since v0.2, you have the option to regenerate (for the selected object or all objects, as above) to regenerate your UI values from the custom property values diff --git a/tools/bevy_components/__init__.py b/tools/bevy_components/__init__.py index 8f41247a..48effb9f 100644 --- a/tools/bevy_components/__init__.py +++ b/tools/bevy_components/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "bevy_components", "author": "kaosigh", - "version": (0, 4, 0), + "version": (0, 4, 1), "blender": (3, 4, 0), "location": "VIEW_3D", "description": "UI to help create Bevy blueprints and components", @@ -16,11 +16,11 @@ from .helpers import load_settings from .blueprints import CreateBlueprintOperator -from .components.operators import CopyComponentOperator, RemoveComponentOperator, GenerateComponent_From_custom_property_Operator, PasteComponentOperator, AddComponentOperator, 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, ReloadRegistryOperator, OT_OpenFilebrowser) -from .registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List) +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) +from .registry.ui import (BEVY_COMPONENTS_PT_Configuration, BEVY_COMPONENTS_PT_AdvancedToolsPanel, BEVY_COMPONENTS_PT_MissingTypesPanel, MISSING_TYPES_UL_List) from .components.metadata import (ComponentMetadata, ComponentsMeta, ensure_metadata_for_all_objects) from .propGroups.prop_groups import (generate_propertyGroups_for_components) @@ -87,6 +87,10 @@ def draw(self, context): CopyComponentOperator, PasteComponentOperator, RemoveComponentOperator, + RemoveComponentFromAllObjectsOperator, + Fix_Component_Operator, + OT_rename_component, + RenameHelper, GenerateComponent_From_custom_property_Operator, Toggle_ComponentVisibility, @@ -105,9 +109,13 @@ def draw(self, context): COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, + + OT_select_object, + OT_select_component_name_to_replace, BEVY_COMPONENTS_PT_MainPanel, BEVY_COMPONENTS_PT_ComponentsPanel, + BEVY_COMPONENTS_PT_AdvancedToolsPanel, BEVY_COMPONENTS_PT_Configuration, MISSING_TYPES_UL_List, BEVY_COMPONENTS_PT_MissingTypesPanel, diff --git a/tools/bevy_components/components/metadata.py b/tools/bevy_components/components/metadata.py index f97a3421..3ba7deb1 100644 --- a/tools/bevy_components/components/metadata.py +++ b/tools/bevy_components/components/metadata.py @@ -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): diff --git a/tools/bevy_components/components/operators.py b/tools/bevy_components/components/operators.py index 13ddffc9..f640a98f 100644 --- a/tools/bevy_components/components/operators.py +++ b/tools/bevy_components/components/operators.py @@ -3,7 +3,7 @@ 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""" @@ -90,12 +90,10 @@ def execute(self, context): return {'FINISHED'} - - class RemoveComponentOperator(Operator): - """Delete component from blueprint""" + """Remove component from object""" bl_idname = "object.remove_bevy_component" - bl_label = "Delete component from blueprint Operator" + bl_label = "Remove component from object Operator" bl_options = {"UNDO"} component_name: StringProperty( @@ -103,11 +101,18 @@ class RemoveComponentOperator(Operator): description="component to delete", ) # type: ignore - def execute(self, context): - object = context.object + object_name: StringProperty( + name="object name", + description="object whose component to delete", + default="" + ) # type: ignore + def execute(self, context): + if self.object_name == "": + object = context.object + else: + object = bpy.data.objects[self.object_name] print("removing component ", self.component_name, "from object '"+object.name+"'") - if object is not None and self.component_name in object: remove_component_from_object(object, self.component_name) else: @@ -116,6 +121,154 @@ def execute(self, context): return {'FINISHED'} +class RemoveComponentFromAllObjectsOperator(Operator): + """Remove component from all object""" + bl_idname = "object.remove_bevy_component_all" + bl_label = "Remove component from all objects Operator" + bl_options = {"UNDO"} + + component_name: StringProperty( + name="component name", + description="component to delete", + ) # type: ignore + + @classmethod + def register(cls): + bpy.types.WindowManager.components_remove_progress = bpy.props.FloatProperty(default=-1.0) + + @classmethod + def unregister(cls): + del bpy.types.WindowManager.components_remove_progress + + def execute(self, context): + print("removing component ", self.component_name, "from all objects") + total = len(bpy.data.objects) + for index, object in enumerate(bpy.data.objects): + if len(object.keys()) > 0: + if object is not None and self.component_name in object: + remove_component_from_object(object, self.component_name) + + progress = index / total + context.window_manager.components_remove_progress = progress + # now force refresh the ui + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) + context.window_manager.components_remove_progress = -1.0 + + return {'FINISHED'} + + +class RenameHelper(bpy.types.PropertyGroup): + original_name: bpy.props.StringProperty(name="") # type: ignore + new_name: bpy.props.StringProperty(name="") # type: ignore + + #object: bpy.props.PointerProperty(type=bpy.types.Object) + @classmethod + def register(cls): + bpy.types.WindowManager.bevy_component_rename_helper = bpy.props.PointerProperty(type=RenameHelper) + + @classmethod + def unregister(cls): + # remove handlers & co + del bpy.types.WindowManager.bevy_component_rename_helper + +class OT_rename_component(Operator): + """Rename component""" + bl_idname = "object.rename_bevy_component" + bl_label = "rename component" + bl_options = {"UNDO"} + + original_name: bpy.props.StringProperty(default="") # type: ignore + new_name: StringProperty( + name="new_name", + description="new name of component", + ) # type: ignore + + target_objects: bpy.props.StringProperty() # type: ignore + + @classmethod + def register(cls): + bpy.types.WindowManager.components_rename_progress = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper) + + @classmethod + def unregister(cls): + del bpy.types.WindowManager.components_rename_progress + + def execute(self, context): + registry = context.window_manager.components_registry + type_infos = registry.type_infos + settings = context.window_manager.bevy_component_rename_helper + original_name = settings.original_name if self.original_name == "" else self.original_name + new_name = self.new_name + + + print("renaming components: original name", original_name, "new_name", self.new_name, "targets", self.target_objects) + target_objects = json.loads(self.target_objects) + errors = [] + total = len(target_objects) + + if original_name != '' and new_name != '' and original_name != new_name and len(target_objects) > 0: + for index, object_name in enumerate(target_objects): + object = bpy.data.objects[object_name] + if object and original_name in object: + + # copy data to new component, remove the old one + try: + object[new_name] = object[original_name] + remove_component_from_object(object, original_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 + # 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)) + + try: + # attempt conversion + long_name = registry.short_names_to_long_names[new_name] + component_definition = type_infos[long_name] + add_component_to_object(object, component_definition, object[new_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 + 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 + + 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)) + else: + self.report({'INFO'}, "Sucessfully renamed component") + + #clear data after we are done + self.original_name = "" + context.window_manager.bevy_component_rename_helper.original_name = "" + context.window_manager.components_rename_progress = -1.0 + + + return {'FINISHED'} + + class GenerateComponent_From_custom_property_Operator(Operator): """generate components from custom property""" bl_idname = "object.generate_bevy_component_from_custom_property" @@ -143,6 +296,31 @@ def execute(self, context): 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" diff --git a/tools/bevy_components/components/ui.py b/tools/bevy_components/components/ui.py index 23e8fe38..4a356eb4 100644 --- a/tools/bevy_components/components/ui.py +++ b/tools/bevy_components/components/ui.py @@ -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 @@ def draw(self, context): 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() diff --git a/tools/bevy_components/docs/component_remove_single.png b/tools/bevy_components/docs/component_remove_single.png new file mode 100644 index 0000000000000000000000000000000000000000..30814d6f453ddca43873ba9e43d06ca865b2d47d GIT binary patch literal 2560 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 literal 0 HcmV?d00001 diff --git a/tools/bevy_components/docs/component_rename_overview2.png b/tools/bevy_components/docs/component_rename_overview2.png new file mode 100644 index 0000000000000000000000000000000000000000..bc994b4ab3552635bab6504d58272565e2121162 GIT binary patch literal 18086 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 U^|Jb47ytkO07*qoM6N<$f}lNZB>(^b literal 0 HcmV?d00001 diff --git a/tools/bevy_components/docs/component_rename_remove_bulk.png b/tools/bevy_components/docs/component_rename_remove_bulk.png new file mode 100644 index 0000000000000000000000000000000000000000..e4b91b27020a146c2a7a8f3b9579861daebef9ab GIT binary patch literal 2508 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 rBXge<^=Y&0vye@Zm2pBsHrdJEdw;L@ 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%0A5dZ4U#fIjBqQf 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 R!mmD2igGHlA7l*u{||bSs>1*P literal 8575 zcmY*`R zOTOg$=gyru=iZr2PBOX44N+2%!ohrt2><|aq`!!(001b!fBUglDF45EB#o`0*nc47R7IkEWm~AR+mUhbJQ=BPuE?At50r zCnqH(rL3&1sHjLrMm9V=tgWrh%F3#spkQHPK~GQbSloS~msi&t`Sy>qy8(UOVL`_XyQ&VGWYwPdt-_z5R zot^FC;*yY%U}$Lg`}gnE)YPD$pt`!c@$qpZBO@OlpOBD{xw*N~(b0i{fwi@@ot>Sm ztSoPD?~aa+rKP3({QR!2u9A`xM@Pr>^z^@f|F#eic+%2((a^Nw<0BA=>+9=4VJS0F zaSb-MB}~jdJiNqv04e}L#UU*YQFB{5Ts~1<;v@l{{+yJ};I#=)WlW8PK!DRIjhl9# zP3>~yB6v4nUJT}xP(9&~{wwABotdgC?r*)PZH+&RsQ`8o+_~!nBE(zR*EzZenEoqMRC4IXX`dc9XzH4Rl0lDa33YORetj*_GU0aP#vQONo05OqX_lNMHUA zuAmo+Mj52e&5TFsww%8ptUiwoLS<4p{<80=Li~nqg|zjU?V2p-R;O4EcLz{oDy)}x zwLPE8_1b>LZl%i3pn9iBh)n%{I6*gp7~a1kTLR38d#{rhzeM6o@PM1NLQeUMuT3;{ zC8}RMfTT;7mi(w6CJNYjGpC2bdcA4r%e9kc8$P#?i488SKWRGi1Dwn7Fa6J=kGQKM z53+!XsesW|{(WwMEK@%+(c`gSi|>Kajwp+4Wc zN56LJvfswq_&!K;wRFdW{&_$u^u=mSWE%7d3^5`HrGWYmY{AH7xL~)7pK>hMr?>xnp4@?VDW*59-nzHcIuCHP znzt>ffmZxDGkHHb!8!@UN)8GgME$kNoU!wRIUq45{7eUSC}76(vDs&x0@>DZvPaW- z?bHMkOH?R65Pw1%GN)9tu3~c&< z-`u3#1xe>d5LWe`T*AS#V;SikBV)brsqMJ4YzaJXa+Hhx){`~{{e8k3bQ?FGSi z9JmwifyPX=hnvXe=`5i^~n8ZUJ+rogo-WX0S0o3cPz&`UB@}5Y~e!CGA#x!gG8=ft@#s z@Su#=uOUt*$4)Z9Z~>n*+HIE9DIh7ABu^jwZp-&{DH>NlBiulk7%?Kb3&%S13Y+(D zd21`RZRC+N`+k@ntL0&kDnH6-Nm&i^@Rb(t4#-Ct@;D}5fMftx?u#;t(c{ShOSvTf zFjd#9xDeAlD&sJ=znEzV$*kZobGXyZ5DE?Dm24CKZ1cYQyZ7Y0_xuYWp><65guIB7p@(h!C$}&;pL$Uqt)C;|o(ZuvK5AOYz zy&lf)DvmHskpfM;ZD@Zb{BY0AHwX}3hEI#y_5;UJZ*C+oBEgcqsM~)=gBi3bIdf9$ zCyZoF$hZoKJ`%QwpwfL+F`vPUdW{*(%x^($h;Nm%tyCUieK_wOowWytK_aKlHcj;6eaiAcedi*8MZ4}(GPRcwPH}<``+M<@RN`WQ zpzdjbSj;ZqP=a0{^q~#L^TWTf>gBMS#~p!!5XZ3t%Y_U1)HIgka5aovDbe(P1VmDX zsDlR~4&EgNKnLNq@*7t|v2+qID&d)e5!Qpa+^8^4fp?ewTL!|EgQ3C041=N88)ICj zkU_Ke#f)K}ZvEUSMp!0r+7ji|U=aE?QioT!Sqao{2@F4yZ%-k6LJ=r4tC*z|i9JF_h1twcRgAfI`RmvMo*krcdHoZFa7j9jP$7O9wKumbydE{}5L$wZn zHfZCvdMn33X-3_G_$fzIqPx4~5(^mLGfx;rxd`v&jRVncyQE9o?1b??9UZp{7Y);H zTIf~(nVZ(q6Z7kvc2c8n zjPuo0s^43S-C0|;d9mCsE~Lx6u8|@=VdQGx^8(}$=?|`+-J*Nhz1N8ek7Q`Il2Wmy zkS=T=Lpv{5c0m$$qZOc&+jszYQTaJk*XOo~?W2*S*$U4j^8Vc5Uo_h%-t@9_%;$9$ zj@FjACZMc}7VQl;4F9Ycs7ajk75P23kUvO(vM<)@FfVF*@_kvV6E{9r(B~imw`v|| z*MFT-NhJAojOng5+!g>>nFJR}*el3lpSZ2P_r^sDcsU94xvuH45n?PPBgL`JlSst; z9ayK1DdS9urn&UJhh{jP`<_Z{KqZZ0D)>OX59dUM`oiOH(XK)Tky6(_Lo}a?nxtBn zY<(c1iNnaA72+|NvNKUHQ6LQ9jCZl_Dtt-;Lno&RsMg9DfZ^@h#Sy&+hRp* zbtZVEU%ITxYRMiSj|1BYmOw%DV-B58{(0%^pyxWq)xOMw8mW7VC11+N%QyDp2@)|6 zc`V~!Mb$}j%ic9}c@q{)9rf`ww0bB9tfiq!RlE4>=dsG24|OPsw#CUxpaaay|H$ha zIhLX-XcVO{cewQ3Owc>7svNWT6iqYyc`jZjuAgvs;TU~I|L#Rk;wHY6Sb=IO;$^4y z#r8!n(DPZ9bQMd7t#0uPk~AIkW_wO1@;Mr}Me2@t+q$>2^_cSIrjq?WO89=g))mNx z4dWyU>Ln@oHKWP#^wQ}>WsOabT~-&^HZdhD8cdZm@zB}&9q4W5eZyZUy1Lq^uv$*R zVmM+;sdg8I6yRwHGQ;$Aa%j^XFTnl=!cV~U(I;&v^6KQ!t{g6%WC&tx<#T@*maH?h z1Es2~D@em?W(#Kfl-y<&FxS!*E-Y$ZjurlZWW=(QihR;6_Z;X^?aIrB(+eQ$AIooK zXX1hc%e7Gd{?%JB1FOkF2@Gkxe(-&xO2DWGbdL6?oj9rEI4xwMbH}S!8}8e90w~K0 z#NMFvl|T4I=ZBJ0Sz_oh`jG<;0a=;Yr-A^`k>3^(wd1$uXMe=L;~!jmqS1EP6pPi zKhIARx$y3zHo{JI=W!5T5sH8kj9lcnf^L3K+l}u&$vRwZK}Kaf$J1Gsvglw@@a@u-Hk$r2V%vcd0XZ=!2U z^3T7(j4F7a<+KqyUe4Q=BQu+#5t7yJF|ZQ2tYDQaN4{Kig9u(#xQMN~^ zyS4Iok{lqQB~NJc+=YUtQiH1>S0gVM zLfQj%@$Qqu-HvDPA`20R_d{PrUhSJfXsgnxfLn$b}$eQ`^pUm!gX5>;${ z)M#5nNOIGw)MshA-F1vxh=&TKQn$`mMGhN7NY;qo|IWB88hmH|I;bV7 z@!vDLyO$T*)#=vW@70MP-Y2L@&$q8Ee_Sn`*iTLj^Z09+xyH;e+k4U7E!+;ZG^>}n zI=u)3T{p|Gy=<^P@RL&V7x9SX^c04k?-HmDL@isaT{A^hVw+ z!tC^8C@y@e4fUVCB*-Na-kMQ|CXmYp8Nyti3cglY=k?~&=Q zcgE3HMOWT@7sRi+5Q$EpxvKaZdI;gGT%Z{7$^QS6yDOehe&qsEVL8lR3j){1;quw| z*~I+>y8V8+$IKgGKy4GtszQMaXi!QqEwvU8XYa~yXzUJYYV`HqZu|9;zlv+=T9M>P zZNFE<9OOm&VLq!b#F&ixfX_2;nV+PyLpS|vA+P@Mq3mGR)BT?gDFhUK8*a98sUGIA zlC-X@@|L!_7i#;tSWA=Us=;l9w8WHBwdt8YMDJl6t|3Od0V-%?Qc@NhMxFK8`xuB? z{zosNozak@fjf>_?T?OpDUUZjnayjdCy#2cV{fULVojD>&1WlpzFcYQ1qzm+XQt)n zIB{5yQ^G@G9*%-}P+;9J3@#hR8oMf*Fcv+{(`*jfi!`-k>lyailyul)lWQ`Use=8m zhO}aFl}EUTy*aIPMan|w?UhLYv>n{;8w6)h3#tm=ABSP~WH~K+>~HjxgYH9{qT+MXOJ?jOGtEquvfZyo%T&Bm07xp?1npa2)hKB-)pa@MCSh-c26XgU}6 z*NUy@sHcbNn$140j(_=i6T^xmOCX+LS!5plb&IL}){>P)CWDqhOc{yCWWgo5yLG~I zNacT&GX|pS>~^xNlkKgP z9|pu(E8FQr-i%|Z7A`dQA?435UW=H|WqlAL++Poj;mH#t50mpGScN#Bn1N1oDqz4mvkH>){ zAq&jq?Q{lTsaM$ZLJ78t;t$Q1Z8cGvJ|iu63fS2gX76c|% zlPiQ%e%_&}yMy{HQhxppX>l((C0i*vB=c)YJMZK;wGZI}7JijyD|AYgo1EKHQtZh- z{)uAoabqQNL>gktRNG&^ zfg#t{L`s8l?h%pq?0qe_qZn#E8d_QWR&-fk7h-8$XRn2TcP~->PCxw$BWOOZm;*R= zJaH!oqMp09qlImkg1_U#(T96ai@j}wb^q=&egtY{rQp70_h_Fg9!1)~X&)_4aqyJv z?BdV69$;Syc>BmA4|bzKC{z-UOjqbTOq;iv zuxn}RoXl>%K6xJWgN^hfpYeR@ifruJg!$7FFic%pJDNC72F>HvRt6MKL0Xcsj%5VrO zK*Ov@pOn*o_qk%qylkGUW%ftkRIqkNDIZR(Q?7aPdw@UB(QoccMi7l^4j&PUcAF(1 z(CH4}=_YWQ^=!ky5p4%`995_0H{g&C5^E*bLx+N;5(UmP`La&LP8s&2>2E|Xdy|}T z=5@XpvLW_Q)dDuQywC)IObb>YwsXa}ns}KWOCkp%x`9Z%ntO%QcyXfMfV=6l^u5{g zMr3z2XH7d@lF^%Cd^@LqQvWv!vVuu@4NzM_s%hI+hdGE9o*}=b(xE8cF<;-;1RL6O zeFPeU$K+Y~KPmd4Th+~Mcj<>MCO^ZYaS7$r&PtwC%+m2~MgI7k)Uwc_i3-}IYKmS| zDGW83ptDn^ZsoNhro_yWVZ9E39~_?ew?VPotIpkMt;5vnL~HS#Z$K4g2m=+?<>zT} zSQ#HhGS8rXH&2gCvt}Zi>%U2LZl>BLckqpC(NRe8zCAylrd6Z#jL?l@tULL>Q|9k=owoqL=>;6vXv!^5}vcpkjKO|Y&>+v17Vw`5uzq)<_3 z+E+h*pa$f8d3y0Je0V*l!ggKI9^IyZ4u%U&3XCmpMhB=u|A7-M+KX^N3@^Bd@O{$!#m_T|Hvpr5q>saJfGE|u zK1drT??Uh?)0DsvDoiZnEptbPM+@Ma1sejvdqa_q-`sOYA;pT!nJ`f-rV7U_kb~V5 zc3{xIXm_P09bp=MeQy8u5w4^}L)K|W%MdY|x^HL4jk!f~UZt8`S?8*id-(X|)mfX-YF~r1XmZ zc^qdl$H}HPTcXwPow`-iLKhxs#LDwX)zd2JW*6)q4OqOU$M4uU@(yKbq~_7RtHT6Sj%D0%K! zm92A+D@j})<M!lZ_rUiZ(vU`gqaK4|_7( zDdNphj**tDx1N-T69bCQt)&IwB_`rqN?4P@-m$`kYVSqB-Nz3+B5K}UtR%eGlNT|` zpros`Dq$}Q!H&K4p9M-X`S@o;Ey;Kl=S3HRXFm8QXTMKN!V;PG>n1q+Uqv4Sq`ZxN zsF|%8czfzPKLk_QGlndzO!xaGlWD3sGhi$=lCRV-%S($!QKu7V*m3*y$UCWab@$X~ zWM}WXBcJHMHoo{g3+$V9NFti7#rnv$$0f8s&^5TBIhB3{L7THtUNF{Wi+}>(kn7TSB z(0+b!LE89~0`{F-*l+z(t##eRb@ZKs5P% zMDAtMbC`_Go1!eJ5@KGt>GXS+Hs;7#b&ZY4!ms6QOX`Q`m7CT{&hifOn6aY^8U#Mw zq%YC$h2wlgeZWKdUT^jKOVbRvX&v5`bE+_JKILLJl9Dfrt{dknt6?sY*q?1j^Faaa zKPpI+OD4}Sbg)Z(^n1c9?f(^an{jAT9hkgN@hL-f?k5}u<(B(SgGe8wNUP#2HPqeb znBW94n(@727Dvaw@_Z9EBY zuCmNr=dI`_Q3~yb-FuAaDnZ3b;-2S@V0_4L*7j9=jVu>!D&35@C=p!hqM_>w{*eY4 zX<8khM0~!iSj^Tn4wPyX3w|y3<)p0{6Exu3dQp4jg8L{YLdl2yQ zy~K;|w?k8qV3H#{0k_il14YvCgFkT53*GKavd_F52~f$!&K7Wd$2BG(N2J3O%_aXN4`Jke^W&0$SV}E;c$3 za*-Z&P61hPh_VR}%4Dco6DL$29?kh@ZBa={f z-#b=#bG7K40JjJamzw%xU1HlpVSu}rD_<0w*m-Z%(QXw)@ef#9G=%#%3H=8sj7^;X z#d7nF2?e)5*Ze!V+4ccK0N3kuEBwgw-=+Ksb#05*L}4eH!2cZdnjSC6NC);ql>{v`2Pr4rLr^*9lEweBYzKABZ;rv2#*d} z$AW;~xMk$W|Epkk+H;Lxr{8t!mO+_&d)wM8{d{U7?7zjS&)csk;EVUoTaZQN)Qh~c zNE;B!igHm);Lb7k-AD4SqbbdME#di!=|-q3trtn}%L`d#dnd)D6bgC##io1RWpgot zV(OnaTKnv=LHmXO7e-b>6A9`E3n9Q{XiR@=lgP$1wUV4JHa3NyLm#Oi;EJ#IB5M%f zq{0o+MfkJfAvviKR-C^NmPH`>sfT|mI3KgVe#Z>^#xVgLAo};Q0w67+AYLJ6_~ZWo D0-nYd diff --git a/tools/bevy_components/propGroups/conversions_to_prop_group.py b/tools/bevy_components/propGroups/conversions_to_prop_group.py index 6eec325b..84d10e2b 100644 --- a/tools/bevy_components/propGroups/conversions_to_prop_group.py +++ b/tools/bevy_components/propGroups/conversions_to_prop_group.py @@ -224,8 +224,9 @@ def property_group_value_from_custom_property_value(property_group, definition, else: - pass - #print("struct with zero fields") + if len(value) > 2: #a unit struct should be two chars long :() + #print("struct with zero fields") + raise Exception("input string too big for a unit struct") elif type_info == "Tuple": custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 1) diff --git a/tools/bevy_components/registry/operators.py b/tools/bevy_components/registry/operators.py index 8abee81f..8d844131 100644 --- a/tools/bevy_components/registry/operators.py +++ b/tools/bevy_components/registry/operators.py @@ -42,11 +42,26 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL(Operator): bl_label = "Apply Registry to all objects" bl_options = {"UNDO"} + @classmethod + def register(cls): + bpy.types.WindowManager.custom_properties_from_components_progress_all = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper) + + @classmethod + def unregister(cls): + del bpy.types.WindowManager.custom_properties_from_components_progress_all + def execute(self, context): print("apply registry to all") #context.window_manager.components_registry.load_schema() - for object in bpy.data.objects: + total = len(bpy.data.objects) + + for index, object in enumerate(bpy.data.objects): apply_propertyGroup_values_to_object_customProperties(object) + progress = index / total + context.window_manager.custom_properties_from_components_progress_all = progress + # now force refresh the ui + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) + context.window_manager.custom_properties_from_components_progress_all = -1.0 return {'FINISHED'} @@ -56,10 +71,23 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT(Operator): bl_label = "Apply Registry to current object" bl_options = {"UNDO"} + @classmethod + def register(cls): + bpy.types.WindowManager.custom_properties_from_components_progress = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper) + + @classmethod + def unregister(cls): + del bpy.types.WindowManager.custom_properties_from_components_progress + def execute(self, context): print("apply registry to current object") object = context.object + context.window_manager.custom_properties_from_components_progress = 0.5 + # now force refresh the ui + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) apply_propertyGroup_values_to_object_customProperties(object) + + context.window_manager.custom_properties_from_components_progress = -1.0 return {'FINISHED'} @@ -69,44 +97,79 @@ class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT(Operator): bl_label = "Apply custom_properties to current object" bl_options = {"UNDO"} + @classmethod + def register(cls): + bpy.types.WindowManager.components_from_custom_properties_progress = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper) + + @classmethod + def unregister(cls): + del bpy.types.WindowManager.components_from_custom_properties_progress + def execute(self, context): print("apply custom properties to current object") object = context.object error = False try: apply_customProperty_values_to_object_propertyGroups(object) + progress = 0.5 + context.window_manager.components_from_custom_properties_progress = progress + 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 error = True self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error)) if not error: self.report({'INFO'}, "Sucessfully generated UI values for custom properties for selected object") + context.window_manager.components_from_custom_properties_progress = -1.0 return {'FINISHED'} + class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator): """Update UI values from custom properties to ALL object""" bl_idname = "object.refresh_ui_from_custom_properties_all" bl_label = "Apply custom_properties to all objects" bl_options = {"UNDO"} + @classmethod + def register(cls): + bpy.types.WindowManager.components_from_custom_properties_progress_all = bpy.props.FloatProperty(default=-1.0) #bpy.props.PointerProperty(type=RenameHelper) + + @classmethod + def unregister(cls): + del bpy.types.WindowManager.components_from_custom_properties_progress_all + def execute(self, context): print("apply custom properties to all object") bpy.context.window_manager.components_registry.disable_all_object_updates = True errors = [] - for object in bpy.data.objects: + total = len(bpy.data.objects) + + for index, object in enumerate(bpy.data.objects): + try: apply_customProperty_values_to_object_propertyGroups(object) except Exception as error: del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure errors.append( "object: '" + object.name + "', error: " + str(error)) + + progress = index / total + context.window_manager.components_from_custom_properties_progress_all = progress + # now force refresh the ui + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) + + + if len(errors) > 0: self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Errors:" + str(errors)) else: self.report({'INFO'}, "Sucessfully generated UI values for custom properties for all objects") bpy.context.window_manager.components_registry.disable_all_object_updates = False - - + context.window_manager.components_from_custom_properties_progress_all = -1.0 return {'FINISHED'} class OT_OpenFilebrowser(Operator, ImportHelper): @@ -133,3 +196,41 @@ def execute(self, context): return {'FINISHED'} + +class OT_select_object(Operator): + """Select object by name""" + bl_idname = "object.select" + bl_label = "Select object" + bl_options = {"UNDO"} + + object_name: StringProperty( + name="object_name", + description="object to select's name ", + ) # type: ignore + + def execute(self, context): + if self.object_name: + object = bpy.data.objects[self.object_name] + scenes_of_object = list(object.users_scene) + if len(scenes_of_object) > 0: + bpy.ops.object.select_all(action='DESELECT') + bpy.context.window.scene = scenes_of_object[0] + object.select_set(True) + bpy.context.view_layer.objects.active = object + return {'FINISHED'} + +class OT_select_component_name_to_replace(Operator): + """Select component name to replace""" + bl_idname = "object.select_component_name_to_replace" + bl_label = "Select component name for bulk replace" + bl_options = {"UNDO"} + + component_name: StringProperty( + name="component_name", + description="component name to replace", + ) # type: ignore + + def execute(self, context): + context.window_manager.bevy_component_rename_helper.original_name = self.component_name + return {'FINISHED'} + \ No newline at end of file diff --git a/tools/bevy_components/registry/registry.py b/tools/bevy_components/registry/registry.py index 1299130c..20eb14a4 100644 --- a/tools/bevy_components/registry/registry.py +++ b/tools/bevy_components/registry/registry.py @@ -241,9 +241,6 @@ def unregister(cls): del bpy.types.WindowManager.components_registry - - - def load_schema(self): print("load schema", self) # cleanup previous data if any @@ -352,9 +349,9 @@ def generate_propGroup_name(self, nesting, shortName): return propGroupName def get_propertyGroupName_from_shortName(self, shortName): - return self.short_names_to_propgroup_names.get(shortName, None) + ########### """ object[component_definition.name] = 0.5 diff --git a/tools/bevy_components/registry/ui.py b/tools/bevy_components/registry/ui.py index 18ff21a6..9df1ab09 100644 --- a/tools/bevy_components/registry/ui.py +++ b/tools/bevy_components/registry/ui.py @@ -1,9 +1,15 @@ +import json import bpy from bpy_types import (UIList) +from bpy.props import (StringProperty) + +from ..components.operators import OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator from .operators import( COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT, - OT_OpenFilebrowser, ReloadRegistryOperator, + OT_OpenFilebrowser, + OT_select_component_name_to_replace, + OT_select_object, ReloadRegistryOperator, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL, COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT) @@ -21,8 +27,7 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel): def draw(self, context): layout = self.layout registry = context.window_manager.components_registry - registry_has_type_infos = registry.has_type_infos() - selected_object = context.selected_objects[0] if len(context.selected_objects) > 0 else None + row = layout.row() col = row.column() @@ -43,31 +48,212 @@ def draw(self, context): layout.separator() layout.separator() + +class BEVY_COMPONENTS_PT_AdvancedToolsPanel(bpy.types.Panel): + """panel listing all the missing bevy types in the schema""" + bl_idname = "BEVY_COMPONENTS_PT_AdvancedToolsPanel" + bl_label = "Advanced tools" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Bevy Components" + bl_context = "objectmode" + bl_parent_id = "BEVY_COMPONENTS_PT_MainPanel" + bl_options = {'DEFAULT_CLOSED'} + bl_description = "advanced tooling" + + + def draw_invalid_or_unregistered_header(self, layout, items): + row = layout.row() + + for item in items: + col = row.column() + col.label(text=item) + + + def draw_invalid_or_unregistered(self, layout, status, component_name, object): + available_components = bpy.context.window_manager.components_list + registry = bpy.context.window_manager.components_registry + registry_has_type_infos = registry.has_type_infos() + + row = layout.row() + + col = row.column() + col.label(text=component_name) + + col = row.column() + operator = col.operator(OT_select_object.bl_idname, text=object.name) + operator.object_name = object.name + + col = row.column() + col.label(text=status) + + col = row.column() + col.prop(available_components, "list", text="") + + col = row.column() + operator = col.operator(OT_rename_component.bl_idname, text="", icon="SHADERFX") #rename + new_name = registry.type_infos[available_components.list]['short_name'] if available_components.list in registry.type_infos else "" + operator.original_name = component_name + operator.target_objects = json.dumps([object.name]) + operator.new_name = new_name + col.enabled = registry_has_type_infos and component_name != "" and component_name != new_name + + + col = row.column() + operator = col.operator(RemoveComponentOperator.bl_idname, text="", icon="X") + operator.object_name = object.name + operator.component_name = component_name + + col = row.column() + col = row.column() + operator = col.operator(OT_select_component_name_to_replace.bl_idname, text="", icon="EYEDROPPER") #text="select for rename", + operator.component_name = component_name + + def draw(self, context): + layout = self.layout + registry = bpy.context.window_manager.components_registry + registry_has_type_infos = registry.has_type_infos() + selected_object = context.selected_objects[0] if len(context.selected_objects) > 0 else None + available_components = bpy.context.window_manager.components_list + + row = layout.row() + box= row.box() + box.label(text="Invalid/ unregistered components") + + objects_with_invalid_components = [] + invalid_component_names = [] + + self.draw_invalid_or_unregistered_header(layout, ["Component", "Object", "Status", "Target"]) + + for object in bpy.data.objects: # TODO: very inneficent + if len(object.keys()) > 0: + if "components_meta" in object: + components_metadata = object.components_meta.components + comp_names = [] + for index, component_meta in enumerate(components_metadata): + short_name = component_meta.name + if component_meta.invalid: + self.draw_invalid_or_unregistered(layout, "Invalid", short_name, object) + + if not object.name in objects_with_invalid_components: + objects_with_invalid_components.append(object.name) + + if not short_name in invalid_component_names: + invalid_component_names.append(short_name) + + + comp_names.append(short_name) + + for custom_property in object.keys(): + if custom_property != 'components_meta' and custom_property not in comp_names: + self.draw_invalid_or_unregistered(layout, "Unregistered", custom_property, object) + + if not object.name in objects_with_invalid_components: + objects_with_invalid_components.append(object.name) + if not short_name in invalid_component_names: + invalid_component_names.append(custom_property) + layout.separator() + layout.separator() + original_name = bpy.context.window_manager.bevy_component_rename_helper.original_name + + row = layout.row() + col = row.column() + col.label(text="Original") + col = row.column() + col.label(text="New") + col = row.column() + col.label(text="------") + + row = layout.row() + col = row.column() + box = col.box() + box.label(text=original_name) + + col = row.column() + col.prop(available_components, "list", text="") + #row.prop(available_components, "filter",text="Filter") + + col = row.column() + components_rename_progress = context.window_manager.components_rename_progress + + if components_rename_progress == -1.0: + operator = col.operator(OT_rename_component.bl_idname, text="apply", icon="SHADERFX") + operator.target_objects = json.dumps(objects_with_invalid_components) + new_name = registry.type_infos[available_components.list]['short_name'] if available_components.list in registry.type_infos else "" + operator.new_name = new_name + col.enabled = registry_has_type_infos and original_name != "" and original_name != new_name + else: + if hasattr(layout,"progress") : # only for Blender > 4.0 + col.progress(factor = components_rename_progress, text=f"updating {components_rename_progress * 100.0:.2f}%") + + col = row.column() + remove_components_progress = context.window_manager.components_remove_progress + if remove_components_progress == -1.0: + operator = row.operator(RemoveComponentFromAllObjectsOperator.bl_idname, text="", icon="X") + operator.component_name = context.window_manager.bevy_component_rename_helper.original_name + col.enabled = registry_has_type_infos and original_name != "" + else: + if hasattr(layout,"progress") : # only for Blender > 4.0 + col.progress(factor = remove_components_progress, text=f"updating {remove_components_progress * 100.0:.2f}%") + + layout.separator() + layout.separator() + row = layout.row() + box= row.box() + box.label(text="Conversions between custom properties and components & vice-versa") + row = layout.row() row.label(text="WARNING ! The following operations will overwrite your existing custom properties if they have matching types on the bevy side !") row.alert = True + ## row = layout.row() - row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="LOOP_FORWARDS") - row.enabled = registry_has_type_infos and selected_object is not None + custom_properties_from_components_progress_current = context.window_manager.custom_properties_from_components_progress + + if custom_properties_from_components_progress_current == -1.0: + row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="LOOP_FORWARDS") + row.enabled = registry_has_type_infos and selected_object is not None + else: + if hasattr(layout,"progress") : # only for Blender > 4.0 + layout.progress(factor = custom_properties_from_components_progress_current, text=f"updating {custom_properties_from_components_progress_current * 100.0:.2f}%") layout.separator() row = layout.row() - row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="LOOP_FORWARDS") - row.enabled = registry_has_type_infos + custom_properties_from_components_progress_all = context.window_manager.custom_properties_from_components_progress_all + + if custom_properties_from_components_progress_all == -1.0: + row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="LOOP_FORWARDS") + row.enabled = registry_has_type_infos + else: + if hasattr(layout,"progress") : # only for Blender > 4.0 + layout.progress(factor = custom_properties_from_components_progress_all, text=f"updating {custom_properties_from_components_progress_all * 100.0:.2f}%") + + ######################## row = layout.row() row.label(text="WARNING ! The following operations will try to overwrite your existing ui values if they have matching types on the bevy side !") row.alert = True + components_from_custom_properties_progress_current = context.window_manager.components_from_custom_properties_progress + row = layout.row() - row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update UI FROM custom properties of current object" , icon="LOOP_BACK") - row.enabled = registry_has_type_infos and selected_object is not None + if components_from_custom_properties_progress_current == -1.0: + row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update UI FROM custom properties of current object" , icon="LOOP_BACK") + row.enabled = registry_has_type_infos and selected_object is not None + else: + if hasattr(layout,"progress") : # only for Blender > 4.0 + layout.progress(factor = components_from_custom_properties_progress_current, text=f"updating {components_from_custom_properties_progress_current * 100.0:.2f}%") layout.separator() row = layout.row() - row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL.bl_idname, text="update UI FROM custom properties of ALL objects" , icon="LOOP_BACK") - row.enabled = registry_has_type_infos + components_from_custom_properties_progress_all = context.window_manager.components_from_custom_properties_progress_all + + if components_from_custom_properties_progress_all == -1.0: + row.operator(COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL.bl_idname, text="update UI FROM custom properties of ALL objects" , icon="LOOP_BACK") + row.enabled = registry_has_type_infos + else: + if hasattr(layout,"progress") : # only for Blender > 4.0 + layout.progress(factor = components_from_custom_properties_progress_all, text=f"updating {components_from_custom_properties_progress_all * 100.0:.2f}%") class BEVY_COMPONENTS_PT_MissingTypesPanel(bpy.types.Panel): diff --git a/tools/bevy_components/tests/test_rename_components.py b/tools/bevy_components/tests/test_rename_components.py new file mode 100644 index 00000000..13efed6d --- /dev/null +++ b/tools/bevy_components/tests/test_rename_components.py @@ -0,0 +1,159 @@ +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_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(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 = f'Error: Failed to rename component: Errors:["wrong custom property values to generate target component: object: \'{object.name}\', 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_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 = f'Error: Failed to rename component: Errors:["wrong custom property values to generate target component: object: \'{object.name}\', 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