mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Support for nested IfcGroups
This commit is contained in:
@@ -21,8 +21,10 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.LoadGroups,
|
||||
operator.ToggleGroup,
|
||||
operator.DisableGroupEditingUI,
|
||||
operator.AddGroup,
|
||||
operator.AddGroupToGroup,
|
||||
operator.EditGroup,
|
||||
operator.RemoveGroup,
|
||||
operator.ToggleAssigningGroup,
|
||||
@@ -32,6 +34,7 @@ classes = (
|
||||
operator.DisableEditingGroup,
|
||||
operator.SelectGroupProducts,
|
||||
operator.UpdateGroup,
|
||||
prop.ExpandedGroups,
|
||||
prop.Group,
|
||||
prop.BIMGroupProperties,
|
||||
ui.BIM_PT_groups,
|
||||
@@ -43,7 +46,9 @@ classes = (
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMGroupProperties = bpy.props.PointerProperty(type=prop.BIMGroupProperties)
|
||||
bpy.types.Scene.ExpandedGroups = bpy.props.PointerProperty(type=prop.ExpandedGroups)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMGroupProperties
|
||||
del bpy.types.Scene.ExpandedGroups
|
||||
|
||||
@@ -23,23 +23,73 @@ import blenderbim.bim.helper
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.group.data import Data
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import json
|
||||
|
||||
|
||||
class LoadGroups(bpy.types.Operator):
|
||||
bl_idname = "bim.load_groups"
|
||||
bl_label = "Load Groups"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
is_refresh: bpy.props.BoolProperty(default=False)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMGroupProperties
|
||||
props.groups.clear()
|
||||
self.props = context.scene.BIMGroupProperties
|
||||
self.expanded_groups = json.loads(context.scene.ExpandedGroups.json_string)
|
||||
self.props.groups.clear()
|
||||
self.ifc = IfcStore.get_file()
|
||||
if not self.is_refresh:
|
||||
context.scene.ExpandedGroups.json_string = "{}"
|
||||
|
||||
for ifc_definition_id, group in Data.groups.items():
|
||||
new = props.groups.add()
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
new.name = group["Name"]
|
||||
new.selection_query = group["Description"].split("*selector*")[1] if group["Description"] else ""
|
||||
props.is_editing = True
|
||||
if not group["HasAssignments"]:
|
||||
new = self.props.groups.add()
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
new.name = group["Name"]
|
||||
new.selection_query = group["Description"].split("*selector*")[1] if group["Description"] else ""
|
||||
new.has_children = True if len(group["IsGroupedBy"]) != 0 else False
|
||||
new.tree_depth = 0
|
||||
|
||||
if self.is_refresh:
|
||||
self.recursively_load_sub_groups(ifc_definition_id, new)
|
||||
|
||||
self.props.is_editing = True
|
||||
bpy.ops.bim.disable_editing_group()
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
def recursively_load_sub_groups(self, ifc_definition_id, parent):
|
||||
sub_groups = ([k for k,v in Data.products.items() if self.ifc.by_id(k).is_a("IfcGroup") and ifc_definition_id in v])
|
||||
if str(ifc_definition_id) not in self.expanded_groups or sub_groups == []:
|
||||
return
|
||||
else:
|
||||
parent.is_expanded = True
|
||||
for group in sub_groups:
|
||||
new = self.props.groups.add()
|
||||
new.ifc_definition_id = group
|
||||
new.name = Data.groups[group]["Name"]
|
||||
new.selection_query = Data.groups[group]["Description"].split("*selector*")[1] if Data.groups[group]["Description"] else ""
|
||||
new.has_children = True if len(Data.groups[group]["IsGroupedBy"]) != 0 else False
|
||||
new.tree_depth = parent.tree_depth + 1
|
||||
self.recursively_load_sub_groups(new.ifc_definition_id, new)
|
||||
|
||||
|
||||
class ToggleGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_group"
|
||||
bl_label = "Toggle Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
ifc_definition_id: bpy.props.IntProperty()
|
||||
index: bpy.props.IntProperty()
|
||||
option: bpy.props.StringProperty(name="Expand or Collapse")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMGroupProperties.groups[self.index].is_expanded = True if self.option == "Expand" else False
|
||||
json_string = json.loads(context.scene.ExpandedGroups.json_string)
|
||||
if self.option == "Expand":
|
||||
json_string.setdefault(str(self.ifc_definition_id), None)
|
||||
else:
|
||||
json_string.pop(str(self.ifc_definition_id), None)
|
||||
context.scene.ExpandedGroups.json_string = json.dumps(json_string)
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -56,7 +106,7 @@ class DisableGroupEditingUI(bpy.types.Operator):
|
||||
|
||||
class AddGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.add_group"
|
||||
bl_label = "Add Group"
|
||||
bl_label = "Add New Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
@@ -65,15 +115,43 @@ class AddGroup(bpy.types.Operator):
|
||||
def _execute(self, context):
|
||||
result = ifcopenshell.api.run("group.add_group", IfcStore.get_file())
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_groups()
|
||||
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
bpy.ops.bim.enable_editing_group(group=result.id())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddGroupToGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.add_group_to_group"
|
||||
bl_label = "Add Group to Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
group: bpy.props.IntProperty(name="Group ID")
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
result = ifcopenshell.api.run("group.add_group", self.file)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group",
|
||||
IfcStore.get_file(),
|
||||
**{
|
||||
"product": [result],
|
||||
"group" : self.file.by_id(self.group)
|
||||
}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
bpy.ops.bim.disable_group_editing_ui()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_group"
|
||||
bl_label = "Edit Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
copy_from_selector: bpy.props.BoolProperty(name="Copy from Selector", default=False)
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
@@ -86,12 +164,16 @@ class EditGroup(bpy.types.Operator):
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
|
||||
if self.copy_from_selector:
|
||||
attributes["Description"] = context.scene.IFCSelector.selection_query
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"group.edit_group", self.file, **{"group": self.file.by_id(props.active_group_id), "attributes": attributes}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_groups()
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -109,7 +191,7 @@ class RemoveGroup(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run("group.remove_group", self.file, **{"group": self.file.by_id(self.group)})
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_groups()
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -122,9 +204,7 @@ class EnableEditingGroup(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMGroupProperties
|
||||
props.group_attributes.clear()
|
||||
|
||||
blenderbim.bim.helper.import_attributes("IfcGroup", props.group_attributes, Data.groups[self.group])
|
||||
|
||||
props.active_group_id = self.group
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -247,5 +327,5 @@ class UpdateGroup(bpy.types.Operator):
|
||||
}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_groups()
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
return {"FINISHED"}
|
||||
@@ -18,7 +18,10 @@
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.helper import import_attributes
|
||||
from ifcopenshell.api.group.data import Data
|
||||
from bpy.types import PropertyGroup
|
||||
import json
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
@@ -31,12 +34,17 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
class ExpandedGroups(StrProperty):
|
||||
json_string: StringProperty(name="JSON String", default="{}")
|
||||
|
||||
class Group(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
selection_query: StringProperty(name="Selection Query")
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||
has_children: BoolProperty(name="Has Children")
|
||||
tree_depth: IntProperty(name="Tree Depth")
|
||||
|
||||
|
||||
class BIMGroupProperties(PropertyGroup):
|
||||
group_attributes: CollectionProperty(name="Group Attributes", type=Attribute)
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
@@ -44,3 +52,4 @@ class BIMGroupProperties(PropertyGroup):
|
||||
groups: CollectionProperty(name="Groups", type=Group)
|
||||
active_group_index: IntProperty(name="Active Group Index")
|
||||
active_group_id: IntProperty(name="Active Group Id")
|
||||
|
||||
@@ -115,9 +115,23 @@ class BIM_PT_object_groups(Panel):
|
||||
|
||||
|
||||
class BIM_UL_groups(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
for i in range(0, item.tree_depth):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.has_children:
|
||||
op = row.operator(
|
||||
"bim.toggle_group",
|
||||
icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT",
|
||||
text="",
|
||||
emboss=False)
|
||||
op.ifc_definition_id = item.ifc_definition_id
|
||||
op.index = index
|
||||
op.option = "Collapse" if item.is_expanded else "Expand"
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
|
||||
row.label(text=f"*{item.name}") if item.selection_query != "" else row.label(text=item.name)
|
||||
group_id = item.ifc_definition_id
|
||||
if context.scene.BIMGroupProperties.active_group_id == group_id:
|
||||
@@ -128,6 +142,8 @@ class BIM_UL_groups(UIList):
|
||||
elif context.scene.BIMGroupProperties.active_group_id:
|
||||
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.group = group_id
|
||||
op = row.operator("bim.add_group_to_group", text="", icon="ADD")
|
||||
op.group = group_id
|
||||
op = row.operator("bim.remove_group", text="", icon="X")
|
||||
op.group = group_id
|
||||
if item.selection_query != "":
|
||||
@@ -139,6 +155,8 @@ class BIM_UL_groups(UIList):
|
||||
op.group = group_id
|
||||
op = row.operator("bim.enable_editing_group", text="", icon="GREASEPENCIL")
|
||||
op.group = group_id
|
||||
op = row.operator("bim.add_group_to_group", text="", icon="ADD")
|
||||
op.group = group_id
|
||||
op = row.operator("bim.remove_group", text="", icon="X")
|
||||
op.group = group_id
|
||||
if item.selection_query != "":
|
||||
@@ -148,10 +166,23 @@ class BIM_UL_groups(UIList):
|
||||
|
||||
|
||||
class BIM_UL_object_groups(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"*{item.name}") if item.selection_query != "" else row.label(text=item.name)
|
||||
for i in range(0, item.tree_depth):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.has_children:
|
||||
op = row.operator(
|
||||
"bim.toggle_group",
|
||||
icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT",
|
||||
text="",
|
||||
emboss=False)
|
||||
op.ifc_definition_id = item.ifc_definition_id
|
||||
op.index = index
|
||||
op.option = "Collapse" if item.is_expanded else "Expand"
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
row.label(text=item.name)
|
||||
op = row.operator("bim.remove_group", text="", icon="X")
|
||||
op.group = item.ifc_definition_id
|
||||
op = row.operator("bim.assign_group", text="", icon="ADD")
|
||||
|
||||
@@ -25,6 +25,7 @@ from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import close_operator_panel
|
||||
from blenderbim.bim.module.group import ui
|
||||
from itertools import cycle
|
||||
from bpy.types import PropertyGroup, Operator
|
||||
from bpy.props import (
|
||||
@@ -646,23 +647,48 @@ class AddToIfcGroup(Operator):
|
||||
bl_idname = "bim.add_to_ifc_group"
|
||||
bl_label = "Add to IFC Group"
|
||||
group_name: StringProperty(name="Group Name")
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
bpy.ops.bim.load_groups()
|
||||
return context.window_manager.invoke_props_dialog(self, width=400)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "group_name")
|
||||
self.props = context.scene.BIMGroupProperties
|
||||
row = self.layout.row()
|
||||
row.operator("bim.add_group")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_groups",
|
||||
"",
|
||||
self.props,
|
||||
"groups",
|
||||
self.props,
|
||||
"active_group_index",
|
||||
)
|
||||
|
||||
if self.props.active_group_id:
|
||||
for attribute in self.props.group_attributes:
|
||||
if attribute.name in ["Name", "Description"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
ifc_selector = context.scene.IfcSelectorProperties
|
||||
selector_query_syntax = ifc_selector.selector_query_syntax
|
||||
active_group_index = self.props.active_group_index
|
||||
ifc_definition_id = self.props.groups[active_group_index].ifc_definition_id
|
||||
|
||||
group = ifcopenshell.api.run("group.add_group", self.file, **{"Name": self.group_name, "Description": f'*selector*{selector_query_syntax}*selector*'})
|
||||
objects = Selector.parse(self.file, selector_query_syntax)
|
||||
|
||||
ifcopenshell.api.run("group.assign_group", self.file, **{"product": objects, "group": group})
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.enable_editing_group(group=ifc_definition_id)
|
||||
|
||||
selector_query_syntax = context.scene.IfcSelectorProperties.selector_query_syntax
|
||||
|
||||
for attribute in self.props.group_attributes:
|
||||
if attribute.name == "Description":
|
||||
if "*selector*" not in attribute.string_value:
|
||||
attribute.string_value += f" *selector*{selector_query_syntax}*selector*"
|
||||
else:
|
||||
new_description = attribute.string_value.split("*selector*")
|
||||
new_description[1] = selector_query_syntax
|
||||
attribute.string_value = "*selector*".join(new_description)
|
||||
|
||||
bpy.ops.bim.edit_group()
|
||||
bpy.ops.bim.disable_group_editing_ui()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -38,6 +38,8 @@ class Data:
|
||||
for product in rel.RelatedObjects:
|
||||
cls.products.setdefault(product.id(), []).append(group.id())
|
||||
data = group.get_info()
|
||||
data["HasAssignments"] = group.HasAssignments
|
||||
data["IsGroupedBy"] = group.IsGroupedBy
|
||||
del data["OwnerHistory"]
|
||||
cls.groups[group.id()] = data
|
||||
cls.is_loaded = True
|
||||
|
||||
Reference in New Issue
Block a user