mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +00:00
application ui - support changing organization
This commit is contained in:
@@ -273,6 +273,15 @@ def export_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]
|
||||
|
||||
|
||||
|
||||
@@ -84,7 +84,13 @@ def cache_string(s: Any) -> 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
|
||||
items: list[tuple[str, str, str]] = []
|
||||
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"
|
||||
)
|
||||
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_value: EnumProperty(items=get_attribute_enum_values, name="Value", update=update_attribute_value)
|
||||
filepath_value: PointerProperty(type=MultipleFileSelect)
|
||||
@@ -343,6 +354,7 @@ class Attribute(PropertyGroup):
|
||||
float_value: float
|
||||
length_value: float
|
||||
enum_items: str
|
||||
enum_items_dynamic: str
|
||||
enum_descriptions: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
enum_value: str
|
||||
filepath_value: MultipleFileSelect
|
||||
@@ -365,7 +377,11 @@ class Attribute(PropertyGroup):
|
||||
return self.string_value.replace("\\n", "\n")
|
||||
if self.data_type == "file":
|
||||
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":
|
||||
# IfcOpenShell expects bool if IfcLogical is True/False.
|
||||
value = value == "TRUE"
|
||||
|
||||
@@ -36,6 +36,7 @@ import bonsai.tool as tool
|
||||
from ifcopenshell.util.file import IfcHeaderExtractor
|
||||
from bonsai.bim.prop import Attribute
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from natsort import natsorted
|
||||
|
||||
|
||||
class IFCFileSelector:
|
||||
@@ -1167,6 +1168,22 @@ class BIM_PT_tab_operations(Panel):
|
||||
|
||||
def refresh():
|
||||
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:
|
||||
|
||||
@@ -93,7 +93,7 @@ class Blender(bonsai.core.tool.Blender):
|
||||
|
||||
- (identifier, name, description, icon, number)
|
||||
"""
|
||||
BLENDER_ENUM_ITEMS = list[BLENDER_ENUM_ITEM]
|
||||
BLENDER_ENUM_ITEMS = Iterable[BLENDER_ENUM_ITEM]
|
||||
|
||||
@classmethod
|
||||
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:
|
||||
from bonsai.bim.module.owner.prop import BIMOwnerProperties
|
||||
from bonsai.bim.prop import Attribute
|
||||
|
||||
|
||||
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)
|
||||
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
|
||||
def export_application_attributes(cls) -> dict[str, Any]:
|
||||
props = cls.get_owner_props()
|
||||
attributes = bonsai.bim.helper.export_attributes(props.application_attributes)
|
||||
bonsai.bim.helper.process_exported_entity_attribute(attributes, "ApplicationDeveloper")
|
||||
return attributes
|
||||
|
||||
@@ -23,6 +23,8 @@ import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
from test.bim.bootstrap import NewFile
|
||||
from bonsai.tool.owner import Owner as subject
|
||||
from typing import Any
|
||||
from functools import cache
|
||||
|
||||
|
||||
class TestImplementsTool(NewFile):
|
||||
@@ -584,12 +586,18 @@ class TestSetUser(NewFile):
|
||||
|
||||
class TestApplicationUI(NewFile):
|
||||
ifc_class = "IfcApplication"
|
||||
attrs = {
|
||||
attrs_: dict[str, Any] = {
|
||||
"Version": "v001",
|
||||
"ApplicationFullName": "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):
|
||||
application = ifcopenshell.file().create_entity(self.ifc_class)
|
||||
subject.set_application(application)
|
||||
@@ -614,14 +622,16 @@ class TestApplicationUI(NewFile):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
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)
|
||||
subject.set_application(application)
|
||||
subject.import_application_attributes()
|
||||
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
|
||||
|
||||
def test_export(self):
|
||||
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