mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Spatial manager now allows inline editing of names and elevations for a slicker UI
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user