From a0e5d350bdb0ec05564b4c7a52768abc58264ffa Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 24 Jun 2024 18:19:53 +0500 Subject: [PATCH] Small UI in Blender material properties tab with unlinking #4843 Example - https://i.imgur.com/7VqIowz.png This tab is needed only to indicate whether style is linked to some IFC element and to unlink it. Unlinking from Blender material makes much more sense as you can see from this tab what you're actually unlinking. Unlinking for styles now works very similar to how it works for objects. --- .../blenderbim/bim/module/style/__init__.py | 1 + .../blenderbim/bim/module/style/data.py | 54 +++++++++++++++++++ .../blenderbim/bim/module/style/operator.py | 41 +++++++++++++- .../blenderbim/bim/module/style/ui.py | 45 ++++++++++++++-- 4 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/style/__init__.py b/src/blenderbim/blenderbim/bim/module/style/__init__.py index e2ec666495..09fe3102aa 100644 --- a/src/blenderbim/blenderbim/bim/module/style/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/style/__init__.py @@ -52,6 +52,7 @@ classes = ( prop.BIMStyleProperties, ui.BIM_PT_styles, ui.BIM_UL_styles, + ui.BIM_PT_style, ) diff --git a/src/blenderbim/blenderbim/bim/module/style/data.py b/src/blenderbim/blenderbim/bim/module/style/data.py index 9a6cab4819..8e0d7571b9 100644 --- a/src/blenderbim/blenderbim/bim/module/style/data.py +++ b/src/blenderbim/blenderbim/bim/module/style/data.py @@ -20,10 +20,12 @@ import bpy import ifcopenshell import blenderbim.tool as tool from ifcopenshell.util.doc import get_entity_doc +from typing import Union def refresh(): StylesData.is_loaded = False + BlenderMaterialStyleData.is_loaded = False class StylesData: @@ -67,3 +69,55 @@ class StylesData: @classmethod def total_styles(cls): return len(tool.Ifc.get().by_type("IfcPresentationStyle")) + + +class BlenderMaterialStyleData: + style: Union[ifcopenshell.entity_instance, None] = None + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "is_linked_to_style": cls.is_linked_to_style(), + # After is_linked_to_style. + "linked_style_name": cls.linked_style_name(), + } + cls.is_loaded = True + + @classmethod + def is_linked_to_style(cls) -> bool: + ifc_file = tool.Ifc.get() + if not ifc_file: + return False + context = bpy.context + cls.linked_style = None + obj = context.active_object + if not obj: + return False + material = obj.active_material + if not material: + return False + props = material.BIMMaterialProperties + style_id = props.ifc_style_id + style = tool.Ifc.get_entity_by_id(style_id) + if not style: + return False + + # Double check that it's the right material + # and not material from other Blender session. + linked_material = tool.Ifc.get_object(style) + if linked_material != material: + return False + cls.style = style + return True + + @classmethod + def linked_style_name(cls) -> Union[str, None]: + """Return str if Blender Material is linked to IFC style or None if it's not. + + Method will return "Unnamed" if style name is None. + """ + if cls.style: + return cls.style.Name or "Unnamed" + return None diff --git a/src/blenderbim/blenderbim/bim/module/style/operator.py b/src/blenderbim/blenderbim/bim/module/style/operator.py index 79d20bacc4..08eec96e90 100644 --- a/src/blenderbim/blenderbim/bim/module/style/operator.py +++ b/src/blenderbim/blenderbim/bim/module/style/operator.py @@ -96,11 +96,48 @@ class AddStyle(bpy.types.Operator, tool.Ifc.Operator): class UnlinkStyle(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.unlink_style" bl_label = "Unlink Style" + bl_description = ( + "Unlink Blender material from it's linked IFC style.\n\n" + "You can either remove style the material is linked to from IFC or keep it. " + "Note that keeping the unlinked style in IFC might lead to unpredictable issues" + "and should be used only by advanced users" + ) bl_options = {"REGISTER", "UNDO"} - style: bpy.props.IntProperty(default=0) + blender_material: bpy.props.StringProperty(name="Blender Material") + should_delete: bpy.props.BoolProperty(name="Delete IFC Style", default=True) + + def draw(self, context): + row = self.layout.row() + row.prop(self, "should_delete") + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) def _execute(self, context): - core.unlink_style(tool.Ifc, style=tool.Ifc.get().by_id(self.style)) + # TODO: move to core.style + # Don't check blender_material and style_id as this operator is only called from UI. + assert isinstance(self.blender_material, str) # Type checker. + material = bpy.data.materials[self.blender_material] + style_id = material.BIMMaterialProperties.ifc_style_id + style = tool.Ifc.get_entity_by_id(style_id) + + # Material is linked to a style from a different project. + if not style or tool.Ifc.get_object(style) != material: + tool.Ifc.unlink(obj=material) + return {"FINISHED"} + + if self.should_delete: + # Create a copy that will be removed + # and leave user with unlinked original material. + # It's needed so we don't need to search everywhere original + # material was used and replace it with the unlinked version. + material_copy = material.copy() + tool.Ifc.unlink(element=style) + tool.Ifc.link(style, material_copy) + core.remove_style(tool.Ifc, tool.Style, style) + else: + tool.Ifc.unlink(element=style) + return {"FINISHED"} class EnableEditingStyle(bpy.types.Operator, tool.Ifc.Operator): diff --git a/src/blenderbim/blenderbim/bim/module/style/ui.py b/src/blenderbim/blenderbim/bim/module/style/ui.py index 4fbfee85fb..eb25db8035 100644 --- a/src/blenderbim/blenderbim/bim/module/style/ui.py +++ b/src/blenderbim/blenderbim/bim/module/style/ui.py @@ -21,8 +21,7 @@ import blenderbim.bim.helper import blenderbim.tool as tool from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.style.data import StylesData -from bl_ui.properties_material import MaterialButtonsPanel +from blenderbim.bim.module.style.data import StylesData, BlenderMaterialStyleData class BIM_PT_styles(Panel): @@ -67,8 +66,6 @@ class BIM_PT_styles(Panel): row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id op = row.operator("bim.assign_style_to_selected", text="", icon="BRUSH_DATA") op.style_id = style.ifc_definition_id - op = row.operator("bim.unlink_style", text="", icon="UNLINKED") - op.style = style.ifc_definition_id op = row.operator("bim.enable_editing_style", text="", icon="GREASEPENCIL") op.style = style.ifc_definition_id row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id @@ -291,3 +288,43 @@ class BIM_UL_styles(UIList): row2.label(text="", icon="LIGHT_POINT") elif style.name == "IfcExternallyDefinedSurfaceStyle": row2.label(text="", icon="APPEND_BLEND") + + +class BIM_PT_style(Panel): + bl_label = "Style" + bl_idname = "BIM_PT_style" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + + @classmethod + def poll(cls, context): + return bool(tool.Ifc.get() and context.material.BIMMaterialProperties.ifc_style_id) + + def draw(self, context): + # NOTE: this UI is needed only to indicate whether blender material is linked to IFC + # and shouldn't be overloaded with any other features (they should be added to the general Styles UI). + + if not BlenderMaterialStyleData.is_loaded: + BlenderMaterialStyleData.load() + + material = context.material + style_id = material.BIMMaterialProperties.ifc_style_id + row = self.layout.row(align=True) + + if style_id and not BlenderMaterialStyleData.data["is_linked_to_style"]: + row.label(text="Material has linked IFC from a different project.") + op = row.operator("bim.unlink_style", icon="UNLINKED", text="") + op.blender_material = material.name + return + else: + row.label(text="Material is linked to an IFC style.") + op = row.operator("bim.unlink_style", icon="UNLINKED", text="") + op.blender_material = material.name + + row = self.layout.row(align=True) + row.label(text="IFC Style ID:") + row.label(text=str(style_id)) + row = self.layout.row(align=True) + row.label(text="IFC Style Name:") + row.label(text=BlenderMaterialStyleData.data["linked_style_name"])