mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Closes #7765: Default IFC file path to relative and auto-convert on blend save
Previously, saving an IFC file stored an absolute path in `bim_props.ifc_file` by default. Users had to manually check "Use Relative Path" each time. This made projects less portable — moving or sharing a project folder broke the stored path, requiring manual relinking. What changed: - `use_relative_project_path` now defaults to `True` in `BIMProjectProperties`, `LoadProject`, `ExportIFC`, and `SelectIfcFile`. Relative paths are opt-out rather than opt-in. - Added a `save_post` handler in `handler.py` (registered in `__init__.py`) that fires whenever the user saves the `.blend` file. If `use_relative_project_path` is enabled and the stored `ifc_file` path is still absolute, it converts it to a path relative to the blend file's directory. `IfcStore.path` is kept absolute internally so file loading continues to work correctly. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -271,6 +271,7 @@ def register():
|
||||
parametric_lifecycle.install_parametric_lifecycle_handlers()
|
||||
bpy.app.handlers.load_post.append(handler.load_post)
|
||||
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
||||
bpy.app.handlers.save_post.append(handler.save_post)
|
||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||
bpy.types.Scene.BIMSnapProperties = bpy.props.PointerProperty(type=prop.BIMSnapProperties)
|
||||
bpy.types.Scene.BIMSnapGroups = bpy.props.PointerProperty(type=prop.BIMSnapGroups)
|
||||
@@ -329,6 +330,7 @@ def unregister():
|
||||
parametric_lifecycle.uninstall_parametric_lifecycle_handlers()
|
||||
bpy.app.handlers.load_post.remove(handler.load_post)
|
||||
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
|
||||
bpy.app.handlers.save_post.remove(handler.save_post)
|
||||
del bpy.types.Scene.BIMProperties
|
||||
del bpy.types.Collection.BIMCollectionProperties
|
||||
del bpy.types.Object.BIMObjectProperties
|
||||
|
||||
@@ -432,6 +432,25 @@ def subscribe_to_viewport_shading_changes():
|
||||
)
|
||||
|
||||
|
||||
@persistent
|
||||
def save_post(scene) -> None:
|
||||
"""After saving the .blend file, convert the stored IFC path to relative if enabled."""
|
||||
pprops = tool.Project.get_project_props()
|
||||
if not pprops.use_relative_project_path:
|
||||
return
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
ifc_path = bim_props.ifc_file
|
||||
if not ifc_path or not os.path.isabs(ifc_path):
|
||||
return
|
||||
blend_dir = bpy.path.abspath("//")
|
||||
if not blend_dir:
|
||||
return
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
rel_path = os.path.relpath(ifc_path, blend_dir)
|
||||
bim_props.ifc_file = rel_path
|
||||
IfcStore.set_path(ifc_path) # keep IfcStore.path absolute for loading
|
||||
|
||||
|
||||
def _apply_save_file_invariants(scene: bpy.types.Scene) -> None:
|
||||
"""Invariants enforced on every load_post: msgbus subscription, IFC owner
|
||||
settings, scene-bound caches, load-transient parametric state, and the
|
||||
|
||||
@@ -964,7 +964,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path",
|
||||
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
||||
default=False,
|
||||
default=True,
|
||||
)
|
||||
should_start_fresh_session: bpy.props.BoolProperty(
|
||||
name="Should Start Fresh Session",
|
||||
@@ -1875,7 +1875,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
||||
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
|
||||
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
|
||||
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
filter_glob: str
|
||||
|
||||
@@ -444,7 +444,7 @@ class BIMProjectProperties(PropertyGroup):
|
||||
items=get_parent_libaries,
|
||||
)
|
||||
|
||||
use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False)
|
||||
use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=True)
|
||||
should_save_metadata_for_this_file: BoolProperty(
|
||||
name="Save Session Data for This File",
|
||||
description="Enable saving session data (window layout, settings) to a metadata blend file for this specific IFC file",
|
||||
|
||||
@@ -219,7 +219,7 @@ class SelectIfcFile(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = f"Select a different IFC file.\n{tool.Blender.operator_invoke_filepath_hotkeys_description}"
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
filename_ext = ".ifc"
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
Reference in New Issue
Block a user