From 8300868a9b17d3efbf9e0dd8649998fe4a9138ab Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 14 Aug 2021 12:34:18 +1000 Subject: [PATCH] New profile browser with basic profile edit and remove feature. See #1641. --- .../blenderbim/bim/module/profile/__init__.py | 20 +++- .../blenderbim/bim/module/profile/operator.py | 100 ++++++++++++++++++ .../blenderbim/bim/module/profile/prop.py | 29 +++++ .../blenderbim/bim/module/profile/ui.py | 67 ++++++++++++ src/blenderbim/generate_demo_library.py | 2 + .../ifcopenshell/api/material/edit_profile.py | 1 + .../api/material/remove_profile.py | 1 + .../ifcopenshell/api/profile/edit_profile.py | 10 ++ .../api/profile/remove_profile.py | 10 ++ 9 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 src/blenderbim/blenderbim/bim/module/profile/operator.py create mode 100644 src/blenderbim/blenderbim/bim/module/profile/prop.py create mode 100644 src/blenderbim/blenderbim/bim/module/profile/ui.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py diff --git a/src/blenderbim/blenderbim/bim/module/profile/__init__.py b/src/blenderbim/blenderbim/bim/module/profile/__init__.py index 3f64d0c93c..8b4d495a80 100644 --- a/src/blenderbim/blenderbim/bim/module/profile/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/profile/__init__.py @@ -1,9 +1,23 @@ -classes = () +import bpy +from . import ui, prop, operator + +classes = ( + operator.LoadProfiles, + operator.DisableProfileEditingUI, + operator.RemoveProfileDef, + operator.EnableEditingProfile, + operator.DisableEditingProfile, + operator.EditProfile, + prop.Profile, + prop.BIMProfileProperties, + ui.BIM_PT_profiles, + ui.BIM_UL_profiles, +) def register(): - pass + bpy.types.Scene.BIMProfileProperties = bpy.props.PointerProperty(type=prop.BIMProfileProperties) def unregister(): - pass + del bpy.types.Scene.BIMProfileProperties diff --git a/src/blenderbim/blenderbim/bim/module/profile/operator.py b/src/blenderbim/blenderbim/bim/module/profile/operator.py new file mode 100644 index 0000000000..c5999173f4 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/profile/operator.py @@ -0,0 +1,100 @@ +import bpy +import ifcopenshell.api +import blenderbim.bim.helper +from blenderbim.bim.ifc import IfcStore +from ifcopenshell.api.profile.data import Data + + +class LoadProfiles(bpy.types.Operator): + bl_idname = "bim.load_profiles" + bl_label = "Load Profiles" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + props = context.scene.BIMProfileProperties + while len(props.profiles) > 0: + props.profiles.remove(0) + + for ifc_definition_id, profile in Data.profiles.items(): + new = props.profiles.add() + new.ifc_definition_id = ifc_definition_id + new.name = profile.get("ProfileName", "") or "Unnamed" + + props.is_editing = True + bpy.ops.bim.disable_editing_profile() + return {"FINISHED"} + + +class DisableProfileEditingUI(bpy.types.Operator): + bl_idname = "bim.disable_profile_editing_ui" + bl_label = "Disable Profile Editing UI" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + context.scene.BIMProfileProperties.is_editing = False + return {"FINISHED"} + + +class RemoveProfileDef(bpy.types.Operator): + bl_idname = "bim.remove_profile_def" + bl_label = "Remove Profile Definition" + bl_options = {"REGISTER", "UNDO"} + profile: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMProfileProperties + self.file = IfcStore.get_file() + ifcopenshell.api.run("profile.remove_profile", self.file, **{"profile": self.file.by_id(self.profile)}) + Data.load(self.file) + bpy.ops.bim.load_profiles() + return {"FINISHED"} + + +class EnableEditingProfile(bpy.types.Operator): + bl_idname = "bim.enable_editing_profile" + bl_label = "Enable Editing Profile" + bl_options = {"REGISTER", "UNDO"} + profile: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMProfileProperties + while len(props.profile_attributes) > 0: + props.profile_attributes.remove(0) + + data = Data.profiles[self.profile] + + blenderbim.bim.helper.import_attributes(data["type"], props.profile_attributes, data) + props.active_profile_id = self.profile + return {"FINISHED"} + + +class DisableEditingProfile(bpy.types.Operator): + bl_idname = "bim.disable_editing_profile" + bl_label = "Disable Editing Profile" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + context.scene.BIMProfileProperties.active_profile_id = 0 + return {"FINISHED"} + + +class EditProfile(bpy.types.Operator): + bl_idname = "bim.edit_profile" + bl_label = "Edit Profile" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMProfileProperties + attributes = blenderbim.bim.helper.export_attributes(props.profile_attributes) + self.file = IfcStore.get_file() + profile = self.file.by_id(props.active_profile_id) + ifcopenshell.api.run("profile.edit_profile", self.file, **{"profile": profile, "attributes": attributes}) + Data.load(IfcStore.get_file()) + bpy.ops.bim.load_profiles() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/profile/prop.py b/src/blenderbim/blenderbim/bim/module/profile/prop.py new file mode 100644 index 0000000000..b2392e8b06 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/profile/prop.py @@ -0,0 +1,29 @@ +import bpy +import ifcopenshell +import ifcopenshell.util.schema +import ifcopenshell.util.attribute +from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.prop import StrProperty, Attribute +from bpy.types import PropertyGroup +from bpy.props import ( + PointerProperty, + StringProperty, + EnumProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + CollectionProperty, +) + +class Profile(PropertyGroup): + name: StringProperty(name="Name") + ifc_definition_id: IntProperty(name="IFC Definition ID") + + +class BIMProfileProperties(PropertyGroup): + is_editing: BoolProperty(name="Is Editing") + profiles: CollectionProperty(name="Profiles", type=Profile) + active_profile_index: IntProperty(name="Active Profile Index") + active_profile_id: IntProperty(name="Active Profile Id") + profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/profile/ui.py b/src/blenderbim/blenderbim/bim/module/profile/ui.py new file mode 100644 index 0000000000..3a0dcec9a7 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/profile/ui.py @@ -0,0 +1,67 @@ +import blenderbim.bim.helper +from bpy.types import Panel, UIList +from blenderbim.bim.ifc import IfcStore +from ifcopenshell.api.profile.data import Data + + +class BIM_PT_profiles(Panel): + bl_label = "IFC Profiles" + bl_idname = "BIM_PT_profiles" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + @classmethod + def poll(cls, context): + file = IfcStore.get_file() + return file + + def draw(self, context): + self.file = IfcStore.get_file() + if not Data.is_loaded: + Data.load(self.file) + self.props = context.scene.BIMProfileProperties + + row = self.layout.row(align=True) + row.label(text="{} Profiles Found".format(len(Data.profiles)), icon="SNAP_GRID") + if self.props.is_editing: + row.operator("bim.disable_profile_editing_ui", text="", icon="CANCEL") + else: + row.operator("bim.load_profiles", text="", icon="GREASEPENCIL") + + if not self.props.is_editing: + return + + self.layout.template_list( + "BIM_UL_profiles", + "", + self.props, + "profiles", + self.props, + "active_profile_index", + ) + + if self.props.active_profile_id: + self.draw_editable_ui(context) + + def draw_editable_ui(self, context): + blenderbim.bim.helper.draw_attributes(self.props.profile_attributes, self.layout) + + +class BIM_UL_profiles(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + props = context.scene.BIMProfileProperties + if item: + row = layout.row(align=True) + row.label(text=item.name or "Unnamed") + + if props.active_profile_id == item.ifc_definition_id: + row.operator("bim.edit_profile", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_profile", text="", icon="CANCEL") + elif props.active_profile_id: + row.operator("bim.remove_profile_def", text="", icon="X").profile = item.ifc_definition_id + else: + op = row.operator("bim.enable_editing_profile", text="", icon="GREASEPENCIL") + op.profile = item.ifc_definition_id + row.operator("bim.remove_profile_def", text="", icon="X").profile = item.ifc_definition_id diff --git a/src/blenderbim/generate_demo_library.py b/src/blenderbim/generate_demo_library.py index 0904fd8c84..cc4213d0f8 100644 --- a/src/blenderbim/generate_demo_library.py +++ b/src/blenderbim/generate_demo_library.py @@ -58,6 +58,7 @@ class LibraryGenerator: profile = self.file.create_entity( "IfcIShapeProfileDef", + ProfileName="DEMO-I", ProfileType="AREA", OverallWidth=0.1, OverallDepth=0.2, @@ -69,6 +70,7 @@ class LibraryGenerator: profile = self.file.create_entity( "IfcCShapeProfileDef", + ProfileName="DEMO-C", ProfileType="AREA", Depth=0.2, Width=0.1, diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py index 5ab716644b..00777c6dba 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py @@ -11,6 +11,7 @@ class Usecase(): self.settings[key] = value def execute(self): + # TODO: don't also edit the profile def in this usecase for name, value in self.settings["attributes"].items(): setattr(self.settings["profile"], name, value) self.settings["profile"].Material = self.settings["material"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py index 33b00ce347..679ff905f8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py @@ -7,3 +7,4 @@ class Usecase: def execute(self): self.file.remove(self.settings["profile"]) + # TODO: deep purge diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py new file mode 100644 index 0000000000..1dd4eac2b8 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py @@ -0,0 +1,10 @@ +class Usecase(): + def __init__(self, file, **settings): + self.file = file + self.settings = {"profile": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, value in self.settings["attributes"].items(): + setattr(self.settings["profile"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py new file mode 100644 index 0000000000..679ff905f8 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py @@ -0,0 +1,10 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"profile": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.file.remove(self.settings["profile"]) + # TODO: deep purge