mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 10:33:20 +00:00
Fix #6307. Warning: critical bug where saving a file broke undo state.
Previously, changing props.ifc_file had an update hook to reload information about the IFC model. But this isn't always correct because there are two situations: 1. The ifc_file path changed because you saved the file for the first time or saved as, and this is merely recording a new saved path of the existing file object. 2. The ifc_file path changed because the user manually changed it or selected a new file. This could reference an entirely new file object. This is dangerous because we can't trust anything anymore, including our undo history. So the new default situation is that there is no magic hook. If you change props.ifc_file, that's all it changes ... just a path stored in Blender with not much significance. If the user runs select_ifc_file to explicitly relink the file, it now explicitly purges in that situation and clears the undo history. Basically now behaviour is explicit, not using magic hooks.
This commit is contained in:
@@ -24,17 +24,15 @@ import ifcopenshell.util.unit
|
||||
import ifcopenshell.api.owner.settings
|
||||
import bonsai.bim
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.owner as core_owner
|
||||
from bpy.app.handlers import persistent
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.owner.prop import get_user_person, get_user_organisation
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
|
||||
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
from bonsai.bim.module.model.decorator import WallAxisDecorator, SlabDirectionDecorator
|
||||
from bonsai.bim.module.nest.decorator import NestDecorator
|
||||
from mathutils import Vector
|
||||
from math import cos, degrees
|
||||
from math import cos
|
||||
from typing import Union, Callable
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import bonsai.bim.handler
|
||||
import bonsai.tool as tool
|
||||
from pathlib import Path
|
||||
from bonsai.tool.brick import BrickStore
|
||||
from typing import Set, Union, Optional, TypedDict, Callable, NotRequired, cast
|
||||
from typing import Set, Union, Optional, TypedDict, Callable, NotRequired
|
||||
|
||||
|
||||
IFC_CONNECTED_TYPE = Union[bpy.types.Material, bpy.types.Object]
|
||||
@@ -106,10 +106,7 @@ class IfcStore:
|
||||
def get_file():
|
||||
if IfcStore.file is None:
|
||||
props = tool.Blender.get_bim_props()
|
||||
IfcStore.path = props.ifc_file
|
||||
# Interpret relative paths as relative to .blend file.
|
||||
if IfcStore.path and not os.path.isabs(IfcStore.path):
|
||||
IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path))
|
||||
IfcStore.set_path(props.ifc_file)
|
||||
if IfcStore.path:
|
||||
try:
|
||||
IfcStore.load_file(IfcStore.path)
|
||||
@@ -117,6 +114,13 @@ class IfcStore:
|
||||
print(f"Failed to load file {IfcStore.path}. Error details: {e}")
|
||||
return IfcStore.file
|
||||
|
||||
@staticmethod
|
||||
def set_path(value):
|
||||
IfcStore.path = value
|
||||
# Interpret relative paths as relative to .blend file.
|
||||
if IfcStore.path and not os.path.isabs(IfcStore.path):
|
||||
IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path))
|
||||
|
||||
@staticmethod
|
||||
def get_cache():
|
||||
if IfcStore.cache is None and IfcStore.path:
|
||||
|
||||
@@ -985,8 +985,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
||||
if not self.is_advanced and not self.should_start_fresh_session:
|
||||
bpy.ops.bim.convert_to_blender()
|
||||
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
bim_props.ifc_file = filepath
|
||||
tool.Ifc.set_path(filepath)
|
||||
if not tool.Ifc.get():
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
@@ -1650,7 +1649,7 @@ class ExportIFC(bpy.types.Operator):
|
||||
output_file = os.path.relpath(output_file, bpy.path.abspath("//"))
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if bim_props.ifc_file != output_file and extension not in ("ifczip", "ifcjson"):
|
||||
bim_props.ifc_file = output_file
|
||||
tool.Ifc.set_path(output_file)
|
||||
save_blend_file = bool(bpy.data.is_saved and bpy.data.is_dirty and bpy.data.filepath)
|
||||
if save_blend_file:
|
||||
bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath)
|
||||
|
||||
@@ -30,6 +30,7 @@ import webbrowser
|
||||
import ifcopenshell
|
||||
import bonsai.bim
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.handler
|
||||
from bonsai.bim import import_ifc
|
||||
from bonsai.bim.prop import StrProperty
|
||||
from bonsai.bim.ui import IFCFileSelector
|
||||
@@ -222,6 +223,8 @@ class SelectIfcFile(bpy.types.Operator, IFCFileSelector):
|
||||
if self.is_existing_ifc_file():
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.ifc_file = self.get_filepath()
|
||||
bonsai.bim.handler.loadIfcStore(bpy.context.scene)
|
||||
tool.Blender.clear_undo_history()
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
@@ -137,12 +137,6 @@ def update_cache_dir(self: "BIMProperties", context: bpy.types.Context) -> None:
|
||||
bonsai.bim.schema.ifc.cache_dir = bim_props.cache_dir
|
||||
|
||||
|
||||
def update_ifc_file(self: "BIMProperties", context: bpy.types.Context) -> None:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if bim_props.ifc_file:
|
||||
bonsai.bim.handler.loadIfcStore(context.scene)
|
||||
|
||||
|
||||
def update_section_color(self: "BIMProperties", context: bpy.types.Context) -> None:
|
||||
section_node_group = bpy.data.node_groups.get("Section Override")
|
||||
if section_node_group is None:
|
||||
@@ -506,7 +500,7 @@ class BIMProperties(PropertyGroup):
|
||||
)
|
||||
has_blend_warning: BoolProperty(name="Has Blend Warning", default=False)
|
||||
pset_dir: StringProperty(default=os.path.join("psets") + os.path.sep, name="Default Psets Directory")
|
||||
ifc_file: StringProperty(name="IFC File", update=update_ifc_file)
|
||||
ifc_file: StringProperty(name="IFC File")
|
||||
last_transaction: StringProperty(name="Last Transaction")
|
||||
should_section_selected_objects: BoolProperty(name="Section Selected Objects", default=False)
|
||||
section_plane_colour: FloatVectorProperty(
|
||||
|
||||
@@ -1616,3 +1616,16 @@ class Blender(bonsai.core.tool.Blender):
|
||||
if 0 <= index < len(collection):
|
||||
return collection[index]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def clear_undo_history(cls) -> None:
|
||||
"""Clears the Blender history, Bonsai history, and IfcOpenShell history"""
|
||||
old_undo_steps = bpy.context.preferences.edit.undo_steps
|
||||
bpy.context.preferences.edit.undo_steps = 2
|
||||
for i in range(3):
|
||||
bpy.ops.ed.undo_push(message="Undo history cleared")
|
||||
bpy.context.preferences.edit.undo_steps = old_undo_steps
|
||||
tool.Ifc.clear_history()
|
||||
old_history_size = tool.Ifc.get().history_size
|
||||
tool.Ifc.get().set_history_size(0)
|
||||
tool.Ifc.get().set_history_size(old_history_size)
|
||||
|
||||
@@ -56,6 +56,12 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
def get(cls) -> ifcopenshell.file:
|
||||
return IfcStore.get_file()
|
||||
|
||||
@classmethod
|
||||
def set_path(cls, value: str) -> None:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
bim_props.ifc_file = value
|
||||
IfcStore.set_path(value)
|
||||
|
||||
@classmethod
|
||||
def get_path(cls) -> str:
|
||||
"""Get absolute filepath to the IFC file, return empty string if file is not saved."""
|
||||
@@ -66,6 +72,12 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
if IfcStore.get_file():
|
||||
return IfcStore.get_file().schema
|
||||
|
||||
@classmethod
|
||||
def clear_history(cls) -> None:
|
||||
IfcStore.last_transaction = ""
|
||||
IfcStore.history = []
|
||||
IfcStore.future = []
|
||||
|
||||
@classmethod
|
||||
def is_edited(cls, obj: bpy.types.Object, *, ignore_scale: bool = False) -> bool:
|
||||
return (not ignore_scale and tool.Geometry.is_scaled(obj)) or obj in IfcStore.edited_objs
|
||||
|
||||
Reference in New Issue
Block a user