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
@ -57,6 +57,14 @@ Before you can use the add-on you need to configure it
|
|||||||
|
|
||||||
![configuration 3](./docs/configuration3.png)
|
![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
|
## Use
|
||||||
@ -173,8 +181,8 @@ It will add the component to the select object
|
|||||||
|
|
||||||
![invalid component](./docs/invalid_components.png)
|
![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
|
> see [here](#invalidunregistered-type-renaming--conversion) for ways to convert invalid / unregistered components to other types.
|
||||||
this will be adress in the future
|
|
||||||
|
|
||||||
- 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
|
- 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
* by default, the add-on will check for changes in your registry file every second, and refresh the UI accordingly
|
#### Single object component renaming/ conversion
|
||||||
* you can set the polling frequency or turn it off if you do not want auto-refresh
|
|
||||||
|
|
||||||
![registry file polling](./docs/registry_polling.png)
|
- 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)
|
||||||
|
|
||||||
|
|
||||||
### regenerate custom property values
|
### For conversion between custom properties & components & vice-versa
|
||||||
|
|
||||||
|
#### 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
|
- "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
|
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
|
- 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
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "bevy_components",
|
"name": "bevy_components",
|
||||||
"author": "kaosigh",
|
"author": "kaosigh",
|
||||||
"version": (0, 4, 0),
|
"version": (0, 4, 1),
|
||||||
"blender": (3, 4, 0),
|
"blender": (3, 4, 0),
|
||||||
"location": "VIEW_3D",
|
"location": "VIEW_3D",
|
||||||
"description": "UI to help create Bevy blueprints and components",
|
"description": "UI to help create Bevy blueprints and components",
|
||||||
@ -16,11 +16,11 @@ from bpy.props import (StringProperty)
|
|||||||
|
|
||||||
from .helpers import load_settings
|
from .helpers import load_settings
|
||||||
from .blueprints import CreateBlueprintOperator
|
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.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.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_MissingTypesPanel, MISSING_TYPES_UL_List)
|
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 .components.metadata import (ComponentMetadata, ComponentsMeta, ensure_metadata_for_all_objects)
|
||||||
from .propGroups.prop_groups import (generate_propertyGroups_for_components)
|
from .propGroups.prop_groups import (generate_propertyGroups_for_components)
|
||||||
@ -87,6 +87,10 @@ classes = [
|
|||||||
CopyComponentOperator,
|
CopyComponentOperator,
|
||||||
PasteComponentOperator,
|
PasteComponentOperator,
|
||||||
RemoveComponentOperator,
|
RemoveComponentOperator,
|
||||||
|
RemoveComponentFromAllObjectsOperator,
|
||||||
|
Fix_Component_Operator,
|
||||||
|
OT_rename_component,
|
||||||
|
RenameHelper,
|
||||||
GenerateComponent_From_custom_property_Operator,
|
GenerateComponent_From_custom_property_Operator,
|
||||||
Toggle_ComponentVisibility,
|
Toggle_ComponentVisibility,
|
||||||
|
|
||||||
@ -106,8 +110,12 @@ classes = [
|
|||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
||||||
|
|
||||||
|
OT_select_object,
|
||||||
|
OT_select_component_name_to_replace,
|
||||||
|
|
||||||
BEVY_COMPONENTS_PT_MainPanel,
|
BEVY_COMPONENTS_PT_MainPanel,
|
||||||
BEVY_COMPONENTS_PT_ComponentsPanel,
|
BEVY_COMPONENTS_PT_ComponentsPanel,
|
||||||
|
BEVY_COMPONENTS_PT_AdvancedToolsPanel,
|
||||||
BEVY_COMPONENTS_PT_Configuration,
|
BEVY_COMPONENTS_PT_Configuration,
|
||||||
MISSING_TYPES_UL_List,
|
MISSING_TYPES_UL_List,
|
||||||
BEVY_COMPONENTS_PT_MissingTypesPanel,
|
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)
|
value = property_group_value_to_custom_property_value(propertyGroup, component_definition, registry, None)
|
||||||
object[component_name] = value
|
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):
|
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)
|
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)
|
property_group_value_from_custom_property_value(propertyGroup, component_definition, registry, customProperty_value)
|
||||||
del object["__disable__update"]
|
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
|
# 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):
|
def remove_component_from_object(object, component_name):
|
||||||
|
@ -3,7 +3,7 @@ import json
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy_types import Operator
|
from bpy_types import Operator
|
||||||
from bpy.props import (StringProperty)
|
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):
|
class AddComponentOperator(Operator):
|
||||||
"""Add component to blueprint"""
|
"""Add component to blueprint"""
|
||||||
@ -90,12 +90,10 @@ class PasteComponentOperator(Operator):
|
|||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveComponentOperator(Operator):
|
class RemoveComponentOperator(Operator):
|
||||||
"""Delete component from blueprint"""
|
"""Remove component from object"""
|
||||||
bl_idname = "object.remove_bevy_component"
|
bl_idname = "object.remove_bevy_component"
|
||||||
bl_label = "Delete component from blueprint Operator"
|
bl_label = "Remove component from object Operator"
|
||||||
bl_options = {"UNDO"}
|
bl_options = {"UNDO"}
|
||||||
|
|
||||||
component_name: StringProperty(
|
component_name: StringProperty(
|
||||||
@ -103,11 +101,18 @@ class RemoveComponentOperator(Operator):
|
|||||||
description="component to delete",
|
description="component to delete",
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
|
object_name: StringProperty(
|
||||||
|
name="object name",
|
||||||
|
description="object whose component to delete",
|
||||||
|
default=""
|
||||||
|
) # type: ignore
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
object = context.object
|
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+"'")
|
print("removing component ", self.component_name, "from object '"+object.name+"'")
|
||||||
|
|
||||||
if object is not None and self.component_name in object:
|
if object is not None and self.component_name in object:
|
||||||
remove_component_from_object(object, self.component_name)
|
remove_component_from_object(object, self.component_name)
|
||||||
else:
|
else:
|
||||||
@ -116,6 +121,154 @@ class RemoveComponentOperator(Operator):
|
|||||||
return {'FINISHED'}
|
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):
|
class GenerateComponent_From_custom_property_Operator(Operator):
|
||||||
"""generate components from custom property"""
|
"""generate components from custom property"""
|
||||||
bl_idname = "object.generate_bevy_component_from_custom_property"
|
bl_idname = "object.generate_bevy_component_from_custom_property"
|
||||||
@ -143,6 +296,31 @@ class GenerateComponent_From_custom_property_Operator(Operator):
|
|||||||
return {'FINISHED'}
|
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):
|
class Toggle_ComponentVisibility(Operator):
|
||||||
"""toggles components visibility"""
|
"""toggles components visibility"""
|
||||||
bl_idname = "object.toggle_bevy_component_visibility"
|
bl_idname = "object.toggle_bevy_component_visibility"
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import json
|
import json
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
from ..registry.operators import COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT
|
||||||
from .metadata import do_object_custom_properties_have_missing_metadata
|
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):
|
def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None):
|
||||||
is_enum = getattr(propertyGroup, "with_enum")
|
is_enum = getattr(propertyGroup, "with_enum")
|
||||||
@ -193,6 +195,16 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel):
|
|||||||
row.label(text=error_message)
|
row.label(text=error_message)
|
||||||
|
|
||||||
# "footer" with additional controls
|
# "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 = row.operator(RemoveComponentOperator.bl_idname, text="", icon="X")
|
||||||
op.component_name = component_name
|
op.component_name = component_name
|
||||||
row.separator()
|
row.separator()
|
||||||
|
BIN
tools/bevy_components/docs/component_remove_single.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
tools/bevy_components/docs/component_rename_object_select.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
tools/bevy_components/docs/component_rename_overview2.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
tools/bevy_components/docs/component_rename_remove_bulk.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
tools/bevy_components/docs/component_rename_remove_bulk2.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
tools/bevy_components/docs/component_rename_single.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 14 KiB |
@ -224,8 +224,9 @@ def property_group_value_from_custom_property_value(property_group, definition,
|
|||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pass
|
if len(value) > 2: #a unit struct should be two chars long :()
|
||||||
#print("struct with zero fields")
|
#print("struct with zero fields")
|
||||||
|
raise Exception("input string too big for a unit struct")
|
||||||
|
|
||||||
elif type_info == "Tuple":
|
elif type_info == "Tuple":
|
||||||
custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 1)
|
custom_property_values = parse_tuplestruct_string(value, start_nesting=1 if len(nesting) == 1 else 1)
|
||||||
|
@ -42,11 +42,26 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL(Operator):
|
|||||||
bl_label = "Apply Registry to all objects"
|
bl_label = "Apply Registry to all objects"
|
||||||
bl_options = {"UNDO"}
|
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):
|
def execute(self, context):
|
||||||
print("apply registry to all")
|
print("apply registry to all")
|
||||||
#context.window_manager.components_registry.load_schema()
|
#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)
|
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'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -56,10 +71,23 @@ class COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT(Operator):
|
|||||||
bl_label = "Apply Registry to current object"
|
bl_label = "Apply Registry to current object"
|
||||||
bl_options = {"UNDO"}
|
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):
|
def execute(self, context):
|
||||||
print("apply registry to current object")
|
print("apply registry to current object")
|
||||||
object = context.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)
|
apply_propertyGroup_values_to_object_customProperties(object)
|
||||||
|
|
||||||
|
context.window_manager.custom_properties_from_components_progress = -1.0
|
||||||
return {'FINISHED'}
|
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_label = "Apply custom_properties to current object"
|
||||||
bl_options = {"UNDO"}
|
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):
|
def execute(self, context):
|
||||||
print("apply custom properties to current object")
|
print("apply custom properties to current object")
|
||||||
object = context.object
|
object = context.object
|
||||||
error = False
|
error = False
|
||||||
try:
|
try:
|
||||||
apply_customProperty_values_to_object_propertyGroups(object)
|
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:
|
except Exception as error:
|
||||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
||||||
error = True
|
error = True
|
||||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error))
|
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Error:" + str(error))
|
||||||
if not error:
|
if not error:
|
||||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for selected object")
|
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'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator):
|
class COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL(Operator):
|
||||||
"""Update UI values from custom properties to ALL object"""
|
"""Update UI values from custom properties to ALL object"""
|
||||||
bl_idname = "object.refresh_ui_from_custom_properties_all"
|
bl_idname = "object.refresh_ui_from_custom_properties_all"
|
||||||
bl_label = "Apply custom_properties to all objects"
|
bl_label = "Apply custom_properties to all objects"
|
||||||
bl_options = {"UNDO"}
|
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):
|
def execute(self, context):
|
||||||
print("apply custom properties to all object")
|
print("apply custom properties to all object")
|
||||||
bpy.context.window_manager.components_registry.disable_all_object_updates = True
|
bpy.context.window_manager.components_registry.disable_all_object_updates = True
|
||||||
errors = []
|
errors = []
|
||||||
for object in bpy.data.objects:
|
total = len(bpy.data.objects)
|
||||||
|
|
||||||
|
for index, object in enumerate(bpy.data.objects):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
apply_customProperty_values_to_object_propertyGroups(object)
|
apply_customProperty_values_to_object_propertyGroups(object)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
del object["__disable__update"] # make sure custom properties are updateable afterwards, even in the case of failure
|
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))
|
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:
|
if len(errors) > 0:
|
||||||
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Errors:" + str(errors))
|
self.report({'ERROR'}, "Failed to update propertyGroup values from custom property: Errors:" + str(errors))
|
||||||
else:
|
else:
|
||||||
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for all objects")
|
self.report({'INFO'}, "Sucessfully generated UI values for custom properties for all objects")
|
||||||
bpy.context.window_manager.components_registry.disable_all_object_updates = False
|
bpy.context.window_manager.components_registry.disable_all_object_updates = False
|
||||||
|
context.window_manager.components_from_custom_properties_progress_all = -1.0
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
class OT_OpenFilebrowser(Operator, ImportHelper):
|
class OT_OpenFilebrowser(Operator, ImportHelper):
|
||||||
@ -133,3 +196,41 @@ class OT_OpenFilebrowser(Operator, ImportHelper):
|
|||||||
|
|
||||||
return {'FINISHED'}
|
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'}
|
||||||
|
|
@ -241,9 +241,6 @@ class ComponentsRegistry(PropertyGroup):
|
|||||||
|
|
||||||
del bpy.types.WindowManager.components_registry
|
del bpy.types.WindowManager.components_registry
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def load_schema(self):
|
def load_schema(self):
|
||||||
print("load schema", self)
|
print("load schema", self)
|
||||||
# cleanup previous data if any
|
# cleanup previous data if any
|
||||||
@ -352,9 +349,9 @@ class ComponentsRegistry(PropertyGroup):
|
|||||||
return propGroupName
|
return propGroupName
|
||||||
|
|
||||||
def get_propertyGroupName_from_shortName(self, shortName):
|
def get_propertyGroupName_from_shortName(self, shortName):
|
||||||
|
|
||||||
return self.short_names_to_propgroup_names.get(shortName, None)
|
return self.short_names_to_propgroup_names.get(shortName, None)
|
||||||
|
|
||||||
|
###########
|
||||||
|
|
||||||
"""
|
"""
|
||||||
object[component_definition.name] = 0.5
|
object[component_definition.name] = 0.5
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
import json
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_types import (UIList)
|
from bpy_types import (UIList)
|
||||||
|
from bpy.props import (StringProperty)
|
||||||
|
|
||||||
|
from ..components.operators import OT_rename_component, RemoveComponentFromAllObjectsOperator, RemoveComponentOperator
|
||||||
from .operators import(
|
from .operators import(
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_ALL,
|
||||||
COMPONENTS_OT_REFRESH_PROPGROUPS_FROM_CUSTOM_PROPERTIES_CURRENT,
|
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_ALL,
|
||||||
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT)
|
COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT)
|
||||||
|
|
||||||
@ -21,8 +27,7 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
registry = context.window_manager.components_registry
|
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()
|
row = layout.row()
|
||||||
col = row.column()
|
col = row.column()
|
||||||
@ -43,31 +48,212 @@ class BEVY_COMPONENTS_PT_Configuration(bpy.types.Panel):
|
|||||||
layout.separator()
|
layout.separator()
|
||||||
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 = 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.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.alert = True
|
||||||
|
|
||||||
|
##
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_CURRENT.bl_idname, text="update custom properties of current object" , icon="LOOP_FORWARDS")
|
custom_properties_from_components_progress_current = context.window_manager.custom_properties_from_components_progress
|
||||||
row.enabled = registry_has_type_infos and selected_object is not None
|
|
||||||
|
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()
|
layout.separator()
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator(COMPONENTS_OT_REFRESH_CUSTOM_PROPERTIES_ALL.bl_idname, text="update custom properties of ALL objects" , icon="LOOP_FORWARDS")
|
custom_properties_from_components_progress_all = context.window_manager.custom_properties_from_components_progress_all
|
||||||
row.enabled = registry_has_type_infos
|
|
||||||
|
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 = 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.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
|
row.alert = True
|
||||||
|
|
||||||
|
components_from_custom_properties_progress_current = context.window_manager.components_from_custom_properties_progress
|
||||||
|
|
||||||
row = layout.row()
|
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")
|
if components_from_custom_properties_progress_current == -1.0:
|
||||||
row.enabled = registry_has_type_infos and selected_object is not None
|
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()
|
layout.separator()
|
||||||
row = layout.row()
|
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")
|
components_from_custom_properties_progress_all = context.window_manager.components_from_custom_properties_progress_all
|
||||||
row.enabled = registry_has_type_infos
|
|
||||||
|
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):
|
class BEVY_COMPONENTS_PT_MissingTypesPanel(bpy.types.Panel):
|
||||||
|
159
tools/bevy_components/tests/test_rename_components.py
Normal file
@ -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
|