feat: Allow merging multiple ifc projects at once with the MergeProject recipe

This commit is contained in:
Blender Defender
2024-07-30 12:41:05 +02:00
committed by Dion Moult
parent ec29e3d60b
commit 653b9288c0
7 changed files with 64 additions and 34 deletions
+1 -1
View File
@@ -121,6 +121,7 @@ classes = [
prop.StrProperty,
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
prop.ObjProperty,
prop.MultipleFileSelect,
prop.Attribute,
prop.BIMAreaProperties,
prop.BIMTabProperties,
@@ -133,7 +134,6 @@ classes = [
prop.BIMMeshProperties,
prop.BIMFacet,
prop.BIMFilterGroup,
prop.MultipleFileSelect,
ui.BIM_UL_clipping_plane,
ui.BIM_UL_generic,
ui.BIM_UL_topics,
+2
View File
@@ -50,6 +50,8 @@ def draw_attribute(attribute, layout, copy_operator=None):
return
if value_name == "enum_value":
prop_with_search(layout, attribute, "enum_value", text=attribute.name)
elif value_name == "filepath_value":
attribute.filepath_value.layout_file_select(layout, filter_glob=attribute.filter_glob, text=attribute.name)
elif attribute.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]:
propis = bpy.context.scene.BIMWorkScheduleProperties
for item in propis.durations_attributes:
@@ -122,9 +122,14 @@ class UpdateIfcPatchArguments(bpy.types.Operator):
new_attr = patch_args.add()
data_type = arg_info.get("type", "str")
if isinstance(data_type, list):
if "file" in data_type:
data_type = ["file"]
data_type = [dt for dt in data_type if dt != "NoneType"][0]
new_attr.data_type = {
"Literal": "enum",
"file": "file",
"str": "string",
"float": "float",
"int": "integer",
@@ -136,6 +141,11 @@ class UpdateIfcPatchArguments(bpy.types.Operator):
new_attr.enum_value = arg_info.get("default", new_attr.get_value_default())
continue
if new_attr.data_type == "file":
new_attr.filepath_value.single_file = arg_info.get("default", new_attr.get_value_default())
new_attr.filter_glob = arg_info.get("filter_glob", "*.ifc;*.ifczip;*.ifcxml")
continue
new_attr.set_value(arg_info.get("default", new_attr.get_value_default()))
return {"FINISHED"}
+1 -1
View File
@@ -147,7 +147,7 @@ class BIM_OT_multiple_file_selector(bpy.types.Operator):
"""Open Blender's file explorer to select one or multiple files."""
bl_idname = "bim.multiple_file_selector"
bl_label = "Select Multiple Files"
bl_label = "Select File(s)"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
files: bpy.props.CollectionProperty(name="File Path", type=bpy.types.OperatorFileListElement)
+36 -28
View File
@@ -156,6 +156,34 @@ class ObjProperty(PropertyGroup):
obj: bpy.props.PointerProperty(type=bpy.types.Object)
def update_single_file(self, context):
self.file_list.clear()
new = self.file_list.add()
new.name = self.single_file
class MultipleFileSelect(PropertyGroup):
single_file: bpy.props.StringProperty(name="Single File Path", description="", update=update_single_file)
file_list: bpy.props.CollectionProperty(type=StrProperty)
def set_file_list(self, dirname: str, files: list[str]):
self.file_list.clear()
for f in files:
new = self.file_list.add()
new.name = os.path.join(dirname, f)
def layout_file_select(self, layout, filter_glob="", text=""):
if len(self.file_list) > 1:
layout.label(text=f"{len(self.file_list)} Files Selected")
else:
layout.prop(self, "single_file", text=text)
layout.context_pointer_set("file_props", self)
op = layout.operator("bim.multiple_file_selector", icon="FILE_FOLDER", text="")
op.filter_glob = filter_glob
def update_attribute_value(self, context):
value_name = self.get_value_name()
if value_name:
@@ -248,6 +276,8 @@ class Attribute(PropertyGroup):
enum_items: StringProperty(name="Value")
enum_descriptions: CollectionProperty(type=StrProperty)
enum_value: EnumProperty(items=get_attribute_enum_values, name="Value", update=update_attribute_value)
filepath_value: PointerProperty(type=MultipleFileSelect)
filter_glob: StringProperty()
is_null: BoolProperty(name="Is Null", update=update_is_null)
is_optional: BoolProperty(name="Is Optional")
is_uri: BoolProperty(name="Is Uri", default=False)
@@ -263,6 +293,8 @@ class Attribute(PropertyGroup):
return None
if self.data_type == "string":
return self.string_value.replace("\\n", "\n")
if self.data_type == "file":
return [f.name for f in self.filepath_value.file_list]
return getattr(self, str(self.get_value_name()), None)
def get_value_default(self):
@@ -276,6 +308,8 @@ class Attribute(PropertyGroup):
return False
elif self.data_type == "enum":
return "0"
elif self.data_type == "file":
return ""
def get_value_name(self, display_only=False):
if self.data_type == "string":
@@ -290,6 +324,8 @@ class Attribute(PropertyGroup):
return "float_value"
elif self.data_type == "enum":
return "enum_value"
elif self.data_type == "file":
return "filepath_value"
def set_value(self, value):
if isinstance(value, str):
@@ -478,31 +514,3 @@ class BIMFacet(PropertyGroup):
class BIMFilterGroup(PropertyGroup):
filters: CollectionProperty(type=BIMFacet, name="filters")
def update_single_file(self, context):
self.file_list.clear()
new = self.file_list.add()
new.name = self.single_file
class MultipleFileSelect(bpy.types.PropertyGroup):
single_file: bpy.props.StringProperty(name="Single File Path", description="", update=update_single_file)
file_list: bpy.props.CollectionProperty(type=StrProperty)
def set_file_list(self, dirname: str, files: list[str]):
self.file_list.clear()
for f in files:
new = self.file_list.add()
new.name = os.path.join(dirname, f)
def layout_file_select(self, layout, filter_glob="", text=""):
if len(self.file_list) > 1:
layout.label(text=f"{len(self.file_list)} Files Selected")
else:
layout.prop(self, "single_file", text=text)
layout.context_pointer_set("file_props", self)
op = layout.operator("bim.multiple_file_selector", icon="FILE_FOLDER", text="")
op.filter_glob = filter_glob
+4
View File
@@ -177,6 +177,10 @@ def _extract_docs(cls, method_name, boilerplate_args):
param_name = line.split(":")[1].strip().replace("param ", "")
if param_name in inputs:
inputs[param_name]["description"] = line.split(":")[2].strip()
elif line.startswith(":filter_glob"):
param_name = line.split(":")[1].strip().replace("filter_glob ", "")
if param_name in inputs:
inputs[param_name]["filter_glob"] = line.split(":")[2].strip()
elif i == 2:
description += line
elif i > 2:
+10 -4
View File
@@ -40,6 +40,8 @@ class Patcher:
:param filepath: The filepath of the second IFC model to merge into the
first. The first model is already specified as the input to
IfcPatch.
:type filepath: Union[str, ifcopenshell.file]
:filter_glob filepath: *.ifc;*.ifczip;*.ifcxml
Example:
@@ -53,11 +55,15 @@ class Patcher:
self.filepath = filepath
def patch(self):
if isinstance(self.filepath, ifcopenshell.file):
other = self.filepath
else:
other = ifcopenshell.open(self.filepath)
for filepath in self.filepath:
if isinstance(filepath, ifcopenshell.file):
other = filepath
else:
other = ifcopenshell.open(filepath)
self.merge(other)
def merge(self, other):
if (main_unit := self.get_unit_name(self.file)) != self.get_unit_name(other):
other = ifcopenshell.util.unit.convert_file_length_units(other, main_unit)