mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-29 11:46:33 +00:00
application ui - support changing organization
This commit is contained in:
@@ -273,6 +273,15 @@ def export_attributes(
|
|||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
|
def process_exported_entity_attribute(attributes: dict[str, Any], attribute_name: str) -> None:
|
||||||
|
entity_id = attributes[attribute_name]
|
||||||
|
if entity_id is None:
|
||||||
|
# Maybe it was removed by now and enum is invalid.
|
||||||
|
del attributes[attribute_name]
|
||||||
|
else:
|
||||||
|
attributes[attribute_name] = tool.Ifc.get().by_id(int(entity_id))
|
||||||
|
|
||||||
|
|
||||||
ENUM_ITEMS_DATA = Union[bpy.types.PropertyGroup, bpy.types.ID, bpy.types.Operator, bpy.types.OperatorProperties]
|
ENUM_ITEMS_DATA = Union[bpy.types.PropertyGroup, bpy.types.ID, bpy.types.Operator, bpy.types.OperatorProperties]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,13 @@ def cache_string(s: Any) -> str:
|
|||||||
cache_string.data: dict[str, str] = {}
|
cache_string.data: dict[str, str] = {}
|
||||||
|
|
||||||
|
|
||||||
def get_attribute_enum_values(prop: "Attribute", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
def get_attribute_enum_values(prop: "Attribute", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||||
|
if "EnumData" not in globals() or TYPE_CHECKING:
|
||||||
|
from bonsai.bim.ui import EnumData
|
||||||
|
|
||||||
|
if dynamic_identifier := prop.enum_items_dynamic:
|
||||||
|
return EnumData.get_data(dynamic_identifier)
|
||||||
|
|
||||||
# Support weird buildingSMART dictionary mappings which behave like enums
|
# Support weird buildingSMART dictionary mappings which behave like enums
|
||||||
items: list[tuple[str, str, str]] = []
|
items: list[tuple[str, str, str]] = []
|
||||||
data = json.loads(prop.enum_items)
|
data = json.loads(prop.enum_items)
|
||||||
@@ -314,6 +320,11 @@ class Attribute(PropertyGroup):
|
|||||||
name="Value", description=tooltip, get=get_length_value, set=set_length_value, unit="LENGTH"
|
name="Value", description=tooltip, get=get_length_value, set=set_length_value, unit="LENGTH"
|
||||||
)
|
)
|
||||||
enum_items: StringProperty(name="Value")
|
enum_items: StringProperty(name="Value")
|
||||||
|
"""Json serialized mapping of enum items:
|
||||||
|
Typically a dictionary of string identifiers to item names.
|
||||||
|
"""
|
||||||
|
enum_items_dynamic: StringProperty()
|
||||||
|
"""Dynamic enum items identifier."""
|
||||||
enum_descriptions: CollectionProperty(type=StrProperty)
|
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)
|
||||||
filepath_value: PointerProperty(type=MultipleFileSelect)
|
filepath_value: PointerProperty(type=MultipleFileSelect)
|
||||||
@@ -343,6 +354,7 @@ class Attribute(PropertyGroup):
|
|||||||
float_value: float
|
float_value: float
|
||||||
length_value: float
|
length_value: float
|
||||||
enum_items: str
|
enum_items: str
|
||||||
|
enum_items_dynamic: str
|
||||||
enum_descriptions: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
enum_descriptions: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
enum_value: str
|
enum_value: str
|
||||||
filepath_value: MultipleFileSelect
|
filepath_value: MultipleFileSelect
|
||||||
@@ -365,7 +377,11 @@ class Attribute(PropertyGroup):
|
|||||||
return self.string_value.replace("\\n", "\n")
|
return self.string_value.replace("\\n", "\n")
|
||||||
if self.data_type == "file":
|
if self.data_type == "file":
|
||||||
return [f.name for f in self.filepath_value.file_list]
|
return [f.name for f in self.filepath_value.file_list]
|
||||||
value = getattr(self, str(self.get_value_name()), None)
|
value_name = self.get_value_name()
|
||||||
|
if value_name == "enum_value":
|
||||||
|
value = tool.Blender.get_enum_safe(self, "enum_value")
|
||||||
|
else:
|
||||||
|
value = getattr(self, value_name, None)
|
||||||
if self.special_type == "LOGICAL" and value != "UNKNOWN":
|
if self.special_type == "LOGICAL" and value != "UNKNOWN":
|
||||||
# IfcOpenShell expects bool if IfcLogical is True/False.
|
# IfcOpenShell expects bool if IfcLogical is True/False.
|
||||||
value = value == "TRUE"
|
value = value == "TRUE"
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import bonsai.tool as tool
|
|||||||
from ifcopenshell.util.file import IfcHeaderExtractor
|
from ifcopenshell.util.file import IfcHeaderExtractor
|
||||||
from bonsai.bim.prop import Attribute
|
from bonsai.bim.prop import Attribute
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
from natsort import natsorted
|
||||||
|
|
||||||
|
|
||||||
class IFCFileSelector:
|
class IFCFileSelector:
|
||||||
@@ -1167,6 +1168,22 @@ class BIM_PT_tab_operations(Panel):
|
|||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
UIData.is_loaded = False
|
UIData.is_loaded = False
|
||||||
|
EnumData.data.clear()
|
||||||
|
|
||||||
|
|
||||||
|
class EnumData:
|
||||||
|
data: dict[str, tool.Blender.BLENDER_ENUM_ITEMS] = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_data(cls, identifier: str) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||||
|
if identifier not in EnumData.data:
|
||||||
|
cls.data[identifier] = getattr(cls, identifier)()
|
||||||
|
return cls.data[identifier]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def organizations(cls) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||||
|
organizations = tool.Ifc.get().by_type("IfcOrganization")
|
||||||
|
return natsorted(((str(e.id()), e.Name, "") for e in organizations), key=lambda x: x[1])
|
||||||
|
|
||||||
|
|
||||||
class UIData:
|
class UIData:
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
|
|
||||||
- (identifier, name, description, icon, number)
|
- (identifier, name, description, icon, number)
|
||||||
"""
|
"""
|
||||||
BLENDER_ENUM_ITEMS = list[BLENDER_ENUM_ITEM]
|
BLENDER_ENUM_ITEMS = Iterable[BLENDER_ENUM_ITEM]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def activate_camera(cls, obj: bpy.types.Object) -> None:
|
def activate_camera(cls, obj: bpy.types.Object) -> None:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from typing import Union, Any, TYPE_CHECKING, Literal, assert_never
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.module.owner.prop import BIMOwnerProperties
|
from bonsai.bim.module.owner.prop import BIMOwnerProperties
|
||||||
|
from bonsai.bim.prop import Attribute
|
||||||
|
|
||||||
|
|
||||||
class Owner(bonsai.core.tool.Owner):
|
class Owner(bonsai.core.tool.Owner):
|
||||||
@@ -327,10 +328,23 @@ class Owner(bonsai.core.tool.Owner):
|
|||||||
application = tool.Ifc.get().by_id(props.active_application_id)
|
application = tool.Ifc.get().by_id(props.active_application_id)
|
||||||
props.application_attributes.clear()
|
props.application_attributes.clear()
|
||||||
|
|
||||||
bonsai.bim.helper.import_attributes("IfcApplication", props.application_attributes, application.get_info())
|
def callback(name: str, prop: Union[Attribute, None], data: dict[str, Any]):
|
||||||
|
if name == "ApplicationDeveloper":
|
||||||
|
new = props.application_attributes.add()
|
||||||
|
new.name = name
|
||||||
|
new.data_type = "enum"
|
||||||
|
new.is_optional = False
|
||||||
|
new.enum_items_dynamic = "organizations"
|
||||||
|
new.enum_value = str(data["ApplicationDeveloper"].id())
|
||||||
|
return True
|
||||||
|
|
||||||
|
bonsai.bim.helper.import_attributes(
|
||||||
|
"IfcApplication", props.application_attributes, application.get_info(), callback
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_application_attributes(cls) -> dict[str, Any]:
|
def export_application_attributes(cls) -> dict[str, Any]:
|
||||||
props = cls.get_owner_props()
|
props = cls.get_owner_props()
|
||||||
attributes = bonsai.bim.helper.export_attributes(props.application_attributes)
|
attributes = bonsai.bim.helper.export_attributes(props.application_attributes)
|
||||||
|
bonsai.bim.helper.process_exported_entity_attribute(attributes, "ApplicationDeveloper")
|
||||||
return attributes
|
return attributes
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import bonsai.core.tool
|
|||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from test.bim.bootstrap import NewFile
|
from test.bim.bootstrap import NewFile
|
||||||
from bonsai.tool.owner import Owner as subject
|
from bonsai.tool.owner import Owner as subject
|
||||||
|
from typing import Any
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
|
|
||||||
class TestImplementsTool(NewFile):
|
class TestImplementsTool(NewFile):
|
||||||
@@ -584,12 +586,18 @@ class TestSetUser(NewFile):
|
|||||||
|
|
||||||
class TestApplicationUI(NewFile):
|
class TestApplicationUI(NewFile):
|
||||||
ifc_class = "IfcApplication"
|
ifc_class = "IfcApplication"
|
||||||
attrs = {
|
attrs_: dict[str, Any] = {
|
||||||
"Version": "v001",
|
"Version": "v001",
|
||||||
"ApplicationFullName": "IfcOpenShell",
|
"ApplicationFullName": "IfcOpenShell",
|
||||||
"ApplicationIdentifier": "IfcOpenShell",
|
"ApplicationIdentifier": "IfcOpenShell",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def get_attrs(self, ifc_file: ifcopenshell.file) -> dict[str, Any]:
|
||||||
|
attrs = self.attrs_.copy()
|
||||||
|
attrs["ApplicationDeveloper"] = ifc_file.create_entity("IfcOrganization", Name="Test")
|
||||||
|
return attrs
|
||||||
|
|
||||||
def test_set(self):
|
def test_set(self):
|
||||||
application = ifcopenshell.file().create_entity(self.ifc_class)
|
application = ifcopenshell.file().create_entity(self.ifc_class)
|
||||||
subject.set_application(application)
|
subject.set_application(application)
|
||||||
@@ -614,14 +622,16 @@ class TestApplicationUI(NewFile):
|
|||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
tool.Ifc.set(ifc)
|
tool.Ifc.set(ifc)
|
||||||
application = ifc.create_entity(self.ifc_class)
|
application = ifc.create_entity(self.ifc_class)
|
||||||
for attr, value in self.attrs.items():
|
for attr, value in self.get_attrs(ifc).items():
|
||||||
setattr(application, attr, value)
|
setattr(application, attr, value)
|
||||||
subject.set_application(application)
|
subject.set_application(application)
|
||||||
subject.import_application_attributes()
|
subject.import_application_attributes()
|
||||||
props = subject.get_owner_props()
|
props = subject.get_owner_props()
|
||||||
for attr, value in self.attrs.items():
|
for attr, value in self.get_attrs(ifc).items():
|
||||||
|
if isinstance(value, ifcopenshell.entity_instance):
|
||||||
|
value = str(value.id())
|
||||||
assert props.application_attributes[attr].get_value() == value
|
assert props.application_attributes[attr].get_value() == value
|
||||||
|
|
||||||
def test_export(self):
|
def test_export(self):
|
||||||
self.test_import()
|
self.test_import()
|
||||||
assert subject.export_application_attributes() == self.attrs
|
assert subject.export_application_attributes() == self.get_attrs(tool.Ifc.get())
|
||||||
|
|||||||
Reference in New Issue
Block a user