Raumstempel-Stile (Presets) — speichern + anwenden per Doc

Damit User wiederkehrende Stempel-Configs (Wettbewerb / Bauantrag /
Mobiliar etc.) nicht jedes Mal neu klicken muss.

Backend (elemente.py):
- Storage in doc.Strings dossier_raum_stempel_stile als JSON-Array
- load_raum_stempel_stile / save_raum_stempel_stile Helpers
- Bridge-Handler:
  - SAVE_RAUM_STIL: upsert by id (neu wenn leer)
  - DELETE_RAUM_STIL: remove by id
  - APPLY_RAUM_STIL: schreibt Stil-Felder auf Raum-IDs + Regen
- _RAUM_STIL_FIELDS umfasst font/bold/italic/txtH/txtModus/align/
  rundung/fuellung/showSia/layout (alles was die Optik bestimmt)
- raumStempelStile im STATE-Emit zum Frontend

Frontend:
- saveRaumStil/deleteRaumStil/applyRaumStil in rhinoBridge.js
- RaumProperties: neue "Stil"-Sektion oben mit Dropdown
  * gespeicherte Stile + "+ Aktuelle Settings als Stil speichern" +
    "🗑 Aktiven Stil loeschen"
  * Klick auf Stil → applyRaumStil mit aktueller Raum-ID
