mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 22:50:21 +00:00
Improve ifc patch arguments User Experience (#1655)
* Add description field to Attribute Container
* Add helper function to extract IFCPatch arguments
* Add Operator to populate Attributes with IFCPatch arguments
* Support int, float, bool and improve UX/UI
* Move all reflection logic inside helper module
* Improve UI
* Correctly unpack arguments when Patching IFC
* Refactor Attribute class to provide agnostic value access
* Take advantage of Attribute agnosticity refactor
* Use regular json if no docstring when Patching
* centralize docstring extraction in UI module
* Revert "centralize docstring extraction in UI module"
This reverts commit e7a63d2a0b.
* Place Patch description in recipe enum field
* Minor fixes
* Minor fixes
* Remove description field from Attribute
* Auto-update IFC Patch args on recipe change
This commit is contained in:
@@ -11,22 +11,8 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
def draw_attributes(props, layout, copy_operator=None):
|
def draw_attributes(props, layout, copy_operator=None):
|
||||||
for attribute in props:
|
for attribute in props:
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
value = None
|
draw_attribute(attribute, row)
|
||||||
if attribute.data_type == "string":
|
value = attribute.get_value()
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
value = attribute.string_value
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
value = attribute.bool_value
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
value = attribute.int_value
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
value = attribute.float_value
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
value = attribute.enum_value
|
|
||||||
if attribute.is_optional:
|
if attribute.is_optional:
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
if copy_operator:
|
if copy_operator:
|
||||||
@@ -34,6 +20,17 @@ def draw_attributes(props, layout, copy_operator=None):
|
|||||||
op.data = json.dumps({"name": attribute.name, "value": value, "is_null": attribute.is_null})
|
op.data = json.dumps({"name": attribute.name, "value": value, "is_null": attribute.is_null})
|
||||||
|
|
||||||
|
|
||||||
|
def draw_attribute(attribute, layout):
|
||||||
|
if not attribute.get_value_attr():
|
||||||
|
layout.label(text=attribute.name)
|
||||||
|
else:
|
||||||
|
layout.prop(
|
||||||
|
attribute,
|
||||||
|
attribute.get_value_attr(),
|
||||||
|
text=attribute.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def import_attributes(ifc_class, props, data, callback=None):
|
def import_attributes(ifc_class, props, data, callback=None):
|
||||||
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||||
@@ -70,17 +67,5 @@ def export_attributes(props, callback=None):
|
|||||||
is_handled_by_callback = callback(attributes, prop) if callback else False
|
is_handled_by_callback = callback(attributes, prop) if callback else False
|
||||||
if is_handled_by_callback:
|
if is_handled_by_callback:
|
||||||
continue # Our job is done
|
continue # Our job is done
|
||||||
|
attributes[prop.name] = prop.get_value()
|
||||||
if prop.is_null:
|
|
||||||
attributes[prop.name] = None
|
|
||||||
elif prop.data_type == "string":
|
|
||||||
attributes[prop.name] = prop.string_value
|
|
||||||
elif prop.data_type == "boolean":
|
|
||||||
attributes[prop.name] = prop.bool_value
|
|
||||||
elif prop.data_type == "integer":
|
|
||||||
attributes[prop.name] = prop.int_value
|
|
||||||
elif prop.data_type == "float":
|
|
||||||
attributes[prop.name] = prop.float_value
|
|
||||||
elif prop.data_type == "enum":
|
|
||||||
attributes[prop.name] = prop.enum_value
|
|
||||||
return attributes
|
return attributes
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes
|
||||||
from ifcopenshell.api.constraint.data import Data
|
from ifcopenshell.api.constraint.data import Data
|
||||||
|
|
||||||
|
|
||||||
@@ -44,14 +45,7 @@ class BIM_PT_constraints(Panel):
|
|||||||
self.draw_editable_ui(context)
|
self.draw_editable_ui(context)
|
||||||
|
|
||||||
def draw_editable_ui(self, context):
|
def draw_editable_ui(self, context):
|
||||||
for attribute in self.props.constraint_attributes:
|
draw_attributes(self.props.constraint_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_object_constraints(Panel):
|
class BIM_PT_object_constraints(Panel):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import blenderbim.bim.module.cost.prop as CostProp
|
import blenderbim.bim.module.cost.prop as CostProp
|
||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes
|
||||||
from ifcopenshell.api.cost.data import Data
|
from ifcopenshell.api.cost.data import Data
|
||||||
|
|
||||||
|
|
||||||
@@ -66,14 +67,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
self.layout.template_list("BIM_UL_cost_columns", "", self.props, "columns", self.props, "active_column_index")
|
self.layout.template_list("BIM_UL_cost_columns", "", self.props, "columns", self.props, "active_column_index")
|
||||||
|
|
||||||
def draw_editable_cost_schedule_ui(self):
|
def draw_editable_cost_schedule_ui(self):
|
||||||
for attribute in self.props.cost_schedule_attributes:
|
draw_attributes(self.props.cost_schedule_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_editable_cost_item_ui(self, cost_schedule_id):
|
def draw_editable_cost_item_ui(self, cost_schedule_id):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -119,18 +113,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
self.draw_editable_cost_item_values_ui()
|
self.draw_editable_cost_item_values_ui()
|
||||||
|
|
||||||
def draw_editable_cost_item_attributes_ui(self):
|
def draw_editable_cost_item_attributes_ui(self):
|
||||||
for attribute in self.props.cost_item_attributes:
|
draw_attributes(self.props.cost_item_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_editable_cost_item_quantities_ui(self):
|
def draw_editable_cost_item_quantities_ui(self):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -165,20 +148,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
self.draw_editable_cost_item_quantity_ui(box)
|
self.draw_editable_cost_item_quantity_ui(box)
|
||||||
|
|
||||||
def draw_editable_cost_item_quantity_ui(self, layout):
|
def draw_editable_cost_item_quantity_ui(self, layout):
|
||||||
for attribute in self.props.quantity_attributes:
|
draw_attributes(self.props.quantity_attributes, self.layout)
|
||||||
row = layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_editable_cost_item_values_ui(self):
|
def draw_editable_cost_item_values_ui(self):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -253,21 +223,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
|
|
||||||
def draw_editable_cost_value_ui(self, layout, cost_value):
|
def draw_editable_cost_value_ui(self, layout, cost_value):
|
||||||
for attribute in self.props.cost_value_attributes:
|
draw_attributes(self.props.cost_value_attributes, self.layout)
|
||||||
row = layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_cost_item_quantities(Panel):
|
class BIM_PT_cost_item_quantities(Panel):
|
||||||
bl_label = "IFC Cost Item Quantities"
|
bl_label = "IFC Cost Item Quantities"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes
|
||||||
from ifcopenshell.api.document.data import Data
|
from ifcopenshell.api.document.data import Data
|
||||||
|
|
||||||
|
|
||||||
@@ -48,14 +49,7 @@ class BIM_PT_documents(Panel):
|
|||||||
self.draw_editable_ui(context)
|
self.draw_editable_ui(context)
|
||||||
|
|
||||||
def draw_editable_ui(self, context):
|
def draw_editable_ui(self, context):
|
||||||
for attribute in self.props.document_attributes:
|
draw_attributes(self.props.document_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_object_documents(Panel):
|
class BIM_PT_object_documents(Panel):
|
||||||
|
|||||||
@@ -102,16 +102,7 @@ class EditGeoreferencing(bpy.types.Operator):
|
|||||||
if data_type == "entity":
|
if data_type == "entity":
|
||||||
continue
|
continue
|
||||||
blender_attribute = props.projected_crs.get(attribute.name())
|
blender_attribute = props.projected_crs.get(attribute.name())
|
||||||
if blender_attribute.is_null:
|
projected_crs[attribute.name()] = blender_attribute.get_value()
|
||||||
projected_crs[attribute.name()] = None
|
|
||||||
elif blender_attribute.data_type == "string":
|
|
||||||
projected_crs[attribute.name()] = blender_attribute.string_value
|
|
||||||
elif blender_attribute.data_type == "float":
|
|
||||||
projected_crs[attribute.name()] = blender_attribute.float_value
|
|
||||||
elif blender_attribute.data_type == "integer":
|
|
||||||
projected_crs[attribute.name()] = blender_attribute.int_value
|
|
||||||
elif blender_attribute.data_type == "boolean":
|
|
||||||
projected_crs[attribute.name()] = blender_attribute.bool_value
|
|
||||||
|
|
||||||
map_unit = ""
|
map_unit = ""
|
||||||
if not props.is_map_unit_null:
|
if not props.is_map_unit_null:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import ifcopenshell.util.geolocation
|
|||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from ifcopenshell.api.georeference.data import Data
|
from ifcopenshell.api.georeference.data import Data
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes, draw_attribute
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_gis(Panel):
|
class BIM_PT_gis(Panel):
|
||||||
@@ -34,17 +35,7 @@ class BIM_PT_gis(Panel):
|
|||||||
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
||||||
row.operator("bim.disable_editing_georeferencing", icon="X", text="")
|
row.operator("bim.disable_editing_georeferencing", icon="X", text="")
|
||||||
|
|
||||||
for attribute in props.projected_crs:
|
draw_attributes(props.projected_crs, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(props, "map_unit_type", text="MapUnit")
|
row.prop(props, "map_unit_type", text="MapUnit")
|
||||||
@@ -62,17 +53,7 @@ class BIM_PT_gis(Panel):
|
|||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
|
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
|
||||||
row.operator("bim.set_blender_grid_north", text="Set Blender North")
|
row.operator("bim.set_blender_grid_north", text="Set Blender North")
|
||||||
row = self.layout.row(align=True)
|
draw_attribute(attribute, self.layout.row())
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.label(text="True North", icon="LIGHT_SUN")
|
row.label(text="True North", icon="LIGHT_SUN")
|
||||||
|
|||||||
@@ -655,17 +655,7 @@ class EditMaterialSetItem(bpy.types.Operator):
|
|||||||
props = obj.BIMObjectMaterialProperties
|
props = obj.BIMObjectMaterialProperties
|
||||||
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
|
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
|
||||||
|
|
||||||
attributes = {}
|
attributes = {attribute.name: attribute.get_value() for attribute in props.material_set_item_attributes}
|
||||||
for attribute in props.material_set_item_attributes:
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
value = attribute.string_value
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
value = attribute.float_value
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
value = attribute.int_value
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
value = attribute.bool_value
|
|
||||||
attributes[attribute.name] = None if attribute.is_null else value
|
|
||||||
|
|
||||||
if product_data["type"] == "IfcMaterialConstituentSet":
|
if product_data["type"] == "IfcMaterialConstituentSet":
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
@@ -690,19 +680,7 @@ class EditMaterialSetItem(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
Data.load_layers()
|
Data.load_layers()
|
||||||
elif product_data["type"] == "IfcMaterialProfileSet" or product_data["type"] == "IfcMaterialProfileSetUsage":
|
elif product_data["type"] == "IfcMaterialProfileSet" or product_data["type"] == "IfcMaterialProfileSetUsage":
|
||||||
profile_attributes = {}
|
profile_attributes = {attr.name: attr.get_value() for attr in props.material_set_item_profile_attributes}
|
||||||
for attribute in props.material_set_item_profile_attributes:
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
value = attribute.string_value
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
value = attribute.float_value
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
value = attribute.int_value
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
value = attribute.bool_value
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
value = attribute.enum_value
|
|
||||||
profile_attributes[attribute.name] = None if attribute.is_null else value
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"material.edit_profile",
|
"material.edit_profile",
|
||||||
self.file,
|
self.file,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from bpy.types import Panel
|
|||||||
from ifcopenshell.api.material.data import Data
|
from ifcopenshell.api.material.data import Data
|
||||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_material(Panel):
|
class BIM_PT_material(Panel):
|
||||||
@@ -181,17 +182,7 @@ class BIM_PT_object_material(Panel):
|
|||||||
op.material_set_item = set_item_id
|
op.material_set_item = set_item_id
|
||||||
row.operator("bim.disable_editing_material_set_item", icon="CANCEL", text="")
|
row.operator("bim.disable_editing_material_set_item", icon="CANCEL", text="")
|
||||||
|
|
||||||
for attribute in self.props.material_set_item_attributes:
|
draw_attributes(self.props.material_set_item_attributes, self.layout)
|
||||||
row = box.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
if self.set_item_name == "profile":
|
if self.set_item_name == "profile":
|
||||||
self.draw_assign_profile_ui(box, item)
|
self.draw_assign_profile_ui(box, item)
|
||||||
@@ -213,20 +204,7 @@ class BIM_PT_object_material(Panel):
|
|||||||
row.operator("bim.disable_editing_material_set_item", icon="CANCEL", text="")
|
row.operator("bim.disable_editing_material_set_item", icon="CANCEL", text="")
|
||||||
|
|
||||||
def draw_editable_profile_ui(self, layout, item):
|
def draw_editable_profile_ui(self, layout, item):
|
||||||
for attribute in self.props.material_set_item_profile_attributes:
|
draw_attributes(self.props.material_set_item_profile_attributes, self.layout)
|
||||||
row = layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "integer":
|
|
||||||
row.prop(attribute, "int_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "boolean":
|
|
||||||
row.prop(attribute, "bool_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_read_only_set_item_ui(self, set_item_id, index, is_first=False, is_last=False):
|
def draw_read_only_set_item_ui(self, set_item_id, index, is_first=False, is_last=False):
|
||||||
if self.product_data["type"] == "IfcMaterialList":
|
if self.product_data["type"] == "IfcMaterialList":
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ classes = (
|
|||||||
operator.SelectIfcPatchInput,
|
operator.SelectIfcPatchInput,
|
||||||
operator.SelectIfcPatchOutput,
|
operator.SelectIfcPatchOutput,
|
||||||
operator.ExecuteIfcPatch,
|
operator.ExecuteIfcPatch,
|
||||||
|
operator.UpdateIfcPatchArguments,
|
||||||
prop.BIMPatchProperties,
|
prop.BIMPatchProperties,
|
||||||
ui.BIM_PT_patch,
|
ui.BIM_PT_patch,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import typing
|
||||||
|
import inspect
|
||||||
|
import collections
|
||||||
|
import importlib
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
|
def extract_docs(
|
||||||
|
module: ModuleType,
|
||||||
|
submodule_name: str,
|
||||||
|
cls_name: str,
|
||||||
|
method_name: str,
|
||||||
|
boilerplate_args : typing.Iterable[str]=None):
|
||||||
|
"""Extract class docstrings and method arguments
|
||||||
|
|
||||||
|
:param module: Parent module from which to extract the submodule class
|
||||||
|
:param submodule_name: Submodule from which to extract the class
|
||||||
|
:param cls_name: Class from which to extract the docstring and method arguments
|
||||||
|
:param method_name: Class Method name from which to extract arguments
|
||||||
|
:param boilerplate_args: String iterable containing arguments that shall not be parsed
|
||||||
|
"""
|
||||||
|
spec = importlib.util.spec_from_file_location(submodule_name, f"{module.__path__[0]}/recipes/{submodule_name}.py")
|
||||||
|
submodule = importlib.util.module_from_spec(spec)
|
||||||
|
try:
|
||||||
|
spec.loader.exec_module(submodule)
|
||||||
|
try:
|
||||||
|
return _extract_docs(getattr(submodule, cls_name), method_name, boilerplate_args)
|
||||||
|
except AttributeError as e:
|
||||||
|
print(e)
|
||||||
|
except ModuleNotFoundError as e:
|
||||||
|
print(f"Error : IFCPatch {str(submodule)} could not complete because : {str(e)}")
|
||||||
|
|
||||||
|
def _extract_docs(cls, method_name, boilerplate_args):
|
||||||
|
inputs = collections.OrderedDict()
|
||||||
|
method = getattr(cls, method_name)
|
||||||
|
node_data = {"class": cls}
|
||||||
|
|
||||||
|
signature = inspect.signature(method)
|
||||||
|
for name, parameter in signature.parameters.items():
|
||||||
|
if name == "self":
|
||||||
|
continue
|
||||||
|
inputs[name] = {"name": name}
|
||||||
|
if isinstance(parameter.default, (str, float, int, bool)):
|
||||||
|
inputs[name]["default"] = parameter.default
|
||||||
|
|
||||||
|
type_hints = typing.get_type_hints(method)
|
||||||
|
for name, socket_data in inputs.items():
|
||||||
|
type_hint = type_hints.get(name, None)
|
||||||
|
if type_hint is None: # The argument is not type-hinted. (Or hinted to None ??)
|
||||||
|
continue
|
||||||
|
if isinstance(type_hint, typing._UnionGenericAlias):
|
||||||
|
inputs[name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
|
||||||
|
else:
|
||||||
|
inputs[name]["type"] = type_hint.__name__
|
||||||
|
|
||||||
|
description = ""
|
||||||
|
doc = method.__doc__
|
||||||
|
if doc is not None:
|
||||||
|
for i, line in enumerate(doc.split("\n")):
|
||||||
|
line = line.strip()
|
||||||
|
if i == 0:
|
||||||
|
node_data["name"] = line
|
||||||
|
elif line.startswith(":return:"):
|
||||||
|
node_data["output"] = {"name": line.split(":")[2].strip(), "description": line.split(":")[3].strip()}
|
||||||
|
elif line.startswith(":param"):
|
||||||
|
param_name = line.split(":")[1].strip().replace("param ", "")
|
||||||
|
if param_name in inputs:
|
||||||
|
inputs[param_name]["description"] = line.split(":")[2].strip()
|
||||||
|
elif i == 2:
|
||||||
|
description += line
|
||||||
|
elif i > 2:
|
||||||
|
description += "\n" + line
|
||||||
|
|
||||||
|
node_data["description"] = description.strip()
|
||||||
|
node_data["inputs"] = inputs
|
||||||
|
|
||||||
|
if boilerplate_args is not None:
|
||||||
|
for arg in boilerplate_args: # Remove boilerplate arguments
|
||||||
|
node_data["inputs"].pop(arg, None)
|
||||||
|
return node_data
|
||||||
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
|
import ifcpatch
|
||||||
|
from .helper import extract_docs
|
||||||
|
|
||||||
|
|
||||||
class SelectIfcPatchInput(bpy.types.Operator):
|
class SelectIfcPatchInput(bpy.types.Operator):
|
||||||
@@ -39,6 +41,7 @@ class ExecuteIfcPatch(bpy.types.Operator):
|
|||||||
bl_idname = "bim.execute_ifc_patch"
|
bl_idname = "bim.execute_ifc_patch"
|
||||||
bl_label = "Execute IFCPatch"
|
bl_label = "Execute IFCPatch"
|
||||||
file_format: bpy.props.StringProperty()
|
file_format: bpy.props.StringProperty()
|
||||||
|
use_json_for_args: bpy.props.BoolProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -46,15 +49,47 @@ class ExecuteIfcPatch(bpy.types.Operator):
|
|||||||
return os.path.isfile(input_file) and "ifc" in os.path.splitext(input_file)[1]
|
return os.path.isfile(input_file) and "ifc" in os.path.splitext(input_file)[1]
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import ifcpatch
|
props = context.scene.BIMPatchProperties
|
||||||
|
if self.use_json_for_args or not props.ifc_patch_args_attr:
|
||||||
|
arguments = json.loads(props.ifc_patch_args or "[]")
|
||||||
|
else:
|
||||||
|
arguments = [arg.get_value() for arg in props.ifc_patch_args_attr]
|
||||||
|
|
||||||
ifcpatch.execute(
|
ifcpatch.execute(
|
||||||
{
|
{
|
||||||
"input": context.scene.BIMPatchProperties.ifc_patch_input,
|
"input": props.ifc_patch_input,
|
||||||
"output": context.scene.BIMPatchProperties.ifc_patch_output,
|
"output": props.ifc_patch_output,
|
||||||
"recipe": context.scene.BIMPatchProperties.ifc_patch_recipes,
|
"recipe": props.ifc_patch_recipes,
|
||||||
"arguments": json.loads(context.scene.BIMPatchProperties.ifc_patch_args or "[]"),
|
"arguments": arguments,
|
||||||
"log": os.path.join(context.scene.BIMProperties.data_dir, "process.log"),
|
"log": os.path.join(context.scene.BIMProperties.data_dir, "process.log"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateIfcPatchArguments(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.update_ifc_patch_arguments"
|
||||||
|
bl_label = "Update IFC Patch arguments"
|
||||||
|
recipe: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if self.recipe == "":
|
||||||
|
print("No Recipe Selected. Impossible to load arguments")
|
||||||
|
return {"FINISHED"}
|
||||||
|
patch_args = context.scene.BIMPatchProperties.ifc_patch_args_attr
|
||||||
|
patch_args.clear()
|
||||||
|
docs = extract_docs(ifcpatch, self.recipe, "Patcher", "__init__", ("src", "file", "logger", "args"))
|
||||||
|
if docs and "inputs" in docs:
|
||||||
|
inputs = docs["inputs"]
|
||||||
|
for arg_name in inputs:
|
||||||
|
arg_info = inputs[arg_name]
|
||||||
|
new_attr = patch_args.add()
|
||||||
|
new_attr.data_type = {
|
||||||
|
"str": "string",
|
||||||
|
"float": "float",
|
||||||
|
"int": "integer",
|
||||||
|
"bool": "boolean",
|
||||||
|
}[arg_info.get("type", "str")]
|
||||||
|
new_attr.name = arg_name
|
||||||
|
new_attr.set_value(arg_info.get("default", new_attr.get_value_default()))
|
||||||
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import importlib
|
import importlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import ifcpatch
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
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 (
|
||||||
@@ -13,6 +14,8 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
|
from .helper import extract_docs
|
||||||
|
from .operator import UpdateIfcPatchArguments
|
||||||
|
|
||||||
|
|
||||||
ifcpatchrecipes_enum = []
|
ifcpatchrecipes_enum = []
|
||||||
@@ -32,12 +35,17 @@ def getIfcPatchRecipes(self, context):
|
|||||||
f = str(filename.stem)
|
f = str(filename.stem)
|
||||||
if f == "__init__":
|
if f == "__init__":
|
||||||
continue
|
continue
|
||||||
ifcpatchrecipes_enum.append((f, f, ""))
|
docs = extract_docs(ifcpatch, f, "Patcher", "__init__", ("src", "file", "logger", "args"))
|
||||||
|
ifcpatchrecipes_enum.append((f, f, docs.get("description","") if docs else ""))
|
||||||
return ifcpatchrecipes_enum
|
return ifcpatchrecipes_enum
|
||||||
|
|
||||||
|
def update_ifc_patch_recipe(self, context):
|
||||||
|
bpy.ops.bim.update_ifc_patch_arguments(recipe = self.ifc_patch_recipes)
|
||||||
|
|
||||||
|
|
||||||
class BIMPatchProperties(PropertyGroup):
|
class BIMPatchProperties(PropertyGroup):
|
||||||
ifc_patch_recipes: EnumProperty(items=getIfcPatchRecipes, name="Recipes")
|
ifc_patch_recipes: EnumProperty(items=getIfcPatchRecipes, name="Recipes", update=update_ifc_patch_recipe)
|
||||||
ifc_patch_input: StringProperty(default="", name="IFC Patch Input IFC")
|
ifc_patch_input: StringProperty(default="", name="IFC Patch Input IFC")
|
||||||
ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC")
|
ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC")
|
||||||
ifc_patch_args: StringProperty(default="", name="Arguments")
|
ifc_patch_args: StringProperty(default="", name="Arguments")
|
||||||
|
ifc_patch_args_attr: CollectionProperty(type=Attribute, name="Arguments")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.types import Panel
|
from blenderbim.bim.helper import draw_attributes
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_patch(Panel):
|
class BIM_PT_patch(bpy.types.Panel):
|
||||||
bl_label = "IFC Patch"
|
bl_label = "IFC Patch"
|
||||||
bl_idname = "BIM_PT_patch"
|
bl_idname = "BIM_PT_patch"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
@@ -13,20 +13,25 @@ class BIM_PT_patch(Panel):
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.use_property_split = True
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = False
|
||||||
|
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
props = scene.BIMPatchProperties
|
props = scene.BIMPatchProperties
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(props, "ifc_patch_recipes")
|
row.prop(props, "ifc_patch_recipes")
|
||||||
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(props, "ifc_patch_input")
|
row.prop(props, "ifc_patch_input")
|
||||||
row.operator("bim.select_ifc_patch_input", icon="FILE_FOLDER", text="")
|
row.operator("bim.select_ifc_patch_input", icon="FILE_FOLDER", text="")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(props, "ifc_patch_output")
|
row.prop(props, "ifc_patch_output")
|
||||||
row.operator("bim.select_ifc_patch_output", icon="FILE_FOLDER", text="")
|
row.operator("bim.select_ifc_patch_output", icon="FILE_FOLDER", text="")
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "ifc_patch_args")
|
|
||||||
|
|
||||||
row = layout.row()
|
if props.ifc_patch_args_attr:
|
||||||
op = row.operator("bim.execute_ifc_patch")
|
draw_attributes(props.ifc_patch_args_attr, layout)
|
||||||
|
else:
|
||||||
|
layout.row().prop(props, "ifc_patch_args")
|
||||||
|
op = layout.operator("bim.execute_ifc_patch")
|
||||||
|
op.use_json_for_args = len(props.ifc_patch_args_attr) == 0
|
||||||
|
|||||||
@@ -136,31 +136,11 @@ class EnablePsetEditing(bpy.types.Operator):
|
|||||||
prop = Data.properties[prop_id]
|
prop = Data.properties[prop_id]
|
||||||
|
|
||||||
value = prop["NominalValue"]
|
value = prop["NominalValue"]
|
||||||
if isinstance(value, str):
|
|
||||||
data_type = "string"
|
|
||||||
elif isinstance(value, float):
|
|
||||||
data_type = "float"
|
|
||||||
elif isinstance(value, bool):
|
|
||||||
data_type = "boolean"
|
|
||||||
elif isinstance(value, int):
|
|
||||||
data_type = "integer"
|
|
||||||
else:
|
|
||||||
data_type = "string"
|
|
||||||
value = str(value)
|
|
||||||
|
|
||||||
new = self.props.properties.add()
|
new = self.props.properties.add()
|
||||||
|
new.set_value(value)
|
||||||
new.name = prop["Name"]
|
new.name = prop["Name"]
|
||||||
new.is_null = prop["NominalValue"] is None
|
new.is_null = value is None
|
||||||
new.data_type = data_type
|
new.set_value(new.get_value_default() if new.is_null else value)
|
||||||
|
|
||||||
if data_type == "string":
|
|
||||||
new.string_value = "" if new.is_null else value
|
|
||||||
elif data_type == "integer":
|
|
||||||
new.int_value = 0 if new.is_null else value
|
|
||||||
elif data_type == "float":
|
|
||||||
new.float_value = 0.0 if new.is_null else value
|
|
||||||
elif data_type == "boolean":
|
|
||||||
new.bool_value = False if new.is_null else value
|
|
||||||
|
|
||||||
|
|
||||||
class DisablePsetEditing(bpy.types.Operator):
|
class DisablePsetEditing(bpy.types.Operator):
|
||||||
@@ -200,18 +180,7 @@ class EditPset(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
data = Data.psets if pset_id in Data.psets else Data.qtos
|
data = Data.psets if pset_id in Data.psets else Data.qtos
|
||||||
for prop in props.properties:
|
for prop in props.properties:
|
||||||
if prop.is_null:
|
properties[prop.name] = prop.get_value()
|
||||||
properties[prop.name] = None
|
|
||||||
elif prop.data_type == "string":
|
|
||||||
properties[prop.name] = prop.string_value
|
|
||||||
elif prop.data_type == "boolean":
|
|
||||||
properties[prop.name] = prop.bool_value
|
|
||||||
elif prop.data_type == "integer":
|
|
||||||
properties[prop.name] = prop.int_value
|
|
||||||
elif prop.data_type == "float":
|
|
||||||
properties[prop.name] = prop.float_value
|
|
||||||
elif prop.data_type == "enum":
|
|
||||||
properties[prop.name] = prop.enum_value
|
|
||||||
|
|
||||||
if pset_id in Data.psets:
|
if pset_id in Data.psets:
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from ifcopenshell.api.pset.data import Data
|
from ifcopenshell.api.pset.data import Data
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attribute
|
||||||
|
|
||||||
|
|
||||||
def get_active_pset_obj_name(context, obj_type):
|
def get_active_pset_obj_name(context, obj_type):
|
||||||
@@ -67,18 +68,8 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
|
|||||||
|
|
||||||
|
|
||||||
def draw_psetqto_editable_ui(box, props, prop):
|
def draw_psetqto_editable_ui(box, props, prop):
|
||||||
row = box.row(align=True)
|
row = box.row()
|
||||||
if prop.data_type == "string":
|
draw_attribute(prop, row)
|
||||||
row.prop(prop, "string_value", text=prop.name)
|
|
||||||
elif prop.data_type == "integer":
|
|
||||||
row.prop(prop, "int_value", text=prop.name)
|
|
||||||
elif prop.data_type == "float":
|
|
||||||
row.prop(prop, "float_value", text=prop.name)
|
|
||||||
elif prop.data_type == "boolean":
|
|
||||||
row.prop(prop, "bool_value", text=prop.name)
|
|
||||||
elif prop.data_type == "enum":
|
|
||||||
row.prop(prop, "enum_value", text=prop.name)
|
|
||||||
row.prop(prop, "is_null", icon="RADIOBUT_OFF" if prop.is_null else "RADIOBUT_ON", text="")
|
|
||||||
if (
|
if (
|
||||||
"length" in prop.name.lower()
|
"length" in prop.name.lower()
|
||||||
or "width" in prop.name.lower()
|
or "width" in prop.name.lower()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import isodate
|
|||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes
|
||||||
from ifcopenshell.api.sequence.data import Data
|
from ifcopenshell.api.sequence.data import Data
|
||||||
from ifcopenshell.api.resource.data import Data as ResourceData
|
from ifcopenshell.api.resource.data import Data as ResourceData
|
||||||
import blenderbim.bim.module.sequence.helper as helper
|
import blenderbim.bim.module.sequence.helper as helper
|
||||||
@@ -55,14 +56,7 @@ class BIM_PT_work_plans(Panel):
|
|||||||
self.draw_work_schedule_ui()
|
self.draw_work_schedule_ui()
|
||||||
|
|
||||||
def draw_editable_ui(self):
|
def draw_editable_ui(self):
|
||||||
for attribute in self.props.work_plan_attributes:
|
draw_attributes(self.props.work_plan_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_work_schedule_ui(self):
|
def draw_work_schedule_ui(self):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -189,14 +183,7 @@ class BIM_PT_work_schedules(Panel):
|
|||||||
row.prop(self.props, "speed_multiplier", text="")
|
row.prop(self.props, "speed_multiplier", text="")
|
||||||
|
|
||||||
def draw_editable_work_schedule_ui(self):
|
def draw_editable_work_schedule_ui(self):
|
||||||
for attribute in self.props.work_schedule_attributes:
|
draw_attributes(self.props.work_schedule_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_editable_task_ui(self, work_schedule_id):
|
def draw_editable_task_ui(self, work_schedule_id):
|
||||||
self.layout.template_list(
|
self.layout.template_list(
|
||||||
@@ -523,15 +510,7 @@ class BIM_PT_work_calendars(Panel):
|
|||||||
self.draw_editable_work_time_ui(work_time)
|
self.draw_editable_work_time_ui(work_time)
|
||||||
|
|
||||||
def draw_editable_work_time_ui(self, work_time):
|
def draw_editable_work_time_ui(self, work_time):
|
||||||
for attribute in self.props.work_time_attributes:
|
draw_attributes(self.props.work_time_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
if work_time["RecurrencePattern"]:
|
if work_time["RecurrencePattern"]:
|
||||||
self.draw_editable_recurrence_pattern_ui(Data.recurrence_patterns[work_time["RecurrencePattern"]])
|
self.draw_editable_recurrence_pattern_ui(Data.recurrence_patterns[work_time["RecurrencePattern"]])
|
||||||
else:
|
else:
|
||||||
@@ -599,11 +578,4 @@ class BIM_PT_work_calendars(Panel):
|
|||||||
row.prop(self.props, "occurrences")
|
row.prop(self.props, "occurrences")
|
||||||
|
|
||||||
def draw_editable_ui(self):
|
def draw_editable_ui(self):
|
||||||
for attribute in self.props.work_calendar_attributes:
|
draw_attributes(self.props.work_calendar_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|||||||
@@ -279,15 +279,7 @@ class EditStructuralAnalysisModel(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMStructuralProperties
|
props = context.scene.BIMStructuralProperties
|
||||||
attributes = {}
|
attributes = {attribute.name: attribute.get_value() for attribute in props.structural_analysis_model_attributes}
|
||||||
for attribute in props.structural_analysis_model_attributes:
|
|
||||||
if attribute.is_null:
|
|
||||||
attributes[attribute.name] = None
|
|
||||||
else:
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
attributes[attribute.name] = attribute.string_value
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
attributes[attribute.name] = attribute.enum_value
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"structural.edit_structural_analysis_model",
|
"structural.edit_structural_analysis_model",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import bpy
|
|||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.helper import draw_attributes
|
||||||
from ifcopenshell.api.structural.data import Data
|
from ifcopenshell.api.structural.data import Data
|
||||||
|
|
||||||
|
|
||||||
@@ -293,14 +294,7 @@ class BIM_PT_structural_analysis_models(Panel):
|
|||||||
self.draw_editable_ui(context)
|
self.draw_editable_ui(context)
|
||||||
|
|
||||||
def draw_editable_ui(self, context):
|
def draw_editable_ui(self, context):
|
||||||
for attribute in self.props.structural_analysis_model_attributes:
|
draw_attributes(self.props.structural_analysis_model_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_structural_analysis_models(UIList):
|
class BIM_UL_structural_analysis_models(UIList):
|
||||||
@@ -389,16 +383,7 @@ class BIM_PT_structural_load_cases(Panel):
|
|||||||
self.draw_editable_load_case_group_ui(load_case)
|
self.draw_editable_load_case_group_ui(load_case)
|
||||||
|
|
||||||
def draw_editable_load_case_ui(self):
|
def draw_editable_load_case_ui(self):
|
||||||
for attribute in self.props.load_case_attributes:
|
draw_attributes(self.props.load_case_attributes, self.layout)
|
||||||
row = self.layout.row(align=True)
|
|
||||||
if attribute.data_type == "string":
|
|
||||||
row.prop(attribute, "string_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "float":
|
|
||||||
row.prop(attribute, "float_value", text=attribute.name)
|
|
||||||
elif attribute.data_type == "enum":
|
|
||||||
row.prop(attribute, "enum_value", text=attribute.name)
|
|
||||||
if attribute.is_optional:
|
|
||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
|
||||||
|
|
||||||
def draw_editable_load_case_group_ui(self, load_case):
|
def draw_editable_load_case_group_ui(self, load_case):
|
||||||
box = self.layout.box()
|
box = self.layout.box()
|
||||||
|
|||||||
@@ -126,43 +126,66 @@ class StrProperty(PropertyGroup):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def updateAttributeStringValue(self, context):
|
def updateAttributeValue(self, context):
|
||||||
updateAttributeValue(self, self.string_value)
|
if getattr(self, str(self.get_value_attr()), None): # Do not use get_value since it returns None if is_null is True
|
||||||
|
|
||||||
|
|
||||||
def updateAttributeBoolValue(self, context):
|
|
||||||
updateAttributeValue(self, self.bool_value)
|
|
||||||
|
|
||||||
|
|
||||||
def updateAttributeIntValue(self, context):
|
|
||||||
updateAttributeValue(self, self.int_value)
|
|
||||||
|
|
||||||
|
|
||||||
def updateAttributeFloatValue(self, context):
|
|
||||||
updateAttributeValue(self, self.float_value)
|
|
||||||
|
|
||||||
|
|
||||||
def updateAttributeEnumValue(self, context):
|
|
||||||
updateAttributeValue(self, self.enum_value)
|
|
||||||
|
|
||||||
|
|
||||||
def updateAttributeValue(self, value):
|
|
||||||
if value:
|
|
||||||
self.is_null = False
|
self.is_null = False
|
||||||
|
|
||||||
|
|
||||||
class Attribute(PropertyGroup):
|
class Attribute(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
data_type: StringProperty(name="Data Type")
|
data_type: StringProperty(name="Data Type")
|
||||||
string_value: StringProperty(name="Value", update=updateAttributeStringValue)
|
string_value: StringProperty(name="Value", update=updateAttributeValue)
|
||||||
bool_value: BoolProperty(name="Value", update=updateAttributeBoolValue)
|
bool_value: BoolProperty(name="Value", update=updateAttributeValue)
|
||||||
int_value: IntProperty(name="Value", update=updateAttributeIntValue)
|
int_value: IntProperty(name="Value", update=updateAttributeValue)
|
||||||
float_value: FloatProperty(name="Value", update=updateAttributeFloatValue)
|
float_value: FloatProperty(name="Value", update=updateAttributeValue)
|
||||||
is_null: BoolProperty(name="Is Null")
|
is_null: BoolProperty(name="Is Null")
|
||||||
is_optional: BoolProperty(name="Is Optional")
|
is_optional: BoolProperty(name="Is Optional")
|
||||||
enum_items: StringProperty(name="Value")
|
enum_items: StringProperty(name="Value")
|
||||||
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value", update=updateAttributeEnumValue)
|
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value", update=updateAttributeValue)
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
if self.is_null:
|
||||||
|
return None
|
||||||
|
return getattr(self, str(self.get_value_attr()), None)
|
||||||
|
|
||||||
|
def get_value_default(self):
|
||||||
|
if self.data_type == "string":
|
||||||
|
return ""
|
||||||
|
elif self.data_type == "integer":
|
||||||
|
return 0
|
||||||
|
elif self.data_type == "float":
|
||||||
|
return 0.0
|
||||||
|
elif self.data_type == "boolean":
|
||||||
|
return False
|
||||||
|
elif self.data_type == "enum":
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
def get_value_attr(self):
|
||||||
|
if self.data_type == "string":
|
||||||
|
return "string_value"
|
||||||
|
elif self.data_type == "boolean":
|
||||||
|
return "bool_value"
|
||||||
|
elif self.data_type == "integer":
|
||||||
|
return "int_value"
|
||||||
|
elif self.data_type == "float":
|
||||||
|
return "float_value"
|
||||||
|
elif self.data_type == "enum":
|
||||||
|
return "enum_value"
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
self.data_type = "string"
|
||||||
|
elif isinstance(value, float):
|
||||||
|
self.data_type = "float"
|
||||||
|
elif isinstance(value, bool): # Make sure this is evaluated BEFORE integer
|
||||||
|
self.data_type = "boolean"
|
||||||
|
elif isinstance(value, int):
|
||||||
|
self.data_type = "integer"
|
||||||
|
else:
|
||||||
|
self.data_type = "string"
|
||||||
|
value = str(value)
|
||||||
|
setattr(self, self.get_value_attr(), value)
|
||||||
|
|
||||||
|
|
||||||
class BIMProperties(PropertyGroup):
|
class BIMProperties(PropertyGroup):
|
||||||
schema_dir: StringProperty(
|
schema_dir: StringProperty(
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ def execute(args, is_library=None):
|
|||||||
print("# Loading patch recipe ...")
|
print("# Loading patch recipe ...")
|
||||||
recipes = getattr(__import__("ifcpatch.recipes.{}".format(args["recipe"])), "recipes")
|
recipes = getattr(__import__("ifcpatch.recipes.{}".format(args["recipe"])), "recipes")
|
||||||
recipe = getattr(recipes, args["recipe"])
|
recipe = getattr(recipes, args["recipe"])
|
||||||
patcher = recipe.Patcher(args["input"], ifc_file, logger, args["arguments"])
|
try:
|
||||||
|
# We unpack the arguments if the Patcher has been type-hinted and docstringed
|
||||||
|
patcher = recipe.Patcher(args["input"], ifc_file, logger, *args["arguments"])
|
||||||
|
except TypeError:
|
||||||
|
patcher = recipe.Patcher(args["input"], ifc_file, logger, args["arguments"])
|
||||||
print("# Patching ...")
|
print("# Patching ...")
|
||||||
patcher.patch()
|
patcher.patch()
|
||||||
ifc_file = getattr(patcher, "file_patched", patcher.file)
|
ifc_file = getattr(patcher, "file_patched", patcher.file)
|
||||||
|
|||||||
Reference in New Issue
Block a user