Organisations now support null attributes.

This commit is contained in:
Dion Moult
2021-09-29 18:39:10 +10:00
parent 406fd158d3
commit beff90cd5e
16 changed files with 454 additions and 315 deletions
@@ -20,7 +20,6 @@ import bpy
from . import ui, prop, operator from . import ui, prop, operator
classes = ( classes = (
operator.AddOrRemoveElementFromCollection,
operator.EnableEditingPerson, operator.EnableEditingPerson,
operator.DisableEditingPerson, operator.DisableEditingPerson,
operator.AddPerson, operator.AddPerson,
@@ -45,8 +44,6 @@ classes = (
operator.RemoveAddressAttribute, operator.RemoveAddressAttribute,
operator.EditAddress, operator.EditAddress,
operator.RemoveAddress, operator.RemoveAddress,
prop.Person,
prop.Organisation,
prop.BIMOwnerProperties, prop.BIMOwnerProperties,
ui.BIM_PT_people, ui.BIM_PT_people,
ui.BIM_PT_organisations, ui.BIM_PT_organisations,
@@ -20,7 +20,60 @@ import bpy
import blenderbim.tool as tool import blenderbim.tool as tool
class PeopleData: class RolesAddressesData:
@classmethod
def get_roles(cls, parent):
results = []
for role in parent.Roles or []:
results.append(
{
"id": role.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_role_id == role.id(),
"label": role.UserDefinedRole or role.Role,
"props": bpy.context.scene.BIMOwnerProperties.role_attributes,
}
)
return results
@classmethod
def get_addresses(cls, parent):
results = []
for address in parent.Addresses or []:
results.append(
{
"id": address.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_address_id == address.id(),
"label": address.is_a(),
"props": bpy.context.scene.BIMOwnerProperties.address_attributes,
"list_attributes": cls.get_address_list_attributes(address),
}
)
return results
@classmethod
def get_address_list_attributes(cls, address):
results = []
props = bpy.context.scene.BIMOwnerProperties
if address.is_a("IfcPostalAddress"):
names = ["AddressLines"]
elif address.is_a("IfcTelecomAddress"):
names = ["TelephoneNumbers", "FacsimileNumbers", "ElectronicMailAddresses", "MessagingIDs"]
for name in names:
if name == "AddressLines":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.address_lines)]
elif name == "TelephoneNumbers":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.telephone_numbers)]
elif name == "FacsimileNumbers":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.facsimile_numbers)]
elif name == "ElectronicMailAddresses":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.electronic_mail_addresses)]
elif name == "MessagingIDs":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.messaging_ids)]
results.append({"name": name, "items": items})
return results
class PeopleData(RolesAddressesData):
data = {} data = {}
is_loaded = False is_loaded = False
@@ -41,8 +94,8 @@ class PeopleData:
"is_editing": cls.get_person_is_editing(person), "is_editing": cls.get_person_is_editing(person),
"is_engaged": bool(person.EngagedIn), "is_engaged": bool(person.EngagedIn),
"list_attributes": cls.get_person_list_attributes(person), "list_attributes": cls.get_person_list_attributes(person),
"roles": cls.get_person_roles(person), "roles": cls.get_roles(person),
"addresses": cls.get_person_addresses(person), "addresses": cls.get_addresses(person),
} }
) )
return people return people
@@ -77,53 +130,29 @@ class PeopleData:
results.append({"name": name, "items": items}) results.append({"name": name, "items": items})
return results return results
@classmethod
def get_person_roles(cls, person): class OrganisationsData(RolesAddressesData):
results = [] data = {}
for role in person.Roles or []: is_loaded = False
results.append(
{
"id": role.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_role_id == role.id(),
"label": role.UserDefinedRole or role.Role,
"props": bpy.context.scene.BIMOwnerProperties.role_attributes,
}
)
return results
@classmethod @classmethod
def get_person_addresses(cls, person): def load(cls):
results = [] cls.data = {"organisations": cls.get_organisations()}
for address in person.Addresses or []: cls.is_loaded = True
results.append(
{
"id": address.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_address_id == address.id(),
"label": address.is_a(),
"props": bpy.context.scene.BIMOwnerProperties.address_attributes,
"list_attributes": cls.get_address_list_attributes(address),
}
)
return results
@classmethod @classmethod
def get_address_list_attributes(cls, address): def get_organisations(cls):
results = [] organisations = []
props = bpy.context.scene.BIMOwnerProperties for organisation in tool.Ifc().get().by_type("IfcOrganization"):
if address.is_a("IfcPostalAddress"): organisations.append(
names = ["AddressLines"] {
elif address.is_a("IfcTelecomAddress"): "id": organisation.id(),
names = ["TelephoneNumbers", "FacsimileNumbers", "ElectronicMailAddresses", "MessagingIDs"] "props": bpy.context.scene.BIMOwnerProperties.organisation_attributes,
for name in names: "name": organisation.Name,
if name == "AddressLines": "is_editing": bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id(),
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.address_lines)] "is_engaged": bool(organisation.Engages),
elif name == "TelephoneNumbers": "roles": cls.get_roles(organisation),
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.telephone_numbers)] "addresses": cls.get_addresses(organisation),
elif name == "FacsimileNumbers": }
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.facsimile_numbers)] )
elif name == "ElectronicMailAddresses": return organisations
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.electronic_mail_addresses)]
elif name == "MessagingIDs":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.messaging_ids)]
results.append({"name": name, "items": items})
return results
@@ -30,43 +30,7 @@ class Operator:
def execute(self, context): def execute(self, context):
IfcStore.execute_ifc_operator(self, context) IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.module.owner.data.PeopleData.is_loaded = False blenderbim.bim.module.owner.data.PeopleData.is_loaded = False
return {"FINISHED"} blenderbim.bim.module.owner.data.OrganisationsData.is_loaded = False
def flatten_collection(collection):
return [v.name for v in collection] if collection else None
def populate_collection(collection, collection_data):
collection.clear()
if collection_data:
for value in collection_data:
collection.add().name = value
else:
collection.add()
class AddOrRemoveElementFromCollection(bpy.types.Operator):
bl_idname = "bim.add_or_remove_element_from_collection"
bl_label = "Add or Remove Element From Collection"
bl_options = {"REGISTER", "UNDO"}
operation: bpy.props.EnumProperty(
items=(("+", "Add", "Add item to collection"), ("-", "Remove", "Remove item from collection")),
default="+",
)
collection_path: bpy.props.StringProperty()
selected_item_idx: bpy.props.IntProperty(default=-1)
def execute(self, context):
# Ugly but I hate using eval()
collection = context.scene
for attr in self.collection_path.split("."):
if hasattr(collection, attr):
collection = getattr(collection, attr)
if self.operation == "+" and hasattr(collection, "add"):
collection.add()
elif hasattr(collection, "remove") and 0 <= self.selected_item_idx < len(collection):
collection.remove(self.selected_item_idx)
return {"FINISHED"} return {"FINISHED"}
@@ -256,90 +220,50 @@ class RemoveAddress(bpy.types.Operator, Operator):
core.remove_address(tool.Ifc(), address=tool.Ifc().get().by_id(self.address)) core.remove_address(tool.Ifc(), address=tool.Ifc().get().by_id(self.address))
class EnableEditingOrganisation(bpy.types.Operator): class EnableEditingOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_organisation" bl_idname = "bim.enable_editing_organisation"
bl_label = "Enable Editing Organisation" bl_label = "Enable Editing Organisation"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
organisation_id: bpy.props.IntProperty() organisation: bpy.props.IntProperty()
def execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.enable_editing_organisation(
props = context.scene.BIMOwnerProperties tool.OrganisationEditor, organisation=tool.Ifc().get().by_id(self.organisation)
props.active_organisation_id = self.organisation_id )
data = Data.organisations[self.organisation_id]
identification = data["Id"] if self.file.schema == "IFC2X3" else data["Identification"]
props.organisation.identification = identification or ""
props.organisation.name = data["Name"]
props.organisation.description = data["Description"] or ""
return {"FINISHED"}
class DisableEditingOrganisation(bpy.types.Operator): class DisableEditingOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_organisation" bl_idname = "bim.disable_editing_organisation"
bl_label = "Disable Editing Organisation" bl_label = "Disable Editing Organisation"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
context.scene.BIMOwnerProperties.active_organisation_id = 0 core.disable_editing_organisation(tool.OrganisationEditor)
return {"FINISHED"}
class AddOrganisation(bpy.types.Operator): class AddOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.add_organisation" bl_idname = "bim.add_organisation"
bl_label = "Add Organisation" bl_label = "Add Organisation"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
ifcopenshell.api.run("owner.add_organisation", IfcStore.get_file()) core.add_organisation(tool.Ifc)
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EditOrganisation(bpy.types.Operator): class EditOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.edit_organisation" bl_idname = "bim.edit_organisation"
bl_label = "Edit Organisation" bl_label = "Edit Organisation"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.edit_organisation(tool.Ifc, tool.OrganisationEditor)
props = context.scene.BIMOwnerProperties
attributes = {
"Identification": props.organisation.identification or None,
"Name": props.organisation.name,
"Description": props.organisation.description or None,
}
if self.file.schema == "IFC2X3":
attributes["Id"] = attributes["Identification"]
del attributes["Identification"]
ifcopenshell.api.run(
"owner.edit_organisation",
self.file,
**{"organisation": self.file.by_id(props.active_organisation_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_organisation()
return {"FINISHED"}
class RemoveOrganisation(bpy.types.Operator): class RemoveOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.remove_organisation" bl_idname = "bim.remove_organisation"
bl_label = "Remove Organisation" bl_label = "Remove Organisation"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
organisation_id: bpy.props.IntProperty() organisation: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.remove_organisation(tool.Ifc, tool.Ifc.get().by_id(self.organisation))
ifcopenshell.api.run(
"owner.remove_organisation", self.file, **{"organisation": self.file.by_id(self.organisation_id)}
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -67,30 +67,14 @@ def getOrganisations(self, context):
return _organisations_enum return _organisations_enum
class Organisation(PropertyGroup):
identification: StringProperty(name="Identification")
name: StringProperty(name="Name")
description: StringProperty(name="Description")
class Person(PropertyGroup):
name: StringProperty(name="Identification")
family_name: StringProperty(name="Family Name")
given_name: StringProperty(name="Given Name")
middle_names: CollectionProperty(type=StrProperty, name="Middle Names")
prefix_titles: CollectionProperty(type=StrProperty, name="Prefixes")
suffix_titles: CollectionProperty(type=StrProperty, name="Suffixes")
class BIMOwnerProperties(PropertyGroup): class BIMOwnerProperties(PropertyGroup):
person: PointerProperty(type=Person)
person_attributes: CollectionProperty(name="Person Attributes", type=Attribute) person_attributes: CollectionProperty(name="Person Attributes", type=Attribute)
middle_names: CollectionProperty(type=StrProperty, name="Middle Names") middle_names: CollectionProperty(type=StrProperty, name="Middle Names")
prefix_titles: CollectionProperty(type=StrProperty, name="Prefixes") prefix_titles: CollectionProperty(type=StrProperty, name="Prefixes")
suffix_titles: CollectionProperty(type=StrProperty, name="Suffixes") suffix_titles: CollectionProperty(type=StrProperty, name="Suffixes")
active_person_id: IntProperty(name="Active Person Id") active_person_id: IntProperty(name="Active Person Id")
organisation: PointerProperty(type=Organisation)
active_organisation_id: IntProperty(name="Active Organisation Id") active_organisation_id: IntProperty(name="Active Organisation Id")
organisation_attributes: CollectionProperty(name="Organisation Attributes", type=Attribute)
active_role_id: IntProperty(name="Active Role Id") active_role_id: IntProperty(name="Active Role Id")
role_attributes: CollectionProperty(name="Role Attributes", type=Attribute) role_attributes: CollectionProperty(name="Role Attributes", type=Attribute)
active_address_id: IntProperty(name="Active Address Id") active_address_id: IntProperty(name="Active Address Id")
+60 -150
View File
@@ -19,103 +19,70 @@
import bpy import bpy
import blenderbim.bim.helper import blenderbim.bim.helper
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.module.owner.data import PeopleData from blenderbim.bim.module.owner.data import PeopleData, OrganisationsData
from bpy.types import Panel from bpy.types import Panel
from ifcopenshell.api.owner.data import Data from ifcopenshell.api.owner.data import Data
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
def draw_string_collection(layout, owner, collection_name):
column = layout.column(align=True)
collection = getattr(owner, collection_name)
for i in range(len(collection)):
if i == 0:
row = draw_prop_on_new_row(
column, collection[i], "name", align=True, text=f"{owner.bl_rna.properties[collection_name].name}"
)
add_op = row.operator("bim.add_or_remove_element_from_collection", icon="ADD", text="")
add_op.operation = "+"
add_op.collection_path = collection.path_from_id()
else:
row = draw_prop_on_new_row(column, collection[i], "name", align=True, text=f"#{i + 1}")
rem_op = row.operator("bim.add_or_remove_element_from_collection", icon="REMOVE", text="")
rem_op.operation = "-"
rem_op.collection_path = collection.path_from_id()
rem_op.selected_item_idx = i
def draw_prop_on_new_row(layout, owner, attribute, align=False, **kwargs): def draw_prop_on_new_row(layout, owner, attribute, align=False, **kwargs):
row = layout.row(align=align) row = layout.row(align=align)
row.prop(owner, attribute, **kwargs) row.prop(owner, attribute, **kwargs)
return row return row
def draw_roles_ui(box, assigned_object_id, roles, context): def draw_roles(box, parent):
props = context.scene.BIMOwnerProperties
row = box.row(align=True) row = box.row(align=True)
row.label(text="Roles") row.label(text="Roles")
row.operator("bim.add_role", icon="ADD", text="").assigned_object_id = assigned_object_id op = row.operator("bim.add_role", icon="ADD", text="")
for role_id in roles: op.parent = parent["id"]
role = Data.roles[role_id]
if props.active_role_id == role_id: for role in parent["roles"]:
blender_role = props.role if role["is_editing"]:
box2 = box.box() row = box.row(align=True)
row = draw_prop_on_new_row(box2, blender_role, "name", align=True, icon="MOD_CLOTH", text="") row.operator("bim.edit_role", icon="CHECKMARK")
row.operator("bim.edit_role", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_role", icon="CANCEL", text="") row.operator("bim.disable_editing_role", icon="CANCEL", text="")
if blender_role.name == "USERDEFINED": blenderbim.bim.helper.draw_attributes(role["props"], box)
draw_prop_on_new_row(box2, blender_role, "user_defined_role")
draw_prop_on_new_row(box2, blender_role, "description")
else: else:
row = box.row(align=True) row = box.row(align=True)
row.label(text=role["UserDefinedRole"] or role["Role"]) row.label(text=role["label"])
row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role_id = role_id row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role = role["id"]
row.operator("bim.remove_role", icon="X", text="").role_id = role_id row.operator("bim.remove_role", icon="X", text="").role = role["id"]
def draw_addresses_ui(box, assigned_object_id, addresses, file, context): def draw_addresses(box, parent):
props = context.scene.BIMOwnerProperties
row = box.row(align=True) row = box.row(align=True)
row.label(text="Addresses") row.label(text="Addresses")
op = row.operator("bim.add_address", icon="LINK_BLEND", text="") op = row.operator("bim.add_address", icon="LINK_BLEND", text="")
op.assigned_object_id = assigned_object_id op.parent = parent["id"]
op.ifc_class = "IfcTelecomAddress" op.ifc_class = "IfcTelecomAddress"
op = row.operator("bim.add_address", icon="APPEND_BLEND", text="") op = row.operator("bim.add_address", icon="APPEND_BLEND", text="")
op.assigned_object_id = assigned_object_id op.parent = parent["id"]
op.ifc_class = "IfcPostalAddress" op.ifc_class = "IfcPostalAddress"
for address_id in addresses:
address = Data.addresses[address_id]
if props.active_address_id == address_id:
blender_address = props.address
box2 = box.box()
row = draw_prop_on_new_row(box2, blender_address, "purpose", align=True, icon="MOD_CLOTH", text="")
row.operator("bim.edit_address", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_address", icon="CANCEL", text="")
if blender_address.purpose == "USERDEFINED":
draw_prop_on_new_row(box2, blender_address, "user_defined_purpose")
draw_prop_on_new_row(box2, blender_address, "description")
if address["type"] == "IfcTelecomAddress": for address in parent["addresses"]:
draw_string_collection(box2, blender_address, "telephone_numbers") if address["is_editing"]:
draw_string_collection(box2, blender_address, "facsimile_numbers") row = box.row(align=True)
draw_prop_on_new_row(box2, blender_address, "pager_number") row.operator("bim.edit_address", icon="CHECKMARK")
draw_string_collection(box2, blender_address, "electronic_mail_addresses") row.operator("bim.disable_editing_address", icon="CANCEL", text="")
draw_prop_on_new_row(box2, blender_address, "www_home_page_url") blenderbim.bim.helper.draw_attributes(address["props"], box)
if file.schema != "IFC2X3": for attribute in address["list_attributes"]:
draw_string_collection(box2, blender_address, "messaging_ids") row = box.row(align=True)
elif address["type"] == "IfcPostalAddress": row.label(text=attribute["name"])
draw_prop_on_new_row(box2, blender_address, "internal_location") op = row.operator("bim.add_address_attribute", icon="ADD", text="")
draw_string_collection(box2, blender_address, "address_lines") op.name = attribute["name"]
draw_prop_on_new_row(box2, blender_address, "postal_box")
draw_prop_on_new_row(box2, blender_address, "town") for item in attribute["items"]:
draw_prop_on_new_row(box2, blender_address, "region") row = box.row(align=True)
draw_prop_on_new_row(box2, blender_address, "postal_code") row.prop(item["prop"], "name", text="")
draw_prop_on_new_row(box2, blender_address, "country") op = row.operator("bim.remove_address_attribute", icon="REMOVE", text="")
op.name = attribute["name"]
op.id = item["id"]
else: else:
row = box.row(align=True) row = box.row(align=True)
row.label(text=address["type"]) row.label(text=address["label"])
row.operator("bim.enable_editing_address", icon="GREASEPENCIL", text="").address_id = address_id row.operator("bim.enable_editing_address", icon="GREASEPENCIL", text="").address = address["id"]
row.operator("bim.remove_address", icon="X", text="").address_id = address_id row.operator("bim.remove_address", icon="X", text="").address = address["id"]
class BIM_PT_people(bpy.types.Panel): class BIM_PT_people(bpy.types.Panel):
@@ -164,8 +131,8 @@ class BIM_PT_people(bpy.types.Panel):
op.name = attribute["name"] op.name = attribute["name"]
op.id = item["id"] op.id = item["id"]
self.draw_roles(box, person) draw_roles(box, person)
self.draw_addresses(box, person) draw_addresses(box, person)
else: else:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=person["name"]) row.label(text=person["name"])
@@ -173,58 +140,6 @@ class BIM_PT_people(bpy.types.Panel):
if not person["is_engaged"]: if not person["is_engaged"]:
row.operator("bim.remove_person", icon="X", text="").person = person["id"] row.operator("bim.remove_person", icon="X", text="").person = person["id"]
def draw_roles(self, box, person):
row = box.row(align=True)
row.label(text="Roles")
op = row.operator("bim.add_role", icon="ADD", text="")
op.parent = person["id"]
for role in person["roles"]:
if role["is_editing"]:
row = box.row(align=True)
row.operator("bim.edit_role", icon="CHECKMARK")
row.operator("bim.disable_editing_role", icon="CANCEL", text="")
blenderbim.bim.helper.draw_attributes(role["props"], box)
else:
row = box.row(align=True)
row.label(text=role["label"])
row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role = role["id"]
row.operator("bim.remove_role", icon="X", text="").role = role["id"]
def draw_addresses(self, box, person):
row = box.row(align=True)
row.label(text="Addresses")
op = row.operator("bim.add_address", icon="LINK_BLEND", text="")
op.parent = person["id"]
op.ifc_class = "IfcTelecomAddress"
op = row.operator("bim.add_address", icon="APPEND_BLEND", text="")
op.parent = person["id"]
op.ifc_class = "IfcPostalAddress"
for address in person["addresses"]:
if address["is_editing"]:
row = box.row(align=True)
row.operator("bim.edit_address", icon="CHECKMARK")
row.operator("bim.disable_editing_address", icon="CANCEL", text="")
blenderbim.bim.helper.draw_attributes(address["props"], box)
for attribute in address["list_attributes"]:
row = box.row(align=True)
row.label(text=attribute["name"])
op = row.operator("bim.add_address_attribute", icon="ADD", text="")
op.name = attribute["name"]
for item in attribute["items"]:
row = box.row(align=True)
row.prop(item["prop"], "name", text="")
op = row.operator("bim.remove_address_attribute", icon="REMOVE", text="")
op.name = attribute["name"]
op.id = item["id"]
else:
row = box.row(align=True)
row.label(text=address["label"])
row.operator("bim.enable_editing_address", icon="GREASEPENCIL", text="").address = address["id"]
row.operator("bim.remove_address", icon="X", text="").address = address["id"]
class BIM_PT_organisations(Panel): class BIM_PT_organisations(Panel):
bl_label = "IFC Organisations" bl_label = "IFC Organisations"
@@ -239,40 +154,35 @@ class BIM_PT_organisations(Panel):
return IfcStore.get_file() return IfcStore.get_file()
def draw(self, context): def draw(self, context):
if not Data.is_loaded: if not OrganisationsData.is_loaded:
Data.load(IfcStore.get_file()) OrganisationsData.load()
self.file = IfcStore.get_file()
self.layout.use_property_split = True self.layout.use_property_split = True
self.layout.use_property_decorate = False self.layout.use_property_decorate = False
props = context.scene.BIMOwnerProperties
row = self.layout.row() row = self.layout.row()
row.operator("bim.add_organisation", icon="ADD") row.operator("bim.add_organisation", icon="ADD")
for organisation_id, organisation in Data.organisations.items(): for organisation in OrganisationsData.data["organisations"]:
if props.active_organisation_id == organisation_id: self.draw_organisation(organisation)
blender_organisation = props.organisation
box = self.layout.box()
row = box.row(align=True)
row.prop(blender_organisation, "name", icon="USER", text="")
row.operator("bim.edit_organisation", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_organisation", icon="CANCEL", text="")
draw_prop_on_new_row(box, blender_organisation, "identification")
draw_prop_on_new_row(box, blender_organisation, "description")
draw_roles_ui(box, organisation_id, organisation["Roles"], context) def draw_organisation(self, organisation):
draw_addresses_ui(box, organisation_id, organisation["Addresses"], self.file, context) if organisation["is_editing"]:
else: box = self.layout.box()
row = self.layout.row(align=True) row = box.row(align=True)
row.label(text=organisation["Name"]) row.operator("bim.edit_organisation", icon="CHECKMARK")
if organisation["Roles"]: row.operator("bim.disable_editing_organisation", icon="CANCEL", text="")
row.label(text=", ".join([Data.roles[r]["Role"] for r in organisation["Roles"]])) blenderbim.bim.helper.draw_attributes(organisation["props"], box)
row.operator(
"bim.enable_editing_organisation", icon="GREASEPENCIL", text="" draw_roles(box, organisation)
).organisation_id = organisation_id draw_addresses(box, organisation)
if not organisation["is_engaged"]: else:
row.operator("bim.remove_organisation", icon="X", text="").organisation_id = organisation_id row = self.layout.row(align=True)
row.label(text=organisation["name"])
op = row.operator("bim.enable_editing_organisation", icon="GREASEPENCIL", text="")
op.organisation = organisation["id"]
if not organisation["is_engaged"]:
row.operator("bim.remove_organisation", icon="X", text="").organisation = organisation["id"]
class BIM_PT_owner(Panel): class BIM_PT_owner(Panel):
+23
View File
@@ -98,3 +98,26 @@ def add_address_attribute(address_editor, name=None):
def remove_address_attribute(address_editor, name=None, id=None): def remove_address_attribute(address_editor, name=None, id=None):
address_editor.remove_attribute(name, id) address_editor.remove_attribute(name, id)
def add_organisation(ifc):
return ifc.run("owner.add_organisation")
def remove_organisation(ifc, organisation=None):
ifc.run("owner.remove_organisation", organisation=organisation)
def enable_editing_organisation(organisation_editor, organisation=None):
organisation_editor.set_organisation(organisation)
organisation_editor.import_attributes()
def disable_editing_organisation(organisation_editor):
organisation_editor.clear_organisation()
def edit_organisation(ifc, organisation_editor):
organisation = organisation_editor.get_organisation()
ifc.run("owner.edit_organisation", organisation=organisation, attributes=organisation_editor.export_attributes())
organisation_editor.clear_organisation()
@@ -21,3 +21,4 @@ from blenderbim.core.tool.blender import Blender
from blenderbim.core.tool.person_editor import PersonEditor from blenderbim.core.tool.person_editor import PersonEditor
from blenderbim.core.tool.role_editor import RoleEditor from blenderbim.core.tool.role_editor import RoleEditor
from blenderbim.core.tool.address_editor import AddressEditor from blenderbim.core.tool.address_editor import AddressEditor
from blenderbim.core.tool.organisation_editor import OrganisationEditor
@@ -0,0 +1,46 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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/>.
import abc
class OrganisationEditor(abc.ABC):
@classmethod
@abc.abstractmethod
def set_organisation(cls, organisation):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_organisation(cls):
pass
@classmethod
@abc.abstractmethod
def get_organisation(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@@ -21,3 +21,4 @@ from blenderbim.tool.blender import Blender
from blenderbim.tool.person_editor import PersonEditor from blenderbim.tool.person_editor import PersonEditor
from blenderbim.tool.role_editor import RoleEditor from blenderbim.tool.role_editor import RoleEditor
from blenderbim.tool.address_editor import AddressEditor from blenderbim.tool.address_editor import AddressEditor
from blenderbim.tool.organisation_editor import OrganisationEditor
@@ -0,0 +1,53 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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/>.
import bpy
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.bim.helper
import blenderbim.tool as tool
class OrganisationEditor(blenderbim.core.tool.organisation_editor.OrganisationEditor):
@classmethod
def set_organisation(cls, organisation):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = organisation.id()
@classmethod
def import_attributes(cls):
organisation = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
props = bpy.context.scene.BIMOwnerProperties
props.organisation_attributes.clear()
blenderbim.bim.helper.import_attributes(
"IfcOrganization", props.organisation_attributes, organisation.get_info()
)
@classmethod
def clear_organisation(cls):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = 0
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.organisation_attributes)
return attributes
@classmethod
def get_organisation(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
+2 -1
View File
@@ -95,6 +95,7 @@ def additionally_the_object_name_is_selected(name):
bpy.context.view_layer.objects.active = obj bpy.context.view_layer.objects.active = obj
obj.select_set(True) obj.select_set(True)
def i_deselect_all_objects(): def i_deselect_all_objects():
bpy.context.view_layer.objects.active = None bpy.context.view_layer.objects.active = None
bpy.ops.object.select_all(action="DESELECT") bpy.ops.object.select_all(action="DESELECT")
@@ -346,7 +347,7 @@ definitions = {
'I add a cube of size "([0-9]+)" at "(.*)"': i_add_a_cube_of_size_size_at_location, 'I add a cube of size "([0-9]+)" at "(.*)"': i_add_a_cube_of_size_size_at_location,
'the object "(.*)" is selected': the_object_name_is_selected, 'the object "(.*)" is selected': the_object_name_is_selected,
'additionally the object "(.*)" is selected': additionally_the_object_name_is_selected, 'additionally the object "(.*)" is selected': additionally_the_object_name_is_selected,
'I deselect all objects': i_deselect_all_objects, "I deselect all objects": i_deselect_all_objects,
'I am on frame "([0-9]+)"': i_am_on_frame_number, 'I am on frame "([0-9]+)"': i_am_on_frame_number,
'I set "(.*)" to "(.*)"': i_set_prop_to_value, 'I set "(.*)" to "(.*)"': i_set_prop_to_value,
'"(.*)" is "(.*)"': prop_is_value, '"(.*)" is "(.*)"': prop_is_value,
@@ -165,3 +165,38 @@ Scenario: Edit address
And I press "bim.enable_editing_address(address={address})" And I press "bim.enable_editing_address(address={address})"
And I press "bim.edit_address()" And I press "bim.edit_address()"
Then nothing happens Then nothing happens
Scenario: Add organisation
Given an empty IFC project
When I press "bim.add_organisation"
Then nothing happens
Scenario: Enable editing organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.enable_editing_organisation(organisation={organisation})"
Then "scene.BIMOwnerProperties.active_organisation_id" is "{organisation}"
Scenario: Disable editing organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.enable_editing_organisation(organisation={organisation})"
And I press "bim.disable_editing_organisation"
Then "scene.BIMOwnerProperties.active_organisation_id" is "0"
Scenario: Edit organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.enable_editing_organisation(organisation={organisation})"
And I press "bim.edit_organisation"
Then "scene.BIMOwnerProperties.active_organisation_id" is "0"
Scenario: Remove organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.remove_organisation(organisation={organisation})"
Then nothing happens
+7
View File
@@ -56,6 +56,13 @@ def address_editor():
prophet.verify() prophet.verify()
@pytest.fixture
def organisation_editor():
prophet = Prophecy(blenderbim.core.tool.OrganisationEditor)
yield prophet
prophet.verify()
class Prophecy: class Prophecy:
def __init__(self, cls): def __init__(self, cls):
self.subject = cls self.subject = cls
+35 -1
View File
@@ -18,7 +18,7 @@
import blenderbim.core.owner as subject import blenderbim.core.owner as subject
from test.core.bootstrap import ifc, blender, person_editor, role_editor, address_editor from test.core.bootstrap import ifc, blender, person_editor, role_editor, address_editor, organisation_editor
class TestAddPerson: class TestAddPerson:
@@ -147,3 +147,37 @@ class TestRemoveAddressAttribute:
def test_run(self, address_editor): def test_run(self, address_editor):
address_editor.remove_attribute("name", "id").should_be_called() address_editor.remove_attribute("name", "id").should_be_called()
subject.remove_address_attribute(address_editor, name="name", id="id") subject.remove_address_attribute(address_editor, name="name", id="id")
class TestAddOrganisation:
def test_run(self, ifc):
ifc.run("owner.add_organisation").should_be_called().will_return("organisation")
assert subject.add_organisation(ifc) == "organisation"
class TestRemoveOrganisation:
def test_run(self, ifc):
ifc.run("owner.remove_organisation", organisation="organisation").should_be_called()
subject.remove_organisation(ifc, organisation="organisation")
class TestEnableEditingOrganisation:
def test_run(self, organisation_editor):
organisation_editor.set_organisation("organisation").should_be_called()
organisation_editor.import_attributes().should_be_called()
subject.enable_editing_organisation(organisation_editor, organisation="organisation")
class TestDisableEditingOrganisation:
def test_run(self, organisation_editor):
organisation_editor.clear_organisation().should_be_called()
subject.disable_editing_organisation(organisation_editor)
class TestEditOrganisation:
def test_run(self, ifc, organisation_editor):
organisation_editor.get_organisation().should_be_called().will_return("organisation")
organisation_editor.export_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_organisation", organisation="organisation", attributes="attributes").should_be_called()
organisation_editor.clear_organisation().should_be_called()
subject.edit_organisation(ifc, organisation_editor)
@@ -0,0 +1,94 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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/>.
import bpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.organisation_editor import OrganisationEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.organisation_editor.OrganisationEditor)
class TestSetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
organisation = ifcopenshell.file().createIfcOrganization()
subject().set_organisation(organisation)
assert bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Name = "Name"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification"
assert props.organisation_attributes.get("Name").string_value == "Name"
assert props.organisation_attributes.get("Description").string_value == "Description"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_attributes()
organisation.Identification = "Identification2"
organisation.Description = None
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification2"
assert props.organisation_attributes.get("Description").string_value == ""
class TestClearOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_organisation_id = 1
subject().clear_organisation()
assert props.active_organisation_id == 0
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
TestImportAttributes().test_run()
assert subject().export_attributes() == {
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
}
class TestGetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
subject().set_organisation(organisation)
assert subject().get_organisation() == organisation