diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index ca9b679eca..74f02a4075 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -94,6 +94,7 @@ classes = [ operator.SelectSchemaDir, operator.SelectURIAttribute, operator.EditBlenderCollection, + operator.BIM_OT_open_webbrowser, prop.StrProperty, operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty prop.ObjProperty, diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index 4aa5231c1c..f45d1d4c92 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -23,9 +23,11 @@ import math import zipfile import ifcopenshell import ifcopenshell.util.attribute +from ifcopenshell.util.doc import get_entity_doc, get_attribute_doc, get_property_set_doc, get_property_doc from mathutils import geometry from mathutils import Vector from blenderbim.bim.ifc import IfcStore +import blenderbim.tool as tool def draw_attributes(props, layout, copy_operator=None): @@ -124,6 +126,25 @@ def prop_with_search(layout, data, prop_name, **kwargs): op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM") op.prop_name = prop_name + if bpy.context.preferences.addons["blenderbim"].preferences.info_mode: + schema = tool.Ifc.schema() + if schema is not None: + schema = str(schema) + schema = next(identifier for identifier in IfcStore.schema_identifiers if identifier in schema) + docs = {} + entity = getattr(data, prop_name) + if entity: + try: + docs = get_entity_doc(schema, entity) + except KeyError: + # TODO : support attributes, pset, etc. + pass + op_row = row.row(align=True) + url = docs.get("spec_url", "") + url_op = op_row.operator("bim.open_webbrowser", icon="URL", text="") + url_op.url = url + op_row.enabled = bool(url) + def get_enum_items(data, prop_name, context): # Retrieve items from a dynamic EnumProperty, which is otherwise not supported diff --git a/src/blenderbim/blenderbim/bim/module/root/data.py b/src/blenderbim/blenderbim/bim/module/root/data.py index dda12cdac8..2eaa6e1c77 100644 --- a/src/blenderbim/blenderbim/bim/module/root/data.py +++ b/src/blenderbim/blenderbim/bim/module/root/data.py @@ -21,6 +21,7 @@ import bpy import ifcopenshell.util.element import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.prop import get_ifc_entity_description, get_predefined_type_descriptions def refresh(): @@ -42,6 +43,7 @@ class IfcClassData: cls.data["has_entity"] = cls.has_entity() cls.data["name"] = cls.name() cls.data["ifc_class"] = cls.ifc_class() + cls.data["ifc_predefined_types"] = cls.ifc_predefined_types() @classmethod def ifc_products(cls): @@ -65,7 +67,7 @@ class IfcClassData: "IfcAnnotation", "IfcRelSpaceBoundary", ] - return [(e, e, "") for e in products] + return [(e, e, get_ifc_entity_description(e)) for e in products] @classmethod def ifc_classes(cls): @@ -75,7 +77,21 @@ class IfcClassData: names = [d.name() for d in declarations] if ifc_product == "IfcElementType": names.extend(("IfcDoorStyle", "IfcWindowStyle")) - return [(c, c, "") for c in sorted(names)] + + return [(c, c, get_ifc_entity_description(c)) for c in sorted(names)] + + @classmethod + def ifc_predefined_types(cls): + types_enum = [] + ifc_class = bpy.context.scene.BIMRootProperties.ifc_class + declaration = tool.Ifc.schema().declaration_by_name(ifc_class) + for attribute in declaration.attributes(): + if attribute.name() == "PredefinedType": + declared_type = attribute.type_of_attribute().declared_type() + descriptions = get_predefined_type_descriptions(declared_type.name()) + types_enum.extend([(e, e, descriptions.get(e, "")) for e in declared_type.enumeration_items()]) + break + return types_enum @classmethod def ifc_classes_suggestions(cls): diff --git a/src/blenderbim/blenderbim/bim/module/root/prop.py b/src/blenderbim/blenderbim/bim/module/root/prop.py index 53548a7829..34217ffc4a 100644 --- a/src/blenderbim/blenderbim/bim/module/root/prop.py +++ b/src/blenderbim/blenderbim/bim/module/root/prop.py @@ -20,7 +20,6 @@ import bpy import ifcopenshell import ifcopenshell.util.schema from blenderbim.bim.module.root.data import IfcClassData -from blenderbim.bim.ifc import IfcStore from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -33,29 +32,15 @@ from bpy.props import ( CollectionProperty, ) -types_enum = [] -classes_enum = [] - def purge(): - global types_enum - types_enum = [] - global classes_enum - classes_enum = [] + pass -def getIfcPredefinedTypes(self, context): - global types_enum - file = IfcStore.get_file() - if len(types_enum) < 1 and file: - declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class) - for attribute in declaration.attributes(): - if attribute.name() == "PredefinedType": - types_enum.extend( - [(e, e, "") for e in attribute.type_of_attribute().declared_type().enumeration_items()] - ) - break - return types_enum +def get_ifc_predefined_types(self, context): + if not IfcClassData.is_loaded: + IfcClassData.load() + return IfcClassData.data["ifc_predefined_types"] def refresh_classes(self, context): @@ -64,10 +49,9 @@ def refresh_classes(self, context): context.scene.BIMRootProperties.ifc_class = enum[0][0] -def refreshPredefinedTypes(self, context): - global types_enum - types_enum.clear() - enum = getIfcPredefinedTypes(self, context) +def refresh_predefined_types(self, context): + IfcClassData.load() + enum = get_ifc_predefined_types(self, context) if enum: context.scene.BIMRootProperties.ifc_predefined_type = enum[0][0] @@ -103,8 +87,8 @@ def get_contexts(self, context): class BIMRootProperties(PropertyGroup): contexts: EnumProperty(items=get_contexts, name="Contexts") ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes) - ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refreshPredefinedTypes) - ifc_predefined_type: EnumProperty(items=getIfcPredefinedTypes, name="Predefined Type", default=None) + ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refresh_predefined_types) + ifc_predefined_type: EnumProperty(items=get_ifc_predefined_types, name="Predefined Type", default=None) ifc_userdefined_type: StringProperty(name="Userdefined Type") getter_enum_suggestions = { diff --git a/src/blenderbim/blenderbim/bim/module/root/ui.py b/src/blenderbim/blenderbim/bim/module/root/ui.py index 18ca900cea..df06f4bae0 100644 --- a/src/blenderbim/blenderbim/bim/module/root/ui.py +++ b/src/blenderbim/blenderbim/bim/module/root/ui.py @@ -54,7 +54,7 @@ class BIM_PT_class(Panel): row.operator("bim.disable_reassign_class", icon="CANCEL", text="") self.draw_class_dropdowns( context, - root_prop.getIfcPredefinedTypes(context.scene.BIMRootProperties, context), + root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context), is_reassigning_class=True, ) else: @@ -67,7 +67,7 @@ class BIM_PT_class(Panel): if IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcRoot"): row.operator("bim.enable_reassign_class", icon="GREASEPENCIL", text="") else: - ifc_predefined_types = root_prop.getIfcPredefinedTypes(context.scene.BIMRootProperties, context) + ifc_predefined_types = root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context) self.draw_class_dropdowns(context, ifc_predefined_types) row = self.layout.row(align=True) op = row.operator("bim.assign_class") diff --git a/src/blenderbim/blenderbim/bim/operator.py b/src/blenderbim/blenderbim/bim/operator.py index 25ecb1cc1f..671a7eb492 100644 --- a/src/blenderbim/blenderbim/bim/operator.py +++ b/src/blenderbim/blenderbim/bim/operator.py @@ -429,6 +429,20 @@ class RemoveIfcFile(bpy.types.Operator): return {"FINISHED"} +class BIM_OT_open_webbrowser(bpy.types.Operator): + bl_idname = "bim.open_webbrowser" + bl_description = "Open the URL in your Web Browser" + bl_label = "Open URL" + + url: bpy.props.StringProperty() + + def execute(self, context): + import webbrowser + + webbrowser.open(self.url) + return {"FINISHED"} + + class SelectExternalMaterialDir(bpy.types.Operator): bl_idname = "bim.select_external_material_dir" bl_label = "Select Material File" diff --git a/src/blenderbim/blenderbim/bim/prop.py b/src/blenderbim/blenderbim/bim/prop.py index f6beae20c4..e4f2889a21 100644 --- a/src/blenderbim/blenderbim/bim/prop.py +++ b/src/blenderbim/blenderbim/bim/prop.py @@ -16,15 +16,18 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from pathlib import Path import os import bpy import json import importlib import ifcopenshell import ifcopenshell.util.pset +from ifcopenshell.util.doc import get_entity_doc, get_attribute_doc, get_property_set_doc, get_property_doc import blenderbim.bim.handler import blenderbim.bim.schema from blenderbim.bim.ifc import IfcStore +import blenderbim.tool as tool from collections import defaultdict from bpy.types import PropertyGroup from bpy.props import ( @@ -40,6 +43,11 @@ from bpy.props import ( cwd = os.path.dirname(os.path.realpath(__file__)) +BASE_MODULE_PATH = Path(__file__).parent +DESCRIPTION_FILES = { + "PredefinedType": BASE_MODULE_PATH / "schema" / "enum_descriptions.json", +} + materialpsetnames_enum = [] @@ -96,6 +104,22 @@ def cache_string(s): cache_string.data = {} +def get_ifc_entity_description(ifc_entity): + schema = tool.Ifc.get_schema() + if schema is not None: + schema = str(schema) + schema = next(identifier for identifier in IfcStore.schema_identifiers if identifier in schema) + docs = get_entity_doc(schema, ifc_entity) + description = docs.get("description", "") + return description + return "" + + +def get_predefined_type_descriptions(ifc_class_enum): + with open(DESCRIPTION_FILES["PredefinedType"], "r") as fi: + docs = json.load(fi) + return docs.get(ifc_class_enum, None) or {} + def getAttributeEnumValues(prop, context): # Support weird buildingSMART dictionary mappings which behave like enums diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py index 31e5ab9ea5..b290632428 100644 --- a/src/blenderbim/blenderbim/bim/ui.py +++ b/src/blenderbim/blenderbim/bim/ui.py @@ -115,6 +115,9 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False ) lock_grids_on_import: BoolProperty(name="Will lock grids upon import", default=True) + info_mode: BoolProperty( + default=True, name="Info Mode", description="Display additional helpful tooltips and information" + ) def draw(self, context): layout = self.layout @@ -174,6 +177,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): row = layout.row() row.operator("bim.configure_visibility") + layout.row().prop(self, "info_mode") def ifc_units(self, context):