diff --git a/.github/workflows/ci-py-only.yml b/.github/workflows/ci-py-only.yml
new file mode 100644
index 0000000000..b427954ac8
--- /dev/null
+++ b/.github/workflows/ci-py-only.yml
@@ -0,0 +1,95 @@
+name: ci_py_only
+
+on:
+ push:
+ paths:
+ - 'src/**'
+ - 'test/**'
+ - 'conda/**'
+ - 'cmake/**'
+ - '.github/workflows/ci_py_only.yml'
+ pull_request:
+
+jobs:
+ activate:
+ runs-on: ubuntu-latest
+ if: |
+ github.repository == 'IfcOpenShell/IfcOpenShell' &&
+ !contains(github.event.head_commit.message, 'skip ci')
+ steps:
+ - run: echo ok go
+
+ build:
+ runs-on: ubuntu-20.04
+ needs: activate
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ - name: Install C++ dependencies
+ run: |
+ sudo apt update
+ sudo apt-get install --no-install-recommends \
+ git cmake gcc g++ \
+ libboost-date-time-dev \
+ libboost-filesystem-dev \
+ libboost-iostreams-dev \
+ libboost-program-options-dev \
+ libboost-regex-dev \
+ libboost-system-dev \
+ libboost-thread-dev \
+ python3-all-dev python3-pip \
+ swig libpcre3-dev libxml2-dev \
+ libtbb-dev nlohmann-json3-dev \
+ libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
+ libhdf5-dev libcgal-dev
+
+ - name: ccache
+ uses: hendrikmuhs/ccache-action@v1
+
+ - name: Build ifcopenshell
+ run: |
+ mkdir build && cd build
+ cmake \
+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_PREFIX_PATH=/usr \
+ -DCMAKE_SYSTEM_PREFIX_PATH=/usr \
+ -DOCC_INCLUDE_DIR=/usr/include/opencascade \
+ -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
+ -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
+ -DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
+ -DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
+ -DCOLLADA_SUPPORT=Off \
+ "-DSCHEMA_VERSIONS=2x3;4" \
+ -DBUILD_CONVERT=Off \
+ -DGLTF_SUPPORT=On \
+ -DJSON_INCLUDE_DIR=/usr/include \
+ -DCGAL_INCLUDE_DIR=/usr/include \
+ -DGMP_INCLUDE_DIR=/usr/include \
+ -DMPFR_INCLUDE_DIR=/usr/include \
+ -DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
+ -DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
+ -DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
+ ../cmake
+ sudo make -j $(nproc)
+ sudo make install
+
+ - name: Install Python dependencies
+ run: |
+ sudo /usr/bin/python -m pip install -U pip
+ sudo /usr/bin/python -m pip install xmlschema numpy lxml
+ sudo /usr/bin/python -m pip install src/bcf
+ sudo /usr/bin/python -m pip install pytest
+ sudo /usr/bin/python -m pip install isodate
+ sudo /usr/bin/python -m pip install lark
+ sudo /usr/bin/python -m pip install networkx
+
+ - name: Test
+ run: |
+ cd test
+ sudo /usr/bin/python tests.py
+ cd ../src/ifcopenshell-python
+ mv ifcopenshell ifcopenshell-local # Force testing on installed module
+ make test
diff --git a/.gitmodules b/.gitmodules
index ccec1ac990..e15dfb45d8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,4 +10,4 @@
url = https://github.com/IfcOpenShell/svgfill
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files
- url = https://github.com/atomczak/Sample-BIM-Files
+ url = https://github.com/IfcOpenShell/ids-test-files
\ No newline at end of file
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index f19c4eafd0..514c3acdb6 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -666,7 +666,7 @@ if (BUILD_IFCGEOM)
endforeach()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
endif()
-if (BUILD_CONVERT)
+if (BUILD_CONVERT OR BUILD_IFCPYTHON)
foreach(s ${SCHEMA_VERSIONS})
set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${s})
endforeach()
@@ -739,7 +739,7 @@ TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREA
endif(BUILD_IFCGEOM)
-if (BUILD_CONVERT)
+if (BUILD_CONVERT OR BUILD_IFCPYTHON)
# Serializers
file(GLOB SERIALIZERS_H_FILES ../src/serializers/*.h)
@@ -760,6 +760,10 @@ set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS $
TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES} ${OPENCOLLADA_LIBRARIES})
+endif(BUILD_CONVERT or BUILD_IFCPYTHON)
+
+if (BUILD_CONVERT)
+
# IfcConvert
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py
index c29a3f9298..3ad281d07d 100644
--- a/src/blenderbim/blenderbim/bim/__init__.py
+++ b/src/blenderbim/blenderbim/bim/__init__.py
@@ -93,6 +93,7 @@ classes = [
operator.SelectSchemaDir,
operator.SelectURIAttribute,
prop.StrProperty,
+ operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
prop.ObjProperty,
prop.Attribute,
prop.ModuleVisibility,
diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py
index 6703bda0dc..6b11944f87 100644
--- a/src/blenderbim/blenderbim/bim/helper.py
+++ b/src/blenderbim/blenderbim/bim/helper.py
@@ -38,20 +38,13 @@ def draw_attribute(attribute, layout, copy_operator=None):
if not value_name:
layout.label(text=attribute.name)
return
- if len(attribute.enumerated_values) != 0:
- layout.label(text=attribute.name)
- grid = layout.column_flow(columns=4)
- for e in attribute.enumerated_values:
- grid.prop(
- e,
- "is_selected",
- text=str(e[value_name])
- )
+ if value_name == "enum_value":
+ prop_with_search(layout, attribute, "enum_value", text=attribute.name)
else:
layout.prop(
attribute,
value_name,
- text=attribute.name,
+ text=attribute.name,
)
if attribute.is_optional:
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
@@ -115,6 +108,16 @@ def export_attributes(props, callback=None):
return attributes
+def prop_with_search(layout, data, prop_name, **kwargs):
+ # kwargs are layout.prop arguments (text, icon, etc.)
+ row = layout.row(align=True)
+ # Magick courtesy of https://blender.stackexchange.com/a/203443/86891
+ row.context_pointer_set(name="data", data=data)
+ row.prop(data, prop_name, **kwargs)
+ op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
+ op.prop_name = prop_name
+
+
class IfcHeaderExtractor:
def __init__(self, filepath: str):
self.filepath = filepath
diff --git a/src/blenderbim/blenderbim/bim/module/brick/prop.py b/src/blenderbim/blenderbim/bim/module/brick/prop.py
index 6e0ba7c814..8d523b68df 100644
--- a/src/blenderbim/blenderbim/bim/module/brick/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/brick/prop.py
@@ -69,3 +69,9 @@ class BIMBrickProperties(PropertyGroup):
libraries: EnumProperty(name="Libraries", items=get_libraries)
namespace: EnumProperty(name="Namespace", items=get_namespaces)
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
+
+ getter_enum = {
+ "libraries": get_libraries,
+ "namespace": get_namespaces,
+ "brick_equipment_class": get_brick_equipment_classes,
+ }
diff --git a/src/blenderbim/blenderbim/bim/module/brick/ui.py b/src/blenderbim/blenderbim/bim/module/brick/ui.py
index da3647ab6d..bb8898b2c0 100644
--- a/src/blenderbim/blenderbim/bim/module/brick/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/brick/ui.py
@@ -18,6 +18,7 @@
import blenderbim.tool as tool
from bpy.types import Panel, UIList
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
@@ -48,7 +49,8 @@ class BIM_PT_brickschema(Panel):
row.operator("bim.close_brick_project", text="", icon="CANCEL")
row = self.layout.row(align=True)
- row.prop(self.props, "namespace", text="")
+ prop_with_search(row, self.props, "namespace", text="")
+ prop_with_search(row, self.props, "brick_equipment_class", text="")
row.prop(self.props, "brick_equipment_class", text="")
row.operator("bim.add_brick", text="", icon="ADD")
@@ -101,7 +103,7 @@ class BIM_PT_ifc_brickschema_references(Panel):
return
row = self.layout.row(align=True)
- row.prop(self.props, "libraries")
+ prop_with_search(row, self.props, "libraries")
row.operator("bim.convert_brick_project", text="", icon="ADD")
row = self.layout.row(align=True)
diff --git a/src/blenderbim/blenderbim/bim/module/debug/operator.py b/src/blenderbim/blenderbim/bim/module/debug/operator.py
index bdf852b568..5d723020ea 100644
--- a/src/blenderbim/blenderbim/bim/module/debug/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/debug/operator.py
@@ -112,6 +112,7 @@ class CreateAllShapes(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
elements = self.file.by_type("IfcElement") + self.file.by_type("IfcSpace")
+
total = len(elements)
settings = ifcopenshell.geom.settings()
failures = []
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py
index 7dc4ab2428..eca88ec09d 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py
@@ -152,13 +152,13 @@ class CreateDrawing(bpy.types.Operator):
with profile("Generate underlay"):
underlay_svg = self.generate_underlay(context)
-
+
with profile("Generate linework"):
linework_svg = self.generate_linework(context)
with profile("Generate annotation"):
annotation_svg = self.generate_annotation(context)
-
+
with profile("Combine SVG layers"):
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/prop.py b/src/blenderbim/blenderbim/bim/module/drawing/prop.py
index 2016f23dd2..b20f871d11 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/prop.py
@@ -392,6 +392,7 @@ class BIMTextProperties(PropertyGroup):
name="Font Size",
)
+
class BIMAssignedProductProperties(PropertyGroup):
is_editing_product: BoolProperty(name="Is Editing Product", default=False)
relating_product: PointerProperty(name="Relating Product", type=bpy.types.Object)
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
index 7855e6067c..4df0f4e9f9 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
@@ -118,7 +118,7 @@ class SheetBuilder:
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
- for g in sheet_root.findall('{http://www.w3.org/2000/svg}g'):
+ for g in sheet_root.findall("{http://www.w3.org/2000/svg}g"):
if g.attrib.get("data-id") == str(reference.id()):
sheet_root.remove(g)
break
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
index 891b8f2092..74b3b06642 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
@@ -70,7 +70,7 @@ class SvgWriter:
viewBox=("0 0 {} {}".format(self.width, self.height)),
id="root",
data_scale=self.human_scale,
- debug=False, # Disable validation so that we can insert the IFC namespace
+ debug=False, # Disable validation so that we can insert the IFC namespace
)
self.svg.attribs["xmlns:ifc"] = "http://www.ifcopenshell.org/ns"
return self
@@ -81,7 +81,9 @@ class SvgWriter:
def draw_underlay(self, image):
self.svg.add(
self.svg.image(
- os.path.join("..", "diagrams", os.path.basename(image)), width=self.width, height=self.height,
+ os.path.join("..", "diagrams", os.path.basename(image)),
+ width=self.width,
+ height=self.height,
)
)
return self
@@ -252,7 +254,9 @@ class SvgWriter:
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
vector = end - start
line = self.svg.add(
- self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes))
+ self.svg.line(
+ start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
+ )
)
line["stroke-dasharray"] = "12.5, 3, 3, 3"
axis_tag = tool.Ifc.get_entity(obj).Name
@@ -361,7 +365,9 @@ class SvgWriter:
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
vector = end - start
line = self.svg.add(
- self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes))
+ self.svg.line(
+ start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
+ )
)
def draw_leader_annotation(self, obj):
@@ -427,7 +433,9 @@ class SvgWriter:
(symbol_position * self.svg_scale)[1],
)
- self.svg.add(self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform))
+ self.svg.add(
+ self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform)
+ )
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.svg_scale)))
reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
@@ -666,7 +674,7 @@ class SvgWriter:
dir2 = ((arc_matrix.inverted() @ arc_end_pts[1]) - (arc_matrix.inverted() @ center)).normalized()
angle = -dir1.xy.angle_signed(dir2.xy)
- #if is_reflex:
+ # if is_reflex:
# angle = angle % (math.pi * 2)
# Center of gravity of all vertices, used to help position the text
@@ -680,7 +688,9 @@ class SvgWriter:
arc_midpoint = center + ((cog - center).normalized() * radius)
text_position = self.project_point_onto_camera(arc_midpoint)
- text_position = Vector(((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale))
+ text_position = Vector(
+ ((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale)
+ )
center_projected = self.project_point_onto_camera(center)
center_position = Vector(
@@ -704,8 +714,12 @@ class SvgWriter:
# Draw SVG arc, see for details: http://xahlee.info/js/svg_circle_arc.html
arc_proj_end_pts = [self.project_point_onto_camera(v) for v in arc_end_pts]
- p1 = Vector(((x_offset + arc_proj_end_pts[0].x) * self.svg_scale, (y_offset - arc_proj_end_pts[0].y) * self.svg_scale))
- p2 = Vector(((x_offset + arc_proj_end_pts[1].x) * self.svg_scale, (y_offset - arc_proj_end_pts[1].y) * self.svg_scale))
+ p1 = Vector(
+ ((x_offset + arc_proj_end_pts[0].x) * self.svg_scale, (y_offset - arc_proj_end_pts[0].y) * self.svg_scale)
+ )
+ p2 = Vector(
+ ((x_offset + arc_proj_end_pts[1].x) * self.svg_scale, (y_offset - arc_proj_end_pts[1].y) * self.svg_scale)
+ )
r = radius * self.svg_scale
# reflex = 1 if angle > math.pi else 0
reflex = is_reflex
@@ -735,10 +749,16 @@ class SvgWriter:
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
p0 = Vector(
- ((x_offset + projected_points[0].x) * self.svg_scale, (y_offset - projected_points[0].y) * self.svg_scale)
+ (
+ (x_offset + projected_points[0].x) * self.svg_scale,
+ (y_offset - projected_points[0].y) * self.svg_scale,
+ )
)
p1 = Vector(
- ((x_offset + projected_points[1].x) * self.svg_scale, (y_offset - projected_points[1].y) * self.svg_scale)
+ (
+ (x_offset + projected_points[1].x) * self.svg_scale,
+ (y_offset - projected_points[1].y) * self.svg_scale,
+ )
)
text_offset = (p0 - p1).xy.normalized() * 5
text_position = projected_points[0]
@@ -823,7 +843,9 @@ class SvgWriter:
text_position = (mid * self.svg_scale) + perpendicular
rotation = math.degrees(vector.angle_signed(Vector((1, 0))))
line = self.svg.add(
- self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes))
+ self.svg.line(
+ start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
+ )
)
if text_override is not None:
text = text_override
diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py
index 541cb0a92d..81b78dd4a2 100644
--- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py
@@ -207,7 +207,10 @@ class UpdateRepresentation(bpy.types.Operator):
new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
if tool.Geometry.is_body_representation(new_representation):
- [tool.Geometry.run_style_add_style(obj=mat) for mat in tool.Geometry.get_object_materials_without_styles(obj)]
+ [
+ tool.Geometry.run_style_add_style(obj=mat)
+ for mat in tool.Geometry.get_object_materials_without_styles(obj)
+ ]
ifcopenshell.api.run(
"style.assign_representation_styles",
self.file,
diff --git a/src/blenderbim/blenderbim/bim/module/geometry/ui.py b/src/blenderbim/blenderbim/bim/module/geometry/ui.py
index 78c8e21414..5f7c918de0 100644
--- a/src/blenderbim/blenderbim/bim/module/geometry/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/geometry/ui.py
@@ -19,6 +19,7 @@
import bpy
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.geometry.data import RepresentationsData, DerivedPlacementsData
@@ -50,7 +51,7 @@ class BIM_PT_representations(Panel):
layout.label(text="No representations found")
row = layout.row(align=True)
- row.prop(context.scene.BIMRootProperties, "contexts", text="")
+ prop_with_search(row, context.scene.BIMRootProperties, "contexts", text="")
row.operator("bim.add_representation", icon="ADD", text="")
for representation in RepresentationsData.data["representations"]:
diff --git a/src/blenderbim/blenderbim/bim/module/material/data.py b/src/blenderbim/blenderbim/bim/module/material/data.py
index 4d90a29408..78ef924562 100644
--- a/src/blenderbim/blenderbim/bim/module/material/data.py
+++ b/src/blenderbim/blenderbim/bim/module/material/data.py
@@ -84,7 +84,9 @@ class ObjectMaterialData:
@classmethod
def materials(cls):
- return sorted([(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")], key=lambda x: x[1])
+ return sorted(
+ [(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")], key=lambda x: x[1]
+ )
@classmethod
def type_material(cls):
diff --git a/src/blenderbim/blenderbim/bim/module/material/prop.py b/src/blenderbim/blenderbim/bim/module/material/prop.py
index 8f400ab07f..1795156ebe 100644
--- a/src/blenderbim/blenderbim/bim/module/material/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/material/prop.py
@@ -137,3 +137,5 @@ class BIMObjectMaterialProperties(PropertyGroup):
parameterized_profile_classes: EnumProperty(
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
)
+
+ getter_enum = {"material": get_materials}
diff --git a/src/blenderbim/blenderbim/bim/module/material/ui.py b/src/blenderbim/blenderbim/bim/module/material/ui.py
index d0048f5633..cddd181189 100644
--- a/src/blenderbim/blenderbim/bim/module/material/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/material/ui.py
@@ -22,6 +22,7 @@ from ifcopenshell.api.material.data import Data
from ifcopenshell.api.profile.data import Data as ProfileData
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.material.data import MaterialsData, ObjectMaterialData
@@ -190,7 +191,7 @@ class BIM_PT_object_material(Panel):
row = self.layout.row(align=True)
row.prop(self.props, "material_type", text="")
if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList":
- row.prop(self.props, "material", text="")
+ prop_with_search(row, self.props, "material", text="")
row.operator("bim.assign_material", icon="ADD", text="")
def draw_material_ui(self):
@@ -220,8 +221,7 @@ class BIM_PT_object_material(Panel):
return self.draw_read_only_single_ui()
def draw_editable_single_ui(self):
- row = self.layout.row(align=True)
- row.prop(self.props, "material", text="")
+ prop_with_search(self.layout, self.props, "material", text="")
def draw_read_only_single_ui(self):
material = Data.materials[self.product_data["id"]]
@@ -242,7 +242,7 @@ class BIM_PT_object_material(Panel):
row.prop(attribute, "string_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.prop(self.props, "material", text="")
+ prop_with_search(row, self.props, "material", text="")
op = row.operator(f"bim.add_{self.set_item_name}", icon="ADD", text="")
setattr(op, f"{self.set_item_name}_set", self.material_set_id)
diff --git a/src/blenderbim/blenderbim/bim/module/model/mep.py b/src/blenderbim/blenderbim/bim/module/model/mep.py
index 571ea0a3cf..eeeaff9aee 100644
--- a/src/blenderbim/blenderbim/bim/module/model/mep.py
+++ b/src/blenderbim/blenderbim/bim/module/model/mep.py
@@ -65,14 +65,14 @@ class MepGenerator:
def create_rectangle_segment(self):
verts = [
- Vector((- self.width / 2, self.height / 2, 0)),
- Vector((- self.width / 2, - self.height / 2, 0)),
- Vector((- self.width / 2, self.height / 2, self.length)),
- Vector((- self.width / 2, - self.height / 2, self.length)),
+ Vector((-self.width / 2, self.height / 2, 0)),
+ Vector((-self.width / 2, -self.height / 2, 0)),
+ Vector((-self.width / 2, self.height / 2, self.length)),
+ Vector((-self.width / 2, -self.height / 2, self.length)),
Vector((self.width / 2, self.height / 2, 0)),
- Vector((self.width / 2, - self.height / 2, 0)),
+ Vector((self.width / 2, -self.height / 2, 0)),
Vector((self.width / 2, self.height / 2, self.length)),
- Vector((self.width / 2, - self.height / 2, self.length)),
+ Vector((self.width / 2, -self.height / 2, self.length)),
]
faces = [
[1, 3, 2, 0],
diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py
index f18b748cf8..34a5f665c6 100644
--- a/src/blenderbim/blenderbim/bim/module/model/product.py
+++ b/src/blenderbim/blenderbim/bim/module/model/product.py
@@ -121,9 +121,7 @@ class AddTypeInstance(bpy.types.Operator):
collection_obj = bpy.data.objects.get(collection.name)
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
element = tool.Ifc.get_entity(obj)
- blenderbim.core.type.assign_type(
- tool.Ifc, tool.Type, element=element, type=relating_type
- )
+ blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
if building_obj:
if instance_class in ["IfcWindow", "IfcDoor"]:
diff --git a/src/blenderbim/blenderbim/bim/module/owner/data.py b/src/blenderbim/blenderbim/bim/module/owner/data.py
index be1cae0192..068bf74164 100644
--- a/src/blenderbim/blenderbim/bim/module/owner/data.py
+++ b/src/blenderbim/blenderbim/bim/module/owner/data.py
@@ -253,10 +253,7 @@ class ObjectActorData:
@classmethod
def load(cls):
cls.is_loaded = True
- cls.data = {
- "actor": cls.actor(),
- "actors": cls.actors()
- }
+ cls.data = {"actor": cls.actor(), "actors": cls.actors()}
@classmethod
def actor(cls):
@@ -279,9 +276,9 @@ class ObjectActorData:
roles = cls.get_roles(actor.TheActor.ThePerson)
roles.extend(cls.get_roles(actor.TheActor.TheOrganization))
role = ", ".join(roles)
- results.append({
- "id": actor.id(), "name": actor.Name or "Unnamed", "role": role, "ifc_class": actor.is_a()
- })
+ results.append(
+ {"id": actor.id(), "name": actor.Name or "Unnamed", "role": role, "ifc_class": actor.is_a()}
+ )
return results
@classmethod
diff --git a/src/blenderbim/blenderbim/bim/module/patch/prop.py b/src/blenderbim/blenderbim/bim/module/patch/prop.py
index 27c0e44ab5..0fb0a5f341 100644
--- a/src/blenderbim/blenderbim/bim/module/patch/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/patch/prop.py
@@ -66,3 +66,7 @@ class BIMPatchProperties(PropertyGroup):
ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC")
ifc_patch_args: StringProperty(default="", name="Arguments")
ifc_patch_args_attr: CollectionProperty(type=Attribute, name="Arguments")
+
+ getter_enum = {
+ "ifc_patch_recipes": get_ifcpatch_recipes,
+ }
diff --git a/src/blenderbim/blenderbim/bim/module/patch/ui.py b/src/blenderbim/blenderbim/bim/module/patch/ui.py
index 618a5077ca..9e7a241e51 100644
--- a/src/blenderbim/blenderbim/bim/module/patch/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/patch/ui.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
+from blenderbim.bim.helper import prop_with_search
import bpy
from blenderbim.bim.helper import draw_attributes
@@ -37,7 +38,7 @@ class BIM_PT_patch(bpy.types.Panel):
scene = context.scene
props = scene.BIMPatchProperties
row = layout.row()
- row.prop(props, "ifc_patch_recipes")
+ prop_with_search(row, props, "ifc_patch_recipes")
row = layout.row(align=True)
row.prop(props, "ifc_patch_input")
diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py
index f311ff50d5..dc2539db54 100644
--- a/src/blenderbim/blenderbim/bim/module/project/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/project/operator.py
@@ -732,7 +732,7 @@ class ToggleLinkVisibility(bpy.types.Operator):
def toggle_wireframe(self, link):
objs = filter(lambda obj: "IfcOpeningElement" not in obj.name, link.collection.all_objects)
- for i,obj in enumerate(objs):
+ for i, obj in enumerate(objs):
if i == 0:
if obj.display_type == "WIRE":
display_type = "TEXTURED"
diff --git a/src/blenderbim/blenderbim/bim/module/project/prop.py b/src/blenderbim/blenderbim/bim/module/project/prop.py
index 3fe5e5077f..4d38d465fe 100644
--- a/src/blenderbim/blenderbim/bim/module/project/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/project/prop.py
@@ -167,3 +167,28 @@ class BIMProjectProperties(PropertyGroup):
def get_library_element_index(self, lib_element):
return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element))
+
+ getter_enum = {
+ "collection_mode": lambda self, context: [
+ ("DECOMPOSITION", "Decomposition", "Collections represent aggregates and spatial containers"),
+ ("SPATIAL_DECOMPOSITION", "Spatial Decomposition", "Collections represent spatial containers"),
+ ("IFC_CLASS", "IFC Class", "Collections represent IFC class"),
+ ("NONE", "None", "No collections are created"),
+ ],
+ "filter_mode": lambda self, context: [
+ ("NONE", "None", "No filtering is performed"),
+ ("DECOMPOSITION", "Decomposition", "Filter objects by decomposition"),
+ ("IFC_CLASS", "IFC Class", "Filter objects by class"),
+ ("IFC_TYPE", "IFC Type", "Filter objects by type"),
+ ("WHITELIST", "Whitelist", "Filter objects using a custom whitelist query"),
+ ("BLACKLIST", "Blacklist", "Filter objects using a custom blacklist query"),
+ ],
+ "merge_mode": lambda self, context: [
+ ("NONE", "None", "No objects are merged"),
+ ("IFC_CLASS", "IFC Class", "One object per IFC class"),
+ ("IFC_TYPE", "IFC Type", "One object per IFC construction type"),
+ ("MATERIAL", "Material", "One object per material"),
+ ],
+ "export_schema": get_export_schema,
+ "template_file": get_template_file,
+ }
diff --git a/src/blenderbim/blenderbim/bim/module/project/ui.py b/src/blenderbim/blenderbim/bim/module/project/ui.py
index c8be2f2933..4aff3bb1d3 100644
--- a/src/blenderbim/blenderbim/bim/module/project/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/project/ui.py
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see .
import os
+from blenderbim.bim.helper import prop_with_search
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.project.data import ProjectData
@@ -48,10 +49,8 @@ class BIM_PT_project(Panel):
def draw_load_ui(self, context):
pprops = context.scene.BIMProjectProperties
- row = self.layout.row()
- row.prop(pprops, "collection_mode")
- row = self.layout.row()
- row.prop(pprops, "filter_mode")
+ prop_with_search(self.layout, pprops, "collection_mode")
+ prop_with_search(self.layout, pprops, "filter_mode")
if pprops.filter_mode in ["DECOMPOSITION", "IFC_CLASS", "IFC_TYPE"]:
row = self.layout.row(align=True)
row.label(text=f"Total: {pprops.total_elements}")
@@ -84,8 +83,7 @@ class BIM_PT_project(Panel):
row = self.layout.row()
row.prop(pprops, "is_coordinating")
if pprops.is_coordinating:
- row = self.layout.row()
- row.prop(pprops, "merge_mode")
+ prop_with_search(self.layout, pprops, "merge_mode")
row = self.layout.row()
row.prop(pprops, "deflection_tolerance")
row = self.layout.row()
@@ -170,8 +168,7 @@ class BIM_PT_project(Panel):
def draw_create_project_ui(self, context):
props = context.scene.BIMProperties
pprops = context.scene.BIMProjectProperties
- row = self.layout.row()
- row.prop(pprops, "export_schema")
+ prop_with_search(self.layout, pprops, "export_schema")
row = self.layout.row()
row.prop(context.scene.unit_settings, "system")
row = self.layout.row()
@@ -180,8 +177,7 @@ class BIM_PT_project(Panel):
row.prop(props, "area_unit", text="Area Unit")
row = self.layout.row()
row.prop(props, "volume_unit", text="Volume Unit")
- row = self.layout.row()
- row.prop(pprops, "template_file", text="Template")
+ prop_with_search(self.layout, pprops, "template_file", text="Template")
row = self.layout.row(align=True)
row.operator("bim.create_project")
diff --git a/src/blenderbim/blenderbim/bim/module/pset/__init__.py b/src/blenderbim/blenderbim/bim/module/pset/__init__.py
index c2d845661d..5e04e16662 100644
--- a/src/blenderbim/blenderbim/bim/module/pset/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/pset/__init__.py
@@ -35,8 +35,8 @@ classes = (
operator.BIM_OT_rename_parameters,
operator.BIM_OT_add_edit_custom_property,
operator.BIM_OT_bulk_remove_psets,
- prop.EnumerationValues,
- prop.IfcSimpleProperty,
+ prop.IfcPropertyEnumeratedValue,
+ prop.IfcProperty,
prop.PsetProperties,
prop.MaterialPsetProperties,
prop.TaskPsetProperties,
diff --git a/src/blenderbim/blenderbim/bim/module/pset/operator.py b/src/blenderbim/blenderbim/bim/module/pset/operator.py
index 1829ebee78..1758806560 100644
--- a/src/blenderbim/blenderbim/bim/module/pset/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/pset/operator.py
@@ -122,85 +122,91 @@ class EnablePsetEditing(bpy.types.Operator):
for prop_template in pset_template.HasPropertyTemplates:
if not prop_template.is_a("IfcSimplePropertyTemplate"):
continue # Other types not yet supported
-
if prop_template.TemplateType == "P_SINGLEVALUE":
- try:
- data_type = ifcopenshell.util.attribute.get_primitive_type(
- IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
- )
- except:
- # TODO: Occurs if the data type is something that exists in
- # IFC4 and not in IFC2X3. To fully fix this we need to
- # generate the IFC2X3 pset template definitions.
- continue
- elif prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
- data_type = "float"
- elif prop_template.TemplateType == "Q_COUNT":
- data_type = "integer"
+ self.load_single_value(prop_template, data)
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
- enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
- #selected_enum_items = [e.EnumerationValues.wrappedValue for e in Data.properties[e]]
- data_type = ""
- else:
- continue # Other types not yet supported
+ self.load_enumerated_value(prop_template, data)
- new = self.props.properties.add()
- new.name = prop_template.Name
- new.is_null = data.get(prop_template.Name, None) is None
- new.is_optional = True
- new.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
- new.data_type = data_type
-
- if data_type == "string":
- new.string_value = "" if new.is_null else data[prop_template.Name]
- elif data_type == "integer":
- new.int_value = 0 if new.is_null else data[prop_template.Name]
- elif data_type == "float":
- new.float_value = 0.0 if new.is_null else data[prop_template.Name]
- elif data_type == "boolean":
- new.bool_value = False if new.is_null else data[prop_template.Name]
- if prop_template.TemplateType == "P_ENUMERATEDVALUE":
+ def load_single_value(self, prop_template, data):
+ try:
+ data_type = ifcopenshell.util.attribute.get_primitive_type(
+ IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
+ )
+ except:
+ # TODO: Occurs if the data type is something that exists in
+ # IFC4 and not in IFC2X3. To fully fix this we need to
+ # generate the IFC2X3 pset template definitions.
+ return
- new.set_value(prop_template.Enumerators.EnumerationValues[0].wrappedValue)
-
- for enum in enum_items:
- new_enum = new.enumerated_values.add()
- data_type = new.get_value_name()
- setattr(new_enum, data_type, enum)
- if data.get(prop_template.Name):
- new_enum.is_selected = enum in data[prop_template.Name]
+ prop = self.props.properties.add()
+ prop.value_type = "IfcPropertySingleValue"
+ metadata = prop.metadata
+ metadata.name = prop_template.Name
+ metadata.is_null = data.get(prop_template.Name, None) is None
+ metadata.is_optional = True
+ metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
+ metadata.data_type = data_type
+
+ if data_type == "string":
+ metadata.string_value = "" if metadata.is_null else data[prop_template.Name]
+ elif data_type == "integer":
+ metadata.int_value = 0 if metadata.is_null else data[prop_template.Name]
+ elif data_type == "float":
+ metadata.float_value = 0.0 if metadata.is_null else data[prop_template.Name]
+ elif data_type == "boolean":
+ metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
+
+ def load_enumerated_value(self, prop_template, data):
+ enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
+ selected_enum_items = data.get(prop_template.Name, [])
+
+ prop = self.props.properties.add()
+ prop.value_type = "IfcPropertyEnumeratedValue"
+ metadata = prop.metadata
+ metadata.name = prop_template.Name
+ metadata.is_null = data.get(prop_template.Name, None) is None
+ metadata.is_optional = True
+ metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
+
+ # Cute hack to abuse the metadata to find the Blender data_type
+ metadata.set_value(enum_items[0])
+ data_type = metadata.get_value_name()
+
+ for enum in enum_items:
+ new = prop.enumerated_value.enumerated_values.add()
+ setattr(new, data_type, enum)
+ new.is_selected = enum in selected_enum_items
def load_from_pset_data(self, pset_data):
for prop_id in pset_data["Properties"]:
prop = Data.properties[prop_id]
-
+
if prop["type"] == "IfcPropertyEnumeratedValue":
- new = self.props.properties.add()
- new.name = prop["Name"]
- new.is_null = new.enumerated_values is None
- new.is_optional = True
- new.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
-
- enum_ref = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
- enum_vals = [v.wrappedValue for v in prop["EnumerationValues"]]
- for enum in enum_ref:
- new_enum = new.enumerated_values.add()
- data_type = new.get_value_name()
- setattr(new_enum, data_type, enum)
- if enum in enum_vals:
- new_enum.is_selected = True
- else:
- new_enum.is_selected = False
-
+ simple_prop = self.props.properties.add()
+ simple_prop.value_type = "IfcPropertyEnumeratedValue"
+ metadata = simple_prop.metadata
+ metadata.name = prop["Name"]
+ metadata.is_null = len(simple_prop.enumerated_value.enumerated_values) == 0
+ metadata.is_optional = True
+ metadata.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
+
+ enum_items = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
+ selected_enum_items = [v.wrappedValue for v in prop["EnumerationValues"]]
+ data_type = metadata.get_value_name()
+
+ for enum in enum_items:
+ new = simple_prop.enumerated_value.enumerated_values.add()
+ setattr(new, data_type, enum)
+ new.is_selected = enum in selected_enum_items
else:
value = prop["NominalValue"]
- new = self.props.properties.add()
- new.set_value(value)
- new.name = prop["Name"]
- new.is_null = value is None
- new.is_optional = True
- new.set_value(new.get_value_default() if new.is_null else value)
-
+ prop = self.props.properties.add()
+ metadata = prop.metadata
+ metadata.set_value(value)
+ metadata.name = prop["Name"]
+ metadata.is_null = value is None
+ metadata.is_optional = True
+ metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
class DisablePsetEditing(bpy.types.Operator, Operator):
@@ -236,11 +242,13 @@ class EditPset(bpy.types.Operator, Operator):
else:
data = Data.psets if pset_id in Data.psets else Data.qtos
for prop in props.properties:
- if len(prop.enumerated_values) != 0:
- value_name = prop.get_value_name()
- properties[prop.name] = [e[value_name] for e in prop.enumerated_values if e.is_selected]
- else:
- properties[prop.name] = prop.get_value()
+ if prop.value_type == "IfcPropertySingleValue":
+ properties[prop.metadata.name] = prop.metadata.get_value()
+ elif prop.value_type == "IfcPropertyEnumeratedValue":
+ value_name = prop.metadata.get_value_name()
+ properties[prop.metadata.name] = [
+ e[value_name] for e in prop.enumerated_value.enumerated_values if e.is_selected
+ ]
if pset_id in Data.psets:
ifcopenshell.api.run(
@@ -412,7 +420,7 @@ class BIM_OT_add_property_to_edit(bpy.types.Operator):
bl_idname = "bim.add_property_to_edit"
bl_options = {"REGISTER", "UNDO"}
option: bpy.props.StringProperty()
- index: bpy.props.IntProperty()
+ index: bpy.props.IntProperty(default=-1)
def execute(self, context):
if self.index == -1:
@@ -507,15 +515,15 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
continue
ifc_element = tool.Ifc.get().by_id(ifc_definition_id)
- for prop in props:
+ for prop in props:
value = getattr(prop, prop.get_value_name())
- primary_measure_type = prop.primary_measure_type
-
+ primary_measure_type = prop.primary_measure_type
+
if prop.template_type == "IfcPropertyEnumeratedValue":
value_ifc_entity = self.generate_enum_entity(prop)
elif prop.template_type == "IfcPropertySingleValue":
value_ifc_entity = getattr(self.file, f"create{primary_measure_type}")(value)
-
+
new_pset = ifcopenshell.api.run("pset.add_pset", self.file, product=ifc_element, name=prop.pset_name)
ifcopenshell.api.run(
"pset.edit_pset", self.file, pset=new_pset, properties={prop.property_name: value_ifc_entity}
@@ -523,22 +531,26 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
Data.load(IfcStore.get_file(), ifc_definition_id)
self.report({"INFO"}, "Finished applying changes")
return {"FINISHED"}
-
+
def generate_enum_entity(self, prop):
prop_type = prop.get_value_name()
prop_enum = self.file.create_entity(
- "IFCPROPERTYENUMERATION",
- Name=prop.property_name,
- EnumerationValues=tuple(self.file.create_entity(
- prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values)
- )
+ "IFCPROPERTYENUMERATION",
+ Name=prop.property_name,
+ EnumerationValues=tuple(
+ self.file.create_entity(prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values
+ ),
+ )
prop_enum_value = self.file.create_entity(
- "IFCPROPERTYENUMERATEDVALUE",
- Name=prop.property_name,
- EnumerationValues=tuple(self.file.create_entity(
- prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values if ev.is_selected == True),
- EnumerationReference=prop_enum
- )
+ "IFCPROPERTYENUMERATEDVALUE",
+ Name=prop.property_name,
+ EnumerationValues=tuple(
+ self.file.create_entity(prop.primary_measure_type, ev[prop_type])
+ for ev in prop.enum_values
+ if ev.is_selected == True
+ ),
+ EnumerationReference=prop_enum,
+ )
return prop_enum_value
diff --git a/src/blenderbim/blenderbim/bim/module/pset/prop.py b/src/blenderbim/blenderbim/bim/module/pset/prop.py
index 14fedf0f33..7bf754e389 100644
--- a/src/blenderbim/blenderbim/bim/module/pset/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/pset/prop.py
@@ -18,7 +18,7 @@
import bpy
import blenderbim.bim.schema
-from blenderbim.bim.prop import Attribute
+from blenderbim.bim.prop import Attribute, StrProperty
import ifcopenshell
from ifcopenshell.api.pset.data import Data
from blenderbim.bim.module.pset.data import AddEditCustomPropertiesData
@@ -47,7 +47,7 @@ def purge():
qtonames = {}
-def getPsetNames(self, context):
+def get_pset_names(self, context):
global psetnames
obj = context.active_object
if not obj.BIMObjectProperties.ifc_definition_id:
@@ -122,7 +122,7 @@ def getWorkSchedulePsetNames(self, context):
return psetnames[ifc_class]
-def getQtoNames(self, context):
+def get_qto_names(self, context):
global qtonames
if "/" in context.active_object.name:
ifc_class = context.active_object.name.split("/")[0]
@@ -139,28 +139,29 @@ def get_primary_measure_type(self, context):
return AddEditCustomPropertiesData.data["primary_measure_type"]
-class EnumerationValues(PropertyGroup):
- string_value: StringProperty(name="Value")
- bool_value: BoolProperty(name="Value")
- int_value: IntProperty(name="Value")
- float_value: FloatProperty(name="Value")
- is_selected: BoolProperty(default=False)
-
-
-class IfcSimpleProperty(Attribute):
- #bounded_values:
- enumerated_values: CollectionProperty(type=EnumerationValues)
- #list_values:
- #reference_values:
- #table_values:
-
-
+class IfcPropertyEnumeratedValue(PropertyGroup):
+ enumerated_values: CollectionProperty(type=Attribute)
+
+
+class IfcProperty(PropertyGroup):
+ metadata: PointerProperty(type=Attribute)
+ value_type: EnumProperty(
+ items=[(v, v, v) for v in ("IfcPropertySingleValue", "IfcPropertyEnumeratedValue")], name="Value Type"
+ )
+ enumerated_value: PointerProperty(type=IfcPropertyEnumeratedValue)
+
+
class PsetProperties(PropertyGroup):
active_pset_id: IntProperty(name="Active Pset ID")
active_pset_name: StringProperty(name="Pset Name")
- properties: CollectionProperty(name="Properties", type=IfcSimpleProperty)
- pset_name: EnumProperty(items=getPsetNames, name="Pset Name")
- qto_name: EnumProperty(items=getQtoNames, name="Qto Name")
+ properties: CollectionProperty(name="Properties", type=IfcProperty)
+ pset_name: EnumProperty(items=get_pset_names, name="Pset Name")
+ qto_name: EnumProperty(items=get_qto_names, name="Qto Name")
+
+ getter_enum = {
+ "qto_name": get_qto_names,
+ "pset_name": get_pset_names,
+ }
class MaterialPsetProperties(PropertyGroup):
@@ -215,12 +216,20 @@ class AddEditProperties(PropertyGroup):
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
template_type: EnumProperty(
items=[
- ("IfcPropertySingleValue","IfcPropertySingleValue","IfcPropertySingleValue"),
- ("IfcPropertyEnumeratedValue","IfcPropertyEnumeratedValue","IfcPropertyEnumeratedValue")
+ ("IfcPropertySingleValue", "IfcPropertySingleValue", "IfcPropertySingleValue"),
+ ("IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue"),
],
- name="Template Type"
+ name="Template Type",
)
- enum_values: CollectionProperty(name="Enum Values", type=EnumerationValues)
+ enum_values: CollectionProperty(name="Enum Values", type=Attribute)
+
+ getter_enum = {
+ "primary_measure_type": get_primary_measure_type,
+ "template_type": lambda self, context: [
+ ("IfcPropertySingleValue", "IfcPropertySingleValue", "IfcPropertySingleValue"),
+ ("IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue"),
+ ],
+ }
def get_value_name(self):
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
diff --git a/src/blenderbim/blenderbim/bim/module/pset/ui.py b/src/blenderbim/blenderbim/bim/module/pset/ui.py
index 25adeb2156..b6c67b1611 100644
--- a/src/blenderbim/blenderbim/bim/module/pset/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/pset/ui.py
@@ -18,7 +18,7 @@
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
-from blenderbim.bim.helper import draw_attribute
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.pset.data import (
ObjectPsetsData,
ObjectQtosData,
@@ -31,6 +31,45 @@ from blenderbim.bim.module.pset.data import (
)
+def draw_property(prop, layout, copy_operator=None):
+ if prop.value_type == "IfcPropertySingleValue":
+ draw_single_property(prop, layout, copy_operator)
+ elif prop.value_type == "IfcPropertyEnumeratedValue":
+ draw_enumerated_property(prop, layout, copy_operator)
+
+
+def draw_single_property(prop, layout, copy_operator=None):
+ value_name = prop.metadata.get_value_name()
+ if not value_name:
+ layout.label(text=prop.metadata.name)
+ return
+ layout.prop(
+ prop.metadata,
+ value_name,
+ text=prop.metadata.name,
+ )
+ if prop.metadata.is_optional:
+ layout.prop(prop.metadata, "is_null", icon="RADIOBUT_OFF" if prop.metadata.is_null else "RADIOBUT_ON", text="")
+ if copy_operator:
+ op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
+ op.name = prop.metadata.name
+ if prop.metadata.is_uri:
+ op = layout.operator("bim.select_uri_prop", text="", icon="FILE_FOLDER")
+ op.data_path = prop.metadata.path_from_id("string_value")
+
+
+def draw_enumerated_property(prop, layout, copy_operator=None):
+ value_name = prop.metadata.get_value_name()
+ if not value_name:
+ layout.label(text=prop.metadata.name)
+ return
+ if len(prop.enumerated_value.enumerated_values) != 0:
+ layout.label(text=prop.metadata.name)
+ grid = layout.column_flow(columns=3)
+ for e in prop.enumerated_value.enumerated_values:
+ grid.prop(e, "is_selected", text=str(e[value_name]))
+
+
def get_active_pset_obj_name(context, obj_type):
if obj_type == "Object":
return context.active_object.name
@@ -95,7 +134,7 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
def draw_psetqto_editable_ui(box, props, prop):
row = box.row(align=True)
- draw_attribute(box, prop, row, copy_operator="bim.copy_property_to_selection")
+ draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
if (
"length" in prop.name.lower()
or "width" in prop.name.lower()
@@ -138,7 +177,7 @@ class BIM_PT_object_psets(Panel):
props = context.active_object.PsetProperties
row = self.layout.row(align=True)
- row.prop(props, "pset_name", text="")
+ prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj = context.active_object.name
op.obj_type = "Object"
@@ -177,7 +216,7 @@ class BIM_PT_object_qtos(Panel):
props = context.active_object.PsetProperties
row = self.layout.row(align=True)
- row.prop(props, "qto_name", text="")
+ prop_with_search(row, props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
op.obj = context.active_object.name
op.obj_type = "Object"
@@ -450,12 +489,12 @@ class BIM_PT_add_edit_custom_properties(Panel):
row.prop(prop, "property_name", text="")
if prop.template_type == "IfcPropertySingleValue":
row.prop(prop, prop.get_value_name(), text="")
- row.prop(prop, "primary_measure_type", text="")
- row.prop(prop, "template_type", text="")
+ prop_with_search(row, prop, "primary_measure_type", text="")
+ prop_with_search(row, prop, "template_type", text="")
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
op.index = index
op.option = "AddEditProperties"
-
+
if prop.template_type == "IfcPropertyEnumeratedValue":
op = row.operator("bim.add_property_to_edit", icon="ADD", text="Add Enum")
op.option = "AddEditProperties"
@@ -494,7 +533,7 @@ class BIM_PT_delete_psets(Panel):
row = layout.row()
op = row.operator("bim.add_property_to_edit", icon="ADD")
op.option = "DeletePsets"
-
+
if props:
for index, prop in enumerate(props):
row = layout.row(align=True)
diff --git a/src/blenderbim/blenderbim/bim/module/pset_template/__init__.py b/src/blenderbim/blenderbim/bim/module/pset_template/__init__.py
index e4e5591da4..1f822620da 100644
--- a/src/blenderbim/blenderbim/bim/module/pset_template/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/pset_template/__init__.py
@@ -20,6 +20,8 @@ import bpy
from . import ui, prop, operator
classes = (
+ operator.RefreshPsetTemplates,
+ operator.AddPsetFile,
operator.SavePsetTemplateFile,
operator.AddPsetTemplate,
operator.EditPsetTemplate,
@@ -30,8 +32,11 @@ classes = (
operator.EnableEditingPsetTemplate,
operator.DisableEditingPsetTemplate,
operator.EnableEditingPropTemplate,
+ operator.DeletePropEnum,
+ operator.AddPropEnum,
operator.DisableEditingPropTemplate,
prop.PsetTemplate,
+ prop.EnumerationValues,
prop.PropTemplate,
prop.BIMPsetTemplateProperties,
ui.BIM_PT_pset_template,
diff --git a/src/blenderbim/blenderbim/bim/module/pset_template/operator.py b/src/blenderbim/blenderbim/bim/module/pset_template/operator.py
index 8699f6d327..c01aefe847 100644
--- a/src/blenderbim/blenderbim/bim/module/pset_template/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/pset_template/operator.py
@@ -16,16 +16,66 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
+import os
import bpy
import ifcopenshell
import ifcopenshell.api
import blenderbim.bim.schema
import blenderbim.bim.handler
-from blenderbim.bim.module.pset_template.prop import updatePsetTemplateFiles, updatePsetTemplates, getPsetTemplates
+from blenderbim.bim.module.pset_template.prop import purge as purge_templates, updatePsetTemplates, getPsetTemplates
+from blenderbim.bim.module.pset.prop import purge as purge_psets
from ifcopenshell.api.pset_template.data import Data
from blenderbim.bim.ifc import IfcStore
+# This is just a temporary operator until
+# @Moult performs some of his refactor-magic ;) - vulevukusej
+class RefreshPsetTemplates(bpy.types.Operator):
+ bl_idname = "bim.refresh_psettemplates"
+ bl_label = "Refresh the data for PsetTemplates"
+ bl_options = {"REGISTER", "UNDO"}
+
+ def execute(self, context):
+ purge_templates()
+ purge_psets()
+ return {"FINISHED"}
+
+
+class AddPsetFile(bpy.types.Operator):
+ bl_idname = "bim.add_pset_file"
+ bl_label = "Add Pset File"
+ bl_options = {"REGISTER", "UNDO"}
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_props_dialog(self, width=250)
+
+ def draw(self, context):
+ self.props = context.scene.BIMPsetTemplateProperties
+ self.layout.prop(self.props, "new_template_filename", text="Filename:")
+
+ def execute(self, context):
+ template = ifcopenshell.file()
+ filepath = os.path.join(
+ context.scene.BIMProperties.data_dir,
+ "pset",
+ self.props.new_template_filename + ".ifc",
+ )
+
+ template.create_entity(
+ "IFCPROPERTYSETTEMPLATE",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "Name": "Name",
+ "Description": "Description",
+ "TemplateType": "PSET_TYPEDRIVENONLY",
+ "ApplicableEntity": "IfcTypeObject",
+ }
+ )
+ template.write(filepath)
+ self.props.new_template_filename = ""
+ return {"FINISHED"}
+
+
class AddPsetTemplate(bpy.types.Operator):
bl_idname = "bim.add_pset_template"
bl_label = "Add Pset Template"
@@ -41,7 +91,6 @@ class AddPsetTemplate(bpy.types.Operator):
return result
def _execute(self, context):
- props = context.scene.BIMPsetTemplateProperties
ifcopenshell.api.run("pset_template.add_pset_template", IfcStore.pset_template_file)
Data.load(IfcStore.pset_template_file)
updatePsetTemplates(self, context)
@@ -139,6 +188,40 @@ class EnableEditingPropTemplate(bpy.types.Operator):
props.active_prop_template.name = template["Name"] or ""
props.active_prop_template.description = template["Description"] or ""
props.active_prop_template.primary_measure_type = template["PrimaryMeasureType"]
+ props.active_prop_template.template_type = template["TemplateType"]
+ props.active_prop_template.enum_values.clear()
+
+ if template["Enumerators"]:
+ props.active_prop_template.enum_values.clear()
+ data_type = props.active_prop_template.get_value_name()
+ for e in template["Enumerators"].EnumerationValues:
+ new = props.active_prop_template.enum_values.add()
+ setattr(new, data_type, e.wrappedValue)
+
+ return {"FINISHED"}
+
+
+class DeletePropEnum(bpy.types.Operator):
+ bl_idname = "bim.delete_prop_enum"
+ bl_label = "delete property enumeration"
+ bl_options = {"REGISTER", "UNDO"}
+ index: bpy.props.IntProperty()
+
+ def execute(self, context):
+ active_prop = context.scene.BIMPsetTemplateProperties.active_prop_template
+ active_prop.enum_values.remove(self.index)
+ return {"FINISHED"}
+
+
+class AddPropEnum(bpy.types.Operator):
+ bl_idname = "bim.add_prop_enum"
+ bl_label = "add property enumeration"
+ bl_options = {"REGISTER", "UNDO"}
+ index: bpy.props.IntProperty()
+
+ def execute(self, context):
+ active_prop = context.scene.BIMPsetTemplateProperties.active_prop_template
+ active_prop.enum_values.add()
return {"FINISHED"}
@@ -285,6 +368,10 @@ class EditPropTemplate(bpy.types.Operator):
def _execute(self, context):
props = context.scene.BIMPsetTemplateProperties
+ if props.active_prop_template.template_type == "P_ENUMERATEDVALUE":
+ enumerator = self.generate_prop_enum(props)
+ else:
+ enumerator = None
ifcopenshell.api.run(
"pset_template.edit_prop_template",
IfcStore.pset_template_file,
@@ -294,6 +381,8 @@ class EditPropTemplate(bpy.types.Operator):
"Name": props.active_prop_template.name,
"Description": props.active_prop_template.description,
"PrimaryMeasureType": props.active_prop_template.primary_measure_type,
+ "TemplateType": props.active_prop_template.template_type,
+ "Enumerators": enumerator,
},
}
)
@@ -301,6 +390,21 @@ class EditPropTemplate(bpy.types.Operator):
bpy.ops.bim.disable_editing_prop_template()
return {"FINISHED"}
+ # TODO -This will need to go into the
+ # api code at some point - vulevukusej
+ def generate_prop_enum(self, props):
+ self.file = IfcStore.pset_template_file
+ data_type = props.active_prop_template.get_value_name()
+ prop = props.active_prop_template
+ prop_enum = self.file.create_entity(
+ "IFCPROPERTYENUMERATION",
+ Name=prop.name,
+ EnumerationValues=tuple(
+ self.file.create_entity(prop.primary_measure_type, ev[data_type]) for ev in prop.enum_values
+ ),
+ )
+ return prop_enum
+
def rollback(self, data):
IfcStore.pset_template_file.undo()
diff --git a/src/blenderbim/blenderbim/bim/module/pset_template/prop.py b/src/blenderbim/blenderbim/bim/module/pset_template/prop.py
index 0d6a46a94e..62a6bcf474 100644
--- a/src/blenderbim/blenderbim/bim/module/pset_template/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/pset_template/prop.py
@@ -94,63 +94,95 @@ def get_primary_measure_type(self, context):
return PsetTemplatesData.data["primary_measure_type"]
+def get_template_type(self, context):
+ return [
+ (
+ "PSET_TYPEDRIVENONLY",
+ "Pset - IfcTypeObject",
+ "The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
+ ),
+ (
+ "PSET_TYPEDRIVENOVERRIDE",
+ "Pset - IfcTypeObject - Override",
+ "The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
+ ),
+ (
+ "PSET_OCCURRENCEDRIVEN",
+ "Pset - IfcObject",
+ "The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
+ ),
+ (
+ "PSET_PERFORMANCEDRIVEN",
+ "Pset - IfcPerformanceHistory",
+ "The property sets defined by this IfcPropertySetTemplate can only be assigned to IfcPerformanceHistory.",
+ ),
+ (
+ "QTO_TYPEDRIVENONLY",
+ "Qto - IfcTypeObject",
+ "The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
+ ),
+ (
+ "QTO_TYPEDRIVENOVERRIDE",
+ "Qto - IfcTypeObject - Override",
+ "The element quantity defined by this IfcPropertySetTemplate can be assigned to subtypes of IfcTypeObject and can be overridden by an element quantity with same name at subtypes of IfcObject.",
+ ),
+ (
+ "QTO_OCCURRENCEDRIVEN",
+ "Qto - IfcObject",
+ "The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
+ ),
+ (
+ "NOTDEFINED",
+ "Not defined",
+ "No restriction provided, the property sets defined by this IfcPropertySetTemplate can be assigned to any entity, if not otherwise restricted by the ApplicableEntity attribute.",
+ ),
+ ]
+
+
class PsetTemplate(PropertyGroup):
global_id: StringProperty(name="Global ID")
name: StringProperty(name="Name")
description: StringProperty(name="Description")
- template_type: EnumProperty(
- items=[
- (
- "PSET_TYPEDRIVENONLY",
- "Pset - IfcTypeObject",
- "The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
- ),
- (
- "PSET_TYPEDRIVENOVERRIDE",
- "Pset - IfcTypeObject - Override",
- "The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
- ),
- (
- "PSET_OCCURRENCEDRIVEN",
- "Pset - IfcObject",
- "The property sets defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
- ),
- (
- "PSET_PERFORMANCEDRIVEN",
- "Pset - IfcPerformanceHistory",
- "The property sets defined by this IfcPropertySetTemplate can only be assigned to IfcPerformanceHistory.",
- ),
- (
- "QTO_TYPEDRIVENONLY",
- "Qto - IfcTypeObject",
- "The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcTypeObject.",
- ),
- (
- "QTO_TYPEDRIVENOVERRIDE",
- "Qto - IfcTypeObject - Override",
- "The element quantity defined by this IfcPropertySetTemplate can be assigned to subtypes of IfcTypeObject and can be overridden by an element quantity with same name at subtypes of IfcObject.",
- ),
- (
- "QTO_OCCURRENCEDRIVEN",
- "Qto - IfcObject",
- "The element quantity defined by this IfcPropertySetTemplate can only be assigned to subtypes of IfcObject.",
- ),
- (
- "NOTDEFINED",
- "Not defined",
- "No restriction provided, the property sets defined by this IfcPropertySetTemplate can be assigned to any entity, if not otherwise restricted by the ApplicableEntity attribute.",
- ),
- ],
- name="Template Type",
- )
+ template_type: EnumProperty(items=get_template_type, name="Template Type")
applicable_entity: StringProperty(name="Applicable Entity")
+ getter_enum = {
+ "template_type": get_template_type,
+ }
+
+
+class EnumerationValues(PropertyGroup):
+ string_value: StringProperty(name="Value")
+ bool_value: BoolProperty(name="Value")
+ int_value: IntProperty(name="Value")
+ float_value: FloatProperty(name="Value")
+
class PropTemplate(PropertyGroup):
global_id: StringProperty(name="Global ID")
name: StringProperty(name="Name")
description: StringProperty(name="Description")
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
+ template_type: EnumProperty(
+ items=[("P_SINGLEVALUE", "P_SINGLEVALUE", ""), ("P_ENUMERATEDVALUE", "P_ENUMERATEDVALUE", "")],
+ name="Template Type",
+ )
+ enum_values: CollectionProperty(type=EnumerationValues)
+ getter_enum = {
+ "primary_measure_type": get_primary_measure_type,
+ }
+
+ def get_value_name(self):
+ ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
+ data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
+ if data_type == "string":
+ return "string_value"
+ elif data_type == "boolean":
+ return "bool_value"
+ elif data_type == "integer":
+ return "int_value"
+ elif data_type == "float":
+ return "float_value"
class BIMPsetTemplateProperties(PropertyGroup):
@@ -162,3 +194,9 @@ class BIMPsetTemplateProperties(PropertyGroup):
active_prop_template_id: IntProperty(name="Active Prop Template Id")
active_pset_template: PointerProperty(type=PsetTemplate)
active_prop_template: PointerProperty(type=PropTemplate)
+ new_template_filename: StringProperty("New TemplateFileName")
+
+ getter_enum = {
+ "pset_template_files": getPsetTemplateFiles,
+ "pset_templates": getPsetTemplates,
+ }
diff --git a/src/blenderbim/blenderbim/bim/module/pset_template/ui.py b/src/blenderbim/blenderbim/bim/module/pset_template/ui.py
index 91f90f82d8..b15ca05bd4 100644
--- a/src/blenderbim/blenderbim/bim/module/pset_template/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/pset_template/ui.py
@@ -19,6 +19,7 @@
import bpy
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.pset_template.prop import (
getPsetTemplates,
)
@@ -39,13 +40,15 @@ class BIM_PT_pset_template(Panel):
props = context.scene.BIMPsetTemplateProperties
row = layout.row(align=True)
- row.prop(props, "pset_template_files", text="")
+ prop_with_search(row, props, "pset_template_files", text="")
row.operator("bim.save_pset_template_file", text="", icon="EXPORT")
+ row.operator("bim.add_pset_file", icon="ADD", text="")
+ row.operator("bim.refresh_psettemplates", icon="FILE_REFRESH", text="")
row = layout.row(align=True)
if bool(getPsetTemplates(props, context)):
- row.prop(props, "pset_templates", text="", icon="COPY_ID")
+ prop_with_search(row, props, "pset_templates", text="", icon="COPY_ID")
else:
row.label(text="No Pset Templates", icon="COPY_ID")
@@ -69,8 +72,7 @@ class BIM_PT_pset_template(Panel):
row.prop(props.active_pset_template, "name")
row = layout.row()
row.prop(props.active_pset_template, "description")
- row = layout.row()
- row.prop(props.active_pset_template, "template_type")
+ prop_with_search(layout, props.active_pset_template, "template_type")
row = layout.row()
row.prop(props.active_pset_template, "applicable_entity")
else:
@@ -86,6 +88,9 @@ class BIM_PT_pset_template(Panel):
row.label(text="Property Templates", icon="COPY_ID")
row.operator("bim.add_prop_template", icon="ADD", text="")
+ row = layout.row()
+ row.label(text="Name | Description | PrimaryMeasureType | TemplateType")
+
for prop_template_id in pset_template["HasPropertyTemplates"]:
prop_template = Data.prop_templates[prop_template_id]
row = layout.row(align=True)
@@ -93,11 +98,34 @@ class BIM_PT_pset_template(Panel):
if props.active_prop_template_id and props.active_prop_template_id == prop_template_id:
row.prop(props.active_prop_template, "name", text="")
row.prop(props.active_prop_template, "description", text="")
- row.prop(props.active_prop_template, "primary_measure_type", text="")
+ prop_with_search(row, props.active_prop_template, "primary_measure_type", text="")
+ row.prop(props.active_prop_template, "template_type", text="")
+
+ if props.active_prop_template.template_type == "P_ENUMERATEDVALUE":
+ row = layout.row(align=True)
+ split = row.split(factor=0.2)
+ c = split.column()
+ c.label(
+ text="Enumerations:",
+ )
+
+ c = split.column()
+ box = c.box()
+ grid = box.column_flow(columns=4)
+ value_name = props.active_prop_template.get_value_name()
+ r = grid.row()
+ r.operator("bim.add_prop_enum", text="Add Enum")
+ for k, v in enumerate(props.active_prop_template.enum_values):
+ r = grid.row(align=True)
+ r.prop(v, value_name, text="")
+ op = r.operator("bim.delete_prop_enum", icon="X", text="")
+ op.index = k
+
else:
row.label(text=prop_template["Name"])
row.label(text=prop_template["Description"])
row.label(text=prop_template["PrimaryMeasureType"])
+ row.label(text=prop_template["TemplateType"])
if props.active_prop_template_id and props.active_prop_template_id == prop_template_id:
op = row.operator("bim.edit_prop_template", icon="CHECKMARK", text="")
diff --git a/src/blenderbim/blenderbim/bim/module/root/operator.py b/src/blenderbim/blenderbim/bim/module/root/operator.py
index e64b2e5920..b2c39b7b44 100644
--- a/src/blenderbim/blenderbim/bim/module/root/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/root/operator.py
@@ -31,7 +31,6 @@ import blenderbim.core.root as core
import blenderbim.tool as tool
from ifcopenshell.api.void.data import Data as VoidData
from blenderbim.bim.ifc import IfcStore
-from blenderbim.bim.module.root.prop import get_contexts
class Operator:
@@ -127,12 +126,13 @@ class AssignClass(bpy.types.Operator, Operator):
ifc_representation_class: bpy.props.StringProperty()
def _execute(self, context):
+ props = context.scene.BIMRootProperties
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
- ifc_class = self.ifc_class or context.scene.BIMRootProperties.ifc_class
+ ifc_class = self.ifc_class or props.ifc_class
predefined_type = self.userdefined_type if self.predefined_type == "USERDEFINED" else self.predefined_type
ifc_context = self.context_id
- if not ifc_context and get_contexts(self, context):
- ifc_context = int(context.scene.BIMRootProperties.contexts or "0") or None
+ if not ifc_context and props.getter_enum["contexts"](props, context):
+ ifc_context = int(props.contexts or "0") or None
if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context)
active_object = context.active_object
diff --git a/src/blenderbim/blenderbim/bim/module/root/prop.py b/src/blenderbim/blenderbim/bim/module/root/prop.py
index 8b598a2338..2b009a551b 100644
--- a/src/blenderbim/blenderbim/bim/module/root/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/root/prop.py
@@ -34,11 +34,14 @@ from bpy.props import (
)
types_enum = []
+classes_enum = []
def purge():
global types_enum
types_enum = []
+ global classes_enum
+ classes_enum = []
def getIfcPredefinedTypes(self, context):
@@ -69,6 +72,10 @@ def refreshPredefinedTypes(self, context):
context.scene.BIMRootProperties.ifc_predefined_type = enum[0][0]
+def update_class_enum(self, context):
+ self.ifc_class = self.ifc_class_filter_enum
+
+
def get_ifc_products(self, context):
if not IfcClassData.is_loaded:
IfcClassData.load()
@@ -93,3 +100,10 @@ class BIMRootProperties(PropertyGroup):
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refreshPredefinedTypes)
ifc_predefined_type: EnumProperty(items=getIfcPredefinedTypes, name="Predefined Type", default=None)
ifc_userdefined_type: StringProperty(name="Userdefined Type")
+
+ getter_enum = {
+ "contexts": get_contexts,
+ "ifc_product": get_ifc_products,
+ "ifc_class": get_ifc_classes,
+ "ifc_predefined_type": getIfcPredefinedTypes,
+ }
diff --git a/src/blenderbim/blenderbim/bim/module/root/ui.py b/src/blenderbim/blenderbim/bim/module/root/ui.py
index 369f0b50e8..18ca900cea 100644
--- a/src/blenderbim/blenderbim/bim/module/root/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/root/ui.py
@@ -20,6 +20,7 @@ import bpy
import blenderbim.bim.module.root.prop as root_prop
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.root.data import IfcClassData
@@ -76,17 +77,14 @@ class BIM_PT_class(Panel):
def draw_class_dropdowns(self, context, ifc_predefined_types, is_reassigning_class=False):
props = context.scene.BIMRootProperties
+ layout = self.layout
if not is_reassigning_class:
- row = self.layout.row()
- row.prop(props, "ifc_product")
- row = self.layout.row()
- row.prop(props, "ifc_class")
+ prop_with_search(layout, props, "ifc_product")
+ prop_with_search(layout, props, "ifc_class")
if ifc_predefined_types:
- row = self.layout.row()
- row.prop(props, "ifc_predefined_type")
- if ifc_predefined_types == "USERDEFINED":
- row = self.layout.row()
- row.prop(props, "ifc_userdefined_type")
+ prop_with_search(layout, props, "ifc_predefined_type")
+ if props.ifc_predefined_type == "USERDEFINED":
+ row = layout.row()
+ row.prop(props, "ifc_userdefined_type")
if not is_reassigning_class:
- row = self.layout.row()
- row.prop(context.scene.BIMRootProperties, "contexts")
+ prop_with_search(layout, props, "contexts")
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py
index d7082cfa9e..cd67c76fc3 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py
@@ -624,6 +624,7 @@ class EditTaskTime(bpy.types.Operator):
if prop.is_null:
attributes[prop.name] = None
return True
+ # TODO make this parse PT32 as P4D
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
diff --git a/src/blenderbim/blenderbim/bim/module/style/ui.py b/src/blenderbim/blenderbim/bim/module/style/ui.py
index 86f4261866..b5a0cfe586 100644
--- a/src/blenderbim/blenderbim/bim/module/style/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/style/ui.py
@@ -54,7 +54,7 @@ class BIM_PT_styles(Panel):
row = self.layout.row(align=True)
row.alignment = "RIGHT"
- #row.operator("bim.add_presentation_style", text="", icon="ADD")
+ # row.operator("bim.add_presentation_style", text="", icon="ADD")
if self.props.styles and self.props.active_style_index < len(self.props.styles):
style = self.props.styles[self.props.active_style_index]
op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
diff --git a/src/blenderbim/blenderbim/bim/module/unit/data.py b/src/blenderbim/blenderbim/bim/module/unit/data.py
index 971e57b03a..9fa647e08c 100644
--- a/src/blenderbim/blenderbim/bim/module/unit/data.py
+++ b/src/blenderbim/blenderbim/bim/module/unit/data.py
@@ -37,7 +37,7 @@ class UnitsData:
"unit_classes": cls.unit_classes(),
"named_unit_types": cls.named_unit_types(),
"conversion_unit_types": cls.conversion_unit_types(),
- "total_units": cls.get_total_units()
+ "total_units": cls.get_total_units(),
}
cls.is_loaded = True
diff --git a/src/blenderbim/blenderbim/bim/module/unit/prop.py b/src/blenderbim/blenderbim/bim/module/unit/prop.py
index 22ef9c7a0e..f631ac1b69 100644
--- a/src/blenderbim/blenderbim/bim/module/unit/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/unit/prop.py
@@ -68,3 +68,9 @@ class BIMUnitProperties(PropertyGroup):
conversion_unit_types: EnumProperty(items=get_conversion_unit_types, name="Conversion Unit Types")
named_unit_types: EnumProperty(items=get_named_unit_types, name="Named Unit Types")
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
+
+ getter_enum = {
+ "unit_classes": get_unit_classes,
+ "conversion_unit_types": get_conversion_unit_types,
+ "named_unit_types": get_named_unit_types,
+ }
diff --git a/src/blenderbim/blenderbim/bim/module/unit/ui.py b/src/blenderbim/blenderbim/bim/module/unit/ui.py
index a6d13447f5..10a3682296 100644
--- a/src/blenderbim/blenderbim/bim/module/unit/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/unit/ui.py
@@ -19,6 +19,7 @@
import blenderbim.bim.helper
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
+from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.unit.data import UnitsData
@@ -53,22 +54,22 @@ class BIM_PT_units(Panel):
return
row = self.layout.row(align=True)
- row.prop(self.props, "unit_classes", text="")
+ prop_with_search(row, self.props, "unit_classes", text="")
if self.props.unit_classes == "IfcMonetaryUnit":
row.operator("bim.add_monetary_unit", text="", icon="ADD")
elif self.props.unit_classes in ("IfcConversionBasedUnit", "IfcConversionBasedUnitWithOffset"):
- row.prop(self.props, "conversion_unit_types", text="")
+ prop_with_search(row, self.props, "conversion_unit_types", text="")
op = row.operator("bim.add_conversion_based_unit", text="", icon="ADD")
op.name = self.props.conversion_unit_types
elif self.props.unit_classes == "IfcDerivedUnit":
pass # TODO
elif self.props.unit_classes == "IfcSIUnit":
- row.prop(self.props, "named_unit_types", text="")
+ prop_with_search(row, self.props, "named_unit_types", text="")
op = row.operator("bim.add_si_unit", text="", icon="ADD")
op.unit_type = self.props.named_unit_types
elif self.props.unit_classes == "IfcContextDependentUnit":
- row.prop(self.props, "named_unit_types", text="")
+ prop_with_search(row, self.props, "named_unit_types", text="")
op = row.operator("bim.add_context_dependent_unit", text="", icon="ADD")
op.name = "THINGAMAJIG"
op.unit_type = self.props.named_unit_types
diff --git a/src/blenderbim/blenderbim/bim/operator.py b/src/blenderbim/blenderbim/bim/operator.py
index 7b171961e0..91131d772f 100644
--- a/src/blenderbim/blenderbim/bim/operator.py
+++ b/src/blenderbim/blenderbim/bim/operator.py
@@ -25,6 +25,7 @@ import blenderbim.bim.handler
import blenderbim.tool as tool
from . import schema
from blenderbim.bim.ifc import IfcStore
+from blenderbim.bim.prop import StrProperty
from blenderbim.bim.ui import IFCFileSelector
from mathutils import Vector, Matrix, Euler
from math import radians
@@ -523,3 +524,41 @@ class ConfigureVisibility(bpy.types.Operator):
def execute(self, context):
return {"FINISHED"}
+
+
+def update_enum_property_search_prop(self, context):
+ for i, prop in enumerate(self.collection_name):
+ if prop.name == self.dummy_name:
+ setattr(context.data, self.prop_name, self.collection_identifier[i].name)
+
+
+class BIM_OT_enum_property_search(bpy.types.Operator):
+ bl_idname = "bim.enum_property_search"
+ bl_label = "Search For Property"
+ bl_options = {"REGISTER", "UNDO"}
+ dummy_name: bpy.props.StringProperty(name="Property", update=update_enum_property_search_prop)
+ collection_name: bpy.props.CollectionProperty(type=StrProperty)
+ collection_identifier: bpy.props.CollectionProperty(type=StrProperty)
+ prop_name: bpy.props.StringProperty()
+
+ def invoke(self, context, event):
+ self.data = context.data
+ getter = self.data.getter_enum.get(self.prop_name, None)
+ if getter is None:
+ return {"FINISHED"}
+ self.collection_name.clear()
+ self.collection_identifier.clear()
+ for item in getter(self.data, context):
+ self.collection_identifier.add().name = item[0]
+ if item[0] == getattr(self.data, self.prop_name):
+ self.dummy_name = item[1] # We found the current enum value
+ self.collection_name.add().name = item[1]
+ return context.window_manager.invoke_props_dialog(self)
+
+ def execute(self, context):
+ return {"FINISHED"}
+
+ def draw(self, context):
+ # Mandatory to access context.data in update :
+ self.layout.context_pointer_set(name="data", data=self.data)
+ self.layout.prop_search(self, "dummy_name", self, "collection_name")
diff --git a/src/blenderbim/blenderbim/bim/prop.py b/src/blenderbim/blenderbim/bim/prop.py
index 50817c4487..e86896fd39 100644
--- a/src/blenderbim/blenderbim/bim/prop.py
+++ b/src/blenderbim/blenderbim/bim/prop.py
@@ -195,6 +195,7 @@ class Attribute(PropertyGroup):
is_null: BoolProperty(name="Is Null")
is_optional: BoolProperty(name="Is Optional")
is_uri: BoolProperty(name="Is Uri", default=False)
+ is_selected: BoolProperty(name="Is Selected", default=False)
def get_value(self):
if self.is_null:
@@ -239,6 +240,10 @@ class Attribute(PropertyGroup):
value = str(value)
setattr(self, self.get_value_name(), value)
+ getter_enum = {
+ "enum_value": getAttributeEnumValues,
+ }
+
class ModuleVisibility(PropertyGroup):
name: StringProperty(name="Name")
diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py
index 34d9a153bf..62388b23b7 100644
--- a/src/blenderbim/blenderbim/tool/drawing.py
+++ b/src/blenderbim/blenderbim/tool/drawing.py
@@ -375,15 +375,11 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod
def open_spreadsheet(cls, uri):
- cls.open_with_user_command(
- bpy.context.preferences.addons["blenderbim"].preferences.spreadsheet_command, uri
- )
+ cls.open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.spreadsheet_command, uri)
@classmethod
def open_svg(cls, uri):
- cls.open_with_user_command(
- bpy.context.preferences.addons["blenderbim"].preferences.svg_command, uri
- )
+ cls.open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, uri)
@classmethod
def run_root_assign_class(
diff --git a/src/blenderbim/blenderbim/tool/material.py b/src/blenderbim/blenderbim/tool/material.py
index 0b3832ca24..42d70eb35d 100644
--- a/src/blenderbim/blenderbim/tool/material.py
+++ b/src/blenderbim/blenderbim/tool/material.py
@@ -75,7 +75,9 @@ class Material(blenderbim.core.tool.Material):
new = props.materials.add()
new.ifc_definition_id = material.id()
new.name = get_name(material)
- new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
+ new.total_elements = len(
+ ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
+ )
return
for material in materials:
new = props.materials.add()
diff --git a/src/blenderbim/scripts/setup_pytest.py b/src/blenderbim/scripts/setup_pytest.py
index 552dbf55b0..262f97587f 100644
--- a/src/blenderbim/scripts/setup_pytest.py
+++ b/src/blenderbim/scripts/setup_pytest.py
@@ -18,11 +18,14 @@
import subprocess
import sys
+from pathlib import Path
py_exec = str(sys.executable)
+lib = Path(py_exec).parent.parent / "lib"
+
subprocess.call([py_exec, "-m", "ensurepip", "--user"])
-subprocess.call([py_exec, "-m", "pip", "install", "--upgrade", "pip"])
-subprocess.call([py_exec, "-m", "pip", "install", "pytest"])
-subprocess.call([py_exec, "-m", "pip", "install", "pytest-blender"])
-subprocess.call([py_exec, "-m", "pip", "install", "pytest-bdd"])
-subprocess.call([py_exec, "-m", "pip", "install", "pygments"])
+subprocess.call([py_exec, "-m", "pip", "install", "--upgrade", "pip"])
+subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pytest"])
+subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pytest-blender"])
+subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pytest-bdd"])
+subprocess.call([py_exec, "-m", "pip", "install", f"--target={str(lib)}", "pygments"])
diff --git a/src/blenderbim/test/core/test_system.py b/src/blenderbim/test/core/test_system.py
index 01c4171def..a27d0f90e5 100644
--- a/src/blenderbim/test/core/test_system.py
+++ b/src/blenderbim/test/core/test_system.py
@@ -125,7 +125,6 @@ class TestHidePorts:
system.delete_element_objects(["port"]).should_be_called()
subject.hide_ports(ifc, system, element="element")
-
def test_syncing_locations_if_objects_moved_prior_to_hiding_ports(self, ifc, system):
ifc.get_object("element").should_be_called().will_return("obj")
ifc.is_moved("obj").should_be_called().will_return(True)
diff --git a/src/blenderbim/test/tool/test_georeference.py b/src/blenderbim/test/tool/test_georeference.py
index 4dd8661e25..bddb670fbb 100644
--- a/src/blenderbim/test/tool/test_georeference.py
+++ b/src/blenderbim/test/tool/test_georeference.py
@@ -163,6 +163,7 @@ class TestGetMapConversionAttributes(NewFile):
"Scale": 6.0,
}
+
class TestGetTrueNorthAttributes(NewFile):
def test_run(self):
TestImportTrueNorth().test_run()
@@ -185,18 +186,18 @@ class TestDisableEditing(NewFile):
class TestSetCoordinates(NewFile):
def test_run(self):
- subject.set_coordinates("input", [1., 2., 3.])
+ subject.set_coordinates("input", [1.0, 2.0, 3.0])
assert bpy.context.scene.BIMGeoreferenceProperties.coordinate_input == "1.0,2.0,3.0"
- subject.set_coordinates("output", [4., 5., 6.])
+ subject.set_coordinates("output", [4.0, 5.0, 6.0])
assert bpy.context.scene.BIMGeoreferenceProperties.coordinate_output == "4.0,5.0,6.0"
class TestGetCoordinates(NewFile):
def test_run(self):
bpy.context.scene.BIMGeoreferenceProperties.coordinate_input = "1.0,2.0,3.0"
- assert subject.get_coordinates("input") == [1., 2., 3.]
+ assert subject.get_coordinates("input") == [1.0, 2.0, 3.0]
bpy.context.scene.BIMGeoreferenceProperties.coordinate_output = "4.0,5.0,6.0"
- assert subject.get_coordinates("output") == [4., 5., 6.]
+ assert subject.get_coordinates("output") == [4.0, 5.0, 6.0]
class TestGetCursorLocation(NewFile):
@@ -207,8 +208,8 @@ class TestGetCursorLocation(NewFile):
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI", name="METRE")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
- bpy.context.scene.cursor.location = (1., 2., 3.)
- assert subject.get_cursor_location() == [1000., 2000., 3000.]
+ bpy.context.scene.cursor.location = (1.0, 2.0, 3.0)
+ assert subject.get_cursor_location() == [1000.0, 2000.0, 3000.0]
class TestSetCursorLocation(NewFile):
@@ -219,8 +220,8 @@ class TestSetCursorLocation(NewFile):
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI", name="METRE")
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
- subject.set_cursor_location([1000., 2000., 3000.])
- assert list(bpy.context.scene.cursor.location) == [1., 2., 3.]
+ subject.set_cursor_location([1000.0, 2000.0, 3000.0])
+ assert list(bpy.context.scene.cursor.location) == [1.0, 2.0, 3.0]
class TestSetIfcTrueNorth(NewFile):
@@ -258,13 +259,13 @@ class TestGetMapConversion(NewFile):
class TestXyz2Enh(NewFile):
def test_run(self):
- assert subject.xyz2enh([0., 0., 0.], None) == [0., 0., 0.]
+ assert subject.xyz2enh([0.0, 0.0, 0.0], None) == [0.0, 0.0, 0.0]
def test_using_the_blender_offset(self):
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_eastings = "1.0"
- assert subject.xyz2enh([0., 0., 0.], None) == (1., 0., 0.)
+ assert subject.xyz2enh([0.0, 0.0, 0.0], None) == (1.0, 0.0, 0.0)
def test_using_the_map_conversion(self):
ifc = ifcopenshell.file()
@@ -274,7 +275,7 @@ class TestXyz2Enh(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1.0
- assert subject.xyz2enh([0., 0., 0.], map_conversion) == (1., 0., 0.)
+ assert subject.xyz2enh([0.0, 0.0, 0.0], map_conversion) == (1.0, 0.0, 0.0)
def test_applying_both_blender_offset_and_map_conversion(self):
props = bpy.context.scene.BIMGeoreferenceProperties
@@ -287,18 +288,18 @@ class TestXyz2Enh(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Northings = 1.0
- assert subject.xyz2enh([0., 0., 0.], map_conversion) == (1., 1., 0.)
+ assert subject.xyz2enh([0.0, 0.0, 0.0], map_conversion) == (1.0, 1.0, 0.0)
class TestEnh2Xyz(NewFile):
def test_run(self):
- assert subject.enh2xyz([0., 0., 0.], None) == [0., 0., 0.]
+ assert subject.enh2xyz([0.0, 0.0, 0.0], None) == [0.0, 0.0, 0.0]
def test_using_the_blender_offset(self):
props = bpy.context.scene.BIMGeoreferenceProperties
props.has_blender_offset = True
props.blender_eastings = "1.0"
- assert subject.enh2xyz([0., 0., 0.], None) == (-1., 0., 0.)
+ assert subject.enh2xyz([0.0, 0.0, 0.0], None) == (-1.0, 0.0, 0.0)
def test_using_the_map_conversion(self):
ifc = ifcopenshell.file()
@@ -308,7 +309,7 @@ class TestEnh2Xyz(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Eastings = 1.0
- assert subject.enh2xyz([0., 0., 0.], map_conversion) == (-1., 0., 0.)
+ assert subject.enh2xyz([0.0, 0.0, 0.0], map_conversion) == (-1.0, 0.0, 0.0)
def test_applying_both_blender_offset_and_map_conversion(self):
props = bpy.context.scene.BIMGeoreferenceProperties
@@ -321,7 +322,7 @@ class TestEnh2Xyz(NewFile):
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
map_conversion = ifc.by_type("IfcMapConversion")[0]
map_conversion.Northings = 1.0
- assert subject.enh2xyz([0., 0., 0.], map_conversion) == (-1., -1., 0.)
+ assert subject.enh2xyz([0.0, 0.0, 0.0], map_conversion) == (-1.0, -1.0, 0.0)
class TestSetIfcGridNorth(NewFile):
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py
index d6b234071d..5388727517 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py
@@ -102,9 +102,16 @@ class Usecase:
if value is None:
continue
if isinstance(value, ifcopenshell.entity_instance):
- nominal_value = value
if value.is_a(True) == "IFC4.IfcPropertyEnumeratedValue":
properties.append(value)
+ continue
+ else:
+ properties.append(
+ self.file.create_entity(
+ "IfcPropertySingleValue",
+ **{"Name": name, "NominalValue": value},
+ )
+ )
#TODO-The following "elif" is temporary code, will need to refactor at some point - vulevukusej
elif isinstance(value, list):
for pset_template in self.settings["pset_template"].HasPropertyTemplates:
@@ -122,6 +129,7 @@ class Usecase:
EnumerationReference=prop_enum
)
properties.append(prop_enum_value)
+ continue
else:
primary_measure_type = self.get_primary_measure_type(name, new_value=value)
value = self.cast_value_to_primary_measure_type(value, primary_measure_type)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py
index 3c3d9cc334..10061e2ec3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py
@@ -35,6 +35,7 @@ class Usecase:
"PrimaryMeasureType": "IfcLabel",
"TemplateType": "P_SINGLEVALUE",
"AccessState": "READWRITE",
+ "Enumerators": None
}
)
has_property_templates = list(self.settings["pset_template"].HasPropertyTemplates or [])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/data.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/data.py
index 2a1adda379..71b255cc41 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/data.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/data.py
@@ -49,5 +49,7 @@ class Data:
"Name": data["Name"],
"Description": data["Description"],
"PrimaryMeasureType": data["PrimaryMeasureType"],
+ "TemplateType": data["TemplateType"],
+ "Enumerators": data["Enumerators"]
}
cls.is_loaded = True
diff --git a/src/ifcopenshell-python/ifcopenshell/requirements.txt b/src/ifcopenshell-python/ifcopenshell/requirements.txt
index 29dd91befd..61483c9779 100644
--- a/src/ifcopenshell-python/ifcopenshell/requirements.txt
+++ b/src/ifcopenshell-python/ifcopenshell/requirements.txt
@@ -1,4 +1,4 @@
-lxml==4.6.5
+lxml==4.9.1
numpy==1.22.0
regex==2021.4.4
xmlschema==1.6.4
\ No newline at end of file
diff --git a/src/ifcopenshell-python/test/Sample-BIM-Files b/src/ifcopenshell-python/test/Sample-BIM-Files
index 2daf6c46ed..adadc7740b 160000
--- a/src/ifcopenshell-python/test/Sample-BIM-Files
+++ b/src/ifcopenshell-python/test/Sample-BIM-Files
@@ -1 +1 @@
-Subproject commit 2daf6c46ed9fc7a17259ef6d4c17c3553ef642cd
+Subproject commit adadc7740b7337f4cb0935ec8e84ba30ed4ca10d
diff --git a/src/ifcopenshell-python/test/test_wall_opening.py b/src/ifcopenshell-python/test/test_wall_opening.py
index 71e6c945ea..f331297e73 100644
--- a/src/ifcopenshell-python/test/test_wall_opening.py
+++ b/src/ifcopenshell-python/test/test_wall_opening.py
@@ -145,6 +145,7 @@ def create_case(fn, openings):
class TestWallOpenings:
+ @pytest.mark.skipif(shutil.which("IfcConvert") is None, reason="Requires IfcConvert in path")
def test_all(self):
cases = [
@@ -244,4 +245,4 @@ class TestWallOpenings:
if __name__ == "__main__":
- TestWallOpenings().test_all()
+ pytest.main(["-x", __file__])
diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i
index ebc76c9350..4f7f08ca9d 100644
--- a/src/ifcwrap/IfcGeomWrapper.i
+++ b/src/ifcwrap/IfcGeomWrapper.i
@@ -483,6 +483,15 @@ struct ShapeRTTI : public boost::static_visitor
}
}
+ if (!ifc_representation) {
+ if (reps->size()) {
+ // Return a random representation
+ ifc_representation = *reps->begin();
+ } else {
+ throw IfcParse::IfcException("No suitable IfcRepresentation found");
+ }
+ }
+
// Read precision for found representation's context
auto context = ifc_representation->ContextOfItems();
if (context->template as()) {
@@ -499,15 +508,6 @@ struct ShapeRTTI : public boost::static_visitor
kernel.setValue(IfcGeom::Kernel::GV_PRECISION, p);
}
- if (!ifc_representation) {
- if (reps->size()) {
- // Return a random representation
- ifc_representation = *reps->begin();
- } else {
- throw IfcParse::IfcException("No suitable IfcRepresentation found");
- }
- }
-
IfcGeom::BRepElement* brep = kernel.convert(settings, ifc_representation, product);
if (!brep) {
throw IfcParse::IfcException("Failed to process shape");
diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp
index 96738e9942..9fdfc22586 100644
--- a/src/serializers/SvgSerializer.cpp
+++ b/src/serializers/SvgSerializer.cpp
@@ -808,7 +808,7 @@ void SvgSerializer::write(const geometry_data& data) {
const std::string& ty_entity_name = ty->declaration().name();
// Damn you, IFC
if (ty_entity_name == "IfcDoorStyle" || ty_entity_name == "IfcDoorType") {
- operation_type = *((IfcUtil::IfcBaseEntity*)ty)->get("OperationType");
+ operation_type = (std::string)*((IfcUtil::IfcBaseEntity*)ty)->get("OperationType");
}
}
}