mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 14:33:28 +00:00
Merge pull request #2544 from Gorgious56/Tooltips
Blenderbim : Implement tooltips and online docs in the interface
This commit is contained in:
@@ -95,6 +95,7 @@ classes = [
|
|||||||
operator.SelectURIAttribute,
|
operator.SelectURIAttribute,
|
||||||
operator.EditBlenderCollection,
|
operator.EditBlenderCollection,
|
||||||
operator.BIM_OT_open_webbrowser,
|
operator.BIM_OT_open_webbrowser,
|
||||||
|
operator.BIM_OT_show_description,
|
||||||
prop.StrProperty,
|
prop.StrProperty,
|
||||||
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
|
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
|
||||||
prop.ObjProperty,
|
prop.ObjProperty,
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ import math
|
|||||||
import zipfile
|
import zipfile
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.attribute
|
import ifcopenshell.util.attribute
|
||||||
|
from ifcopenshell.util.doc import get_attribute_doc, get_predefined_type_doc, get_property_doc
|
||||||
from mathutils import geometry
|
from mathutils import geometry
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ def import_attribute(attribute, props, data, callback=None):
|
|||||||
new.is_null = data[attribute.name()] is None
|
new.is_null = data[attribute.name()] is None
|
||||||
new.is_optional = attribute.optional()
|
new.is_optional = attribute.optional()
|
||||||
new.data_type = data_type if isinstance(data_type, str) else ""
|
new.data_type = data_type if isinstance(data_type, str) else ""
|
||||||
|
new.ifc_class = data["type"]
|
||||||
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
|
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
|
||||||
|
|
||||||
if is_handled_by_callback:
|
if is_handled_by_callback:
|
||||||
@@ -100,9 +103,38 @@ def import_attribute(attribute, props, data, callback=None):
|
|||||||
elif data_type == "float":
|
elif data_type == "float":
|
||||||
new.float_value = 0.0 if new.is_null else data[attribute.name()]
|
new.float_value = 0.0 if new.is_null else data[attribute.name()]
|
||||||
elif data_type == "enum":
|
elif data_type == "enum":
|
||||||
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
enum_items = ifcopenshell.util.attribute.get_enum_items(attribute)
|
||||||
if data[attribute.name()]:
|
new.enum_items = json.dumps(enum_items)
|
||||||
new.enum_value = data[attribute.name()]
|
add_attribute_enum_items_descriptions(new, enum_items)
|
||||||
|
if data[new.name]:
|
||||||
|
new.enum_value = data[new.name]
|
||||||
|
add_attribute_description(new)
|
||||||
|
|
||||||
|
|
||||||
|
def add_attribute_enum_items_descriptions(attribute_blender, enum_items):
|
||||||
|
attribute_blender.enum_descriptions.clear()
|
||||||
|
if isinstance(enum_items, dict):
|
||||||
|
enum_items = enum_items.keys()
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
|
for enum_item in enum_items:
|
||||||
|
new_enum_description = attribute_blender.enum_descriptions.add()
|
||||||
|
try:
|
||||||
|
description = get_predefined_type_doc(version, attribute_blender.ifc_class, enum_item)
|
||||||
|
except KeyError: # TODO this only supports predefined type enums. Add support for other types of enums ?
|
||||||
|
description = ""
|
||||||
|
new_enum_description.name = description
|
||||||
|
|
||||||
|
|
||||||
|
def add_attribute_description(attribute_blender):
|
||||||
|
if not attribute_blender.name:
|
||||||
|
return
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
|
try:
|
||||||
|
description = get_attribute_doc(version, attribute_blender.ifc_class, attribute_blender.name)
|
||||||
|
except RuntimeError: # It's not an Entity Attribute. Let's try a Property Set attribute.
|
||||||
|
description = get_property_doc(version, attribute_blender.ifc_class, attribute_blender.name).get("description")
|
||||||
|
if description:
|
||||||
|
attribute_blender.description = description
|
||||||
|
|
||||||
|
|
||||||
def export_attributes(props, callback=None):
|
def export_attributes(props, callback=None):
|
||||||
@@ -119,11 +151,14 @@ def prop_with_search(layout, data, prop_name, **kwargs):
|
|||||||
# kwargs are layout.prop arguments (text, icon, etc.)
|
# kwargs are layout.prop arguments (text, icon, etc.)
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(data, prop_name, **kwargs)
|
row.prop(data, prop_name, **kwargs)
|
||||||
if len(get_enum_items(data, prop_name)) > 10:
|
try:
|
||||||
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
|
if len(get_enum_items(data, prop_name)) > 10:
|
||||||
row.context_pointer_set(name="data", data=data)
|
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
|
||||||
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
|
row.context_pointer_set(name="data", data=data)
|
||||||
op.prop_name = prop_name
|
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
|
||||||
|
op.prop_name = prop_name
|
||||||
|
except TypeError: # Prop is not iterable
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_enum_items(data, prop_name, context=None):
|
def get_enum_items(data, prop_name, context=None):
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute, get_ifc_entity_description
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.bim.prop import Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@@ -32,7 +34,8 @@ from bpy.props import (
|
|||||||
|
|
||||||
|
|
||||||
def get_available_constraint_types(self, context):
|
def get_available_constraint_types(self, context):
|
||||||
return [(c, c, get_ifc_entity_description(c)) for c in ["IfcObjective"]]
|
version = tool.Ifc.get_schema()
|
||||||
|
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in ["IfcObjective"]]
|
||||||
|
|
||||||
|
|
||||||
class Constraint(PropertyGroup):
|
class Constraint(PropertyGroup):
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import bpy
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description
|
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -66,9 +65,12 @@ class MaterialsData:
|
|||||||
"IfcMaterialProfileSet",
|
"IfcMaterialProfileSet",
|
||||||
"IfcMaterialList",
|
"IfcMaterialList",
|
||||||
]
|
]
|
||||||
if tool.Ifc.get_schema() == "IFC2X3":
|
version = tool.Ifc.get_schema()
|
||||||
|
if version == "IFC2X3":
|
||||||
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialList"]
|
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialList"]
|
||||||
return [(m, m, get_ifc_entity_description(m)) for m in material_types]
|
return [
|
||||||
|
(m, m, ifcopenshell.util.doc.get_entity_doc(version, m).get("description", "")) for m in material_types
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ObjectMaterialData:
|
class ObjectMaterialData:
|
||||||
|
|||||||
@@ -617,6 +617,7 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
|||||||
continue
|
continue
|
||||||
if attribute.name() in material_set_item_data:
|
if attribute.name() in material_set_item_data:
|
||||||
new = self.props.material_set_item_attributes.add()
|
new = self.props.material_set_item_attributes.add()
|
||||||
|
new.ifc_class = material_set_item.is_a()
|
||||||
new.name = attribute.name()
|
new.name = attribute.name()
|
||||||
new.is_null = material_set_item_data[attribute.name()] is None
|
new.is_null = material_set_item_data[attribute.name()] is None
|
||||||
new.data_type = data_type
|
new.data_type = data_type
|
||||||
@@ -628,7 +629,8 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
|||||||
new.int_value = 0 if new.is_null else material_set_item_data[attribute.name()]
|
new.int_value = 0 if new.is_null else material_set_item_data[attribute.name()]
|
||||||
elif data_type == "boolean":
|
elif data_type == "boolean":
|
||||||
new.bool_value = False if new.is_null else material_set_item_data[attribute.name()]
|
new.bool_value = False if new.is_null else material_set_item_data[attribute.name()]
|
||||||
|
blenderbim.bim.helper.add_attribute_description(new)
|
||||||
|
|
||||||
def load_profile_attributes(self, material_set_item, material_set_item_data):
|
def load_profile_attributes(self, material_set_item, material_set_item_data):
|
||||||
self.props.material_set_item_profile_attributes.clear()
|
self.props.material_set_item_profile_attributes.clear()
|
||||||
|
|
||||||
@@ -645,6 +647,7 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
|||||||
if attribute.name() in profile_data:
|
if attribute.name() in profile_data:
|
||||||
new = self.props.material_set_item_profile_attributes.add()
|
new = self.props.material_set_item_profile_attributes.add()
|
||||||
new.name = attribute.name()
|
new.name = attribute.name()
|
||||||
|
new.ifc_class = profile.is_a()
|
||||||
new.is_null = profile_data[attribute.name()] is None
|
new.is_null = profile_data[attribute.name()] is None
|
||||||
new.is_optional = attribute.optional()
|
new.is_optional = attribute.optional()
|
||||||
new.data_type = data_type
|
new.data_type = data_type
|
||||||
@@ -661,6 +664,8 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
|||||||
if profile_data[attribute.name()]:
|
if profile_data[attribute.name()]:
|
||||||
new.enum_value = profile_data[attribute.name()]
|
new.enum_value = profile_data[attribute.name()]
|
||||||
|
|
||||||
|
blenderbim.bim.helper.add_attribute_description(new)
|
||||||
|
|
||||||
# Force null to be false if the attribute is mandatory because when we first assign a profile, all of
|
# Force null to be false if the attribute is mandatory because when we first assign a profile, all of
|
||||||
# its fields are null (which is illegal).
|
# its fields are null (which is illegal).
|
||||||
# TODO: find a better solution.
|
# TODO: find a better solution.
|
||||||
|
|||||||
@@ -17,10 +17,12 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
from ifcopenshell.api.material.data import Data
|
from ifcopenshell.api.material.data import Data
|
||||||
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData
|
from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute, get_ifc_entity_description
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@@ -47,28 +49,31 @@ def purge():
|
|||||||
parameterizedprofileclasses_enum = []
|
parameterizedprofileclasses_enum = []
|
||||||
|
|
||||||
|
|
||||||
def getProfileClasses(self, context):
|
def get_profile_classes(self, context):
|
||||||
global profileclasses_enum
|
global profileclasses_enum
|
||||||
if len(profileclasses_enum) == 0 and IfcStore.get_schema():
|
if len(profileclasses_enum) == 0 and IfcStore.get_schema():
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
profileclasses_enum.clear()
|
profileclasses_enum.clear()
|
||||||
profileclasses_enum = [
|
profileclasses_enum = [
|
||||||
(t.name(), t.name(), "") for t in IfcStore.get_schema().declaration_by_name("IfcProfileDef").subtypes()
|
(t.name(), t.name(), get_entity_doc(version, t.name()).get("description", ""))
|
||||||
|
for t in IfcStore.get_schema().declaration_by_name("IfcProfileDef").subtypes()
|
||||||
]
|
]
|
||||||
return profileclasses_enum
|
return profileclasses_enum
|
||||||
|
|
||||||
|
|
||||||
def getParameterizedProfileClasses(self, context):
|
def get_parameterized_profile_classes(self, context):
|
||||||
global parameterizedprofileclasses_enum
|
global parameterizedprofileclasses_enum
|
||||||
if len(parameterizedprofileclasses_enum) == 0 and IfcStore.get_schema():
|
if len(parameterizedprofileclasses_enum) == 0 and IfcStore.get_schema():
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
parameterizedprofileclasses_enum.clear()
|
parameterizedprofileclasses_enum.clear()
|
||||||
parameterizedprofileclasses_enum = [
|
parameterizedprofileclasses_enum = [
|
||||||
(t.name(), t.name(), "")
|
(t.name(), t.name(), get_entity_doc(version, t.name()).get("description", ""))
|
||||||
for t in IfcStore.get_schema().declaration_by_name("IfcParameterizedProfileDef").subtypes()
|
for t in IfcStore.get_schema().declaration_by_name("IfcParameterizedProfileDef").subtypes()
|
||||||
]
|
]
|
||||||
for ifc_class in parameterizedprofileclasses_enum:
|
for ifc_class in parameterizedprofileclasses_enum:
|
||||||
parameterizedprofileclasses_enum.extend(
|
parameterizedprofileclasses_enum.extend(
|
||||||
[
|
[
|
||||||
(t.name(), t.name(), "")
|
(t.name(), t.name(), get_entity_doc(version, t.name()).get("description", ""))
|
||||||
for t in IfcStore.get_schema().declaration_by_name(ifc_class[0]).subtypes() or []
|
for t in IfcStore.get_schema().declaration_by_name(ifc_class[0]).subtypes() or []
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -93,10 +98,11 @@ def get_object_material_types(self, context):
|
|||||||
"IfcMaterialProfileSetUsage",
|
"IfcMaterialProfileSetUsage",
|
||||||
"IfcMaterialList",
|
"IfcMaterialList",
|
||||||
]
|
]
|
||||||
if IfcStore.get_file().schema == "IFC2X3":
|
version = tool.Ifc.get_schema()
|
||||||
|
if version == "IFC2X3":
|
||||||
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", "IfcMaterialList"]
|
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", "IfcMaterialList"]
|
||||||
materialtypes_enum.clear()
|
materialtypes_enum.clear()
|
||||||
materialtypes_enum = [(m, m, get_ifc_entity_description(m)) for m in material_types]
|
materialtypes_enum = [(m, m, get_entity_doc(version, m).get("description", "")) for m in material_types]
|
||||||
return materialtypes_enum
|
return materialtypes_enum
|
||||||
|
|
||||||
|
|
||||||
@@ -133,7 +139,7 @@ class BIMObjectMaterialProperties(PropertyGroup):
|
|||||||
name="Material Set Item Profile Attributes", type=Attribute
|
name="Material Set Item Profile Attributes", type=Attribute
|
||||||
)
|
)
|
||||||
material_set_item_material: EnumProperty(items=get_materials, name="Material")
|
material_set_item_material: EnumProperty(items=get_materials, name="Material")
|
||||||
profile_classes: EnumProperty(items=getProfileClasses, name="Profile Classes")
|
profile_classes: EnumProperty(items=get_profile_classes, name="Profile Classes")
|
||||||
parameterized_profile_classes: EnumProperty(
|
parameterized_profile_classes: EnumProperty(
|
||||||
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
|
items=get_parameterized_profile_classes, name="Parameterized Profile Classes"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,11 +22,10 @@ import json
|
|||||||
import functools
|
import functools
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description, get_predefined_type_description
|
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
AuthoringData.is_loaded = False
|
AuthoringData.is_loaded = False
|
||||||
@@ -64,17 +63,19 @@ class AuthoringData:
|
|||||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||||
names = [d.name() for d in declarations]
|
names = [d.name() for d in declarations]
|
||||||
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
||||||
return [(c, c, get_ifc_entity_description(c)) for c in sorted(names)]
|
version = tool.Ifc.get_schema()
|
||||||
|
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def type_predefined_type(cls):
|
def type_predefined_type(cls):
|
||||||
results = []
|
results = []
|
||||||
declaration = tool.Ifc().schema().declaration_by_name(cls.props.type_class)
|
declaration = tool.Ifc().schema().declaration_by_name(cls.props.type_class)
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
for attribute in declaration.attributes():
|
for attribute in declaration.attributes():
|
||||||
if attribute.name() == "PredefinedType":
|
if attribute.name() == "PredefinedType":
|
||||||
results.extend(
|
results.extend(
|
||||||
[
|
[
|
||||||
(e, e, get_predefined_type_description(cls.props.type_class, e))
|
(e, e, get_predefined_type_doc(version, cls.props.type_class, e))
|
||||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute, get_ifc_entity_description
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from blenderbim.bim.module.owner.data import OwnerData, ActorData, ObjectActorData
|
from blenderbim.bim.module.owner.data import OwnerData, ActorData, ObjectActorData
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
@@ -65,17 +67,19 @@ def update_actor_class(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def get_actor_class_enum(self, context):
|
def get_actor_class_enum(self, context):
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
return [
|
return [
|
||||||
("IfcActor", "Actor", get_ifc_entity_description("IfcActor")),
|
("IfcActor", "Actor", get_entity_doc(version, "IfcActor").get("description", "")),
|
||||||
("IfcOccupant", "Occupant", get_ifc_entity_description("IfcOccupant")),
|
("IfcOccupant", "Occupant", get_entity_doc(version, "IfcOccupant").get("description", "")),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_actor_type_enum(self, context):
|
def get_actor_type_enum(self, context):
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
return [
|
return [
|
||||||
("IfcPerson", "Person", get_ifc_entity_description("IfcPerson")),
|
("IfcPerson", "Person", get_entity_doc(version, "IfcPerson").get("description", "")),
|
||||||
("IfcOrganization", "Organisation", get_ifc_entity_description("IfcOrganization")),
|
("IfcOrganization", "Organisation", get_entity_doc(version, "IfcOrganization").get("description", "")),
|
||||||
("IfcPersonAndOrganization", "User", get_ifc_entity_description("IfcPersonAndOrganization")),
|
("IfcPersonAndOrganization", "User", get_entity_doc(version, "IfcPersonAndOrganization").get("description", "")),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -96,8 +100,14 @@ class BIMOwnerProperties(PropertyGroup):
|
|||||||
facsimile_numbers: CollectionProperty(type=StrProperty, name="Facsimile Numbers")
|
facsimile_numbers: CollectionProperty(type=StrProperty, name="Facsimile Numbers")
|
||||||
electronic_mail_addresses: CollectionProperty(type=StrProperty, name="Emails")
|
electronic_mail_addresses: CollectionProperty(type=StrProperty, name="Emails")
|
||||||
messaging_ids: CollectionProperty(type=StrProperty, name="IMs")
|
messaging_ids: CollectionProperty(type=StrProperty, name="IMs")
|
||||||
user_person: EnumProperty(items=get_user_person, name="Person")
|
user_person: EnumProperty(
|
||||||
user_organisation: EnumProperty(items=get_user_organisation, name="Organisation")
|
items=get_user_person, name="Person", description="This entity represents an individual human being."
|
||||||
|
)
|
||||||
|
user_organisation: EnumProperty(
|
||||||
|
items=get_user_organisation,
|
||||||
|
name="Organisation",
|
||||||
|
description="A named and structured grouping with a corporate identity.",
|
||||||
|
)
|
||||||
active_user_id: IntProperty(name="Active User Id")
|
active_user_id: IntProperty(name="Active User Id")
|
||||||
active_actor_id: IntProperty(name="Active Actor Id")
|
active_actor_id: IntProperty(name="Active Actor Id")
|
||||||
actor_attributes: CollectionProperty(name="Actor Attributes", type=Attribute)
|
actor_attributes: CollectionProperty(name="Actor Attributes", type=Attribute)
|
||||||
@@ -111,5 +121,7 @@ class BIMOwnerProperties(PropertyGroup):
|
|||||||
name="Actor Type",
|
name="Actor Type",
|
||||||
update=update_actor_type,
|
update=update_actor_type,
|
||||||
)
|
)
|
||||||
the_actor: EnumProperty(items=get_the_actor, name="Actor")
|
the_actor: EnumProperty(
|
||||||
|
items=get_the_actor, name="Actor", description="This entity represents an individual human being."
|
||||||
|
)
|
||||||
actor: EnumProperty(items=get_actor, name="Actor")
|
actor: EnumProperty(items=get_actor, name="Actor")
|
||||||
|
|||||||
@@ -173,4 +173,8 @@ class AddEditCustomPropertiesData:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def primary_measure_type(cls):
|
def primary_measure_type(cls):
|
||||||
schema = tool.Ifc.schema()
|
schema = tool.Ifc.schema()
|
||||||
return [(t, t, "") for t in sorted([d.name() for d in schema.declarations() if hasattr(d, "declared_type")])]
|
version = tool.Ifc.get_schema()
|
||||||
|
return [
|
||||||
|
(t, t, ifcopenshell.util.doc.get_type_doc(version, t).get("description", ""))
|
||||||
|
for t in sorted([d.name() for d in schema.declarations() if hasattr(d, "declared_type")])
|
||||||
|
]
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ class EnablePsetEditing(bpy.types.Operator):
|
|||||||
elif metadata.data_type == "boolean":
|
elif metadata.data_type == "boolean":
|
||||||
metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
|
metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
|
||||||
|
|
||||||
|
metadata.ifc_class = pset_template.Name
|
||||||
|
blenderbim.bim.helper.add_attribute_description(metadata)
|
||||||
|
|
||||||
def get_data_type(self, prop_template):
|
def get_data_type(self, prop_template):
|
||||||
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
||||||
return "float"
|
return "float"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import blenderbim.bim.schema
|
|||||||
from blenderbim.bim.prop import Attribute, StrProperty
|
from blenderbim.bim.prop import Attribute, StrProperty
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from ifcopenshell.api.pset.data import Data
|
from ifcopenshell.api.pset.data import Data
|
||||||
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.module.pset.data import AddEditCustomPropertiesData
|
from blenderbim.bim.module.pset.data import AddEditCustomPropertiesData
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -47,6 +48,15 @@ def purge():
|
|||||||
qtonames = {}
|
qtonames = {}
|
||||||
|
|
||||||
|
|
||||||
|
def blender_formatted_enum_from_psets(psets):
|
||||||
|
enum_items = []
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
|
for pset in psets:
|
||||||
|
doc = ifcopenshell.util.doc.get_property_set_doc(version, pset.Name) or {}
|
||||||
|
enum_items.append((pset.Name, pset.Name, doc.get("description", "")))
|
||||||
|
return enum_items
|
||||||
|
|
||||||
|
|
||||||
def get_pset_names(self, context):
|
def get_pset_names(self, context):
|
||||||
global psetnames
|
global psetnames
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
@@ -58,67 +68,67 @@ def get_pset_names(self, context):
|
|||||||
ifc_class = element.is_a()
|
ifc_class = element.is_a()
|
||||||
if ifc_class not in psetnames:
|
if ifc_class not in psetnames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
assigned_names = [Data.psets[p]["Name"] for p in Data.products[obj.BIMObjectProperties.ifc_definition_id]["psets"]]
|
assigned_names = [Data.psets[p]["Name"] for p in Data.products[obj.BIMObjectProperties.ifc_definition_id]["psets"]]
|
||||||
return [p for p in psetnames[ifc_class] if p[0] not in assigned_names]
|
return [p for p in psetnames[ifc_class] if p[0] not in assigned_names]
|
||||||
|
|
||||||
|
|
||||||
def getMaterialPsetNames(self, context):
|
def get_material_pset_names(self, context):
|
||||||
global psetnames
|
global psetnames
|
||||||
ifc_class = "IfcMaterial"
|
ifc_class = "IfcMaterial"
|
||||||
if ifc_class not in psetnames:
|
if ifc_class not in psetnames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return psetnames[ifc_class]
|
return psetnames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
def getTaskQtoNames(self, context):
|
def get_task_qto_names(self, context):
|
||||||
global qtonames
|
global qtonames
|
||||||
ifc_class = "IfcTask"
|
ifc_class = "IfcTask"
|
||||||
if ifc_class not in qtonames:
|
if ifc_class not in qtonames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
qtonames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return qtonames[ifc_class]
|
return qtonames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
def getResourcePsetNames(self, context):
|
def get_resource_pset_names(self, context):
|
||||||
global psetnames
|
global psetnames
|
||||||
rprops = context.scene.BIMResourceProperties
|
rprops = context.scene.BIMResourceProperties
|
||||||
rtprops = context.scene.BIMResourceTreeProperties
|
rtprops = context.scene.BIMResourceTreeProperties
|
||||||
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||||
if ifc_class not in psetnames:
|
if ifc_class not in psetnames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return psetnames[ifc_class]
|
return psetnames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
def getResourceQtoNames(self, context):
|
def get_resource_qto_names(self, context):
|
||||||
global qtonames
|
global qtonames
|
||||||
rprops = context.scene.BIMResourceProperties
|
rprops = context.scene.BIMResourceProperties
|
||||||
rtprops = context.scene.BIMResourceTreeProperties
|
rtprops = context.scene.BIMResourceTreeProperties
|
||||||
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||||
if ifc_class not in qtonames:
|
if ifc_class not in qtonames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
qtonames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return qtonames[ifc_class]
|
return qtonames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
def getProfilePsetNames(self, context):
|
def get_profile_pset_names(self, context):
|
||||||
global psetnames
|
global psetnames
|
||||||
pprops = context.scene.BIMProfileProperties
|
pprops = context.scene.BIMProfileProperties
|
||||||
ifc_class = IfcStore.get_file().by_id(pprops.profiles[pprops.active_profile_index].ifc_definition_id).is_a()
|
ifc_class = IfcStore.get_file().by_id(pprops.profiles[pprops.active_profile_index].ifc_definition_id).is_a()
|
||||||
if ifc_class not in psetnames:
|
if ifc_class not in psetnames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return psetnames[ifc_class]
|
return psetnames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
def getWorkSchedulePsetNames(self, context):
|
def get_work_schedule_pset_names(self, context):
|
||||||
global psetnames
|
global psetnames
|
||||||
ifc_class = "IfcWorkSchedule"
|
ifc_class = "IfcWorkSchedule"
|
||||||
if ifc_class not in psetnames:
|
if ifc_class not in psetnames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return psetnames[ifc_class]
|
return psetnames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
@@ -128,11 +138,17 @@ def get_qto_names(self, context):
|
|||||||
ifc_class = context.active_object.name.split("/")[0]
|
ifc_class = context.active_object.name.split("/")[0]
|
||||||
if ifc_class not in qtonames:
|
if ifc_class not in qtonames:
|
||||||
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
qtonames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||||
return qtonames[ifc_class]
|
return qtonames[ifc_class]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def get_template_type(self, context):
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
|
for t in ("IfcPropertySingleValue", "IfcPropertyEnumeratedValue"):
|
||||||
|
yield (t, t, ifcopenshell.util.doc.get_entity_doc(version, t).get("description", ""))
|
||||||
|
|
||||||
|
|
||||||
def get_primary_measure_type(self, context):
|
def get_primary_measure_type(self, context):
|
||||||
if not AddEditCustomPropertiesData.is_loaded:
|
if not AddEditCustomPropertiesData.is_loaded:
|
||||||
AddEditCustomPropertiesData.load()
|
AddEditCustomPropertiesData.load()
|
||||||
@@ -163,36 +179,36 @@ class MaterialPsetProperties(PropertyGroup):
|
|||||||
active_pset_id: IntProperty(name="Active Pset ID")
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
active_pset_name: StringProperty(name="Pset Name")
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||||
pset_name: EnumProperty(items=getMaterialPsetNames, name="Pset Name")
|
pset_name: EnumProperty(items=get_material_pset_names, name="Pset Name")
|
||||||
|
|
||||||
|
|
||||||
class TaskPsetProperties(PropertyGroup):
|
class TaskPsetProperties(PropertyGroup):
|
||||||
active_pset_id: IntProperty(name="Active Pset ID")
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
active_pset_name: StringProperty(name="Pset Name")
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||||
qto_name: EnumProperty(items=getTaskQtoNames, name="Qto Name")
|
qto_name: EnumProperty(items=get_task_qto_names, name="Qto Name")
|
||||||
|
|
||||||
|
|
||||||
class ResourcePsetProperties(PropertyGroup):
|
class ResourcePsetProperties(PropertyGroup):
|
||||||
active_pset_id: IntProperty(name="Active Pset ID")
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
active_pset_name: StringProperty(name="Pset Name")
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||||
pset_name: EnumProperty(items=getResourcePsetNames, name="Pset Name")
|
pset_name: EnumProperty(items=get_resource_pset_names, name="Pset Name")
|
||||||
qto_name: EnumProperty(items=getResourceQtoNames, name="Qto Name")
|
qto_name: EnumProperty(items=get_resource_qto_names, name="Qto Name")
|
||||||
|
|
||||||
|
|
||||||
class ProfilePsetProperties(PropertyGroup):
|
class ProfilePsetProperties(PropertyGroup):
|
||||||
active_pset_id: IntProperty(name="Active Pset ID")
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
active_pset_name: StringProperty(name="Pset Name")
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||||
pset_name: EnumProperty(items=getProfilePsetNames, name="Pset Name")
|
pset_name: EnumProperty(items=get_profile_pset_names, name="Pset Name")
|
||||||
|
|
||||||
|
|
||||||
class WorkSchedulePsetProperties(PropertyGroup):
|
class WorkSchedulePsetProperties(PropertyGroup):
|
||||||
active_pset_id: IntProperty(name="Active Pset ID")
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
active_pset_name: StringProperty(name="Pset Name")
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||||
pset_name: EnumProperty(items=getWorkSchedulePsetNames, name="Pset Name")
|
pset_name: EnumProperty(items=get_work_schedule_pset_names, name="Pset Name")
|
||||||
|
|
||||||
|
|
||||||
class RenameProperties(PropertyGroup):
|
class RenameProperties(PropertyGroup):
|
||||||
@@ -209,13 +225,7 @@ class AddEditProperties(PropertyGroup):
|
|||||||
int_value: IntProperty(name="Value")
|
int_value: IntProperty(name="Value")
|
||||||
float_value: FloatProperty(name="Value")
|
float_value: FloatProperty(name="Value")
|
||||||
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
|
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
|
||||||
template_type: EnumProperty(
|
template_type: EnumProperty(items=get_template_type, name="Template Type")
|
||||||
items=[
|
|
||||||
("IfcPropertySingleValue", "IfcPropertySingleValue", "IfcPropertySingleValue"),
|
|
||||||
("IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue"),
|
|
||||||
],
|
|
||||||
name="Template Type",
|
|
||||||
)
|
|
||||||
enum_values: CollectionProperty(name="Enum Values", type=Attribute)
|
enum_values: CollectionProperty(name="Enum Values", type=Attribute)
|
||||||
|
|
||||||
def get_value_name(self):
|
def get_value_name(self):
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ class PsetTemplatesData:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def primary_measure_type(cls):
|
def primary_measure_type(cls):
|
||||||
schema = tool.Ifc.schema()
|
schema = tool.Ifc.schema()
|
||||||
return [(t, t, "") for t in sorted([d.name() for d in schema.declarations() if hasattr(d, "declared_type")])]
|
version = tool.Ifc.get_schema()
|
||||||
|
return [
|
||||||
|
(t, t, ifcopenshell.util.doc.get_type_doc(version, t).get("description", ""))
|
||||||
|
for t in sorted([d.name() for d in schema.declarations() if hasattr(d, "declared_type")])
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def pset_template_files(cls):
|
def pset_template_files(cls):
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
from ifcopenshell.util.doc import get_attribute_doc
|
||||||
from blenderbim.bim.module.pset_template.data import PsetTemplatesData
|
from blenderbim.bim.module.pset_template.data import PsetTemplatesData
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
@@ -111,11 +112,27 @@ def get_template_type(self, context):
|
|||||||
|
|
||||||
|
|
||||||
class PsetTemplate(PropertyGroup):
|
class PsetTemplate(PropertyGroup):
|
||||||
global_id: StringProperty(name="Global ID")
|
global_id: StringProperty(
|
||||||
name: StringProperty(name="Name")
|
name="Global ID",
|
||||||
description: StringProperty(name="Description")
|
description=get_attribute_doc("IFC4", "IfcPropertySetTemplate", "GlobalId"),
|
||||||
template_type: EnumProperty(items=get_template_type, name="Template Type")
|
)
|
||||||
applicable_entity: StringProperty(name="Applicable Entity")
|
name: StringProperty(
|
||||||
|
name="Name",
|
||||||
|
description=get_attribute_doc("IFC4", "IfcPropertySetTemplate", "Name"),
|
||||||
|
)
|
||||||
|
description: StringProperty(
|
||||||
|
name="Description",
|
||||||
|
description=get_attribute_doc("IFC4", "IfcPropertySetTemplate", "Description"),
|
||||||
|
)
|
||||||
|
template_type: EnumProperty(
|
||||||
|
items=get_template_type,
|
||||||
|
name="Template Type",
|
||||||
|
description=get_attribute_doc("IFC4", "IfcPropertySetTemplate", "TemplateType"),
|
||||||
|
)
|
||||||
|
applicable_entity: StringProperty(
|
||||||
|
name="Applicable Entity",
|
||||||
|
description=get_attribute_doc("IFC4", "IfcPropertySetTemplate", "ApplicableEntity"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnumerationValues(PropertyGroup):
|
class EnumerationValues(PropertyGroup):
|
||||||
@@ -126,9 +143,18 @@ class EnumerationValues(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class PropTemplate(PropertyGroup):
|
class PropTemplate(PropertyGroup):
|
||||||
global_id: StringProperty(name="Global ID")
|
global_id: StringProperty(
|
||||||
name: StringProperty(name="Name")
|
name="Global ID",
|
||||||
description: StringProperty(name="Description")
|
description=get_attribute_doc("IFC4", "IfcPropertyTemplate", "GlobalId"),
|
||||||
|
)
|
||||||
|
name: StringProperty(
|
||||||
|
name="Name",
|
||||||
|
description=get_attribute_doc("IFC4", "IfcPropertyTemplate", "Name"),
|
||||||
|
)
|
||||||
|
description: StringProperty(
|
||||||
|
name="Description",
|
||||||
|
description=get_attribute_doc("IFC4", "IfcPropertyTemplate", "Description"),
|
||||||
|
)
|
||||||
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
|
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
|
||||||
template_type: EnumProperty(
|
template_type: EnumProperty(
|
||||||
items=[("P_SINGLEVALUE", "P_SINGLEVALUE", ""), ("P_ENUMERATEDVALUE", "P_ENUMERATEDVALUE", "")],
|
items=[("P_SINGLEVALUE", "P_SINGLEVALUE", ""), ("P_ENUMERATEDVALUE", "P_ENUMERATEDVALUE", "")],
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description, get_predefined_type_description
|
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -57,7 +57,8 @@ class IfcClassData:
|
|||||||
"IfcAnnotation",
|
"IfcAnnotation",
|
||||||
"IfcRelSpaceBoundary",
|
"IfcRelSpaceBoundary",
|
||||||
]
|
]
|
||||||
if tool.Ifc.get_schema() == "IFC2X3":
|
version = tool.Ifc.get_schema()
|
||||||
|
if version == "IFC2X3":
|
||||||
products = [
|
products = [
|
||||||
"IfcElement",
|
"IfcElement",
|
||||||
"IfcElementType",
|
"IfcElementType",
|
||||||
@@ -67,7 +68,7 @@ class IfcClassData:
|
|||||||
"IfcAnnotation",
|
"IfcAnnotation",
|
||||||
"IfcRelSpaceBoundary",
|
"IfcRelSpaceBoundary",
|
||||||
]
|
]
|
||||||
return [(e, e, get_ifc_entity_description(e)) for e in products]
|
return [(e, e, get_entity_doc(version, e).get("description", "")) for e in products]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_classes(cls):
|
def ifc_classes(cls):
|
||||||
@@ -77,18 +78,20 @@ class IfcClassData:
|
|||||||
names = [d.name() for d in declarations]
|
names = [d.name() for d in declarations]
|
||||||
if ifc_product == "IfcElementType":
|
if ifc_product == "IfcElementType":
|
||||||
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
||||||
return [(c, c, get_ifc_entity_description(c)) for c in sorted(names)]
|
version = tool.Ifc.get_schema()
|
||||||
|
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_predefined_types(cls):
|
def ifc_predefined_types(cls):
|
||||||
types_enum = []
|
types_enum = []
|
||||||
ifc_class = bpy.context.scene.BIMRootProperties.ifc_class
|
ifc_class = bpy.context.scene.BIMRootProperties.ifc_class
|
||||||
declaration = tool.Ifc.schema().declaration_by_name(ifc_class)
|
declaration = tool.Ifc.schema().declaration_by_name(ifc_class)
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
for attribute in declaration.attributes():
|
for attribute in declaration.attributes():
|
||||||
if attribute.name() == "PredefinedType":
|
if attribute.name() == "PredefinedType":
|
||||||
types_enum.extend(
|
types_enum.extend(
|
||||||
[
|
[
|
||||||
(e, e, get_predefined_type_description(ifc_class, e))
|
(e, e, get_predefined_type_doc(version, ifc_class, e))
|
||||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,11 +16,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
from math import radians
|
from math import radians
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute, get_ifc_entity_description
|
import bpy
|
||||||
from ifcopenshell.api.structural.data import Data
|
from ifcopenshell.api.structural.data import Data
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@@ -89,8 +91,12 @@ def get_structural_load_types(self, context):
|
|||||||
file = IfcStore.get_file()
|
file = IfcStore.get_file()
|
||||||
if len(structuralloadtypes_enum) < 1 and file:
|
if len(structuralloadtypes_enum) < 1 and file:
|
||||||
declaration = IfcStore.get_schema().declaration_by_name("IfcStructuralLoadStatic")
|
declaration = IfcStore.get_schema().declaration_by_name("IfcStructuralLoadStatic")
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
structuralloadtypes_enum.extend(
|
structuralloadtypes_enum.extend(
|
||||||
[(d.name(), d.name(), get_ifc_entity_description(d.name())) for d in declaration.subtypes()]
|
[
|
||||||
|
(d.name(), d.name(), get_entity_doc(version, d.name()).get("description", ""))
|
||||||
|
for d in declaration.subtypes()
|
||||||
|
]
|
||||||
)
|
)
|
||||||
return structuralloadtypes_enum
|
return structuralloadtypes_enum
|
||||||
|
|
||||||
@@ -99,8 +105,10 @@ def get_boundary_condition_types(self, context):
|
|||||||
file = IfcStore.get_file()
|
file = IfcStore.get_file()
|
||||||
if file:
|
if file:
|
||||||
declaration = IfcStore.get_schema().declaration_by_name("IfcBoundaryCondition")
|
declaration = IfcStore.get_schema().declaration_by_name("IfcBoundaryCondition")
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
boundaryconditiontypes_enum = [
|
boundaryconditiontypes_enum = [
|
||||||
(d.name(), d.name(), get_ifc_entity_description(d.name())) for d in declaration.subtypes()
|
(d.name(), d.name(), get_entity_doc(version, d.name()).get("description", ""))
|
||||||
|
for d in declaration.subtypes()
|
||||||
]
|
]
|
||||||
return boundaryconditiontypes_enum
|
return boundaryconditiontypes_enum
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import blenderbim.tool as tool
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -39,7 +39,10 @@ class StylesData:
|
|||||||
def style_types(cls):
|
def style_types(cls):
|
||||||
declaration = tool.Ifc.schema().declaration_by_name("IfcPresentationStyle")
|
declaration = tool.Ifc.schema().declaration_by_name("IfcPresentationStyle")
|
||||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||||
return [(c, c, get_ifc_entity_description(c)) for c in sorted([d.name() for d in declarations])]
|
version = tool.Ifc.get_schema()
|
||||||
|
return [
|
||||||
|
(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted([d.name() for d in declarations])
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def total_styles(cls):
|
def total_styles(cls):
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description
|
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -45,9 +45,10 @@ class SystemData:
|
|||||||
def system_class(cls):
|
def system_class(cls):
|
||||||
declaration = tool.Ifc.schema().declaration_by_name("IfcSystem")
|
declaration = tool.Ifc.schema().declaration_by_name("IfcSystem")
|
||||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
# We're only interested in systems for services. Not sure why IFC groups these together.
|
# We're only interested in systems for services. Not sure why IFC groups these together.
|
||||||
return [
|
return [
|
||||||
(c, c, get_ifc_entity_description(c))
|
(c, c, get_entity_doc(version, c).get("description", ""))
|
||||||
for c in sorted([d.name() for d in declarations])
|
for c in sorted([d.name() for d in declarations])
|
||||||
if c not in ("IfcZone", "IfcStructuralAnalysisModel")
|
if c not in ("IfcZone", "IfcStructuralAnalysisModel")
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.util.type
|
import ifcopenshell.util.type
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description
|
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -54,8 +54,9 @@ class TypeData:
|
|||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
if not element:
|
if not element:
|
||||||
return []
|
return []
|
||||||
types = ifcopenshell.util.type.get_applicable_types(element.is_a(), schema=tool.Ifc.get_schema())
|
version = tool.Ifc.get_schema()
|
||||||
results.extend((t, t, get_ifc_entity_description(t)) for t in types)
|
types = ifcopenshell.util.type.get_applicable_types(element.is_a(), schema=version)
|
||||||
|
results.extend((t, t, get_entity_doc(version, t).get("description", "")) for t in types)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -87,7 +88,4 @@ class TypeData:
|
|||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||||
element_type = ifcopenshell.util.element.get_type(element)
|
element_type = ifcopenshell.util.element.get_type(element)
|
||||||
if element_type:
|
if element_type:
|
||||||
return {
|
return {"id": element_type.id(), "name": f"{element_type.is_a()}/{element_type.Name or 'Unnamed'}"}
|
||||||
"id": element_type.id(),
|
|
||||||
"name":f"{element_type.is_a()}/{element_type.Name or 'Unnamed'}"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import ifcopenshell
|
|||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
import ifcopenshell.util.attribute
|
import ifcopenshell.util.attribute
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.prop import get_ifc_entity_description
|
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -45,8 +45,20 @@ class UnitsData:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def unit_classes(cls):
|
def unit_classes(cls):
|
||||||
declarations = ifcopenshell.util.schema.get_subtypes(tool.Ifc.schema().declaration_by_name("IfcNamedUnit"))
|
declarations = ifcopenshell.util.schema.get_subtypes(tool.Ifc.schema().declaration_by_name("IfcNamedUnit"))
|
||||||
results = [(c, c, get_ifc_entity_description(c)) for c in sorted([d.name() for d in declarations])]
|
version = tool.Ifc.get_schema()
|
||||||
results.extend([("IfcDerivedUnit", "IfcDerivedUnit", ""), ("IfcMonetaryUnit", "IfcMonetaryUnit", "")])
|
results = [
|
||||||
|
(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted([d.name() for d in declarations])
|
||||||
|
]
|
||||||
|
results.extend(
|
||||||
|
[
|
||||||
|
("IfcDerivedUnit", "IfcDerivedUnit", get_entity_doc(version, "IfcDerivedUnit").get("description", "")),
|
||||||
|
(
|
||||||
|
"IfcMonetaryUnit",
|
||||||
|
"IfcMonetaryUnit",
|
||||||
|
get_entity_doc(version, "IfcMonetaryUnit").get("description", ""),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
import webbrowser
|
import webbrowser
|
||||||
@@ -683,3 +684,31 @@ class EditBlenderCollection(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
getattr(context.bim_prop_group, self.collection).remove(self.index)
|
getattr(context.bim_prop_group, self.collection).remove(self.index)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_show_description(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.show_description"
|
||||||
|
bl_label = "Description"
|
||||||
|
attr_name: bpy.props.StringProperty()
|
||||||
|
description: bpy.props.StringProperty()
|
||||||
|
url: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self, width=450)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
wrapper = textwrap.TextWrapper(width=80)
|
||||||
|
for line in wrapper.wrap(self.attr_name + " : " + self.description):
|
||||||
|
layout.label(text=line)
|
||||||
|
if self.url:
|
||||||
|
url_op = layout.operator("bim.open_webbrowser", icon="URL", text="Online IFC Documentation")
|
||||||
|
url_op.url = self.url
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def description(cls, context, properties):
|
||||||
|
return properties.description
|
||||||
|
|||||||
@@ -106,40 +106,6 @@ def cache_string(s):
|
|||||||
cache_string.data = {}
|
cache_string.data = {}
|
||||||
|
|
||||||
|
|
||||||
def get_ifc_entity_docs(ifc_entity):
|
|
||||||
schema = tool.Ifc.get_schema()
|
|
||||||
if schema is not None:
|
|
||||||
return get_entity_doc(schema, ifc_entity)
|
|
||||||
|
|
||||||
|
|
||||||
def get_ifc_entity_description(ifc_entity):
|
|
||||||
docs = get_ifc_entity_docs(ifc_entity)
|
|
||||||
return docs.get("description", "") if docs is not None else ""
|
|
||||||
|
|
||||||
|
|
||||||
def get_ifc_entity_doc_url(ifc_entity):
|
|
||||||
docs = get_ifc_entity_docs(ifc_entity)
|
|
||||||
return docs.get("spec_url", "") if docs is not None else ""
|
|
||||||
|
|
||||||
|
|
||||||
def get_predefined_type_description(entity, predefined_type):
|
|
||||||
schema = tool.Ifc.get_schema()
|
|
||||||
if schema is not None:
|
|
||||||
return get_predefined_type_doc(schema, entity, predefined_type)
|
|
||||||
|
|
||||||
|
|
||||||
def get_attribute_description(entity, attribute):
|
|
||||||
schema = tool.Ifc.get_schema()
|
|
||||||
if schema is not None:
|
|
||||||
return get_attribute_doc(schema, entity, attribute)
|
|
||||||
|
|
||||||
|
|
||||||
def get_property_set_description(pset):
|
|
||||||
schema = tool.Ifc.get_schema()
|
|
||||||
if schema is not None:
|
|
||||||
return get_property_set_doc(schema, pset)
|
|
||||||
|
|
||||||
|
|
||||||
def get_attribute_enum_values(prop, context):
|
def get_attribute_enum_values(prop, context):
|
||||||
# Support weird buildingSMART dictionary mappings which behave like enums
|
# Support weird buildingSMART dictionary mappings which behave like enums
|
||||||
items = []
|
items = []
|
||||||
@@ -164,6 +130,9 @@ def get_attribute_enum_values(prop, context):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if prop.enum_descriptions:
|
||||||
|
items = [(identifier, name, prop.enum_descriptions[i].name) for i, (identifier, name, _) in enumerate(items)]
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
@@ -227,13 +196,17 @@ def update_attribute_value(self, context):
|
|||||||
|
|
||||||
|
|
||||||
class Attribute(PropertyGroup):
|
class Attribute(PropertyGroup):
|
||||||
|
tooltip = "`Right Click > IFC Description` to read the attribute description and online documentation"
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
|
description: StringProperty(name="Description")
|
||||||
|
ifc_class: StringProperty(name="Ifc Class")
|
||||||
data_type: StringProperty(name="Data Type")
|
data_type: StringProperty(name="Data Type")
|
||||||
string_value: StringProperty(name="Value", update=update_attribute_value)
|
string_value: StringProperty(name="Value", update=update_attribute_value, description=tooltip)
|
||||||
bool_value: BoolProperty(name="Value", update=update_attribute_value)
|
bool_value: BoolProperty(name="Value", update=update_attribute_value, description=tooltip)
|
||||||
int_value: IntProperty(name="Value", update=update_attribute_value)
|
int_value: IntProperty(name="Value", update=update_attribute_value, description=tooltip)
|
||||||
float_value: FloatProperty(name="Value", update=update_attribute_value)
|
float_value: FloatProperty(name="Value", update=update_attribute_value, description=tooltip)
|
||||||
enum_items: StringProperty(name="Value")
|
enum_items: StringProperty(name="Value")
|
||||||
|
enum_descriptions: CollectionProperty(type=StrProperty)
|
||||||
enum_value: EnumProperty(items=get_attribute_enum_values, name="Value", update=update_attribute_value)
|
enum_value: EnumProperty(items=get_attribute_enum_values, name="Value", update=update_attribute_value)
|
||||||
is_null: BoolProperty(name="Is Null")
|
is_null: BoolProperty(name="Is Null")
|
||||||
is_optional: BoolProperty(name="Is Optional")
|
is_optional: BoolProperty(name="Is Optional")
|
||||||
|
|||||||
@@ -17,14 +17,20 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import bpy
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import ifc
|
import bpy
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from bpy.props import StringProperty, IntProperty, BoolProperty
|
from bpy.props import StringProperty, IntProperty, BoolProperty
|
||||||
from blenderbim.bim.helper import IfcHeaderExtractor
|
from ifcopenshell.util.doc import (
|
||||||
from blenderbim.bim.prop import get_ifc_entity_doc_url
|
get_entity_doc,
|
||||||
|
get_property_set_doc,
|
||||||
|
get_type_doc,
|
||||||
|
get_attribute_doc,
|
||||||
|
)
|
||||||
|
from . import ifc
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.bim.helper import IfcHeaderExtractor
|
||||||
|
from blenderbim.bim.prop import Attribute
|
||||||
|
|
||||||
|
|
||||||
class IFCFileSelector:
|
class IFCFileSelector:
|
||||||
@@ -397,14 +403,44 @@ def draw_custom_context_menu(self, context):
|
|||||||
prop_value = getattr(prop, prop_name, None)
|
prop_value = getattr(prop, prop_name, None)
|
||||||
if prop_value is None:
|
if prop_value is None:
|
||||||
return
|
return
|
||||||
try:
|
version = tool.Ifc.get_schema()
|
||||||
url = get_ifc_entity_doc_url(prop_value)
|
layout = self.layout
|
||||||
except KeyError:
|
|
||||||
# TODO : support attributes, pset, etc.
|
if isinstance(context.button_pointer, Attribute):
|
||||||
pass
|
description = getattr(context.button_pointer, "description", None)
|
||||||
else:
|
ifc_class = getattr(context.button_pointer, "ifc_class", "")
|
||||||
if url:
|
if ifc_class:
|
||||||
layout = self.layout
|
try:
|
||||||
|
url = get_entity_doc(version, context.button_pointer.ifc_class).get("spec_url", "")
|
||||||
|
except RuntimeError: # It's not an Entity Attribute. Let's try a Property Set attribute.
|
||||||
|
url = get_property_set_doc(version, context.button_pointer.ifc_class).get("spec_url", "")
|
||||||
|
if description:
|
||||||
layout.separator()
|
layout.separator()
|
||||||
url_op = layout.operator("bim.open_webbrowser", icon="URL", text="Online IFC Documentation")
|
op_description = layout.operator("bim.show_description", text="IFC Description", icon="INFO")
|
||||||
url_op.url = url
|
op_description.attr_name = getattr(context.button_pointer, "name", "")
|
||||||
|
op_description.description = description
|
||||||
|
op_description.url = url
|
||||||
|
else:
|
||||||
|
# Ugly but we can't know which type of data is under the cursor so we test everything until it clicks
|
||||||
|
try:
|
||||||
|
docs = get_entity_doc(version, prop_value)
|
||||||
|
if docs is None:
|
||||||
|
raise RuntimeError
|
||||||
|
except (RuntimeError, AttributeError):
|
||||||
|
try:
|
||||||
|
docs = get_type_doc(version, prop_value)
|
||||||
|
if docs is None:
|
||||||
|
raise RuntimeError
|
||||||
|
except (RuntimeError, AttributeError):
|
||||||
|
try:
|
||||||
|
docs = get_property_set_doc(version, prop_value)
|
||||||
|
if docs is None:
|
||||||
|
raise RuntimeError
|
||||||
|
except (RuntimeError, AttributeError):
|
||||||
|
pass
|
||||||
|
if docs:
|
||||||
|
url = docs.get("spec_url", "")
|
||||||
|
if url:
|
||||||
|
layout.separator()
|
||||||
|
url_op = layout.operator("bim.open_webbrowser", icon="URL", text="Online IFC Documentation")
|
||||||
|
url_op.url = url
|
||||||
|
|||||||
Reference in New Issue
Block a user