mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Dynamic Attribute Info (#1990)
* Add info mode toggle in addon prefs * Add documentation property to Attribute * Add webbrowser open operator * Populate attribute doc on import * Add attribute doc popup operator * use path_resolve instead of eval * Rename operator * Add description field * Panel UI is managed by operator * Revert "Panel UI is managed by operator" This reverts commita7bbd06865. * Revert "Revert "Panel UI is managed by operator"" This reverts commitd62115bf63. * Add info popup to ifc class panel in object properties * Implement fetching doc for non-ifc classes from jsonlike format * Draw doc from non ifc classes * Add utility function to draw info button on a given layout * Add info buttons in the create project UI * Display entity descriptions as tooltips in enum items * Move predefined types fetching to data class * Remove leftover prop code * Use tool to get schema
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user