From cb2995a25556b04aa9513d35c86ee4639847c469 Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Wed, 18 Aug 2021 23:42:41 +0200 Subject: [PATCH] BlenderBim : Improve BCF Project UX (#1668) * Store fields on BCFProperties class rather than individual topics * Clear input fields on active topic change * BCF : Fix resetting comment has_related_viewpoint default value * Prevent global function use from import * Use dynamic enum to choose related topic * Add newline at eof * Show only .bcf and .bcfzip files when saving project --- .../blenderbim/bim/module/bcf/operator.py | 107 +++++++----------- .../blenderbim/bim/module/bcf/prop.py | 58 +++++++--- .../blenderbim/bim/module/bcf/ui.py | 26 ++--- 3 files changed, 101 insertions(+), 90 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/bcf/operator.py b/src/blenderbim/blenderbim/bim/module/bcf/operator.py index f49ff7fdbf..12ed1685b7 100644 --- a/src/blenderbim/blenderbim/bim/module/bcf/operator.py +++ b/src/blenderbim/blenderbim/bim/module/bcf/operator.py @@ -26,7 +26,7 @@ import numpy as np import ifcopenshell import ifcopenshell.util.unit from . import bcfstore -from blenderbim.bim.module.bcf.prop import getBcfViewpoints +import blenderbim.bim.module.bcf.prop as bcf_prop from blenderbim.bim.ifc import IfcStore from math import radians, degrees, atan, tan, cos, sin from mathutils import Vector, Matrix, Euler, geometry @@ -252,6 +252,7 @@ class SaveBcfProject(bpy.types.Operator): bl_label = "Save BCF Project" bl_options = {"REGISTER", "UNDO"} filepath: bpy.props.StringProperty(subtype="FILE_PATH") + filter_glob: bpy.props.StringProperty(default="*.bcf;*.bcfzip", options={"HIDDEN"}) def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() @@ -286,8 +287,7 @@ class AddBcfBimSnippet(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.BCFProperties - return all((getattr(props.topics[props.active_topic_index], attr, False) for attr in ( + return all((getattr(context.scene.BCFProperties, attr, False) for attr in ( "bim_snippet_reference", "bim_snippet_schema", "bim_snippet_type" @@ -299,11 +299,14 @@ class AddBcfBimSnippet(bpy.types.Operator): blender_topic = props.topics[props.active_topic_index] topic = bcfxml.topics[blender_topic.name] bim_snippet = bcf.v2.data.BimSnippet() - bim_snippet.reference = blender_topic.bim_snippet_reference - bim_snippet.reference_schema = blender_topic.bim_snippet_schema - bim_snippet.snippet_type = blender_topic.bim_snippet_type + bim_snippet.reference = props.bim_snippet_reference + bim_snippet.reference_schema = props.bim_snippet_schema + bim_snippet.snippet_type = props.bim_snippet_type bcfxml.add_bim_snippet(topic, bim_snippet) bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index) + bim_snippet.reference = "" + bim_snippet.reference_schema = "" + bim_snippet.snippet_type = "" return {"FINISHED"} @@ -314,33 +317,14 @@ class AddBcfRelatedTopic(bpy.types.Operator): @classmethod def poll(cls, context): - bcfxml = bcfstore.BcfStore.get_bcfxml() - props = context.scene.BCFProperties - blender_topic = props.topics[props.active_topic_index] - if not blender_topic.related_topic: - return False - if blender_topic.related_topic == blender_topic.title: - # Prevent adding self as related topic - return False - related_topic = None - for topic in bcfxml.topics.values(): - if topic.title == blender_topic.related_topic: - related_topic = bcf.v2.data.RelatedTopic() - related_topic.guid = topic.guid - break - if not related_topic: - return False - if str(related_topic.guid) in [t.name for t in blender_topic.related_topics]: - # Prevent adding the same related topic more than once - return False - return True + return bcf_prop.get_related_topics(None, context) def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() props = context.scene.BCFProperties blender_topic = props.topics[props.active_topic_index] related_topic = bcf.v2.data.RelatedTopic() - related_topic.guid = next((t for t in bcfxml.topics.values() if t.title == blender_topic.related_topic)).guid + related_topic.guid = props.related_topic topic = bcfxml.topics[blender_topic.name] topic.related_topics.append(related_topic) bcfxml.edit_topic(topic) @@ -355,8 +339,7 @@ class AddBcfHeaderFile(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.BCFProperties - return props.topics[props.active_topic_index].file_reference + return context.scene.BCFProperties.file_reference def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() @@ -364,15 +347,18 @@ class AddBcfHeaderFile(bpy.types.Operator): blender_topic = props.topics[props.active_topic_index] topic = bcfxml.topics[blender_topic.name] header_file = bcf.v2.data.HeaderFile() - header_file.reference = blender_topic.file_reference + header_file.reference = props.file_reference if not os.path.exists(header_file.reference): header_file.filename = header_file.reference - if len(blender_topic.file_ifc_project) == 22: - header_file.ifc_project = blender_topic.file_ifc_project - if len(blender_topic.file_ifc_spatial_structure_element) == 22: - header_file.ifc_spatial_structure_element = blender_topic.file_ifc_spatial_structure_element + if len(props.file_ifc_project) == 22: + header_file.ifc_project = props.file_ifc_project + if len(props.file_ifc_spatial_structure_element) == 22: + header_file.ifc_spatial_structure_element = props.file_ifc_spatial_structure_element bcfxml.add_file(topic, header_file) props.active_topic_index = props.active_topic_index # refreshes the BCF Topic + props.file_reference = "" + props.file_ifc_project = "" + props.file_ifc_spatial_structure_element = "" return {"FINISHED"} @@ -386,6 +372,7 @@ class ViewBcfTopic(bpy.types.Operator): for index, topic in enumerate(context.scene.BCFProperties.topics): if topic.name.lower() == self.topic_guid.lower(): context.scene.BCFProperties.active_topic_index = index + break return {"FINISHED"} @@ -448,7 +435,7 @@ class RemoveBcfViewpoint(bpy.types.Operator): @classmethod def poll(cls, context): - return getBcfViewpoints(None, context) + return bcf_prop.getBcfViewpoints(None, context) def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() @@ -484,18 +471,17 @@ class AddBcfReferenceLink(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.BCFProperties - return props.topics[props.active_topic_index].reference_link + return context.scene.BCFProperties.reference_link def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() props = context.scene.BCFProperties blender_topic = props.topics[props.active_topic_index] topic = bcfxml.topics[blender_topic.name] - topic.reference_links.append(blender_topic.reference_link) + topic.reference_links.append(props.reference_link) bcfxml.edit_topic(topic) bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index) - blender_topic.reference_link = "" + props.reference_link = "" return {"FINISHED"} @@ -506,8 +492,7 @@ class AddBcfDocumentReference(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.BCFProperties - return props.topics[props.active_topic_index].document_reference + return context.scene.BCFProperties.document_reference def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() @@ -515,12 +500,12 @@ class AddBcfDocumentReference(bpy.types.Operator): blender_topic = props.topics[props.active_topic_index] topic = bcfxml.topics[blender_topic.name] document_reference = bcf.v2.data.DocumentReference() - document_reference.referenced_document = blender_topic.document_reference - document_reference.description = blender_topic.document_reference_description or None + document_reference.referenced_document = props.document_reference + document_reference.description = props.document_reference_description or None bcfxml.add_document_reference(topic, document_reference) bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index) - blender_topic.document_reference = "" - blender_topic.document_reference_description = "" + props.document_reference = "" + props.document_reference_description = "" return {"FINISHED"} @@ -531,8 +516,7 @@ class AddBcfLabel(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.BCFProperties - return props.topics[props.active_topic_index].label + return context.scene.BCFProperties.label def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() @@ -540,10 +524,10 @@ class AddBcfLabel(bpy.types.Operator): blender_topic = props.topics[props.active_topic_index] topic = bcfxml.topics[blender_topic.name] new = blender_topic.labels.add() - new.name = blender_topic.label - topic.labels.append(blender_topic.label) + new.name = props.label + topic.labels.append(props.label) bcfxml.edit_topic(topic) - blender_topic.label = "" + props.label = "" return {"FINISHED"} @@ -713,10 +697,9 @@ class AddBcfComment(bpy.types.Operator): @classmethod def poll(cls, context): props = context.scene.BCFProperties - topic = props.topics[props.active_topic_index] - if not topic.comment: + if not props.comment: return False - if topic.has_related_viewpoint and not getBcfViewpoints(None, context): + if props.has_related_viewpoint and not bcf_prop.getBcfViewpoints(None, context): return False return True @@ -726,12 +709,14 @@ class AddBcfComment(bpy.types.Operator): blender_topic = props.topics[props.active_topic_index] topic = bcfxml.topics[blender_topic.name] comment = bcf.v2.data.Comment() - comment.comment = blender_topic.comment - if blender_topic.has_related_viewpoint: + comment.comment = props.comment + if props.has_related_viewpoint: comment.viewpoint = bcf.v2.data.Viewpoint() comment.viewpoint.guid = blender_topic.viewpoints bcfxml.add_comment(topic, comment) bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid) + props.comment = "" + props.has_related_viewpoint = False return {"FINISHED"} @@ -1022,9 +1007,7 @@ class SelectBcfHeaderFile(bpy.types.Operator): def execute(self, context): if self.filepath: - props = context.scene.BCFProperties - topic = props.topics[props.active_topic_index] - topic.file_reference = self.filepath + context.scene.BCFProperties.file_reference = self.filepath return {"FINISHED"} def invoke(self, context, event): @@ -1040,9 +1023,7 @@ class SelectBcfBimSnippetReference(bpy.types.Operator): def execute(self, context): if self.filepath: - props = context.scene.BCFProperties - topic = props.topics[props.active_topic_index] - topic.bim_snippet_reference = self.filepath + context.scene.BCFProperties.bim_snippet_reference = self.filepath return {"FINISHED"} def invoke(self, context, event): @@ -1058,9 +1039,7 @@ class SelectBcfDocumentReference(bpy.types.Operator): def execute(self, context): if self.filepath: - props = context.scene.BCFProperties - topic = props.topics[props.active_topic_index] - topic.document_reference = self.filepath + context.scene.BCFProperties.document_reference = self.filepath return {"FINISHED"} def invoke(self, context, event): diff --git a/src/blenderbim/blenderbim/bim/module/bcf/prop.py b/src/blenderbim/blenderbim/bim/module/bcf/prop.py index 9f522227df..d21f315234 100644 --- a/src/blenderbim/blenderbim/bim/module/bcf/prop.py +++ b/src/blenderbim/blenderbim/bim/module/bcf/prop.py @@ -34,11 +34,14 @@ from bpy.props import ( bcfviewpoints_enum = None +related_topic_enum = [] def purge(): global bcfviewpoints_enum + global related_topic_enum bcfviewpoints_enum = None + related_topic_enum.clear() def updateBcfReferenceLink(self, context): @@ -76,6 +79,20 @@ def updateBcfCommentIsEditable(self, context): bpy.ops.bim.edit_bcf_comment(comment_guid = self.name) +def get_related_topics(self, context): + global related_topic + related_topic_enum.clear() + props = context.scene.BCFProperties + current_topic = props.topics[props.active_topic_index] + for topic in props.topics: + if topic == current_topic: + continue + if topic.name in current_topic.related_topics: + continue + related_topic_enum.append((topic.name, topic.title, topic.description)) + return related_topic_enum + + def refreshBcfTopic(self, context): global bcfviewpoints_enum bcfviewpoints_enum = None @@ -84,6 +101,7 @@ def refreshBcfTopic(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() topic = props.topics[props.active_topic_index] header = bcfxml.get_header(topic.name) + props.clear_input_fields() getBcfViewpoints(self, context) @@ -148,26 +166,13 @@ class BcfTopic(PropertyGroup): description: StringProperty(default="", name="Description") viewpoints: EnumProperty(items=getBcfViewpoints, name="BCF Viewpoints") files: CollectionProperty(name="Files", type=StrProperty) - file_reference: StringProperty(default="", name="Reference") - file_ifc_project: StringProperty(default="", name="IFC Project") - file_ifc_spatial_structure_element: StringProperty(default="", name="IFC Spatial Structure Element") reference_links: CollectionProperty(name="Reference Links", type=BcfReferenceLink) - reference_link: StringProperty(default="", name="Reference Link") labels: CollectionProperty(name="Labels", type=BcfLabel) - label: StringProperty(default="", name="Label") bim_snippet: PointerProperty(type=BcfBimSnippet) - bim_snippet_type: StringProperty(default="", name="Type") - bim_snippet_reference: StringProperty(default="", name="Reference") - bim_snippet_schema: StringProperty(default="", name="Schema") document_references: CollectionProperty(name="Document References", type=BcfDocumentReference) - document_reference: StringProperty(default="", name="Referenced Document") - document_reference_description: StringProperty(default="", name="Description") related_topics: CollectionProperty(name="Related Topics", type=StrProperty) - related_topic: StringProperty(default="", name="Related Topic") comments: CollectionProperty(name="Comments", type=BcfComment) is_editable: BoolProperty(name="Is Editable", default=False, update=updateBcfTopicIsEditable) - comment: StringProperty(default="", name="Comment") - has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False) class BCFProperties(PropertyGroup): @@ -177,3 +182,30 @@ class BCFProperties(PropertyGroup): author: StringProperty(default="john@doe.com", name="Author Email", update=updateBcfAuthor) topics: CollectionProperty(name="BCF Topics", type=BcfTopic) active_topic_index: IntProperty(name="Active BCF Topic Index", update=refreshBcfTopic) + file_reference: StringProperty(default="", name="Reference") + file_ifc_project: StringProperty(default="", name="IFC Project") + file_ifc_spatial_structure_element: StringProperty(default="", name="IFC Spatial Structure Element") + reference_link: StringProperty(default="", name="Reference Link") + label: StringProperty(default="", name="Label") + bim_snippet_reference: StringProperty(default="", name="Reference") + bim_snippet_type: StringProperty(default="", name="Type") + bim_snippet_schema: StringProperty(default="", name="Schema") + document_reference: StringProperty(default="", name="Referenced Document") + document_reference_description: StringProperty(default="", name="Description") + related_topic: EnumProperty(name="Related Topic", items=get_related_topics) + comment: StringProperty(default="", name="Comment") + has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False) + + def clear_input_fields(self): + self.file_reference = "" + self.file_ifc_project = "" + self.file_ifc_spatial_structure_element = "" + self.reference_link = "" + self.label = "" + self.bim_snippet_reference = "" + self.bim_snippet_type = "" + self.bim_snippet_schema = "" + self.document_reference = "" + self.document_reference_description = "" + self.comment = "" + self.has_related_viewpoint = False diff --git a/src/blenderbim/blenderbim/bim/module/bcf/ui.py b/src/blenderbim/blenderbim/bim/module/bcf/ui.py index 35f11d9773..4aeb717a7c 100644 --- a/src/blenderbim/blenderbim/bim/module/bcf/ui.py +++ b/src/blenderbim/blenderbim/bim/module/bcf/ui.py @@ -151,12 +151,12 @@ class BIM_PT_bcf_metadata(Panel): row.label(text=f.ifc_spatial_structure_element) row = layout.row(align=True) - row.prop(topic, "file_reference") + row.prop(props, "file_reference") row.operator("bim.select_bcf_header_file", icon="FILE_FOLDER", text="") row = layout.row() - row.prop(topic, "file_ifc_project") + row.prop(props, "file_ifc_project") row = layout.row() - row.prop(topic, "file_ifc_spatial_structure_element") + row.prop(props, "file_ifc_spatial_structure_element") row = layout.row() row.operator("bim.add_bcf_header_file") @@ -168,7 +168,7 @@ class BIM_PT_bcf_metadata(Panel): row.operator("bim.open_uri", icon="URL", text="").uri = link.name row.operator("bim.remove_bcf_reference_link", icon="X", text="").index = index row = layout.row() - row.prop(topic, "reference_link") + row.prop(props, "reference_link") row = layout.row() row.operator("bim.add_bcf_reference_link") @@ -178,7 +178,7 @@ class BIM_PT_bcf_metadata(Panel): row.prop(label, "name", text="") row.operator("bim.remove_bcf_label", icon="X", text="").index = index row = layout.row() - row.prop(topic, "label") + row.prop(props, "label") row = layout.row() row.operator("bim.add_bcf_label") @@ -199,12 +199,12 @@ class BIM_PT_bcf_metadata(Panel): row.operator("bim.remove_bcf_bim_snippet", icon="X", text="") else: row = layout.row(align=True) - row.prop(topic, "bim_snippet_reference") + row.prop(props, "bim_snippet_reference") row.operator("bim.select_bcf_bim_snippet_reference", icon="FILE_FOLDER", text="") row = layout.row() - row.prop(topic, "bim_snippet_type") + row.prop(props, "bim_snippet_type") row = layout.row() - row.prop(topic, "bim_snippet_schema") + row.prop(props, "bim_snippet_schema") row = layout.row() row.operator("bim.add_bcf_bim_snippet") @@ -222,10 +222,10 @@ class BIM_PT_bcf_metadata(Panel): row = box.row(align=True) row.prop(doc, "description") row = layout.row(align=True) - row.prop(topic, "document_reference") + row.prop(props, "document_reference") row.operator("bim.select_bcf_document_reference", icon="FILE_FOLDER", text="") row = layout.row() - row.prop(topic, "document_reference_description") + row.prop(props, "document_reference_description") row = layout.row() row.operator("bim.add_bcf_document_reference") @@ -235,7 +235,7 @@ class BIM_PT_bcf_metadata(Panel): row.operator("bim.view_bcf_topic", text=bcfxml.topics[related_topic.name.lower()].title).topic_guid = related_topic.name row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index row = layout.row() - row.prop(topic, "related_topic") + row.prop(props, "related_topic") row = layout.row() row.operator("bim.add_bcf_related_topic") @@ -295,8 +295,8 @@ class BIM_PT_bcf_comments(Panel): col.label(text=" ".join(line_words)) row = layout.row() - row.prop(topic, "comment") + row.prop(props, "comment") row = layout.row() - row.prop(topic, "has_related_viewpoint") + row.prop(props, "has_related_viewpoint") row = layout.row() row.operator("bim.add_bcf_comment")