Spatial manager now allows inline editing of names and elevations for a slicker UI

This commit is contained in:
Dion Moult
2024-06-04 15:10:28 +10:00
parent b14ff94303
commit faaea7db49
7 changed files with 76 additions and 80 deletions
@@ -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,
@@ -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
@@ -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
@@ -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]
@@ -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):
+19 -6
View File
@@ -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):
+27 -27
View File
@@ -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)