Adds bonsai_prefs.py — a Blender startup script that applies personal Bonsai addon settings on every launch via a load_post handler, keeping machine-specific config out of the IfcOpenShell repo. All user settings are in a single CONFIGURE block at the top of the file. Handles Blender extension-style addon keys (bl_ext.user_default.bonsai), ghost addon entries with None preferences, and the register() requirement introduced in Blender 4.2+. Generated with the assistance of an AI coding tool.
125 lines
5.2 KiB
Python
125 lines
5.2 KiB
Python
"""
|
||
Bonsai personal preferences startup script.
|
||
Runs automatically at Blender startup via a load_post handler (after addons load).
|
||
Lives in Blender's user startup scripts folder — NOT in the IfcOpenShell repo.
|
||
|
||
DEBUG: Open Blender's system console (Window > Toggle System Console on Windows)
|
||
to see output from this script.
|
||
"""
|
||
import os
|
||
import bpy
|
||
from bpy.app.handlers import persistent
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# CONFIGURE YOUR SETTINGS HERE
|
||
# Set any value to None to leave that preference unchanged.
|
||
# ---------------------------------------------------------------------------
|
||
|
||
# Root for shared assets/psets, relative to the open .blend file.
|
||
# e.g. os.path.join("..", "..", "my_assets") or an absolute path like "C:/Assets"
|
||
ASSETS_ROOT = os.path.join("..", "..", "..", "..", "OD_Submodules")
|
||
|
||
# Drawing asset paths — set to None to skip individual entries.
|
||
STYLESHEET_PATH = os.path.join(ASSETS_ROOT, "assets", "default.css")
|
||
SCHEDULES_STYLESHEET = os.path.join(ASSETS_ROOT, "assets", "schedule.css")
|
||
MARKERS_PATH = os.path.join(ASSETS_ROOT, "assets", "markers.svg")
|
||
SYMBOLS_PATH = os.path.join(ASSETS_ROOT, "assets", "symbols.svg")
|
||
PATTERNS_PATH = os.path.join(ASSETS_ROOT, "assets", "patterns.svg")
|
||
SHADING_STYLES_PATH = os.path.join(ASSETS_ROOT, "assets", "shading_styles.json")
|
||
|
||
# Pset directory (relative to .blend or absolute). None = leave unchanged.
|
||
PSET_DIR = os.path.join(ASSETS_ROOT, "psets") + os.path.sep
|
||
|
||
# Drawing font filename. None = leave unchanged.
|
||
DRAWING_FONT = "CENTURY_GOTHIC.TTF"
|
||
|
||
# Font scale factor. None = leave unchanged.
|
||
MAGIC_FONT_SCALE = 0.003
|
||
|
||
# Classes shown as wireframe on import. None = leave unchanged.
|
||
CLASSES_TO_WIREFRAME = "IfcVirtualElement, IfcSpace"
|
||
|
||
# Layout SVG command (Inkscape or other SVG editor). None = leave unchanged.
|
||
# Format: '[["<path/to/app>", "path"]]' where "path" is replaced with the SVG file.
|
||
LAYOUT_SVG_COMMAND = '[["C:/Program Files/Inkscape/bin/inkscape.exe", "path"]]'
|
||
|
||
# Decorations overlay colour as RGBA floats (0.0–1.0). None = leave unchanged.
|
||
DECORATIONS_COLOUR = (1, 0, 0, 1) # red
|
||
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def find_bonsai_addon_key() -> str | None:
|
||
"""Find the Bonsai addon key — differs between legacy addons and extensions.
|
||
|
||
Legacy addon: 'bonsai'
|
||
Extension: 'bl_ext.user_default.bonsai' or 'bl_ext.user_default.bonsaiPR'
|
||
"""
|
||
addons = bpy.context.preferences.addons
|
||
if "bonsai" in addons and addons["bonsai"].preferences is not None:
|
||
return "bonsai"
|
||
# Match any key containing 'bonsai', skipping None-preferences entries (e.g. bonsaiPR ghosts).
|
||
for key in addons.keys():
|
||
if "bonsai" in key.lower():
|
||
try:
|
||
if addons[key].preferences is not None:
|
||
return key
|
||
except Exception:
|
||
continue
|
||
return None
|
||
|
||
|
||
def apply_bonsai_prefs():
|
||
addon_key = find_bonsai_addon_key()
|
||
if addon_key is None:
|
||
print("[bonsai_prefs] Bonsai not found, skipping.")
|
||
return
|
||
|
||
print(f"[bonsai_prefs] Applying prefs via addon key: '{addon_key}'")
|
||
try:
|
||
prefs = bpy.context.preferences.addons[addon_key].preferences
|
||
except Exception as e:
|
||
print(f"[bonsai_prefs] Could not access preferences: {e}")
|
||
return
|
||
|
||
if hasattr(prefs, "doc"):
|
||
doc = prefs.doc
|
||
if STYLESHEET_PATH is not None: doc.stylesheet_path = STYLESHEET_PATH
|
||
if SCHEDULES_STYLESHEET is not None: doc.schedules_stylesheet_path = SCHEDULES_STYLESHEET
|
||
if MARKERS_PATH is not None: doc.markers_path = MARKERS_PATH
|
||
if SYMBOLS_PATH is not None: doc.symbols_path = SYMBOLS_PATH
|
||
if PATTERNS_PATH is not None: doc.patterns_path = PATTERNS_PATH
|
||
if SHADING_STYLES_PATH is not None: doc.shadingstyles_path = SHADING_STYLES_PATH
|
||
if DRAWING_FONT is not None: doc.drawing_font = DRAWING_FONT
|
||
if MAGIC_FONT_SCALE is not None: doc.magic_font_scale = MAGIC_FONT_SCALE
|
||
if CLASSES_TO_WIREFRAME is not None: doc.classes_to_wireframe = CLASSES_TO_WIREFRAME
|
||
else:
|
||
print("[bonsai_prefs] prefs.doc not found — listing available properties for diagnosis:")
|
||
try:
|
||
for p in prefs.bl_rna.properties:
|
||
print(f" {p.identifier}")
|
||
except Exception as e:
|
||
print(f" (could not list: {e})")
|
||
|
||
if PSET_DIR is not None and hasattr(prefs, "pset_dir"):
|
||
prefs.pset_dir = PSET_DIR
|
||
if LAYOUT_SVG_COMMAND is not None: prefs.layout_svg_command = LAYOUT_SVG_COMMAND
|
||
if DECORATIONS_COLOUR is not None: prefs.decorations_colour = DECORATIONS_COLOUR
|
||
|
||
print("[bonsai_prefs] Done.")
|
||
|
||
|
||
@persistent
|
||
def _bonsai_prefs_load_post(*args):
|
||
apply_bonsai_prefs()
|
||
|
||
|
||
def register():
|
||
if _bonsai_prefs_load_post not in bpy.app.handlers.load_post:
|
||
bpy.app.handlers.load_post.append(_bonsai_prefs_load_post)
|
||
|
||
|
||
def unregister():
|
||
if _bonsai_prefs_load_post in bpy.app.handlers.load_post:
|
||
bpy.app.handlers.load_post.remove(_bonsai_prefs_load_post)
|