mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 10:06:47 +00:00
feat: Allow merging multiple ifc projects at once with the MergeProject recipe
This commit is contained in:
committed by
Dion Moult
parent
ec29e3d60b
commit
653b9288c0
@@ -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,
|
||||
|
||||
@@ -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"}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user