User 'data' folder #5564

Bonsai now creates a user data folder that allows you overriding Bonsai data files. E.g. you can put you own `default.css` as `data/assets/default.css` in that folder and it will override the `default.css` Bonsai is using for the drawings. Previously you could have change internal `default.css` but Bonsai would always restore the original one on every update / reinstallation.

You can also store your .ifc files in data/libraries or data/templates and Bonsai will ensure they're loaded and you won't need to worry about Bonsai removing them later.

E.g. on Windows this path is `C:\Users\xxx\AppData\Roaming\bonsai\bonsai\data`/

You can also find path in the preferences - https://i.imgur.com/kwMc0JU.png
This commit is contained in:
Andrej730
2025-01-20 18:46:53 +05:00
parent c08d25de42
commit 5e930972d6
22 changed files with 86 additions and 67 deletions
+1 -1
View File
@@ -616,7 +616,7 @@ class AddBcfViewpoint(bpy.types.Operator):
old_file_format = blender_render.image_settings.file_format old_file_format = blender_render.image_settings.file_format
blender_render.image_settings.file_format = "PNG" blender_render.image_settings.file_format = "PNG"
old_filepath = blender_render.filepath old_filepath = blender_render.filepath
blender_render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png") blender_render.filepath = tool.Blender.get_data_dir_path("snapshot.png").__str__()
bpy.ops.render.opengl(write_still=True) bpy.ops.render.opengl(write_still=True)
with open(blender_render.filepath, "rb") as f: with open(blender_render.filepath, "rb") as f:
snapshot = f.read() snapshot = f.read()
@@ -266,7 +266,7 @@ class ExecuteIfcClash(bpy.types.Operator):
context.scene.render.resolution_x = 480 context.scene.render.resolution_x = 480
context.scene.render.resolution_y = 270 context.scene.render.resolution_y = 270
context.scene.render.image_settings.file_format = "PNG" context.scene.render.image_settings.file_format = "PNG"
context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png") context.scene.render.filepath = tool.Blender.get_data_dir_path("shapshot.png").__str__()
bpy.ops.render.opengl(write_still=True) bpy.ops.render.opengl(write_still=True)
with open(context.scene.render.filepath, "rb") as f: with open(context.scene.render.filepath, "rb") as f:
return ("snapshot.png", f.read()) return ("snapshot.png", f.read())
@@ -22,6 +22,7 @@ import math
import bmesh import bmesh
import bonsai.tool as tool import bonsai.tool as tool
import ifcopenshell.util.element import ifcopenshell.util.element
from pathlib import Path
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from typing import Optional from typing import Optional
@@ -43,7 +44,7 @@ class Annotator:
font = bpy.data.fonts.get("OpenGost TypeB TT") font = bpy.data.fonts.get("OpenGost TypeB TT")
if not font: if not font:
font = bpy.data.fonts.load( font = bpy.data.fonts.load(
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf") tool.Blender.get_data_dir_path(Path("fonts") / "OpenGost Type B TT.ttf").__str__()
) )
font.name = "OpenGost Type B TT" font.name = "OpenGost Type B TT"
obj.data.font = font obj.data.font = font
+1 -2
View File
@@ -82,8 +82,7 @@ class SheetsData:
@classmethod @classmethod
def titleblocks(cls): def titleblocks(cls):
files = Path(os.path.join(bpy.context.scene.BIMProperties.data_dir, "templates", "titleblocks")).glob("*.svg") files = [p.stem for p in tool.Blender.get_data_dir_paths(Path("templates") / "titleblocks", "*.svg")]
files = [str(f.stem) for f in files]
if tool.Ifc.get(): if tool.Ifc.get():
project = tool.Ifc.get().by_type("IfcProject")[0] project = tool.Ifc.get().by_type("IfcProject")[0]
@@ -28,6 +28,7 @@ import ifcopenshell.util.element
import ifcopenshell.util.unit import ifcopenshell.util.unit
import bonsai.tool as tool import bonsai.tool as tool
import bonsai.bim.module.drawing.helper as helper import bonsai.bim.module.drawing.helper as helper
from pathlib import Path
from math import pi, sin, cos, tan, acos, atan, degrees, radians, ceil from math import pi, sin, cos, tan, acos, atan, degrees, radians, ceil
from bpy.types import SpaceView3D from bpy.types import SpaceView3D
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
@@ -2022,9 +2023,9 @@ class DecorationsHandler:
self.decorators[object_type] = self.decorators["FALL"] self.decorators[object_type] = self.decorators["FALL"]
self.decorators["MULTI_SYMBOL"] = self.decorators["SYMBOL"] self.decorators["MULTI_SYMBOL"] = self.decorators["SYMBOL"]
if drawing_font := bpy.context.scene.DocProperties.drawing_font: if drawing_font := bpy.context.scene.DocProperties.drawing_font:
drawing_font_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", drawing_font) drawing_font_path = tool.Blender.get_data_dir_path(Path("fonts") / drawing_font)
if os.path.exists(drawing_font_path): if drawing_font_path.is_file():
font_id = blf.load(drawing_font_path) font_id = blf.load(drawing_font_path.__str__())
for decorator in self.decorators.values(): for decorator in self.decorators.values():
decorator.font_id = font_id decorator.font_id = font_id
@@ -276,7 +276,6 @@ class CreateDrawing(bpy.types.Operator):
self.svg_writer = svgwriter.SvgWriter() self.svg_writer = svgwriter.SvgWriter()
self.svg_writer.human_scale = self.human_scale self.svg_writer.human_scale = self.human_scale
self.svg_writer.scale = self.scale self.svg_writer.scale = self.scale
self.svg_writer.data_dir = context.scene.BIMProperties.data_dir
self.svg_writer.camera = self.camera self.svg_writer.camera = self.camera
self.svg_writer.camera_width, self.svg_writer.camera_height = self.get_camera_dimensions() self.svg_writer.camera_width, self.svg_writer.camera_height = self.get_camera_dimensions()
self.svg_writer.camera_projection = tuple( self.svg_writer.camera_projection = tuple(
@@ -1667,7 +1666,6 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
) )
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes) tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_drawing(reference, drawing, sheet) sheet_builder.add_drawing(reference, drawing, sheet)
tool.Drawing.import_sheets() tool.Drawing.import_sheets()
@@ -1697,7 +1695,6 @@ class RemoveDrawingFromSheet(bpy.types.Operator, tool.Ifc.Operator):
sheet = tool.Drawing.get_reference_document(reference) sheet = tool.Drawing.get_reference_document(reference)
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.remove_drawing(reference, sheet) sheet_builder.remove_drawing(reference, sheet)
tool.Ifc.run("document.remove_reference", reference=reference) tool.Ifc.run("document.remove_reference", reference=reference)
@@ -1760,7 +1757,6 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0] name = os.path.splitext(os.path.basename(tool.Drawing.get_document_uri(sheet)))[0]
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = scene.BIMProperties.data_dir
references = sheet_builder.build(sheet) references = sheet_builder.build(sheet)
raster_references = [tool.Ifc.get_relative_uri(r) for r in references["RASTER"]] raster_references = [tool.Ifc.get_relative_uri(r) for r in references["RASTER"]]
@@ -2184,7 +2180,7 @@ class ReloadDrawingStyles(bpy.types.Operator):
json_path = Path(tool.Ifc.resolve_uri(rel_path)) json_path = Path(tool.Ifc.resolve_uri(rel_path))
if not json_path.exists(): if not json_path.exists():
ootb_resource = Path(context.scene.BIMProperties.data_dir) / "assets" / "shading_styles.json" ootb_resource = tool.Blender.get_data_dir_path(Path("assets") / "shading_styles.json")
print( print(
f"WARNING. Couldn't find shading_styles for the drawing by the path: {json_path}. " f"WARNING. Couldn't find shading_styles for the drawing by the path: {json_path}. "
f"Default BBIM resource will be copied from {ootb_resource}" f"Default BBIM resource will be copied from {ootb_resource}"
@@ -2582,7 +2578,6 @@ class AddScheduleToSheet(bpy.types.Operator, tool.Ifc.Operator):
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes) tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_document(reference, schedule, sheet) sheet_builder.add_document(reference, schedule, sheet)
tool.Drawing.import_sheets() tool.Drawing.import_sheets()
@@ -2649,7 +2644,6 @@ class AddReferenceToSheet(bpy.types.Operator, tool.Ifc.Operator):
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes) tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_document(reference, extref, sheet) sheet_builder.add_document(reference, extref, sheet)
tool.Drawing.import_sheets() tool.Drawing.import_sheets()
@@ -3079,7 +3073,6 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
attributes={"Location": tool.Drawing.get_default_titleblock_path(titleblock)}, attributes={"Location": tool.Drawing.get_default_titleblock_path(titleblock)},
) )
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.change_titleblock(sheet, titleblock) sheet_builder.change_titleblock(sheet, titleblock)
tool.Drawing.import_sheets() tool.Drawing.import_sheets()
@@ -22,6 +22,7 @@ import bpy
import string import string
import svgwrite import svgwrite
import openpyxl import openpyxl
import bonsai.tool as tool
from bonsai.bim.module.drawing.svgwriter import SvgWriter from bonsai.bim.module.drawing.svgwriter import SvgWriter
from odf.opendocument import load as load_ods from odf.opendocument import load as load_ods
@@ -77,7 +78,7 @@ class Scheduler:
ifc_file_path = os.path.dirname(IfcStore.path) ifc_file_path = os.path.dirname(IfcStore.path)
stylesheet_path = ifc_file_path + "\\" + stylesheet_rel_path stylesheet_path = ifc_file_path + "\\" + stylesheet_rel_path
if not os.path.exists(stylesheet_path): if not os.path.exists(stylesheet_path):
stylesheet_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", "schedule.css") stylesheet_path = tool.Blender.get_data_dir_path(Path("assets") / "schedule.css")
with open(stylesheet_path, "r") as stylesheet: with open(stylesheet_path, "r") as stylesheet:
css = stylesheet.read() css = stylesheet.read()
@@ -26,6 +26,7 @@ import urllib.parse
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import bonsai.tool as tool import bonsai.tool as tool
import ifcopenshell.util.geolocation import ifcopenshell.util.geolocation
from pathlib import Path
from xml.dom import minidom from xml.dom import minidom
from mathutils import Vector from mathutils import Vector
import re import re
@@ -38,7 +39,6 @@ XLINK = "{http://www.w3.org/1999/xlink}"
class SheetBuilder: class SheetBuilder:
def __init__(self): def __init__(self):
self.data_dir = None
self.scale = "NTS" self.scale = "NTS"
def create(self, layout_path: str, titleblock_name: str) -> None: def create(self, layout_path: str, titleblock_name: str) -> None:
@@ -49,7 +49,9 @@ class SheetBuilder:
root.attrib["version"] = "1.1" root.attrib["version"] = "1.1"
sheet_dir = os.path.dirname(layout_path) sheet_dir = os.path.dirname(layout_path)
ootb_titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg") ootb_titleblock_path = tool.Blender.get_data_dir_path(
Path("templates") / "titleblock" / (titleblock_name + ".svg")
)
titleblock_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_titleblock_path(titleblock_name)) titleblock_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_titleblock_path(titleblock_name))
os.makedirs(sheet_dir, exist_ok=True) os.makedirs(sheet_dir, exist_ok=True)
@@ -241,7 +243,7 @@ class SheetBuilder:
title_path = os.path.join(layout_dir, "assets", "view-title.svg") title_path = os.path.join(layout_dir, "assets", "view-title.svg")
os.makedirs(os.path.dirname(title_path), exist_ok=True) os.makedirs(os.path.dirname(title_path), exist_ok=True)
if not os.path.exists(title_path): if not os.path.exists(title_path):
ootb_title = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", "view-title.svg") ootb_title = tool.Blender.get_data_dir_path(Path("assets") / "view-title.svg")
shutil.copy(ootb_title, title_path) shutil.copy(ootb_title, title_path)
title_tree = ET.parse(title_path) title_tree = ET.parse(title_path)
@@ -492,7 +494,9 @@ class SheetBuilder:
return group return group
def change_titleblock(self, sheet: ifcopenshell.entity_instance, titleblock_name: str) -> None: def change_titleblock(self, sheet: ifcopenshell.entity_instance, titleblock_name: str) -> None:
ootb_titleblock_path = os.path.join(self.data_dir, "templates", "titleblocks", titleblock_name + ".svg") ootb_titleblock_path = tool.Blender.get_data_dir_path(
Path("templates") / "titleblocks" / (titleblock_name + ".svg")
)
titleblock_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_titleblock_path(titleblock_name)) titleblock_path = tool.Ifc.resolve_uri(tool.Drawing.get_default_titleblock_path(titleblock_name))
sheet_path = tool.Drawing.get_document_uri(sheet, "LAYOUT") sheet_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
sheet_dir = os.path.dirname(sheet_path) sheet_dir = os.path.dirname(sheet_path)
@@ -38,6 +38,7 @@ from bonsai.bim.module.drawing.data import DecoratorData
from math import pi, ceil, atan, degrees, acos from math import pi, ceil, atan, degrees, acos
from mathutils import geometry, Vector from mathutils import geometry, Vector
from typing import Optional, Self from typing import Optional, Self
from pathlib import Path
class External(svgwrite.container.Group): class External(svgwrite.container.Group):
@@ -98,7 +99,7 @@ class SvgWriter:
os.makedirs(os.path.dirname(resource_path), exist_ok=True) os.makedirs(os.path.dirname(resource_path), exist_ok=True)
if not os.path.exists(resource_path): if not os.path.exists(resource_path):
resource_basename = os.path.basename(resource_path) resource_basename = os.path.basename(resource_path)
ootb_resource = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", resource_basename) ootb_resource = tool.Blender.get_data_dir_path(Path("assets") / resource_basename)
print( print(
f"WARNING. Couldn't find {resource} for the drawing by the path: {resource_path}. Default BBIM resource will be copied from {ootb_resource}" f"WARNING. Couldn't find {resource} for the drawing by the path: {resource_path}. Default BBIM resource will be copied from {ootb_resource}"
) )
@@ -93,7 +93,7 @@ class ExecuteIfcPatch(bpy.types.Operator):
args = ifcpatch.ArgumentsDict( args = ifcpatch.ArgumentsDict(
recipe=props.ifc_patch_recipes, recipe=props.ifc_patch_recipes,
arguments=arguments, arguments=arguments,
log=os.path.join(context.scene.BIMProperties.data_dir, "process.log"), log=tool.Blender.get_data_dir_path("process.log").__str__(),
) )
if props.should_load_from_memory and tool.Ifc.get(): if props.should_load_from_memory and tool.Ifc.get():
+2 -8
View File
@@ -69,11 +69,8 @@ class ProjectData:
if not ifc_file: if not ifc_file:
return [] return []
current_schema = tool.Ifc.get().schema_identifier current_schema = tool.Ifc.get().schema_identifier
files = (Path(bpy.context.scene.BIMProperties.data_dir) / "libraries").iterdir()
results = [("0", "Custom Library", "")] results = [("0", "Custom Library", "")]
for f in files: for f in tool.Blender.get_data_dir_paths("libraries", "*.ifc*"):
if not f.suffix.lower().startswith(".ifc"):
continue
if cls.get_file_schema(f) != current_schema: if cls.get_file_schema(f) != current_schema:
continue continue
results.append((f.name, f.stem, "Library")) results.append((f.name, f.stem, "Library"))
@@ -85,10 +82,7 @@ class ProjectData:
for export_schema in cls.data["export_schema"]: for export_schema in cls.data["export_schema"]:
template_files[export_schema[0]].append(("0", "Blank Project", "")) template_files[export_schema[0]].append(("0", "Blank Project", ""))
files = (Path(bpy.context.scene.BIMProperties.data_dir) / "templates" / "projects").iterdir() for f in tool.Blender.get_data_dir_paths(Path("templates") / "projects", "*.ifc*"):
for f in files:
if not f.suffix.lower().startswith(".ifc"):
continue
current_schema = cls.get_file_schema(f) current_schema = cls.get_file_schema(f)
if current_schema not in template_files: if current_schema not in template_files:
continue continue
@@ -856,8 +856,8 @@ class LoadProjectElements(bpy.types.Operator):
bonsai.bim.schema.reload(self.file.schema_identifier) bonsai.bim.schema.reload(self.file.schema_identifier)
start = time.time() start = time.time()
logger = logging.getLogger("ImportIFC") logger = logging.getLogger("ImportIFC")
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log") path_log = tool.Blender.get_data_dir_path("process.log")
if not os.access(context.scene.BIMProperties.data_dir, os.W_OK): if not os.access(path_log.parent, os.W_OK):
path_log = os.path.join(tempfile.mkdtemp(), "process.log") path_log = os.path.join(tempfile.mkdtemp(), "process.log")
logging.basicConfig( logging.basicConfig(
filename=path_log, filename=path_log,
@@ -1374,8 +1374,8 @@ class ExportIFC(bpy.types.Operator):
def _execute(self, context): def _execute(self, context):
start = time.time() start = time.time()
logger = logging.getLogger("ExportIFC") logger = logging.getLogger("ExportIFC")
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log") path_log = tool.Blender.get_data_dir_path("process.log")
if not os.access(context.scene.BIMProperties.data_dir, os.W_OK): if not os.access(path_log.parent, os.W_OK):
path_log = os.path.join(tempfile.mkdtemp(), "process.log") path_log = os.path.join(tempfile.mkdtemp(), "process.log")
logging.basicConfig( logging.basicConfig(
filename=path_log, filename=path_log,
+3 -3
View File
@@ -20,6 +20,7 @@ import os
import bpy import bpy
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.placement import ifcopenshell.util.placement
import bonsai.tool as tool
from bonsai.bim.module.project.data import ProjectData from bonsai.bim.module.project.data import ProjectData
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from bonsai.bim.prop import StrProperty, ObjProperty from bonsai.bim.prop import StrProperty, ObjProperty
@@ -60,9 +61,8 @@ def get_library_file(self: "BIMProjectProperties", context: bpy.types.Context) -
def update_library_file(self: "BIMProjectProperties", context: bpy.types.Context) -> None: def update_library_file(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
if self.library_file != "0": if self.library_file != "0":
bpy.ops.bim.select_library_file( filepath = next(p for p in tool.Blender.get_data_dir_paths("libraries", "*.ifc") if p.name == self.library_file)
filepath=os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries", self.library_file) bpy.ops.bim.select_library_file(filepath=filepath)
)
def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context) -> None: def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
@@ -109,19 +109,15 @@ class PsetTemplatesData:
return [(i, i, "") for i in enum_items] return [(i, i, "") for i in enum_items]
@classmethod @classmethod
def pset_template_files(cls): def pset_template_files(cls) -> list[tuple[str, str, str]]:
results = [] results: list[tuple[str, str, str]] = []
pset_dir = os.path.join(bpy.context.scene.BIMProperties.data_dir, "pset") for f in tool.Blender.get_data_dir_paths("pset", "*.ifc"):
files = os.listdir(pset_dir) results.append((f.__str__(), f.stem, "Global Pset Template"))
for f in files:
results.append(
(os.path.join(pset_dir, f), os.path.splitext(os.path.basename(f))[0], "Global Pset Template")
)
pset_dir = tool.Ifc.resolve_uri(bpy.context.scene.BIMProperties.pset_dir) pset_dir = tool.Ifc.resolve_uri(bpy.context.scene.BIMProperties.pset_dir)
if os.path.isdir(pset_dir): if os.path.isdir(pset_dir):
for path in pathlib.Path(pset_dir).glob("*.ifc"): for path in pathlib.Path(pset_dir).glob("*.ifc"):
results.append((str(path), os.path.splitext(os.path.basename(str(path)))[0], "Project Pset Template")) results.append((str(path), path.stem, "Project Pset Template"))
return sorted(results, key=lambda x: x[1]) return sorted(results, key=lambda x: x[1])
@@ -42,7 +42,9 @@ class AddPsetTemplateFile(bpy.types.Operator):
def execute(self, context): def execute(self, context):
template = ifcopenshell.file() template = ifcopenshell.file()
filepath = os.path.join(context.scene.BIMProperties.data_dir, "pset", self.new_template_filename + ".ifc") filepath = next(
p for p in tool.Blender.get_data_dir_paths("pset", "*.ifc") if p.stem == self.new_template_filename
).__str__()
pset_template = ifcopenshell.api.run("pset_template.add_pset_template", template) pset_template = ifcopenshell.api.run("pset_template.add_pset_template", template)
ifcopenshell.api.run("pset_template.add_prop_template", template, pset_template=pset_template) ifcopenshell.api.run("pset_template.add_prop_template", template, pset_template=pset_template)
+2 -2
View File
@@ -812,8 +812,8 @@ class ReloadIfcFile(bpy.types.Operator, tool.Ifc.Operator):
start = time.time() start = time.time()
logger = logging.getLogger("ImportIFC") logger = logging.getLogger("ImportIFC")
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log") path_log = tool.Blender.get_data_dir_path("process.log")
if not os.access(context.scene.BIMProperties.data_dir, os.W_OK): if not os.access(path_log.parent, os.W_OK):
path_log = os.path.join(tempfile.mkdtemp(), "process.log") path_log = os.path.join(tempfile.mkdtemp(), "process.log")
logging.basicConfig( logging.basicConfig(
filename=path_log, filename=path_log,
+3 -1
View File
@@ -439,7 +439,9 @@ class BIMProperties(PropertyGroup):
default=os.path.join(cwd, "schema") + os.path.sep, name="Schema Directory", update=update_schema_dir default=os.path.join(cwd, "schema") + os.path.sep, name="Schema Directory", update=update_schema_dir
) )
data_dir: StringProperty( data_dir: StringProperty(
default=os.path.join(cwd, "data") + os.path.sep, name="Data Directory", update=update_data_dir default=(platformdirs.user_data_path("bonsai", roaming=True, ensure_exists=True) / "data").__str__(),
name="Data Directory",
update=update_data_dir,
) )
cache_dir: StringProperty( cache_dir: StringProperty(
default=platformdirs.user_cache_dir("bonsai"), name="Cache Directory", update=update_cache_dir default=platformdirs.user_cache_dir("bonsai"), name="Cache Directory", update=update_cache_dir
+29 -1
View File
@@ -33,7 +33,7 @@ import importlib
from mathutils import Vector from mathutils import Vector
from pathlib import Path from pathlib import Path
from bonsai.bim.ifc import IFC_CONNECTED_TYPE from bonsai.bim.ifc import IFC_CONNECTED_TYPE
from typing import Any, Optional, Union, Literal, Iterable, Callable, TypeVar from typing import Any, Optional, Union, Literal, Iterable, Callable, TypeVar, Generator
from typing_extensions import assert_never from typing_extensions import assert_never
@@ -1470,3 +1470,31 @@ class Blender(bonsai.core.tool.Blender):
return "dm" if rgb_sum > threshold else "lm" return "dm" if rgb_sum > threshold else "lm"
except Exception: except Exception:
return "dm" # Default to dark mode if an error occurs return "dm" # Default to dark mode if an error occurs
@classmethod
def get_default_data_dir(cls) -> Path:
return Path(__file__).parent.parent / "bim" / "data"
@classmethod
def get_custom_data_dir(cls) -> Path:
return Path(bpy.context.scene.BIMProperties.data_dir)
@classmethod
def get_data_dir_path(cls, relative_path: Union[str, Path]) -> Path:
custom_path = cls.get_custom_data_dir() / relative_path
if custom_path.exists():
return custom_path
return cls.get_default_data_dir() / relative_path
@classmethod
def get_data_dir_paths(cls, relative_dir_path: Union[str, Path], glob_pattern: str) -> Generator[Path, None, None]:
custom_path = cls.get_custom_data_dir() / relative_dir_path
if custom_path.is_dir():
for filepath in custom_path.glob(glob_pattern):
yield filepath
default_data_dir = cls.get_default_data_dir()
if default_data_dir == custom_path:
return
for filepath in (default_data_dir / relative_dir_path).glob(glob_pattern):
yield filepath
+3 -2
View File
@@ -9,6 +9,7 @@ import ifcopenshell.util.cost
import ifcopenshell.util.unit import ifcopenshell.util.unit
import bonsai.bim.helper import bonsai.bim.helper
import json import json
from pathlib import Path
from typing import Optional, Any, Generator, Union, Literal from typing import Optional, Any, Generator, Union, Literal
@@ -111,7 +112,7 @@ class Cost(bonsai.core.tool.Cost):
device = aud.Device() device = aud.Device()
# chaching.mp3 is by Lucish_ CC-BY-3.0 https://freesound.org/people/Lucish_/sounds/554841/ # chaching.mp3 is by Lucish_ CC-BY-3.0 https://freesound.org/people/Lucish_/sounds/554841/
sound = aud.Sound(os.path.join(bpy.context.scene.BIMProperties.data_dir, "chaching.mp3")) sound = aud.Sound(tool.Blender.get_data_dir_path(filename="chaching.mp3").__str__())
handle = device.play(sound) handle = device.play(sound)
sound_buffered = aud.Sound.buffer(sound) sound_buffered = aud.Sound.buffer(sound)
handle_buffered = device.play(sound_buffered) handle_buffered = device.play(sound_buffered)
@@ -627,7 +628,7 @@ class Cost(bonsai.core.tool.Cost):
if filepath: if filepath:
path = filepath path = filepath
else: else:
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", "cost_schedules") path = tool.Blender.get_data_dir_path(Path("build") / "cost_schedules").__str__()
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
+2 -4
View File
@@ -281,7 +281,6 @@ class Drawing(bonsai.core.tool.Drawing):
import bonsai.bim.module.drawing.sheeter as sheeter import bonsai.bim.module.drawing.sheeter as sheeter
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
uri = cls.get_document_uri(document, "LAYOUT") uri = cls.get_document_uri(document, "LAYOUT")
sheet_builder.create(uri, titleblock) sheet_builder.create(uri, titleblock)
return uri return uri
@@ -291,7 +290,6 @@ class Drawing(bonsai.core.tool.Drawing):
import bonsai.bim.module.drawing.sheeter as sheeter import bonsai.bim.module.drawing.sheeter as sheeter
sheet_builder = sheeter.SheetBuilder() sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
drawing_references = {} drawing_references = {}
drawing_names = [] drawing_names = []
for reference in cls.get_document_references(sheet): for reference in cls.get_document_references(sheet):
@@ -1180,8 +1178,8 @@ class Drawing(bonsai.core.tool.Drawing):
os.makedirs(os.path.dirname(resource_path), exist_ok=True) os.makedirs(os.path.dirname(resource_path), exist_ok=True)
if not os.path.exists(resource_path): if not os.path.exists(resource_path):
resource_basename = os.path.basename(resource_path) resource_basename = os.path.basename(resource_path)
ootb_resource = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", resource_basename) ootb_resource = tool.Blender.get_data_dir_path(Path("assets") / resource_basename)
if os.path.exists(ootb_resource): if ootb_resource.is_file():
shutil.copy(ootb_resource, resource_path) shutil.copy(ootb_resource, resource_path)
@classmethod @classmethod
+2 -2
View File
@@ -40,8 +40,8 @@ class Project(bonsai.core.tool.Project):
@classmethod @classmethod
def append_all_types_from_template(cls, template: str) -> None: def append_all_types_from_template(cls, template: str) -> None:
# TODO refactor # TODO refactor
filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "templates", "projects", template) filepath = tool.Blender.get_data_dir_path(Path("templates") / "projects" / template)
bpy.ops.bim.select_library_file(filepath=filepath) bpy.ops.bim.select_library_file(filepath=filepath.__str__())
if IfcStore.library_file.schema != tool.Ifc.get().schema: if IfcStore.library_file.schema != tool.Ifc.get().schema:
return return
for element in IfcStore.library_file.by_type("IfcTypeProduct"): for element in IfcStore.library_file.by_type("IfcTypeProduct"):
+5 -7
View File
@@ -112,8 +112,8 @@ class Web(bonsai.core.tool.Web):
global ws_process global ws_process
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui") webui_path = tool.Blender.get_data_dir_path("webui")
ws_path = os.path.join(webui_path, "sioserver.py") ws_path = (webui_path / "sioserver.py").__str__()
py_version = sys.version_info py_version = sys.version_info
@@ -136,7 +136,7 @@ class Web(bonsai.core.tool.Web):
ws_process = subprocess.Popen( ws_process = subprocess.Popen(
[sys.executable, ws_path, "--p", str(port), "--host", "127.0.0.1"], [sys.executable, ws_path, "--p", str(port), "--host", "127.0.0.1"],
cwd=webui_path, cwd=webui_path.__str__(),
env=env, env=env,
) )
@@ -208,8 +208,7 @@ class Web(bonsai.core.tool.Web):
cls.disconnect_websocket_server() cls.disconnect_websocket_server()
# sleep(0.5) # sleep(0.5)
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui") pid_file = tool.Blender.get_data_dir_path("webui") / "running_pid.json"
pid_file = os.path.join(webui_path, "running_pid.json")
with open(pid_file, "r") as f: with open(pid_file, "r") as f:
pids = json.load(f) pids = json.load(f)
@@ -246,8 +245,7 @@ class Web(bonsai.core.tool.Web):
while True: while True:
if time.time() - start > max_time: if time.time() - start > max_time:
return False return False
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui") pid_file = tool.Blender.get_data_dir_path("webui") / "running_pid.json"
pid_file = os.path.join(webui_path, "running_pid.json")
try: try:
with open(pid_file, "r") as f: with open(pid_file, "r") as f:
data = json.load(f) data = json.load(f)