- Beide PropertiesView-Aufrufe (inline + satellite) bekommen die Liste
This commit is contained in:
2026-05-26 23:17:08 +02:00
parent da0fd365f2
commit f1860ae85d
4 changed files with 215 additions and 3 deletions
+148
View File
@@ -330,6 +330,64 @@ _RAUM_FIELD_IDS = ("nummer", "name", "funktion", "area", "sia")
# Default-Layout (entspricht dem alten Verhalten)
_RAUM_LAYOUT_DEFAULT = [["nummer", "name"], ["funktion"], ["area"]]
# Per-Document Storage-Key fuer Raumstempel-Stile (Presets).
_KEY_RAUM_STILE = "dossier_raum_stempel_stile"
# Felder die ein Stil persistiert (= alles was die Optik bestimmt). Nicht
# enthalten: Name/Nummer/Funktion (inhaltlich pro Raum), area/umfang (aus
# Geometrie berechnet), Geschoss (Raum-Position).
_RAUM_STIL_FIELDS = (
"font", "bold", "italic",
"txtH", "txtModus",
"align", "rundung",
"fuellung",
"showSia",
"layout",
)
def load_raum_stempel_stile(doc):
"""Liest alle Raumstempel-Stile aus doc.Strings als Liste of dicts.
Schema pro Stil: {id, name, <_RAUM_STIL_FIELDS>}. Leer wenn keine.
"""
if doc is None: return []
try:
raw = doc.Strings.GetValue(_KEY_RAUM_STILE) or ""
except Exception: raw = ""
if not raw: return []
try:
import json as _json
data = _json.loads(raw)
if not isinstance(data, list): return []
out = []
for s in data:
if not isinstance(s, dict): continue
if not s.get("id") or not s.get("name"): continue
out.append(s)
return out
except Exception as ex:
print("[ELEMENTE] load_raum_stempel_stile:", ex)
return []
def save_raum_stempel_stile(doc, stile):
"""Persistiert die Stil-Liste in doc.Strings."""
if doc is None or not isinstance(stile, list): return False
try:
import json as _json
clean = []
for s in stile:
if not isinstance(s, dict): continue
if not s.get("id") or not s.get("name"): continue
clean.append(s)
doc.Strings.SetString(_KEY_RAUM_STILE,
_json.dumps(clean, ensure_ascii=False))
return True
except Exception as ex:
print("[ELEMENTE] save_raum_stempel_stile:", ex)
return False
_RAUM_RUNDUNGEN = ("exakt", "0.01", "0.1", "0.5", "1")
@@ -6382,6 +6440,17 @@ class ElementeBridge(panel_base.BaseBridge):
delete_oeff_style(doc, p.get("id"))
except Exception as ex: print("[ELEMENTE] del oeff style:", ex)
self._send_state()
elif t == "SAVE_RAUM_STIL":
# Speichert aktuelle Raum-Stil-Settings (vom Frontend gepacked)
# unter id (neu wenn leer) + name. Liste persistiert in
# doc.Strings, state-emit pushed neue Liste zum UI.
self._cmd_save_raum_stil(p)
elif t == "DELETE_RAUM_STIL":
self._cmd_delete_raum_stil(p)
elif t == "APPLY_RAUM_STIL":
# Wendet einen gespeicherten Stil auf die im Patch genannten
# raum_outline element_ids an. p = {stilId, ids=[…]}.
self._cmd_apply_raum_stil(p)
elif t == "OPEN_ELEMENTE_UEBERSICHT":
try:
import elemente_uebersicht
@@ -6647,6 +6716,7 @@ class ElementeBridge(panel_base.BaseBridge):
{"name": n, "color": m["color"]}
for n, m in _get_all_materials(doc).items()],
"oeffStyles": list_oeff_styles(doc),
"raumStempelStile": load_raum_stempel_stile(doc),
}
self.send("STATE", payload)
# An Properties-Satellite-Window forwarden falls offen
@@ -8464,6 +8534,84 @@ class ElementeBridge(panel_base.BaseBridge):
size=(640, 560),
bridge=b)
def _cmd_save_raum_stil(self, p):
"""Speichert einen Raumstempel-Stil. Payload:
{id?: string, name: string, settings: {font, bold, italic, txtH,
txtModus, align, rundung, fuellung, showSia, layout}}
Wenn id leer neuer Stil mit uuid. State-Emit anschliessend.
"""
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return
name = (p.get("name") or "").strip()
if not name:
print("[ELEMENTE] SAVE_RAUM_STIL: name leer")
return
settings = p.get("settings") or {}
sid = (p.get("id") or "").strip() or ("stil_" + uuid.uuid4().hex[:8])
stile = load_raum_stempel_stile(doc)
# Upsert by id
new_stil = {"id": sid, "name": name}
for f in _RAUM_STIL_FIELDS:
if f in settings:
new_stil[f] = settings[f]
found = False
for i, s in enumerate(stile):
if s.get("id") == sid:
stile[i] = new_stil; found = True; break
if not found:
stile.append(new_stil)
if save_raum_stempel_stile(doc, stile):
print("[ELEMENTE] Stempel-Stil '{}' ({}) gespeichert".format(
name, sid))
self._send_state()
def _cmd_delete_raum_stil(self, p):
"""Loescht einen Stil per id. Payload: {id}."""
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return
sid = (p.get("id") or "").strip()
if not sid: return
stile = load_raum_stempel_stile(doc)
stile = [s for s in stile if s.get("id") != sid]
if save_raum_stempel_stile(doc, stile):
print("[ELEMENTE] Stempel-Stil {} geloescht".format(sid))
self._send_state()
def _cmd_apply_raum_stil(self, p):
"""Wendet einen Stil auf eine Liste von raum_outline-Element-IDs
an. Payload: {stilId, ids: [element_id, ...]}. Schreibt die Stil-
Felder auf die Source-Outlines + triggert Regen pro Raum.
"""
doc = Rhino.RhinoDoc.ActiveDoc
if doc is None: return
sid = (p.get("stilId") or "").strip()
ids = p.get("ids") or []
if not sid or not ids: return
stile = load_raum_stempel_stile(doc)
stil = next((s for s in stile if s.get("id") == sid), None)
if stil is None:
print("[ELEMENTE] APPLY_RAUM_STIL: Stil {} nicht gefunden".format(sid))
return
# Stil-Felder → Patch fuer _update_wall-Pfad
patch_base = {}
for f in _RAUM_STIL_FIELDS:
if f in stil:
patch_base[f] = stil[f]
n_applied = 0
for eid in ids:
try:
# _update_wall braucht id + alle Patch-Felder
patch = dict(patch_base)
patch["id"] = eid
self._update_wall(patch)
n_applied += 1
except Exception as ex:
print("[ELEMENTE] apply stil {} -> {}: {}".format(sid, eid, ex))
if n_applied > 0:
print("[ELEMENTE] Stempel-Stil '{}' auf {} Raum/Raeume angewendet".format(
stil.get("name"), n_applied))
self._send_state()
def _cmd_create_symbol(self, p):
"""Platziert ein Library-Item (symbol/object) im Doc. Interactive
GetPoint im aktiven Viewport User klickt Position.