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 commit a7bbd06865.

* Revert "Revert "Panel UI is managed by operator""

This reverts commit d62115bf63.

* 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:
Gorgious56
2022-10-18 23:17:31 +02:00
committed by GitHub
parent b70bf5ed33
commit ec1fbabd00
8 changed files with 94 additions and 30 deletions
@@ -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,
+21
View File
@@ -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")
+14
View File
@@ -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"
+24
View 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
+4
View File
@@ -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):