add better error handling

This commit is contained in:
Ryan Schultz
2025-11-16 14:22:01 -06:00
parent e33f48c338
commit a4b5251ed2
+110 -17
View File
@@ -2544,20 +2544,69 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
props.is_changing_mode = False props.is_changing_mode = False
class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator): class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.direct_profile_edit" bl_idname = "bim.direct_profile_edit"
bl_label = "IFC Direct Profile Edit" bl_label = "IFC Direct Profile Edit"
bl_description = "Directly enter profile/axis edit mode, or exit back to object mode" bl_description = "Directly enter profile/axis edit mode, or exit back to object mode"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
# Only allow in OBJECT or EDIT modes
if context.mode not in ("OBJECT", "EDIT_MESH", "EDIT_CURVE"):
return False
# Must have an active object
if not context.active_object:
return False
# In edit mode, we're good to go (we'll exit)
if context.mode in ("EDIT_MESH", "EDIT_CURVE"):
return True
# In object mode, check if it's an editable object type
obj = context.active_object
# Only mesh and curve objects are editable
if obj.type not in ("MESH", "CURVE"):
return False
return True
def _execute(self, context): def _execute(self, context):
# If we're already in edit mode, exit to object mode and SAVE changes # If we're already in edit mode, exit to object mode and SAVE changes
if context.mode in ("EDIT_MESH", "EDIT_CURVE"): if context.mode in ("EDIT_MESH", "EDIT_CURVE"):
return self.handle_exit_edit_mode(context)
# We're in object mode, try to enter profile/axis edit
return self.handle_enter_edit_mode(context)
def handle_exit_edit_mode(self, context):
"""Handle exiting from edit mode and saving changes."""
obj = context.active_object obj = context.active_object
if obj and tool.Geometry.is_representation_item(obj): if not obj:
# We're editing a representation item (SweptAreaSolid) return {"CANCELLED"}
# Check if we're editing a representation item
if tool.Geometry.is_representation_item(obj):
return self.exit_item_edit_mode(context, obj)
# Check if we're editing an element-level profile or axis
element = tool.Ifc.get_entity(obj)
if element and tool.Geometry.has_mesh_properties(obj.data):
return self.exit_element_edit_mode(context, obj, element)
# For other edit modes, use standard operator
return bpy.ops.bim.override_mode_set_object()
def exit_item_edit_mode(self, context, obj):
"""Exit from SweptAreaSolid item editing."""
try:
item = tool.Geometry.get_active_representation(obj) item = tool.Geometry.get_active_representation(obj)
if item and item.is_a("IfcSweptAreaSolid"): if not item or not item.is_a("IfcSweptAreaSolid"):
return bpy.ops.bim.override_mode_set_object()
# Use the standard save workflow for items # Use the standard save workflow for items
from bonsai.bim.module.model.decorator import ProfileDecorator from bonsai.bim.module.model.decorator import ProfileDecorator
ProfileDecorator.uninstall() ProfileDecorator.uninstall()
@@ -2588,10 +2637,13 @@ class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
tool.Geometry.disable_item_mode() tool.Geometry.disable_item_mode()
return {"FINISHED"} return {"FINISHED"}
else: except Exception as e:
# We're editing either an extrusion profile or axis (element-level) self.report({"ERROR"}, f"Failed to save item changes: {str(e)}")
element = tool.Ifc.get_entity(obj) return {"CANCELLED"}
if element and tool.Geometry.has_mesh_properties(obj.data):
def exit_element_edit_mode(self, context, obj, element):
"""Exit from element-level profile or axis editing."""
try:
mesh_props = tool.Geometry.get_mesh_props(obj.data) mesh_props = tool.Geometry.get_mesh_props(obj.data)
# Check if we're editing axis # Check if we're editing axis
@@ -2605,46 +2657,75 @@ class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
extrusion = tool.Model.get_extrusion(body) extrusion = tool.Model.get_extrusion(body)
if extrusion: if extrusion:
return bpy.ops.bim.edit_extrusion_profile() return bpy.ops.bim.edit_extrusion_profile()
except Exception as e:
self.report({"ERROR"}, f"Failed to save profile/axis changes: {str(e)}")
return {"CANCELLED"}
# For other edit modes, use standard operator # Fallback to standard operator
return bpy.ops.bim.override_mode_set_object() return bpy.ops.bim.override_mode_set_object()
# We're in object mode, try to enter profile/axis edit def handle_enter_edit_mode(self, context):
"""Handle entering edit mode from object mode."""
obj = context.active_object obj = context.active_object
if not obj: if not obj:
self.report({"ERROR"}, "No active object") return {"CANCELLED"}
# Check if object has mesh or curve data
if not hasattr(obj, 'data') or not obj.data:
self.report({"INFO"}, "Object has no editable data")
return {"CANCELLED"} return {"CANCELLED"}
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element: if not element:
self.report({"ERROR"}, "Active object is not an IFC element") self.report({"INFO"}, "Active object is not an IFC element")
return {"CANCELLED"} return {"CANCELLED"}
# Check if this is a LAYER2 element (wall, railing, etc.) # Check if this is a LAYER2 element (wall, railing, etc.)
try:
material_usage = tool.Model.get_usage_type(element) material_usage = tool.Model.get_usage_type(element)
except:
material_usage = None
if material_usage == "LAYER2": if material_usage == "LAYER2":
self.report({"ERROR"}, "LAYER2 elements (walls, railings, etc.) cannot use profile editing.") self.report({"ERROR"}, "LAYER2 elements (walls, railings, etc.) cannot use profile editing. Use axis editing instead (Alt+E).")
return {"CANCELLED"} return {"CANCELLED"}
# Try to get representation
try:
representation = tool.Geometry.get_active_representation(obj) representation = tool.Geometry.get_active_representation(obj)
except:
representation = None
if not representation: if not representation:
self.report({"ERROR"}, "Object has no active representation") self.report({"INFO"}, "Object has no active representation")
return {"CANCELLED"} return {"CANCELLED"}
# Check for PROFILE usage (beams, columns, members) - should edit axis # Check for PROFILE usage (beams, columns, members) - should edit axis
if material_usage == "PROFILE": if material_usage == "PROFILE":
try:
return bpy.ops.bim.enable_editing_extrusion_axis() return bpy.ops.bim.enable_editing_extrusion_axis()
except Exception as e:
self.report({"ERROR"}, f"Failed to enable axis editing: {str(e)}")
return {"CANCELLED"}
# Check if this is an element with an extrusion profile (LAYER3, etc) # Check if this is an element with an extrusion profile (LAYER3, etc)
try:
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if body: if body:
body = ifcopenshell.util.representation.resolve_representation(body) body = ifcopenshell.util.representation.resolve_representation(body)
extrusion = tool.Model.get_extrusion(body) extrusion = tool.Model.get_extrusion(body)
if extrusion and not tool.Geometry.is_representation_item(obj): if extrusion and not tool.Geometry.is_representation_item(obj):
# This is an element-level extrusion, use enable_editing_extrusion_profile
return bpy.ops.bim.enable_editing_extrusion_profile() return bpy.ops.bim.enable_editing_extrusion_profile()
except Exception as e:
self.report({"ERROR"}, f"Failed to enable profile editing: {str(e)}")
return {"CANCELLED"}
# Otherwise, enter item mode and edit SweptAreaSolid # Otherwise, try to enter item mode and edit SweptAreaSolid
return self.enter_item_edit_mode(context, obj)
def enter_item_edit_mode(self, context, obj):
"""Enter item mode for SweptAreaSolid editing."""
try:
props = tool.Geometry.get_geometry_props() props = tool.Geometry.get_geometry_props()
if not props.representation_obj: if not props.representation_obj:
bpy.ops.bim.import_representation_items() bpy.ops.bim.import_representation_items()
@@ -2656,13 +2737,16 @@ class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
item_obj = item_obj_data.obj item_obj = item_obj_data.obj
if not item_obj: if not item_obj:
continue continue
try:
item = tool.Geometry.get_active_representation(item_obj) item = tool.Geometry.get_active_representation(item_obj)
if item and item.is_a("IfcSweptAreaSolid"): if item and item.is_a("IfcSweptAreaSolid"):
swept_solid_obj = item_obj swept_solid_obj = item_obj
break break
except:
continue
if not swept_solid_obj: if not swept_solid_obj:
self.report({"ERROR"}, "No IfcSweptAreaSolid item found in representation") self.report({"INFO"}, "No editable SweptAreaSolid item found")
tool.Geometry.disable_item_mode() tool.Geometry.disable_item_mode()
return {"CANCELLED"} return {"CANCELLED"}
@@ -2677,7 +2761,7 @@ class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
profile = item.SweptArea profile = item.SweptArea
res = tool.Model.import_profile(profile, obj=swept_solid_obj) res = tool.Model.import_profile(profile, obj=swept_solid_obj)
if res is None: if res is None:
self.report({"ERROR"}, f"Couldn't import profile for editing") self.report({"ERROR"}, "Couldn't import profile for editing")
tool.Geometry.disable_item_mode() tool.Geometry.disable_item_mode()
return {"CANCELLED"} return {"CANCELLED"}
@@ -2691,6 +2775,15 @@ class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
tool.Blender.set_viewport_tool("bim.cad_tool") tool.Blender.set_viewport_tool("bim.cad_tool")
return {"FINISHED"} return {"FINISHED"}
except Exception as e:
self.report({"ERROR"}, f"Failed to enter item edit mode: {str(e)}")
try:
tool.Geometry.disable_item_mode()
except:
pass
return {"CANCELLED"}
return {"CANCELLED"}