project.operator - use pathlib

This commit is contained in:
Andrej730
2024-09-30 12:09:13 +05:00
parent 45ae7634a8
commit e6e6bd841c
3 changed files with 57 additions and 52 deletions
@@ -54,7 +54,7 @@ from bonsai.bim.module.project.data import LinksData
from bonsai.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator, MeasureDecorator from bonsai.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator, MeasureDecorator
from bonsai.bim.module.model.decorator import PolylineDecorator from bonsai.bim.module.model.decorator import PolylineDecorator
from bonsai.bim.module.model.polyline import PolylineOperator from bonsai.bim.module.model.polyline import PolylineOperator
from typing import Union from typing import Union, TYPE_CHECKING
class NewProject(bpy.types.Operator): class NewProject(bpy.types.Operator):
@@ -933,6 +933,11 @@ class LinkIfc(bpy.types.Operator):
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False) use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
if TYPE_CHECKING:
filepath: str
files: list[bpy.types.OperatorFileListElement]
directory: str
def draw(self, context): def draw(self, context):
pprops = context.scene.BIMProjectProperties pprops = context.scene.BIMProjectProperties
row = self.layout.row() row = self.layout.row()
@@ -949,20 +954,20 @@ class LinkIfc(bpy.types.Operator):
def execute(self, context): def execute(self, context):
start = time.time() start = time.time()
files = [f.name.replace("\\", "/") for f in self.files] if self.files else [self.filepath.replace("\\", "/")] files = [f.name for f in self.files] if self.files else [self.filepath]
for filename in files: for filename in files:
filepath = os.path.join(self.directory, filename).replace("\\", "/") filepath = Path(self.directory) / filename
if bpy.data.filepath and Path(filepath).samefile(bpy.data.filepath): if bpy.data.filepath and filepath.samefile(bpy.data.filepath):
self.report({"INFO"}, "Can't link the current .blend file") self.report({"INFO"}, "Can't link the current .blend file")
continue continue
new = context.scene.BIMProjectProperties.links.add() new = context.scene.BIMProjectProperties.links.add()
if self.use_relative_path: if self.use_relative_path:
try: try:
filepath = os.path.relpath(filepath, bpy.path.abspath("//")).replace("\\", "/") filepath = filepath.relative_to(bpy.path.abspath("//"))
except: except:
pass # Perhaps on another drive or something pass # Perhaps on another drive or something
new.name = filepath new.name = filepath.as_posix()
status = bpy.ops.bim.load_link(filepath=filepath, use_cache=self.use_cache) status = bpy.ops.bim.load_link(filepath=filepath.as_posix(), use_cache=self.use_cache)
if status == {"CANCELLED"}: if status == {"CANCELLED"}:
error_msg = ( error_msg = (
f'Error processing IFC file "{self.filepath}" ' f'Error processing IFC file "{self.filepath}" '
@@ -988,9 +993,9 @@ class UnlinkIfc(bpy.types.Operator):
filepath: bpy.props.StringProperty() filepath: bpy.props.StringProperty()
def execute(self, context): def execute(self, context):
self.filepath = self.filepath.replace("\\", "/") filepath = Path(self.filepath).as_posix()
bpy.ops.bim.unload_link(filepath=self.filepath) bpy.ops.bim.unload_link(filepath=filepath)
index = context.scene.BIMProjectProperties.links.find(self.filepath) index = context.scene.BIMProjectProperties.links.find(filepath)
if index != -1: if index != -1:
context.scene.BIMProjectProperties.links.remove(index) context.scene.BIMProjectProperties.links.remove(index)
return {"FINISHED"} return {"FINISHED"}
@@ -1004,11 +1009,7 @@ class UnloadLink(bpy.types.Operator):
filepath: bpy.props.StringProperty() filepath: bpy.props.StringProperty()
def execute(self, context): def execute(self, context):
self.filepath = self.filepath.replace("\\", "/") filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.filepath))
filepath = self.filepath
if not os.path.isabs(filepath):
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath))
filepath = Path(filepath)
if filepath.suffix.lower() == ".ifc": if filepath.suffix.lower() == ".ifc":
filepath = filepath.with_suffix(".ifc.cache.blend") filepath = filepath.with_suffix(".ifc.cache.blend")
@@ -1046,25 +1047,25 @@ class LoadLink(bpy.types.Operator):
filepath: bpy.props.StringProperty() filepath: bpy.props.StringProperty()
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
filepath_: Path
def execute(self, context): def execute(self, context):
self.filepath = self.filepath.replace("\\", "/") filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.filepath))
filepath = self.filepath self.filepath_ = filepath
if not os.path.isabs(filepath): if filepath.suffix.lower().endswith(".blend"):
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath)).replace("\\", "/")
if self.filepath.lower().endswith(".blend"):
self.link_blend(filepath) self.link_blend(filepath)
elif self.filepath.lower().endswith(".ifc"): elif filepath.suffix.lower().endswith(".ifc"):
status = self.link_ifc() status = self.link_ifc()
if status: if status:
return status return status
return {"FINISHED"} return {"FINISHED"}
def link_blend(self, filepath: str) -> None: def link_blend(self, filepath: Path) -> None:
with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to): with bpy.data.libraries.load(str(filepath), link=True) as (data_from, data_to):
data_to.scenes = data_from.scenes data_to.scenes = data_from.scenes
link = bpy.context.scene.BIMProjectProperties.links.get(self.filepath) link = bpy.context.scene.BIMProjectProperties.links.get(self.filepath_.as_posix())
for scene in bpy.data.scenes: for scene in bpy.data.scenes:
if not scene.library or scene.library.filepath.replace("\\", "/") != filepath: if not scene.library or Path(scene.library.filepath) != filepath:
continue continue
for child in scene.collection.children: for child in scene.collection.children:
if "IfcProject" not in child.name: if "IfcProject" not in child.name:
@@ -1080,13 +1081,13 @@ class LoadLink(bpy.types.Operator):
tool.Blender.select_and_activate_single_object(bpy.context, empty) tool.Blender.select_and_activate_single_object(bpy.context, empty)
def link_ifc(self) -> Union[set[str], None]: def link_ifc(self) -> Union[set[str], None]:
blend_filepath = self.filepath + ".cache.blend" blend_filepath = self.filepath_.with_suffix(".ifc.cache.blend")
h5_filepath = self.filepath + ".cache.h5" h5_filepath = self.filepath_.with_suffix(".ifc.cache.h5")
if not self.use_cache and os.path.exists(blend_filepath): if not self.use_cache and blend_filepath.exists():
os.remove(blend_filepath) os.remove(blend_filepath)
if not os.path.exists(blend_filepath): if not blend_filepath.exists():
pprops = bpy.context.scene.BIMProjectProperties pprops = bpy.context.scene.BIMProjectProperties
gprops = bpy.context.scene.BIMGeoreferenceProperties gprops = bpy.context.scene.BIMGeoreferenceProperties
@@ -1111,7 +1112,7 @@ def run():
pprops.false_origin = "{pprops.false_origin}" pprops.false_origin = "{pprops.false_origin}"
pprops.project_north = "{pprops.project_north}" pprops.project_north = "{pprops.project_north}"
bpy.ops.bim.load_linked_project(filepath="{self.filepath}") bpy.ops.bim.load_linked_project(filepath="{self.filepath}")
bpy.ops.wm.save_as_mainfile(filepath="{blend_filepath}") bpy.ops.wm.save_as_mainfile(filepath="{blend_filepath.as_posix()}")
try: try:
run() run()
@@ -1127,19 +1128,19 @@ except Exception as e:
run = subprocess.run([bpy.app.binary_path, "-b", "--python", temp_file.name, "--python-exit-code", "1"]) run = subprocess.run([bpy.app.binary_path, "-b", "--python", temp_file.name, "--python-exit-code", "1"])
if run.returncode == 1: if run.returncode == 1:
print("An error occurred while processing your IFC.") print("An error occurred while processing your IFC.")
if not os.path.exists(blend_filepath) or os.stat(blend_filepath).st_mtime < t: if not blend_filepath.exists() or blend_filepath.stat().st_mtime < t:
return {"CANCELLED"} return {"CANCELLED"}
self.set_model_origin_from_link() self.set_model_origin_from_link()
self.link_blend(blend_filepath) self.link_blend(blend_filepath)
def set_model_origin_from_link(self): def set_model_origin_from_link(self) -> None:
if tool.Ifc.get(): if tool.Ifc.get():
return # The current model's coordinates always take priority. return # The current model's coordinates always take priority.
json_filepath = self.filepath + ".cache.json" json_filepath = self.filepath_.with_suffix(".ifc.cache.json")
if not os.path.exists(json_filepath): if not json_filepath.exists():
return return
with open(json_filepath, "r") as f: with open(json_filepath, "r") as f:
@@ -1159,15 +1160,16 @@ class ReloadLink(bpy.types.Operator):
filepath: bpy.props.StringProperty() filepath: bpy.props.StringProperty()
def execute(self, context): def execute(self, context):
def get_linked_ifcs(): filepath = Path(self.filepath)
selected_filename = os.path.basename(self.filepath.replace("\\", "/"))
return [ def get_linked_ifcs() -> set[bpy.types.Library]:
return {
c.library c.library
for c in bpy.data.collections for c in bpy.data.collections
if "IfcProject" in c.name and c.library and os.path.basename(c.library.filepath) == selected_filename if "IfcProject" in c.name and c.library and Path(c.library.filepath) == filepath
] }
for library in get_linked_ifcs() or []: for library in get_linked_ifcs():
library.reload() library.reload()
return {"FINISHED"} return {"FINISHED"}
@@ -1182,9 +1184,7 @@ class ToggleLinkSelectability(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMProjectProperties props = context.scene.BIMProjectProperties
link = props.links.get(self.link) link = props.links.get(self.link)
if not os.path.isabs(self.link): self.library_filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.link).with_suffix(".ifc.cache.blend"))
self.link = os.path.abspath(os.path.join(bpy.path.abspath("//"), self.link))
self.library_filepath = Path(self.link).with_suffix(".ifc.cache.blend")
for collection in self.get_linked_collections(): for collection in self.get_linked_collections():
collection.hide_select = not collection.hide_select collection.hide_select = not collection.hide_select
link.is_selectable = not collection.hide_select link.is_selectable = not collection.hide_select
@@ -1209,9 +1209,7 @@ class ToggleLinkVisibility(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMProjectProperties props = context.scene.BIMProjectProperties
link = props.links.get(self.link) link = props.links.get(self.link)
if not os.path.isabs(self.link): self.library_filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.link).with_suffix(".ifc.cache.blend"))
self.link = os.path.abspath(os.path.join(bpy.path.abspath("//"), self.link))
self.library_filepath = Path(self.link).with_suffix(".ifc.cache.blend")
if self.mode == "WIREFRAME": if self.mode == "WIREFRAME":
self.toggle_wireframe(link) self.toggle_wireframe(link)
elif self.mode == "VISIBLE": elif self.mode == "VISIBLE":
@@ -1298,10 +1296,8 @@ class ExportIFCBase:
return {"FINISHED"} return {"FINISHED"}
self.save_as_invoked = False self.save_as_invoked = False
if context.scene.BIMProperties.ifc_file and not self.should_save_as: if (filepath := context.scene.BIMProperties.ifc_file) and not self.should_save_as:
self.filepath = context.scene.BIMProperties.ifc_file self.filepath = str(tool.Blender.ensure_blender_path_is_abs(Path(filepath)))
if not os.path.isabs(self.filepath):
self.filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), self.filepath))
return self.execute(context) return self.execute(context)
if not self.filepath: if not self.filepath:
if bpy.data.is_saved: if bpy.data.is_saved:
@@ -1408,7 +1404,7 @@ class LoadLinkedProject(bpy.types.Operator):
pprops = bpy.context.scene.BIMProjectProperties pprops = bpy.context.scene.BIMProjectProperties
gprops = bpy.context.scene.BIMGeoreferenceProperties gprops = bpy.context.scene.BIMGeoreferenceProperties
self.filepath = self.filepath.replace("\\", "/") self.filepath = Path(self.filepath).as_posix()
print("Processing", self.filepath) print("Processing", self.filepath)
self.collection = bpy.data.collections.new("IfcProject/" + os.path.basename(self.filepath)) self.collection = bpy.data.collections.new("IfcProject/" + os.path.basename(self.filepath))
+4 -1
View File
@@ -106,7 +106,10 @@ class FilterCategory(PropertyGroup):
class Link(PropertyGroup): class Link(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(
name="Name",
description="Filepath to linked .ifc file, stored in posix format (could be relative to .blend file, not to .ifc)",
)
is_loaded: BoolProperty(name="Is Loaded", default=False) is_loaded: BoolProperty(name="Is Loaded", default=False)
is_selectable: BoolProperty(name="Is Selectable", default=True) is_selectable: BoolProperty(name="Is Selectable", default=True)
is_wireframe: BoolProperty(name="Is Wireframe", default=False) is_wireframe: BoolProperty(name="Is Wireframe", default=False)
+6
View File
@@ -414,6 +414,12 @@ class Blender(bonsai.core.tool.Blender):
return path.as_posix() return path.as_posix()
@classmethod
def ensure_blender_path_is_abs(cls, blender_path: Path) -> Path:
if blender_path.is_absolute():
return blender_path
return bpy.path.abspath("//") / blender_path
@classmethod @classmethod
def get_default_selection_keypmap(cls) -> tuple: def get_default_selection_keypmap(cls) -> tuple:
"""keymap to replicate default blender selection behaviour with click and box selection""" """keymap to replicate default blender selection behaviour with click and box selection"""