diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index 9db15ef2a0..6d2ef80a55 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -142,6 +142,7 @@ classes = [ ui.BIM_PT_tabs, # Project overview ui.BIM_PT_tab_project_info, + ui.BIM_PT_tab_project_tree, ui.BIM_PT_tab_project_setup, ui.BIM_PT_tab_geometry, ui.BIM_PT_tab_stakeholders, diff --git a/src/blenderbim/blenderbim/bim/module/covering/__init__.py b/src/blenderbim/blenderbim/bim/module/covering/__init__.py index 73c39a93af..fed1174854 100644 --- a/src/blenderbim/blenderbim/bim/module/covering/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/covering/__init__.py @@ -29,13 +29,9 @@ def register(): if not bpy.app.background: bpy.utils.register_tool(workspace.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False) bpy.types.Scene.BIMCoveringProperties = bpy.props.PointerProperty(type=prop.BIMCoveringProperties) -# bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties) -# bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties) def unregister(): if not bpy.app.background: bpy.utils.unregister_tool(workspace.CoveringTool) del bpy.types.Scene.BIMCoveringProperties -# del bpy.types.Object.BIMObjectSpatialProperties -# del bpy.types.Scene.BIMSpatialManagerProperties diff --git a/src/blenderbim/blenderbim/bim/module/spatial/__init__.py b/src/blenderbim/blenderbim/bim/module/spatial/__init__.py index 261e309834..f48afcfa3a 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/__init__.py @@ -42,7 +42,7 @@ classes = ( prop.BIMSpatialProperties, prop.BIMObjectSpatialProperties, prop.BIMContainer, - prop.BIMSpatialManagerProperties, + prop.BIMProjectTreeProperties, ui.BIM_PT_spatial, ui.BIM_UL_containers, ui.BIM_UL_containers_manager, @@ -56,7 +56,7 @@ def register(): bpy.utils.register_tool(workspace.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False) bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties) bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties) - bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties) + bpy.types.Scene.BIMProjectTreeProperties = bpy.props.PointerProperty(type=prop.BIMProjectTreeProperties) def unregister(): @@ -64,4 +64,4 @@ def unregister(): bpy.utils.unregister_tool(workspace.SpatialTool) del bpy.types.Scene.BIMSpatialProperties del bpy.types.Object.BIMObjectSpatialProperties - del bpy.types.Scene.BIMSpatialManagerProperties + del bpy.types.Scene.BIMProjectTreeProperties diff --git a/src/blenderbim/blenderbim/bim/module/spatial/prop.py b/src/blenderbim/blenderbim/bim/module/spatial/prop.py index 59bc311948..f6480ae31a 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/prop.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/prop.py @@ -36,27 +36,17 @@ import ifcopenshell def update_elevation(self, context): - entity = tool.Ifc.get().by_id(self.active_container_id) - obj = tool.Ifc.get_object(entity) - if not obj: - return - obj.location.z = self.elevation + if ifc_definition_id := self.ifc_definition_id: + entity = tool.Ifc.get().by_id(ifc_definition_id) + obj = tool.Ifc.get_object(entity) + if not obj: + return + obj.location.z = self.elevation -def update_active_container_index(self, context): - if self.active_container_index < 0: - return - self.active_container_id = self.containers[self.active_container_index].ifc_definition_id - self.container_name = self.containers[self.active_container_index].name - self.elevation = self.containers[self.active_container_index].elevation - - -def updateContainerName(self, context): - props = context.scene.BIMSpatialManagerProperties - if not props.is_container_update_enabled or self.name == "Unnamed": - return - tool.Spatial.edit_container_name(tool.Ifc.get().by_id(props.active_container_id), self.name) - props.container_name = self.name +def update_name(self, context): + if ifc_definition_id := self.ifc_definition_id: + tool.Spatial.edit_container_name(tool.Ifc.get().by_id(ifc_definition_id), self.name) def update_relating_container_from_object(self, context): @@ -104,23 +94,24 @@ class BIMObjectSpatialProperties(PropertyGroup): class BIMContainer(PropertyGroup): - name: StringProperty(name="Name", update=updateContainerName) + name: StringProperty(name="Name", update=update_name) ifc_class: StringProperty(name="IFC Class") description: StringProperty(name="Description") long_name: StringProperty(name="Long Name") - elevation: FloatProperty(name="Elevation", subtype="DISTANCE") + elevation: FloatProperty(name="Elevation", subtype="DISTANCE", update=update_elevation) level_index: IntProperty(name="Level Index") has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") ifc_definition_id: IntProperty(name="IFC Definition ID") -class BIMSpatialManagerProperties(PropertyGroup): +class BIMProjectTreeProperties(PropertyGroup): containers: CollectionProperty(name="Containers", type=BIMContainer) contracted_containers: StringProperty(name="Contracted containers", default="[]") expanded_containers: StringProperty(name="Expanded containers", default="[]") - active_container_index: IntProperty(name="Active Container Index", update=update_active_container_index) - active_container_id: IntProperty(name="Active Container Id") - container_name: StringProperty(name="Container Name") - elevation: FloatProperty(name="Elevation", update=update_elevation, subtype="DISTANCE") - is_container_update_enabled: BoolProperty(name="Is Container Update Enabled", default=True) # TODO:review + active_container_index: IntProperty(name="Active Container Index") + + @property + def active_container(self): + if self.active_container_index < len(self.containers): + return self.containers[self.active_container_index] diff --git a/src/blenderbim/blenderbim/bim/module/spatial/ui.py b/src/blenderbim/blenderbim/bim/module/spatial/ui.py index 90411c4ac1..32fbc6cee9 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/ui.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/ui.py @@ -97,7 +97,8 @@ class BIM_PT_project_tree(Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "scene" - bl_parent_id = "BIM_PT_tab_project_setup" + bl_parent_id = "BIM_PT_tab_project_tree" + bl_options = {"HIDE_HEADER"} @classmethod def poll(cls, context): @@ -106,18 +107,18 @@ class BIM_PT_project_tree(Panel): def draw(self, context): if not ProjectTreeData.is_loaded: ProjectTreeData.load() - self.props = context.scene.BIMSpatialManagerProperties + self.props = context.scene.BIMProjectTreeProperties row = self.layout.row() row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure") - if 0 <= self.props.active_container_index < len(self.props.containers): + if self.props.active_container: ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id - row = self.layout.row() + row = self.layout.row(align=True) row.alignment = "RIGHT" - row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="Select Children") + row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="") spatial_data = ProjectTreeData.data["containers"].get(ifc_definition_id, None) if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]: row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey" - row.operator("bim.delete_container", icon="X", text="Delete").container = ifc_definition_id + row.operator("bim.delete_container", icon="X", text="").container = ifc_definition_id self.layout.template_list( "BIM_UL_containers_manager", "", @@ -126,12 +127,6 @@ class BIM_PT_project_tree(Panel): self.props, "active_container_index", ) - row = self.layout.row() - if 0 <= self.props.active_container_index < len(self.props.containers): - row.prop(self.props, "container_name", text="") - row.prop(self.props, "elevation", text="") - op = row.operator("bim.edit_container_attributes", icon="CHECKMARK", text="Apply") - op.container = self.props.containers[self.props.active_container_index].ifc_definition_id class BIM_UL_containers_manager(UIList): @@ -143,7 +138,7 @@ class BIM_UL_containers_manager(UIList): split1.prop(item, "name", emboss=False, text="") split2 = row.split(factor=1) split2.alignment = "RIGHT" - split2.label(icon="BLANK1", text=tool.Unit.blender_format_unit(item.elevation)) + split2.prop(item, "elevation", emboss=False, text="") def draw_hierarchy(self, row, item): for i in range(0, item.level_index): diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py index 01d5c13db5..eb6a0709d2 100644 --- a/src/blenderbim/blenderbim/bim/ui.py +++ b/src/blenderbim/blenderbim/bim/ui.py @@ -202,9 +202,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): default=True, description="If disabled, the toolbar will only load when an IFC model is active", ) - should_play_chaching_sound: BoolProperty( - name="Play A Cha-Ching Sound When Project Costs Updates", default=False - ) + should_play_chaching_sound: BoolProperty(name="Play A Cha-Ching Sound When Project Costs Updates", default=False) lock_grids_on_import: BoolProperty(name="Lock Grids By Default", default=True) spatial_elements_unselectable: BoolProperty(name="Make Spatial Elements Unselectable By Default", default=True) decorations_colour: bpy.props.FloatVectorProperty( @@ -407,7 +405,7 @@ class BIM_PT_tabs(Panel): if blenderbim.last_error: box = self.layout.box() - box.alert=True + box.alert = True row = box.row(align=True) row.label(text="BlenderBIM experienced an error :(", icon="ERROR") row.operator("bim.close_error", text="", icon="CANCEL") @@ -442,6 +440,20 @@ class BIM_PT_tab_project_info(Panel): pass +class BIM_PT_tab_project_tree(Panel): + bl_label = "Project Tree" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + @classmethod + def poll(cls, context): + return tool.Blender.is_tab(context, "PROJECT") + + def draw(self, context): + pass + + class BIM_PT_tab_project_setup(Panel): bl_label = "Project Setup" bl_space_type = "PROPERTIES" @@ -490,7 +502,7 @@ class BIM_PT_tab_grouping_and_filtering(Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "scene" - bl_options = {"DEFAULT_CLOSED","HEADER_LAYOUT_EXPAND"} + bl_options = {"DEFAULT_CLOSED", "HEADER_LAYOUT_EXPAND"} @classmethod def poll(cls, context): @@ -503,8 +515,9 @@ class BIM_PT_tab_grouping_and_filtering(Panel): # Draws help button on the right row = self.layout.row(align=True) row.label(text="") # empty text occupies the left of the row - row.operator("bim.open_uri", text="", icon="HELP").uri = \ + row.operator("bim.open_uri", text="", icon="HELP").uri = ( "https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html" + ) class BIM_PT_tab_geometry(Panel): diff --git a/src/blenderbim/blenderbim/tool/spatial.py b/src/blenderbim/blenderbim/tool/spatial.py index bd1ecb634b..19ffce1230 100644 --- a/src/blenderbim/blenderbim/tool/spatial.py +++ b/src/blenderbim/blenderbim/tool/spatial.py @@ -208,44 +208,44 @@ class Spatial(blenderbim.core.tool.Spatial): @classmethod def load_container_manager(cls): - cls.props = bpy.context.scene.BIMSpatialManagerProperties - previous_container_index = cls.props.active_container_index - cls.props.containers.clear() - cls.contracted_containers = json.loads(cls.props.contracted_containers) - cls.props.is_container_update_enabled = False + props = bpy.context.scene.BIMProjectTreeProperties + previous_container_index = props.active_container_index + props.containers.clear() + cls.contracted_containers = json.loads(props.contracted_containers) + props.is_container_update_enabled = False parent = tool.Ifc.get().by_type("IfcProject")[0] - for object in ifcopenshell.util.element.get_parts(parent) or []: - if object.is_a("IfcSpatialElement") or object.is_a("IfcSpatialStructureElement"): - cls.create_new_storey_li(object, 0) - cls.props.is_container_update_enabled = True + for subelement in ifcopenshell.util.element.get_parts(parent) or []: + if subelement.is_a("IfcSpatialElement") or subelement.is_a("IfcSpatialStructureElement"): + cls.import_spatial_structure(subelement, 0) + props.is_container_update_enabled = True # triggers spatial manager props setup - cls.props.active_container_index = min(previous_container_index, len(cls.props.containers) - 1) + props.active_container_index = min(previous_container_index, len(props.containers) - 1) @classmethod - def create_new_storey_li(cls, element, level_index): - si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - new = cls.props.containers.add() + def import_spatial_structure(cls, element, level_index): + props = bpy.context.scene.BIMProjectTreeProperties + new = props.containers.add() + new.ifc_class = element.is_a() new.name = element.Name or "Unnamed" + new.description = element.Description or "" new.long_name = element.LongName or "" - new.has_decomposition = bool(element.IsDecomposedBy) - new.ifc_definition_id = element.id() - new.elevation = ifcopenshell.util.placement.get_storey_elevation(element) * si_conversion - + new.elevation = ifcopenshell.util.placement.get_storey_elevation(element) new.is_expanded = element.id() not in cls.contracted_containers new.level_index = level_index - if new.has_decomposition: - new.has_children = True - if new.is_expanded: - for related_object in ifcopenshell.util.element.get_parts(element) or []: - if related_object.is_a("IfcSpatialElement") or related_object.is_a("IfcSpatialStructureElement"): - cls.create_new_storey_li(related_object, level_index + 1) + children = ifcopenshell.util.element.get_parts(element) + new.has_children = bool(children) + new.ifc_definition_id = element.id() + if new.is_expanded: + for child in children or []: + cls.import_spatial_structure(child, level_index + 1) @classmethod def edit_container_attributes(cls, entity): + # TODO obj = tool.Ifc.get_object(entity) blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj) - name = bpy.context.scene.BIMSpatialManagerProperties.container_name + name = bpy.context.scene.BIMProjectTreeProperties.container_name if name != entity.Name: cls.edit_container_name(entity, name) @@ -255,21 +255,21 @@ class Spatial(blenderbim.core.tool.Spatial): @classmethod def get_active_container(cls): - props = bpy.context.scene.BIMSpatialManagerProperties + props = bpy.context.scene.BIMProjectTreeProperties if props.active_container_index < len(props.containers): container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id) return container @classmethod def contract_container(cls, container): - props = bpy.context.scene.BIMSpatialManagerProperties + props = bpy.context.scene.BIMProjectTreeProperties contracted_containers = json.loads(props.contracted_containers) contracted_containers.append(container.id()) props.contracted_containers = json.dumps(contracted_containers) @classmethod def expand_container(cls, container): - props = bpy.context.scene.BIMSpatialManagerProperties + props = bpy.context.scene.BIMProjectTreeProperties contracted_containers = json.loads(props.contracted_containers) contracted_containers.remove(container.id()) props.contracted_containers = json.dumps(contracted_containers)