Fix remaining German log messages: Panel registered, Listener active, sync all files to PROJECTS

- Fix sed $-anchor issue: 'Panel registriert' now replaced globally
- Translate: Listener aktiv, Select-Handler, Closing-Hook, Doppelklick-Handler
- Translate SPLASH messages: gesetzt/angewendet
- Translate ALIAS-LOADER, WELCOME, ELEMENTE migration messages
- Full rsync of all rhino/*.py to PROJECTS (previously partial sync missed schnitte.py, wand_grips.py, treppe_grips.py, text_editor.py, welcome.py etc.)
This commit is contained in:
2026-06-06 12:09:12 +02:00
parent b9f661cdb3
commit 9fcada260e
22 changed files with 95 additions and 95 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ else:
print(" err:", ex) print(" err:", ex)
if css is None: if css is None:
print(" None (kein Custom-SectionStyle gesetzt)") print(" None (kein Custom-SectionStyle set)")
else: else:
print(" Type:", type(css).__name__) print(" Type:", type(css).__name__)
print("") print("")
+3 -3
View File
@@ -109,7 +109,7 @@ def _try_borderless_mac(form):
style_type = type(current) style_type = type(current)
borderless = System.Enum.ToObject(style_type, 0) borderless = System.Enum.ToObject(style_type, 0)
nswindow.StyleMask = borderless nswindow.StyleMask = borderless
print("[SPLASH] StyleMask=0 (Borderless) gesetzt") print("[SPLASH] StyleMask=0 (Borderless) applied")
ok = True ok = True
except Exception as ex: except Exception as ex:
print("[SPLASH] StyleMask Enum:", ex) print("[SPLASH] StyleMask Enum:", ex)
@@ -120,7 +120,7 @@ def _try_borderless_mac(form):
style_type = type(current) style_type = type(current)
full = System.Enum.ToObject(style_type, 1 | 32768) full = System.Enum.ToObject(style_type, 1 | 32768)
nswindow.StyleMask = full nswindow.StyleMask = full
print("[SPLASH] StyleMask=Titled|FullSize gesetzt (Fallback)") print("[SPLASH] StyleMask=Titled|FullSize set (Fallback)")
ok = True ok = True
except Exception as ex2: except Exception as ex2:
print("[SPLASH] StyleMask Fallback:", ex2) print("[SPLASH] StyleMask Fallback:", ex2)
@@ -333,7 +333,7 @@ def show():
# die NSWindow existiert # die NSWindow existiert
try: try:
if _try_borderless_mac(form): if _try_borderless_mac(form):
print("[SPLASH] Borderless (Mac NSWindow) angewendet") print("[SPLASH] Borderless (Mac NSWindow) applied")
except Exception as ex: except Exception as ex:
print("[SPLASH] borderless-mac:", ex) print("[SPLASH] borderless-mac:", ex)
# WebView transparent (rounded corners via HTML border-radius) # WebView transparent (rounded corners via HTML border-radius)
+1 -1
View File
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Wrapper fuer dSection: interaktiver Schnitt-Pick (2 Punkte + Blickrichtung). # Wrapper fuer dSection: interaktiver Schnitt-Pick (2 Punkte + Blickrichtung).
# Defaults kommen aus Project-Settings.defaults; nach erfolgreicher # Defaults kommen aus Project-Settings.defaults; nach erfolgreicher
# Erstellung wird der neue Schnitt als aktive Zeichnungs-Ebene gesetzt. # Erstellung wird der neue Schnitt als aktive Zeichnungs-Ebene set.
import sys, os import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import Rhino import Rhino
+3 -3
View File
@@ -427,7 +427,7 @@ def apply_all():
_quit_xml_pairs.append((xml_key, macro)) _quit_xml_pairs.append((xml_key, macro))
else: else:
print("[ALIAS-LOADER] WARN F-Key {} ({}) konnte weder " print("[ALIAS-LOADER] WARN F-Key {} ({}) konnte weder "
"API noch XML gesetzt werden".format(trigger, action_id)) "API noch XML set werden".format(trigger, action_id))
n_skipped += 1; continue n_skipped += 1; continue
n_fkey += 1 n_fkey += 1
elif spec_type == "cmd": elif spec_type == "cmd":
@@ -452,12 +452,12 @@ def apply_all():
print("[ALIAS-LOADER] Apply", action_id, "->", trigger, ":", ex) print("[ALIAS-LOADER] Apply", action_id, "->", trigger, ":", ex)
n_skipped += 1 n_skipped += 1
# Quit-Hook installieren falls XML-only Shortcuts gesetzt wurden — diese # Quit-Hook installieren falls XML-only Shortcuts set wurden — diese
# ueberlebt sonst Rhino's Auto-Save beim Quit nicht. # ueberlebt sonst Rhino's Auto-Save beim Quit nicht.
if _quit_xml_pairs: if _quit_xml_pairs:
_install_quit_xml_save(list(_quit_xml_pairs)) _install_quit_xml_save(list(_quit_xml_pairs))
print("[ALIAS-LOADER] {} XML-only Shortcuts werden bei Quit " print("[ALIAS-LOADER] {} XML-only Shortcuts werden bei Quit "
"re-persistiert (Closing-Hook installiert)" "re-persistiert (closing hook installed)"
.format(len(_quit_xml_pairs))) .format(len(_quit_xml_pairs)))
return n_alias, n_fkey, n_cmd, n_skipped return n_alias, n_fkey, n_cmd, n_skipped
+3 -3
View File
@@ -300,7 +300,7 @@ def apply_snapshot_to_detail(doc, detail, snap_id):
(page_view or doc.Views).Redraw() (page_view or doc.Views).Redraw()
except Exception: except Exception:
doc.Views.Redraw() doc.Views.Redraw()
print("[AUSSCHNITTE] '{}' auf Detail {} angewendet".format(snap.get("name"), detail.Id)) print("[AUSSCHNITTE] '{}' auf Detail {} applied".format(snap.get("name"), detail.Id))
return True return True
@@ -585,7 +585,7 @@ class AusschnittBridge(panel_base.BaseBridge):
except Exception: pass except Exception: pass
except Exception as ex: except Exception as ex:
print("[AUSSCHNITTE] darstellung apply:", ex) print("[AUSSCHNITTE] darstellung apply:", ex)
# Overrides: nur anwenden wenn das Snap "applyOverrides" gesetzt hat. # Overrides: nur anwenden wenn das Snap "applyOverrides" set hat.
# Sonst bleibt der aktuelle User-Override-State unangetastet. # Sonst bleibt der aktuelle User-Override-State unangetastet.
if snap.get("applyOverrides"): if snap.get("applyOverrides"):
try: try:
@@ -621,7 +621,7 @@ class AusschnittBridge(panel_base.BaseBridge):
_, model_v = ratio # (page=1, model=N) -> N _, model_v = ratio # (page=1, model=N) -> N
import massstab import massstab
massstab._apply_scale(doc, vp, float(model_v)) massstab._apply_scale(doc, vp, float(model_v))
print("[AUSSCHNITTE] Massstab gesetzt auf 1:{} (applied={})".format( print("[AUSSCHNITTE] Massstab set auf 1:{} (applied={})".format(
model_v, massstab.get_applied_scale_ratio())) model_v, massstab.get_applied_scale_ratio()))
# Andere Panels (Massstab, Oberleiste) sofort ueber den # Andere Panels (Massstab, Oberleiste) sofort ueber den
# neuen appliedScale informieren — sonst zeigt das Dropdown # neuen appliedScale informieren — sonst zeigt das Dropdown
+1 -1
View File
@@ -50,4 +50,4 @@ else:
print("[clean_layers] Nichts geloescht (schon sauber?)") print("[clean_layers] Nichts geloescht (schon sauber?)")
if skip: if skip:
print("[clean_layers] Uebersprungen (Objekte drauf): {}".format(", ".join(skip))) print("[clean_layers] Uebersprungen (Objekte drauf): {}".format(", ".join(skip)))
print("[clean_layers] Panel-Sticky zurueckgesetzt") print("[clean_layers] Panel-Sticky zurueckset")
+41 -41
View File
@@ -280,7 +280,7 @@ _KEY_TREPPE_SOLL = "dossier_treppe_soll" # JSON {s:[lo,hi,on], a:[lo
_KEY_TREPPE_2D_SHOW = "dossier_treppe_2d_show" # doc-Setting "1"/"0" — Plansymbol an/aus _KEY_TREPPE_2D_SHOW = "dossier_treppe_2d_show" # doc-Setting "1"/"0" — Plansymbol an/aus
# Per-Treppe Sichtbarkeits-Flags fuer 2D-Plansymbol-Bestandteile # Per-Treppe Sichtbarkeits-Flags fuer 2D-Plansymbol-Bestandteile
# (UserString auf treppe_axis-Objekt). Default "1" wenn nicht gesetzt. # (UserString auf treppe_axis-Objekt). Default "1" wenn nicht set.
_KEY_TREPPE_SHOW_TRITTE = "dossier_treppe_show_tritte" _KEY_TREPPE_SHOW_TRITTE = "dossier_treppe_show_tritte"
_KEY_TREPPE_SHOW_LAUFLINIE = "dossier_treppe_show_lauflinie" _KEY_TREPPE_SHOW_LAUFLINIE = "dossier_treppe_show_lauflinie"
_KEY_TREPPE_SHOW_AUSSEN = "dossier_treppe_show_aussen" _KEY_TREPPE_SHOW_AUSSEN = "dossier_treppe_show_aussen"
@@ -350,7 +350,7 @@ _RAUM_LAYOUT_DEFAULT = [["nummer", "name"], ["funktion"], ["area"]]
# Per-Document Storage-Key fuer Raumstempel-Stile (Presets). # Per-Document Storage-Key fuer Raumstempel-Stile (Presets).
_KEY_RAUM_STILE = "dossier_raum_stempel_stile" _KEY_RAUM_STILE = "dossier_raum_stempel_stile"
# Auf der Raum-Outline: id des zuletzt angewendeten Stils. Damit das UI # Auf der Raum-Outline: id des zuletzt applieden Stils. Damit das UI
# anzeigen kann welcher Stil aktiv ist. # anzeigen kann welcher Stil aktiv ist.
_KEY_RAUM_STIL_ID = "dossier_raum_stil_id" _KEY_RAUM_STIL_ID = "dossier_raum_stil_id"
@@ -414,8 +414,8 @@ _RAUM_RUNDUNGEN = ("exakt", "0.01", "0.1", "0.5", "1")
def _resolve_raum_rundung(meta, doc=None): def _resolve_raum_rundung(meta, doc=None):
"""Loest die Raum-Rundung auf. Wenn am Raum eine explizite UserString- """Loest die Raum-Rundung auf. Wenn am Raum eine explizite UserString-
Rundung gesetzt ist (raum_rundung != ""), gewinnt die. Sonst Default aus Rundung set ist (raum_rundung != ""), gewinnt die. Sonst Default aus
dem aktiven Mass-Style. Doc-Default fallback "0.1" wenn nichts gesetzt.""" dem aktiven Mass-Style. Doc-Default fallback "0.1" wenn nichts set."""
explicit = (meta or {}).get("raum_rundung") or "" explicit = (meta or {}).get("raum_rundung") or ""
if explicit in _RAUM_RUNDUNGEN: return explicit if explicit in _RAUM_RUNDUNGEN: return explicit
if doc is None: doc = Rhino.RhinoDoc.ActiveDoc if doc is None: doc = Rhino.RhinoDoc.ActiveDoc
@@ -658,7 +658,7 @@ def _geschoss_by_id(doc, gid):
def _active_geschoss_id(doc): def _active_geschoss_id(doc):
"""Liefert die ID des aktuell aktiven Geschosses (= im Ebenen-Manager """Liefert die ID des aktuell aktiven Geschosses (= im Ebenen-Manager
blau hervorgehoben). Falls keins gesetzt oder das aktive keine blau hervorgehoben). Falls keins set oder das aktive keine
Geschoss-Ebene ist (z.B. Schnitt/Ansicht), wird das erste echte Geschoss-Ebene ist (z.B. Schnitt/Ansicht), wird das erste echte
Geschoss zurueckgegeben.""" Geschoss zurueckgegeben."""
try: try:
@@ -701,9 +701,9 @@ def _resolve_decke_z(doc, gid, dicke, uk_over, ok_over):
der natuerliche Fixpunkt: aendert sich die Dicke, wandert UK mit. der natuerliche Fixpunkt: aendert sich die Dicke, wandert UK mit.
Override-Logik: Override-Logik:
- Nur OK_override gesetzt OK = override, UK = OK - dicke - Nur OK_override set OK = override, UK = OK - dicke
- Nur UK_override gesetzt UK = override, OK = UK + dicke - Nur UK_override set UK = override, OK = UK + dicke
- Beide gesetzt beide literal""" - Beide set beide literal"""
g = _geschoss_by_id(doc, gid) g = _geschoss_by_id(doc, gid)
okff = float(g.get("okff", 0.0)) if g else 0.0 okff = float(g.get("okff", 0.0)) if g else 0.0
auto_ok = okff auto_ok = okff
@@ -1425,7 +1425,7 @@ def _collect_circle(doc, center):
def _make_decke_preview_handler(committed_points): def _make_decke_preview_handler(committed_points):
"""Live-Preview waehrend Decken-Outline gezeichnet wird: gesetzte Segmente """Live-Preview waehrend Decken-Outline gezeichnet wird: sete Segmente
+ Rubberband + gestrichelte Schliessungs-Linie zurueck zum Startpunkt.""" + Rubberband + gestrichelte Schliessungs-Linie zurueck zum Startpunkt."""
import System.Drawing as SD import System.Drawing as SD
color_line = SD.Color.FromArgb(255, 95, 200, 180) color_line = SD.Color.FromArgb(255, 95, 200, 180)
@@ -1665,7 +1665,7 @@ def _make_treppe_wendel_preview(center, start, breite, referenz, n_stufen,
def _make_treppe_l_corner_preview(p0, breite, referenz, total_n, total_h, def _make_treppe_l_corner_preview(p0, breite, referenz, total_n, total_h,
max_length=None): max_length=None):
"""Preview fuer den 2. Klick einer L-Treppe (Podest-Eck). Zeigt: """Preview fuer den 2. Klick einer L-Treppe (Podest-Eck). Zeigt:
- Lauflinie + Aussenkanten (geclamped auf max_length wenn gesetzt) - Lauflinie + Aussenkanten (geclamped auf max_length wenn set)
- Step-Lines an A_opt-Abstaenden - Step-Lines an A_opt-Abstaenden
- Live-Label mit N1 / N2 - Live-Label mit N1 / N2
""" """
@@ -2016,7 +2016,7 @@ def _t_snap_to_wand_axis(doc, pt, tol=0.15):
def _make_preview_handler(committed_points, dicke, referenz): def _make_preview_handler(committed_points, dicke, referenz):
"""Preview fuer Polylinie-Wand: gesetzte Punkte + Rubberband + Wand-Kanten.""" """Preview fuer Polylinie-Wand: sete Punkte + Rubberband + Wand-Kanten."""
import System.Drawing as SD import System.Drawing as SD
color_axis = SD.Color.FromArgb(255, 95, 200, 180) color_axis = SD.Color.FromArgb(255, 95, 200, 180)
color_edge = SD.Color.FromArgb(180, 140, 215, 200) color_edge = SD.Color.FromArgb(180, 140, 215, 200)
@@ -2963,7 +2963,7 @@ def install_cluster_select_handler():
h = _ClusterVolumeSelectHandler() h = _ClusterVolumeSelectHandler()
h.Enabled = True h.Enabled = True
sc.sticky[_STICKY_CLUSTER_SELECT] = h sc.sticky[_STICKY_CLUSTER_SELECT] = h
print("[ELEMENTE] Cluster-Volume Select-Handler aktiv") print("[ELEMENTE] Cluster-Volume Select-Handler active")
except Exception as ex: except Exception as ex:
print("[ELEMENTE] cluster-select install:", ex) print("[ELEMENTE] cluster-select install:", ex)
@@ -3916,7 +3916,7 @@ def _set_layer_section_hatch(doc, layer_idx, hatch_name, scale=1.0,
def _ensure_material_sublayer(doc, geschoss_name, material_name): def _ensure_material_sublayer(doc, geschoss_name, material_name):
"""Stellt sicher dass `<geschoss>::20_WAENDE::<material>` existiert, """Stellt sicher dass `<geschoss>::20_WAENDE::<material>` existiert,
mit Material-Farbe konfiguriert. Liefert Layer-Index. mit Material-Farbe konfiguriert. Liefert Layer-Index.
Section-Hatch (2D-Schnitt) wird NICHT mehr aus dem Material gesetzt Section-Hatch (2D-Schnitt) wird NICHT mehr aus dem Material set
der kommt vom Parent-Layer (= Ebenen-Settings). Sub-Layer haben default der kommt vom Parent-Layer (= Ebenen-Settings). Sub-Layer haben default
keine eigene Section-Hatch, sie inherit'en Rhino-typisch ihre Optik keine eigene Section-Hatch, sie inherit'en Rhino-typisch ihre Optik
fuer den Schnitt vom Parent. fuer den Schnitt vom Parent.
@@ -4004,7 +4004,7 @@ def _ensure_pbr_material(doc, mat_dict):
- PhysicallyBased.Roughness, Opacity (= 1-transparency), OpacityIor - PhysicallyBased.Roughness, Opacity (= 1-transparency), OpacityIor
- PhysicallyBased.Metallic = reflection (Naeherung) - PhysicallyBased.Metallic = reflection (Naeherung)
- SetBitmapTexture / SetBumpTexture / SetTransparencyTexture wenn - SetBitmapTexture / SetBumpTexture / SetTransparencyTexture wenn
Pfade gesetzt Pfade set
- Texture.Repeat = (1/uvScaleM, 1/uvScaleM)""" - Texture.Repeat = (1/uvScaleM, 1/uvScaleM)"""
if not isinstance(mat_dict, dict): return -1 if not isinstance(mat_dict, dict): return -1
color = mat_dict.get("color") or "#888888" color = mat_dict.get("color") or "#888888"
@@ -4085,7 +4085,7 @@ def _ensure_pbr_material(doc, mat_dict):
if setter is None: return if setter is None: return
ok = setter(p) ok = setter(p)
if not ok: return if not ok: return
# Repeat auf den frisch gesetzten Texture-Slot — alle # Repeat auf den frisch seten Texture-Slot — alle
# Slot-Indices durchgehen und matchen. # Slot-Indices durchgehen und matchen.
try: try:
import Rhino.Geometry as rgg import Rhino.Geometry as rgg
@@ -4604,7 +4604,7 @@ def _read_meta(obj):
otrah = a.GetUserString(_KEY_OEFF_TUER_RAHMEN) or "zarge" otrah = a.GetUserString(_KEY_OEFF_TUER_RAHMEN) or "zarge"
if otrah not in _OEFF_TUER_RAHMEN: otrah = "zarge" if otrah not in _OEFF_TUER_RAHMEN: otrah = "zarge"
# Rahmen-Offset (m, von Wand-Innenseite). Default 5cm. Wenn Legacy- # Rahmen-Offset (m, von Wand-Innenseite). Default 5cm. Wenn Legacy-
# Wert (rahmen_pos) gesetzt aber kein offset, benutzt build-Logik # Wert (rahmen_pos) set aber kein offset, benutzt build-Logik
# weiterhin den Preset. # weiterhin den Preset.
try: oro = float(a.GetUserString(_KEY_OEFF_RAHMEN_OFFSET) or "0.05") try: oro = float(a.GetUserString(_KEY_OEFF_RAHMEN_OFFSET) or "0.05")
except Exception: oro = 0.05 except Exception: oro = 0.05
@@ -4638,7 +4638,7 @@ def _read_meta(obj):
if tart not in _TREPPE_ARTEN: tart = "gerade" if tart not in _TREPPE_ARTEN: tart = "gerade"
thov = a.GetUserString(_KEY_TREPPE_H_OVER) or "" thov = a.GetUserString(_KEY_TREPPE_H_OVER) or ""
tukov = a.GetUserString(_KEY_TREPPE_UK_OVER) or "" tukov = a.GetUserString(_KEY_TREPPE_UK_OVER) or ""
# 2D-Plansymbol-Flags — default True wenn nicht gesetzt # 2D-Plansymbol-Flags — default True wenn nicht set
def _flag_on(key): def _flag_on(key):
v = a.GetUserString(key) v = a.GetUserString(key)
return v != "0" # None, "", "1" → True return v != "0" # None, "", "1" → True
@@ -4657,7 +4657,7 @@ def _read_meta(obj):
except Exception: t_target_a = 0.0 except Exception: t_target_a = 0.0
try: t_podest_min = float(a.GetUserString(_KEY_TREPPE_PODEST_MIN) or "0") try: t_podest_min = float(a.GetUserString(_KEY_TREPPE_PODEST_MIN) or "0")
except Exception: t_podest_min = 0.0 except Exception: t_podest_min = 0.0
# Soll-Werte JSON, mit Defaults wenn nicht gesetzt # Soll-Werte JSON, mit Defaults wenn nicht set
import json import json
tsoll = dict(_TREPPE_SOLL_DEFAULT) tsoll = dict(_TREPPE_SOLL_DEFAULT)
soll_raw = a.GetUserString(_KEY_TREPPE_SOLL) soll_raw = a.GetUserString(_KEY_TREPPE_SOLL)
@@ -6739,7 +6739,7 @@ def _make_raum_stamp_text(centroid, name, nummer, funktion, area, rundung,
return tag if (tag and tag != "") else None return tag if (tag and tag != "") else None
return None return None
# Layout resolven: explizit gesetzt > show_*-Flags > GF/AGF-Default # Layout resolven: explizit set > show_*-Flags > GF/AGF-Default
# Sonderfall GF/AGF: das sind reine Flaechen-Outlines (Geschoss- # Sonderfall GF/AGF: das sind reine Flaechen-Outlines (Geschoss-
# flaeche / Aussengeschossflaeche) — kein Raum-Inhalt. Stempel # flaeche / Aussengeschossflaeche) — kein Raum-Inhalt. Stempel
# zeigt nur 'GF 234.5 m²' bzw. 'AGF 18.0 m²' — keine Nummer/ # zeigt nur 'GF 234.5 m²' bzw. 'AGF 18.0 m²' — keine Nummer/
@@ -6806,7 +6806,7 @@ def _make_raum_stamp_text(centroid, name, nummer, funktion, area, rundung,
except Exception as ex: except Exception as ex:
print("[ELEMENTE] Raum Stamp Font:", ex) print("[ELEMENTE] Raum Stamp Font:", ex)
elif doc is not None and (bold or italic): elif doc is not None and (bold or italic):
# Kein expliziter Font, aber Stil gesetzt — auf Default-Font # Kein expliziter Font, aber Stil set — auf Default-Font
# bold/italic anwenden # bold/italic anwenden
try: try:
cur_idx = te.FontIndex cur_idx = te.FontIndex
@@ -8542,7 +8542,7 @@ def _regenerate_element(doc, element_id):
geom, geschoss_name) geom, geschoss_name)
finally: finally:
sc.sticky[_REGEN_BUSY] = _was_busy sc.sticky[_REGEN_BUSY] = _was_busy
# Wenn ein raum_outline-Regen einen Stempel-Dirty-Marker gesetzt hat # Wenn ein raum_outline-Regen einen Stempel-Dirty-Marker set hat
# (oder generell raeumlich-Aenderungen), Stempel im selben Geschoss # (oder generell raeumlich-Aenderungen), Stempel im selben Geschoss
# (+ Total) neu rechnen. Lauft AUSSERHALB des REGEN_BUSY-Blocks damit # (+ Total) neu rechnen. Lauft AUSSERHALB des REGEN_BUSY-Blocks damit
# die Stempel selber regen koennen. # die Stempel selber regen koennen.
@@ -9326,7 +9326,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
sw_attrs.SetUserString("dossier_oeff_piece", "schwung") sw_attrs.SetUserString("dossier_oeff_piece", "schwung")
doc.Objects.AddCurve(crv, sw_attrs) doc.Objects.AddCurve(crv, sw_attrs)
# Sturzlinien (Lintel-Projektion) — nur bei 1:100 Tueren mit # Sturzlinien (Lintel-Projektion) — nur bei 1:100 Tueren mit
# explizit gesetztem oeff_sturz. Bei standard/detail uebernimmt # explizit setem oeff_sturz. Bei standard/detail uebernimmt
# der Rahmen-Brep die Sturz-Darstellung visuell. # der Rahmen-Brep die Sturz-Darstellung visuell.
old_st = list(_find_objects_by_wall_id(doc, op_meta["id"], old_st = list(_find_objects_by_wall_id(doc, op_meta["id"],
"oeffnung_sturz")) "oeffnung_sturz"))
@@ -9452,7 +9452,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
layers_json = (_json.dumps(layers_def, ensure_ascii=False) layers_json = (_json.dumps(layers_def, ensure_ascii=False)
if is_layered else "") if is_layered else "")
# Per-Schicht Material-Lookup: wenn ein Material-Name aus der # Per-Schicht Material-Lookup: wenn ein Material-Name aus der
# Library gesetzt ist, nimm dessen Farbe/Hatch + leg den Brep auf # Library set ist, nimm dessen Farbe/Hatch + leg den Brep auf
# die Material-Sub-Ebene (Section-Hatch greift dann bei Clipping # die Material-Sub-Ebene (Section-Hatch greift dann bei Clipping
# Planes). Sonst Fallback auf inline color + Standard-Wand-Layer. # Planes). Sonst Fallback auf inline color + Standard-Wand-Layer.
for idx, (lbrep, color, lname) in enumerate(layer_breps): for idx, (lbrep, color, lname) in enumerate(layer_breps):
@@ -9790,7 +9790,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
# Override). Der ModifyAttributes-Aufruf oben loest KEIN # Override). Der ModifyAttributes-Aufruf oben loest KEIN
# ReplaceRhinoObject-Event aus, also wuerde der Override-Listener # ReplaceRhinoObject-Event aus, also wuerde der Override-Listener
# die neue Hatch sonst nicht erreichen. Bei force_solid haben wir # die neue Hatch sonst nicht erreichen. Bei force_solid haben wir
# die Farbe schon direkt gesetzt — das hier wirkt idempotent. # die Farbe schon direkt set — das hier wirkt idempotent.
try: try:
import overrides as _ov import overrides as _ov
cfg = _ov.load_config(doc) cfg = _ov.load_config(doc)
@@ -11541,7 +11541,7 @@ class ElementeBridge(panel_base.BaseBridge):
referenz_def = _last("oeff_referenz", "mid") referenz_def = _last("oeff_referenz", "mid")
darst_def = "auto" darst_def = "auto"
tuer_rahmen_def = "zarge" tuer_rahmen_def = "zarge"
# Pending-Style-ID aus sticky (von Stil-Picker gesetzt). Falls noch # Pending-Style-ID aus sticky (von Stil-Picker set). Falls noch
# kein Style gepickt, nutzen wir den zuletzt-aktiven fuer diesen typ # kein Style gepickt, nutzen wir den zuletzt-aktiven fuer diesen typ
# als Default-Source. # als Default-Source.
active_sid = get_active_oeff_style_id(doc, typ) active_sid = get_active_oeff_style_id(doc, typ)
@@ -11819,7 +11819,7 @@ class ElementeBridge(panel_base.BaseBridge):
l_podest_mode = _last("treppe_l_podest_mode", "kompakt") l_podest_mode = _last("treppe_l_podest_mode", "kompakt")
if l_podest_mode not in ("kompakt", "explizit"): l_podest_mode = "kompakt" if l_podest_mode not in ("kompakt", "explizit"): l_podest_mode = "kompakt"
# Soll-Werte (Editable in der Treppe-Property-Card) aus sticky laden. # Soll-Werte (Editable in der Treppe-Property-Card) aus sticky laden.
# Default falls noch nichts gesetzt: 0.15-0.20 / 0.21-0.35 / 0.60-0.65. # Default falls noch nichts set: 0.15-0.20 / 0.21-0.35 / 0.60-0.65.
soll_last = _last("treppe_soll", None) soll_last = _last("treppe_soll", None)
soll = dict(_TREPPE_SOLL_DEFAULT) soll = dict(_TREPPE_SOLL_DEFAULT)
if soll_last: if soll_last:
@@ -12998,7 +12998,7 @@ class ElementeBridge(panel_base.BaseBridge):
except Exception as ex: except Exception as ex:
print("[ELEMENTE] apply stil {} -> {}: {}".format(sid, eid, ex)) print("[ELEMENTE] apply stil {} -> {}: {}".format(sid, eid, ex))
if n_applied > 0: if n_applied > 0:
print("[ELEMENTE] Stempel-Stil '{}' auf {} Raum/Raeume angewendet".format( print("[ELEMENTE] Stempel-Stil '{}' auf {} Raum/Raeume applied".format(
stil.get("name"), n_applied)) stil.get("name"), n_applied))
self._send_state() self._send_state()
@@ -13093,7 +13093,7 @@ class ElementeBridge(panel_base.BaseBridge):
print("[ELEMENTE] apply stempel-stil {} -> {}: {}".format( print("[ELEMENTE] apply stempel-stil {} -> {}: {}".format(
sid, eid, ex)) sid, eid, ex))
if n > 0: if n > 0:
print("[ELEMENTE] Stempel-Stil '{}' auf {} Stempel angewendet".format( print("[ELEMENTE] Stempel-Stil '{}' auf {} Stempel applied".format(
stil.get("name"), n)) stil.get("name"), n))
self._send_state() self._send_state()
@@ -13288,7 +13288,7 @@ class ElementeBridge(panel_base.BaseBridge):
print("[ELEMENTE] Swisstopo-Import: {} neue Objekte".format(len(new_objs))) print("[ELEMENTE] Swisstopo-Import: {} neue Objekte".format(len(new_objs)))
if not new_objs: return if not new_objs: return
# Target-Layer finden + Objekte verschieben (nur wenn sub_name gesetzt) # Target-Layer finden + Objekte verschieben (nur wenn sub_name set)
if sub_name: if sub_name:
z_id = doc.Strings.GetValue("dossier_active_id") z_id = doc.Strings.GetValue("dossier_active_id")
if not z_id: if not z_id:
@@ -13433,7 +13433,7 @@ class ElementeBridge(panel_base.BaseBridge):
self._push_log("Achtung: kein aktives Geschoss — Objekte bleiben auf Default-Layer") self._push_log("Achtung: kein aktives Geschoss — Objekte bleiben auf Default-Layer")
# Bestehende swisstopo-Objekte loeschen wenn gewuenscht. # Bestehende swisstopo-Objekte loeschen wenn gewuenscht.
# Tag wird beim Import gesetzt (UserString dossier_swisstopo_kind). # Tag wird beim Import set (UserString dossier_swisstopo_kind).
if replace_existing: if replace_existing:
self._push_log("Loesche bestehende swisstopo-Objekte (alte Imports)...") self._push_log("Loesche bestehende swisstopo-Objekte (alte Imports)...")
removed = 0 removed = 0
@@ -14974,7 +14974,7 @@ class ElementeBridge(panel_base.BaseBridge):
tobe_dsh = _flag("obereDashed", "treppe_obere_dashed", False) tobe_dsh = _flag("obereDashed", "treppe_obere_dashed", False)
# Schrittmass-Lock: wenn aktiv, recompute n_stufen damit S # Schrittmass-Lock: wenn aktiv, recompute n_stufen damit S
# konstant bleibt. Beim Aktivieren via Patch wird targetS auf # konstant bleibt. Beim Aktivieren via Patch wird targetS auf
# aktuelles S gesetzt (sofern Patch keinen Wert mitgibt). # aktuelles S set (sofern Patch keinen Wert mitgibt).
tlock_s = _flag("lockS", "treppe_lock_s", False) tlock_s = _flag("lockS", "treppe_lock_s", False)
old_target_s = float(old_meta.get("treppe_target_s", 0.0) or 0.0) old_target_s = float(old_meta.get("treppe_target_s", 0.0) or 0.0)
old_target_a = float(old_meta.get("treppe_target_a", 0.0) or 0.0) old_target_a = float(old_meta.get("treppe_target_a", 0.0) or 0.0)
@@ -15480,7 +15480,7 @@ def _apply_wand_z_drag_constraint(new_obj, meta):
Delta-Logik: max-magnitude der zwei Endpunkt-Z's gewinnt — entspricht Delta-Logik: max-magnitude der zwei Endpunkt-Z's gewinnt — entspricht
'letzter Drag gewinnt' wenn nur ein End-Grip gezogen wurde. 'letzter Drag gewinnt' wenn nur ein End-Grip gezogen wurde.
Geometry-Z wird auf 0 zurueckgesetzt. Geometry-Z wird auf 0 zurueckset.
""" """
geom = new_obj.Geometry geom = new_obj.Geometry
# Sticky reset — bei JEDEM Replace, damit kein alter Delta haengt # Sticky reset — bei JEDEM Replace, damit kein alter Delta haengt
@@ -15615,7 +15615,7 @@ def _apply_oeffnung_constraint(new_obj, meta, old_obj=None):
target_x, target_y = pt_new.X, pt_new.Y target_x, target_y = pt_new.X, pt_new.Y
# Wenn die Wand gerade migrate'd wurde (Rotation/Reshape/XY-Move) → # Wenn die Wand gerade migrate'd wurde (Rotation/Reshape/XY-Move) →
# XY-Projektion HIER UEBERSPRINGEN. Migrate hat den Punkt schon per # XY-Projektion HIER UEBERSPRINGEN. Migrate hat den Punkt schon per
# Bogenlaengen-Mapping auf die neue Achse gesetzt. Eine zweite XY- # Bogenlaengen-Mapping auf die neue Achse set. Eine zweite XY-
# Projektion mit ClosestPoint(pt_old) auf der NEUEN Achse wuerde die # Projektion mit ClosestPoint(pt_old) auf der NEUEN Achse wuerde die
# Position wieder verschieben (Rotation: pt_old liegt nicht mehr auf # Position wieder verschieben (Rotation: pt_old liegt nicht mehr auf
# der neuen Achse → ClosestPoint+Tangent stimmen nicht zusammen). # der neuen Achse → ClosestPoint+Tangent stimmen nicht zusammen).
@@ -16005,7 +16005,7 @@ def _migrate_openings_to_new_axis(wall_id, old_geom, new_geom, old_positions=Non
new_len = new_geom.GetLength() new_len = new_geom.GetLength()
except Exception: return except Exception: return
if old_len < 1e-9 or new_len < 1e-9: return if old_len < 1e-9 or new_len < 1e-9: return
# Wand-UK aufloesen damit Oeffnungs-Punkte auf UK+Brueestung gesetzt # Wand-UK aufloesen damit Oeffnungs-Punkte auf UK+Brueestung set
# werden (= visuell auf Unterkante Oeffnung). Sonst landen sie auf # werden (= visuell auf Unterkante Oeffnung). Sonst landen sie auf
# reiner Brueest-Hoehe und der nachfolgende Constraint interpretiert # reiner Brueest-Hoehe und der nachfolgende Constraint interpretiert
# die Diskrepanz als User-Z-Drag → Brueest dropt. # die Diskrepanz als User-Z-Drag → Brueest dropt.
@@ -16022,7 +16022,7 @@ def _migrate_openings_to_new_axis(wall_id, old_geom, new_geom, old_positions=Non
wall_uk = 0.0 wall_uk = 0.0
# Migrierten Wand registrieren — der Constraint soll fuer Oeffnungen # Migrierten Wand registrieren — der Constraint soll fuer Oeffnungen
# dieser Wand die XY-Projektion ueberspringen (migrate hat XY bereits # dieser Wand die XY-Projektion ueberspringen (migrate hat XY bereits
# via Bogenlaengen-Mapping korrekt gesetzt). # via Bogenlaengen-Mapping korrekt set).
migrated = sc.sticky.get("_dossier_migrated_walls") migrated = sc.sticky.get("_dossier_migrated_walls")
if not isinstance(migrated, set): if not isinstance(migrated, set):
migrated = set() migrated = set()
@@ -16498,7 +16498,7 @@ def _on_select_objects(sender, e):
So bewegen sich beide synchron bei Move/Gumball, und die Endpunkte So bewegen sich beide synchron bei Move/Gumball, und die Endpunkte
der Lauflinie sind als Grips zum Drag verfuegbar. der Lauflinie sind als Grips zum Drag verfuegbar.
Partner-Selection wird AUCH bei hidden Layern angewendet damit der Partner-Selection wird AUCH bei hidden Layern applied damit der
Pure-Transform-Pfad (CommandEnd) die ganze Treppe als zusammenhaengendes Pure-Transform-Pfad (CommandEnd) die ganze Treppe als zusammenhaengendes
Element behandelt und das 3D-Volume mitbewegt selbst wenn nur das 2D- Element behandelt und das 3D-Volume mitbewegt selbst wenn nur das 2D-
Symbol sichtbar ist. GripsOn aber NUR auf sichtbaren Sources (Grips auf Symbol sichtbar ist. GripsOn aber NUR auf sichtbaren Sources (Grips auf
@@ -16867,8 +16867,8 @@ def _migrate_referenz_layer_once(doc):
if doc.Objects.ModifyAttributes(obj, a, True): if doc.Objects.ModifyAttributes(obj, a, True):
n_moved += 1 n_moved += 1
except Exception: pass except Exception: pass
print("[ELEMENTE] Referenz-Migration (v3): {} Geschoss(e) registriert, " print("[ELEMENTE] Reference migration (v3): {} floor(s) registered, "
"{} Objekte bewegt, {} eingeblendet".format( "{} objects moved, {} shown".format(
n_geschosse, n_moved, n_shown)) n_geschosse, n_moved, n_shown))
# Finaler Broadcast — falls _find_ebene_sublayer_name den Eintrag # Finaler Broadcast — falls _find_ebene_sublayer_name den Eintrag
# nicht neu angelegt hat (z.B. weil er schon existiert) wird das # nicht neu angelegt hat (z.B. weil er schon existiert) wird das
@@ -17649,7 +17649,7 @@ def _on_command_end(sender, e):
return return
name = sc.sticky.get(_UT_ACTIVE_KEY) name = sc.sticky.get(_UT_ACTIVE_KEY)
if not name: return if not name: return
# _UT_ACTIVE_KEY bleibt gesetzt bis am Ende der Funktion — sonst feuern # _UT_ACTIVE_KEY bleibt set bis am Ende der Funktion — sonst feuern
# gestaltungs Listener auf die Replace-Events die wir hier selber # gestaltungs Listener auf die Replace-Events die wir hier selber
# erzeugen (Pure-Translate translates Volumen via Replace; Regen-Pfad # erzeugen (Pure-Translate translates Volumen via Replace; Regen-Pfad
# ersetzt Sub-Volumen). Cleanup im finally-Block am Ende. # ersetzt Sub-Volumen). Cleanup im finally-Block am Ende.
@@ -17677,7 +17677,7 @@ def _on_command_end(sender, e):
print("[ELEMENTE] stamp-sync:", ex) print("[ELEMENTE] stamp-sync:", ex)
# RedrawEnabled wurde idR schon beim ersten Object-Event nach dem # RedrawEnabled wurde idR schon beim ersten Object-Event nach dem
# User-Klick auf False gesetzt (`_suppress_redraw_until_cmd_end`). Den # User-Klick auf False set (`_suppress_redraw_until_cmd_end`). Den
# gemerkten prev-Wert lesen. Falls kein Event gefeuert hat (z.B. Move # gemerkten prev-Wert lesen. Falls kein Event gefeuert hat (z.B. Move
# ohne tatsaechliche Aenderung), suppressen wir jetzt selber. # ohne tatsaechliche Aenderung), suppressen wir jetzt selber.
if sc.sticky.get("_dossier_cmd_redraw_suppressed"): if sc.sticky.get("_dossier_cmd_redraw_suppressed"):
@@ -18211,7 +18211,7 @@ def _on_command_end(sender, e):
# Regen ausloesen → mehrere Regens pro Wand. Wir machen am Schluss EINEN # Regen ausloesen → mehrere Regens pro Wand. Wir machen am Schluss EINEN
# Regen pro affected_wall — viel schneller bei mehreren Oeffnungen. # Regen pro affected_wall — viel schneller bei mehreren Oeffnungen.
sc.sticky["_dossier_skip_sync_regen"] = True sc.sticky["_dossier_skip_sync_regen"] = True
# RedrawEnabled wurde schon in _on_command_begin auf False gesetzt — # RedrawEnabled wurde schon in _on_command_begin auf False set —
# damit unterdruecken wir auch Rhinos automatischen Post-Move-Redraw # damit unterdruecken wir auch Rhinos automatischen Post-Move-Redraw
# (sonst kurzer Mismatch-Frame: Oeffnung an neuer Pos, Wand-Loch noch # (sonst kurzer Mismatch-Frame: Oeffnung an neuer Pos, Wand-Loch noch
# an alter Pos). # an alter Pos).
+1 -1
View File
@@ -433,7 +433,7 @@ def set_ebene_locked(doc, code, locked):
def delete_ebene(doc, code, move_to=None): def delete_ebene(doc, code, move_to=None):
""" """
Loescht alle Sublayer mit dem gegebenen Code in allen Zeichnungsebenen. Loescht alle Sublayer mit dem gegebenen Code in allen Zeichnungsebenen.
Falls move_to gesetzt: verschiebt vorher alle Objekte zum Sublayer Falls move_to set: verschiebt vorher alle Objekte zum Sublayer
mit move_to-Code unter dem selben Parent. Sonst: loescht Objekte mit. mit move_to-Code unter dem selben Parent. Sonst: loescht Objekte mit.
""" """
if not code: if not code:
+5 -5
View File
@@ -220,7 +220,7 @@ def _list_linetypes_full(doc):
def _read_launcher_schema(): def _read_launcher_schema():
"""Liest das Default-Layer-Schema aus dossier_settings.json (Launcher-Pfad). """Liest das Default-Layer-Schema aus dossier_settings.json (Launcher-Pfad).
Liefert eine Liste {code, name, color, lw} oder None wenn nicht gesetzt.""" Liefert eine Liste {code, name, color, lw} oder None wenn nicht set."""
paths = [ paths = [
os.path.expanduser("~/Library/Application Support/" os.path.expanduser("~/Library/Application Support/"
"ch.gabrielevarano.Dossier/dossier_settings.json"), "ch.gabrielevarano.Dossier/dossier_settings.json"),
@@ -1638,7 +1638,7 @@ class EbenenBridge(panel_base.BaseBridge):
"""Datei-Picker → kopieren in library/assets/ → Item zum """Datei-Picker → kopieren in library/assets/ → Item zum
Manifest hinzufuegen. Payload: {variant: '2d'|'3d', Manifest hinzufuegen. Payload: {variant: '2d'|'3d',
targetId: ?, itemType: 'symbol'|'object'}. targetId: ?, itemType: 'symbol'|'object'}.
Wenn targetId gesetzt: bestehendes Item updaten (Datei Wenn targetId set: bestehendes Item updaten (Datei
wird seinem files2d/3d zugewiesen). Sonst: neues Item.""" wird seinem files2d/3d zugewiesen). Sonst: neues Item."""
try: try:
import library import library
@@ -1743,7 +1743,7 @@ class EbenenBridge(panel_base.BaseBridge):
def _save_selection_as_library(self, payload): def _save_selection_as_library(self, payload):
"""Aktuelle Selection im Doc als Library-Item speichern. """Aktuelle Selection im Doc als Library-Item speichern.
payload: {variant: '2d'|'3d', targetId?: str, itemType?: str}. payload: {variant: '2d'|'3d', targetId?: str, itemType?: str}.
Wenn targetId gesetzt: bestehendes Item updaten. Sonst: Wenn targetId set: bestehendes Item updaten. Sonst:
Rhino-Prompt nach Name + neues Item anlegen.""" Rhino-Prompt nach Name + neues Item anlegen."""
d = Rhino.RhinoDoc.ActiveDoc d = Rhino.RhinoDoc.ActiveDoc
if d is None: return if d is None: return
@@ -2500,7 +2500,7 @@ class EbenenBridge(panel_base.BaseBridge):
# trip (30 ms debounce in React) feuert ohnehin layer_builder # trip (30 ms debounce in React) feuert ohnehin layer_builder
# .apply_visibility() das am Ende selbst redrawt. Sparen wir uns # .apply_visibility() das am Ende selbst redrawt. Sparen wir uns
# einen doppelten Full-Repaint pro Geschoss-Klick. # einen doppelten Full-Repaint pro Geschoss-Klick.
print("[EBENEN] CPlane Z={} auf {} Top-Style View(s) gesetzt".format(okff, updated)) print("[EBENEN] CPlane Z={} auf {} Top-Style View(s) set".format(okff, updated))
def _needs_clipping_update(self, doc, prev_active_id, new_z): def _needs_clipping_update(self, doc, prev_active_id, new_z):
"""Liefert True wenn entweder das alte oder das neue Geschoss """Liefert True wenn entweder das alte oder das neue Geschoss
@@ -2637,7 +2637,7 @@ class EbenenBridge(panel_base.BaseBridge):
if z_id: if z_id:
layer_builder.set_active_sublayer(doc, z_id, code) layer_builder.set_active_sublayer(doc, z_id, code)
else: else:
print("[EBENEN] Aktive Zeichnungsebene unbekannt — Layer wird nicht gesetzt") print("[EBENEN] Aktive Zeichnungsebene unbekannt — Layer wird nicht set")
def _remove_ebene_from_state(self, code): def _remove_ebene_from_state(self, code):
doc = Rhino.RhinoDoc.ActiveDoc doc = Rhino.RhinoDoc.ActiveDoc
+1 -1
View File
@@ -5,7 +5,7 @@
""" """
layouts.py layouts.py
LAYOUTS-Panel: Layout-Pages erstellen + Details mit Ausschnitten bestuecken. LAYOUTS-Panel: Layout-Pages erstellen + Details mit Ausschnitten bestuecken.
Phase 1 — Snapshot-Mode: Ausschnitt wird beim Zuweisen auf das Detail angewendet, Phase 1 — Snapshot-Mode: Ausschnitt wird beim Zuweisen auf das Detail applied,
Re-Sync per Knopf. Live-Link und Masterlayouts kommen spaeter. Re-Sync per Knopf. Live-Link und Masterlayouts kommen spaeter.
""" """
import os import os
+3 -3
View File
@@ -14,7 +14,7 @@ Persistiert als JSON in doc.Strings["dossier_mass_styles"] (Liste) und
doc.Strings["dossier_mass_style_active"] (aktive ID). doc.Strings["dossier_mass_style_active"] (aktive ID).
Ein Mass-Style wird als globale Vorgabe gelesen. Per-Element-Override Ein Mass-Style wird als globale Vorgabe gelesen. Per-Element-Override
(z.B. raum_rundung UserString am einzelnen Raum) hat Vorrang wenn gesetzt. (z.B. raum_rundung UserString am einzelnen Raum) hat Vorrang wenn set.
""" """
import json import json
import uuid import uuid
@@ -88,7 +88,7 @@ def list_presets(doc):
# Erst-Initialisierung: Default-Liste schreiben # Erst-Initialisierung: Default-Liste schreiben
items = [_normalize(p) for p in _DEFAULT_PRESETS] items = [_normalize(p) for p in _DEFAULT_PRESETS]
_save_all(doc, items) _save_all(doc, items)
# Default-Aktiv setzen falls noch nichts gesetzt # Default-Aktiv setzen falls noch nichts set
if not doc.Strings.GetValue(_KEY_ACTIVE): if not doc.Strings.GetValue(_KEY_ACTIVE):
doc.Strings.SetString(_KEY_ACTIVE, items[0]["id"]) doc.Strings.SetString(_KEY_ACTIVE, items[0]["id"])
return items return items
@@ -175,7 +175,7 @@ def delete_preset(doc, preset_id):
def raum_rundung_default(doc): def raum_rundung_default(doc):
"""Default-Rundung fuer Raum-Stempel wenn keine per-Raum-Override """Default-Rundung fuer Raum-Stempel wenn keine per-Raum-Override
gesetzt ist.""" set ist."""
p = get_active(doc) p = get_active(doc)
return p["raumRundung"] if p else "0.1" return p["raumRundung"] if p else "0.1"
+7 -7
View File
@@ -34,11 +34,11 @@ PANEL_GUID_STR = "5c8e4f3f-6d0e-4f1a-a3d4-e5f607182941"
# Wir legen sie in einer Config-Datei im Home des Users ab. # Wir legen sie in einer Config-Datei im Home des Users ab.
_DOC_DPI_KEY = "dossier_dpi" # Legacy, fuer Migration _DOC_DPI_KEY = "dossier_dpi" # Legacy, fuer Migration
# Pro Viewport-Name der zuletzt vom User explizit gesetzte Massstab # Pro Viewport-Name der zuletzt vom User explizit sete Massstab
# (Dropdown/Input/100%-Button/Ausschnitt-Restore). NICHT der Live-Zoom — der # (Dropdown/Input/100%-Button/Ausschnitt-Restore). NICHT der Live-Zoom — der
# drifted bei Pan/Zoom. Wird von Ausschnitten beim Speichern als "der # drifted bei Pan/Zoom. Wird von Ausschnitten beim Speichern als "der
# eingestellte Massstab" gelesen. Per-doc persistiert in doc.Strings als # eingestellte Massstab" gelesen. Per-doc persistiert in doc.Strings als
# JSON-Dict, damit ein Wechsel zurueck auf einen frueher gesetzten Viewport # JSON-Dict, damit ein Wechsel zurueck auf einen frueher seten Viewport
# den korrekten Wert wieder rausgibt — auch nach Restart. # den korrekten Wert wieder rausgibt — auch nach Restart.
_user_set_scales = {} # {viewport_name: float} _user_set_scales = {} # {viewport_name: float}
_user_set_scales_loaded = False # lazy load aus doc.Strings beim ersten Zugriff _user_set_scales_loaded = False # lazy load aus doc.Strings beim ersten Zugriff
@@ -348,7 +348,7 @@ def _compute_scale(doc, vp):
pass pass
# appliedScale pro Viewport. Map ist gefuettert durch _apply_scale und # appliedScale pro Viewport. Map ist gefuettert durch _apply_scale und
# Ausschnitt-Restore — wenn ein anderer Viewport aktiv ist als beim letzten # Ausschnitt-Restore — wenn ein anderer Viewport aktiv ist als beim letzten
# Setzen, kommt entweder dessen frueher gesetzter Wert oder None zurueck. # Setzen, kommt entweder dessen frueher seter Wert oder None zurueck.
# Niemals auf die Live-Skala mappen — das Dropdown soll STATISCH sein. # Niemals auf die Live-Skala mappen — das Dropdown soll STATISCH sein.
# Wichtig: nur bei Parallelprojektion zurueckgeben. In Perspective ist ein # Wichtig: nur bei Parallelprojektion zurueckgeben. In Perspective ist ein
# Massstab konzeptionell unsinnig — selbst wenn der gleiche Viewport vorher # Massstab konzeptionell unsinnig — selbst wenn der gleiche Viewport vorher
@@ -479,7 +479,7 @@ def write_plotweight(doc, target, value):
Print-Mode-aware. value = "echter" Wert in mm wie er auf Papier landet. Print-Mode-aware. value = "echter" Wert in mm wie er auf Papier landet.
Speichert value als Original-UserString. Wenn Print-Mode aktiv ist wird Speichert value als Original-UserString. Wenn Print-Mode aktiv ist wird
PlotWeight = value * scale gesetzt damit die Anzeige direkt skaliert. PlotWeight = value * scale set damit die Anzeige direkt skaliert.
Aufrufer ist verantwortlich fuer ModifyAttributes / Doc-Refresh.""" Aufrufer ist verantwortlich fuer ModifyAttributes / Doc-Refresh."""
if target is None: return if target is None: return
@@ -685,7 +685,7 @@ def _set_lineweights_enabled(doc, enabled):
def _read_user_scale(doc, default=1.0): def _read_user_scale(doc, default=1.0):
"""Persistierter eingestellter Massstab oder default. Setze default=None """Persistierter eingestellter Massstab oder default. Setze default=None
um "nie gesetzt" zu erkennen.""" um "nie set" zu erkennen."""
if doc is None: return default if doc is None: return default
try: try:
raw = doc.Strings.GetValue(_DOC_USER_SCALE_KEY) raw = doc.Strings.GetValue(_DOC_USER_SCALE_KEY)
@@ -786,7 +786,7 @@ def _rescale_doc_patterns(doc, factor):
if hasattr(a, prop): if hasattr(a, prop):
cur = getattr(a, prop) cur = getattr(a, prop)
if cur and cur > 0 and abs(cur - 1.0) > 1e-9: if cur and cur > 0 and abs(cur - 1.0) > 1e-9:
# Nur Objekte mit explizit gesetzter Skala anfassen # Nur Objekte mit explizit seter Skala anfassen
# (Default=1.0 ueberlassen wir dem globalen Multiplikator). # (Default=1.0 ueberlassen wir dem globalen Multiplikator).
new_a = a.Duplicate() new_a = a.Duplicate()
setattr(new_a, prop, cur * factor) setattr(new_a, prop, cur * factor)
@@ -910,7 +910,7 @@ def _apply_scale(doc, vp, ratio):
print("[MASSSTAB] Raumstempel-Regen:", ex) print("[MASSSTAB] Raumstempel-Regen:", ex)
try: doc.Views.Redraw() try: doc.Views.Redraw()
except Exception: pass except Exception: pass
print("[MASSSTAB] Skala 1:{:.2f} gesetzt (Faktor {:.4f}, soll-frustum {:.4f} {})".format( print("[MASSSTAB] Skala 1:{:.2f} set (Faktor {:.4f}, soll-frustum {:.4f} {})".format(
ratio, factor, new_frustum_u, str(doc.ModelUnitSystem))) ratio, factor, new_frustum_u, str(doc.ModelUnitSystem)))
return True return True
except Exception as ex: except Exception as ex:
+2 -2
View File
@@ -258,7 +258,7 @@ def set_active_preset(doc, name):
"""Aktiviert ein gespeichertes Preset: kopiert dessen Rules ins Doc-Config """Aktiviert ein gespeichertes Preset: kopiert dessen Rules ins Doc-Config
und markiert es als activePreset. Wenn name leer/None: aktives Preset und markiert es als activePreset. Wenn name leer/None: aktives Preset
geclear-t, Rules bleiben unveraendert (User waehlt "kein Preset"). Bei geclear-t, Rules bleiben unveraendert (User waehlt "kein Preset"). Bei
aktivem enabled-Flag wird sofort neu angewendet. True bei Erfolg.""" aktivem enabled-Flag wird sofort neu applied. True bei Erfolg."""
if doc is None: return False if doc is None: return False
cfg = load_config(doc) cfg = load_config(doc)
if name: if name:
@@ -676,7 +676,7 @@ def _apply_to_object(doc, obj, overrides):
def apply_all(doc): def apply_all(doc):
"""Wendet alle aktiven Regeln auf alle Objekte im Doc an. """Wendet alle aktiven Regeln auf alle Objekte im Doc an.
Objekte die NICHT (mehr) matchen werden auf Originale zurueckgesetzt.""" Objekte die NICHT (mehr) matchen werden auf Originale zurueckset."""
if doc is None: return 0, 0 if doc is None: return 0, 0
cfg = load_config(doc) cfg = load_config(doc)
if not cfg.get("enabled"): return 0, 0 if not cfg.get("enabled"): return 0, 0
+1 -1
View File
@@ -199,7 +199,7 @@ class OverridesBridge(panel_base.BaseBridge):
overrides.set_active_preset(doc, name) overrides.set_active_preset(doc, name)
else: else:
# Append-Mode: bestehende + Preset-Rules. activePreset wird # Append-Mode: bestehende + Preset-Rules. activePreset wird
# in update_rules auf None gesetzt — passt, weil's eine # in update_rules auf None set — passt, weil's eine
# Mischung ist, kein einzelnes Preset mehr. # Mischung ist, kein einzelnes Preset mehr.
rules = overrides.load_preset(name) rules = overrides.load_preset(name)
if rules is not None: if rules is not None:
+2 -2
View File
@@ -874,7 +874,7 @@ def register_and_open(mode, caption, guid_str, bridge_factory, icon_spec=None, m
try: try:
panel.MinimumSize = drawing.Size(int(min_size[0]), int(min_size[1])) panel.MinimumSize = drawing.Size(int(min_size[0]), int(min_size[1]))
except Exception as ex: except Exception as ex:
print("[{}] MinimumSize konnte nicht gesetzt werden: {}".format(mode.upper(), ex)) print("[{}] MinimumSize konnte nicht set werden: {}".format(mode.upper(), ex))
# Auf einigen Eto-Versionen gibt es zusaetzlich Size/ClientSize # Auf einigen Eto-Versionen gibt es zusaetzlich Size/ClientSize
for attr in ("Size", "ClientSize"): for attr in ("Size", "ClientSize"):
try: try:
@@ -925,7 +925,7 @@ def register_and_open(mode, caption, guid_str, bridge_factory, icon_spec=None, m
return return
sc.sticky[sticky_reg] = True sc.sticky[sticky_reg] = True
sc.sticky[sticky_guid] = System.Guid(guid_str) sc.sticky[sticky_guid] = System.Guid(guid_str)
print("[{}] Panel registriert".format(mode.upper())) print("[{}] Panel registered".format(mode.upper()))
except Exception as ex: except Exception as ex:
print("[{}] Registrierung fehlgeschlagen: {}".format(mode.upper(), ex)) print("[{}] Registrierung fehlgeschlagen: {}".format(mode.upper(), ex))
return return
+4 -4
View File
@@ -96,7 +96,7 @@ def _collect_viewport_ids(doc):
def find_schnitt_clip_objects(doc): def find_schnitt_clip_objects(doc):
"""Findet alle Clipping-Plane-Objekte die zu einem aktiven Schnitt """Findet alle Clipping-Plane-Objekte die zu einem aktiven Schnitt
gehoeren (UserString _KEY_SCHNITT_CLIP gesetzt).""" gehoeren (UserString _KEY_SCHNITT_CLIP set)."""
out = [] out = []
try: try:
for obj in doc.Objects: for obj in doc.Objects:
@@ -252,7 +252,7 @@ def activate_schnitt(doc, z, skip_view=False):
# Projektion: 'parallel' (klassischer Schnitt) oder 'perspective' # Projektion: 'parallel' (klassischer Schnitt) oder 'perspective'
# (Schnittperspektive — perspektivische Section mit gleicher Cut- # (Schnittperspektive — perspektivische Section mit gleicher Cut-
# Logik). Bei perspective wird Kamera leicht naeher geholt + FOV # Logik). Bei perspective wird Kamera leicht naeher geholt + FOV
# gesetzt; Cut-Planes sind identisch. # set; Cut-Planes sind identisch.
projection = (z.get("projection") or "parallel").strip().lower() projection = (z.get("projection") or "parallel").strip().lower()
if projection not in ("parallel", "perspective"): projection = "parallel" if projection not in ("parallel", "perspective"): projection = "parallel"
@@ -276,7 +276,7 @@ def activate_schnitt(doc, z, skip_view=False):
cam_dist = max(50.0, depth_back * 3 + line_len) cam_dist = max(50.0, depth_back * 3 + line_len)
# Bei Perspektive: Kamera + Target auf cam_z. Bei Parallel: # Bei Perspektive: Kamera + Target auf cam_z. Bei Parallel:
# plane_z (Mitte Hoehenrange) — Z spielt eh keine Rolle # plane_z (Mitte Hoehenrange) — Z spielt eh keine Rolle
# fuers Bild, aber sauber gesetzt fuer konsistente # fuers Bild, aber sauber set fuer konsistente
# Kamera-Ausrichtung. # Kamera-Ausrichtung.
view_z = cam_z if projection == "perspective" else plane_z view_z = cam_z if projection == "perspective" else plane_z
cam_pos = rg.Point3d( cam_pos = rg.Point3d(
@@ -556,7 +556,7 @@ class _SchnittDoubleClickHandler(Rhino.UI.MouseCallback):
Wichtig: die Klicks selektieren das Curve vorab (Rhino-Default), wir Wichtig: die Klicks selektieren das Curve vorab (Rhino-Default), wir
pruefen also einfach die aktuelle Selection. Bei Treffer wird der pruefen also einfach die aktuelle Selection. Bei Treffer wird der
Schnitt aktiviert + e.Cancel=True gesetzt damit Rhinos default Schnitt aktiviert + e.Cancel=True set damit Rhinos default
Edit-Modus nicht zusaetzlich aufpoppt.""" Edit-Modus nicht zusaetzlich aufpoppt."""
def OnMouseDoubleClick(self, e): def OnMouseDoubleClick(self, e):
try: try:
+1 -1
View File
@@ -201,7 +201,7 @@ def _check_doc_unit(doc):
Idempotent pro Doc via doc.Strings-Flag wird nur EINMAL pro Doc gefragt. Idempotent pro Doc via doc.Strings-Flag wird nur EINMAL pro Doc gefragt.
Wenn User "Spaeter" waehlt, fragt DOSSIER beim selben Doc nicht mehr (Flag Wenn User "Spaeter" waehlt, fragt DOSSIER beim selben Doc nicht mehr (Flag
bleibt gesetzt). Fuer erneute Frage: doc.Strings-Key loeschen. bleibt set). Fuer erneute Frage: doc.Strings-Key loeschen.
""" """
if doc is None: return if doc is None: return
try: try:
+3 -3
View File
@@ -54,7 +54,7 @@ def _sync_plot_color_to_display(attrs):
_FILL_KEY = "ebenen_fill_hatch_id" _FILL_KEY = "ebenen_fill_hatch_id"
_FILL_SOURCE_KEY = "ebenen_fill_source" # "layer" oder "object" _FILL_SOURCE_KEY = "ebenen_fill_source" # "layer" oder "object"
_FILL_OWNER_KEY = "ebenen_fill_owner" # Curve-ID, auf Hatch gesetzt _FILL_OWNER_KEY = "ebenen_fill_owner" # Curve-ID, auf Hatch set
_NO_FILL_KEY = "ebenen_no_fill" # "1" wenn User Fuellung explizit aus hat _NO_FILL_KEY = "ebenen_no_fill" # "1" wenn User Fuellung explizit aus hat
# Loop-Guard fuer Live-Update # Loop-Guard fuer Live-Update
@@ -910,7 +910,7 @@ def _selection_summary(doc):
# WICHTIG: layer.FullPath/Name liefert auf Mac mit Umlauten (Ä in WAENDE etc.) # WICHTIG: layer.FullPath/Name liefert auf Mac mit Umlauten (Ä in WAENDE etc.)
# eine UnicodeDecodeError ueber die IronPython<->.NET-Bruecke. Wir benutzen # eine UnicodeDecodeError ueber die IronPython<->.NET-Bruecke. Wir benutzen
# stattdessen unsere ASCII-only UserStrings (dossier_id + dossier_code) die wir # stattdessen unsere ASCII-only UserStrings (dossier_id + dossier_code) die wir
# beim Layer-Bau gesetzt haben. # beim Layer-Bau set haben.
nm = _safe_layer_label(doc, layer, a.LayerIndex) nm = _safe_layer_label(doc, layer, a.LayerIndex)
layer_names.add(nm) layer_names.add(nm)
@@ -1311,7 +1311,7 @@ class GestaltungBridge(panel_base.BaseBridge):
if ok == 0: if ok == 0:
print("[GESTALTUNG] Linetype-Scale nicht unterstuetzt (Rhino-Version?)") print("[GESTALTUNG] Linetype-Scale nicht unterstuetzt (Rhino-Version?)")
else: else:
print("[GESTALTUNG] Linetype-Scale auf {} Objekt(e) angewendet".format(ok)) print("[GESTALTUNG] Linetype-Scale auf {} Objekt(e) applied".format(ok))
self._send_selection() self._send_selection()
def _set_linetype_source(self, source, name): def _set_linetype_source(self, source, name):
+1 -1
View File
@@ -709,7 +709,7 @@ def merge_grids(grids):
def mesh_from_grid(grid, origin_shift=(0, 0, 0), unit_scale=1.0): def mesh_from_grid(grid, origin_shift=(0, 0, 0), unit_scale=1.0):
"""Baut ein Rhino-Mesh aus dem XYZ-Grid. origin_shift wird auf jeden """Baut ein Rhino-Mesh aus dem XYZ-Grid. origin_shift wird auf jeden
Vertex angewendet (typisch: bbox-Center zu Welt-0/0/0 schieben). Vertex applied (typisch: bbox-Center zu Welt-0/0/0 schieben).
unit_scale: Skalierung von Meter (Quelle XYZ) auf Doc-Units. Bei unit_scale: Skalierung von Meter (Quelle XYZ) auf Doc-Units. Bei
mm-Doc = 1000, bei m-Doc = 1.0 .""" mm-Doc = 1000, bei m-Doc = 1.0 ."""
es = grid["es"]; ns = grid["ns"] es = grid["es"]; ns = grid["ns"]
+2 -2
View File
@@ -93,7 +93,7 @@ class TextEditorBridge(panel_base.BaseBridge):
self._initial_settings = settings self._initial_settings = settings
self._fonts = fonts self._fonts = fonts
self._form_ref = None self._form_ref = None
self._edit_obj_id = edit_obj_id # bei Doppelklick-Edit gesetzt self._edit_obj_id = edit_obj_id # bei Doppelklick-Edit set
self._initial_text = initial_text self._initial_text = initial_text
self._initial_runs = initial_runs # rich-format-Runs falls vorhanden self._initial_runs = initial_runs # rich-format-Runs falls vorhanden
self._initial_html = initial_html # 1:1 Editor-HTML beim Reopen self._initial_html = initial_html # 1:1 Editor-HTML beim Reopen
@@ -204,7 +204,7 @@ class TextEditorBridge(panel_base.BaseBridge):
st.get("underline")) st.get("underline"))
# 3. Text-Wrap im Frame — NACH dem Content damit es nicht # 3. Text-Wrap im Frame — NACH dem Content damit es nicht
# durch RichText-Set zurueckgesetzt wird. Beide Setter # durch RichText-Set zurueckset wird. Beide Setter
# versuchen (verschiedene Rhino-Versions-APIs). # versuchen (verschiedene Rhino-Versions-APIs).
applied_w = None applied_w = None
for attr in ("FormatWidth", "TextWidth", "MaskWidth"): for attr in ("FormatWidth", "TextWidth", "MaskWidth"):
+8 -8
View File
@@ -773,7 +773,7 @@ def _rhino_last_restored_layout_guid():
Layout-GUID. Rhino schreibt diesen Eintrag nach jedem erfolgreichen Layout-GUID. Rhino schreibt diesen Eintrag nach jedem erfolgreichen
Restore (sowohl beim App-Start als auch nach `_-WindowLayout`-Command). Restore (sowohl beim App-Start als auch nach `_-WindowLayout`-Command).
Wenn die GUID == unser Ziel ist, hat Rhino's Auto-Restore das Layout Wenn die GUID == unser Ziel ist, hat Rhino's Auto-Restore das Layout
schon beim Cold-Start angewendet wir koennen den teuren Command- schon beim Cold-Start applied wir koennen den teuren Command-
Apply skippen.""" Apply skippen."""
try: try:
if not os.path.isfile(_RHINO_SETTINGS_XML): return None if not os.path.isfile(_RHINO_SETTINGS_XML): return None
@@ -881,7 +881,7 @@ def _apply_window_layout_impl(name):
[(c, m) for c, m, _ in api_candidates]) [(c, m) for c, m, _ in api_candidates])
else: else:
print("[OBERLEISTE] No Rhino.UI API candidates (Mac Rhino " print("[OBERLEISTE] No Rhino.UI API candidates (Mac Rhino "
"exposed das nicht statisch). Falle auf Scripted Commands.") "exposed das nicht statisch). Falling back to scripted commands.")
# Args zum Probieren: GUID zuerst (falls vorhanden) dann Name. # Args zum Probieren: GUID zuerst (falls vorhanden) dann Name.
# Beide als 1-arg Tuple. Doppelte Klammern haben in der alten Version # Beide als 1-arg Tuple. Doppelte Klammern haben in der alten Version
# zu mix von String/Tuple gefuehrt — hier sauber als Liste of Tuples. # zu mix von String/Tuple gefuehrt — hier sauber als Liste of Tuples.
@@ -1176,7 +1176,7 @@ _display_modes_cache = None # gecacht — Liste aendert sich pro Rhino-Session
def _list_display_modes(): def _list_display_modes():
"""Alle verfuegbaren Display-Modes (LocalName + Id-String). """Alle verfuegbaren Display-Modes (LocalName + Id-String).
Gecacht Liste aendert sich nur wenn User Display-Modes ergaenzt/loescht. Gecacht Liste aendert sich nur wenn User Display-Modes ergaenzt/loescht.
Bei Bedarf kann _display_modes_cache von aussen auf None gesetzt werden.""" Bei Bedarf kann _display_modes_cache von aussen auf None set werden."""
global _display_modes_cache global _display_modes_cache
if _display_modes_cache is not None: if _display_modes_cache is not None:
return _display_modes_cache return _display_modes_cache
@@ -1339,7 +1339,7 @@ class OberleisteBridge(panel_base.BaseBridge):
self._last_state_sig = None # Fingerprint des letzten Push — dedupe self._last_state_sig = None # Fingerprint des letzten Push — dedupe
self._cached_overrides = None # (enabled, count) — invalidiert bei Toggle/Update self._cached_overrides = None # (enabled, count) — invalidiert bei Toggle/Update
self._cached_combinations = None # (names, active) — invalidiert bei jeder Comb-Aenderung self._cached_combinations = None # (names, active) — invalidiert bei jeder Comb-Aenderung
self._last_set_view = None # Letzte ueber Topbar gesetzte Ansicht (fuer Active-Highlight) self._last_set_view = None # Letzte ueber Topbar sete Ansicht (fuer Active-Highlight)
# Command-Liste LAZY laden — die Enumeration durchlaeuft alle Plugins # Command-Liste LAZY laden — die Enumeration durchlaeuft alle Plugins
# und ist teuer (~hundert ms). Wird erst beim ersten _send_state, oder # und ist teuer (~hundert ms). Wird erst beim ersten _send_state, oder
# explizit bei Command-Input-Fokus, gebaut. # explizit bei Command-Input-Fokus, gebaut.
@@ -2029,7 +2029,7 @@ class OberleisteBridge(panel_base.BaseBridge):
info["northAngle"] = kamera.get_north_angle(doc) info["northAngle"] = kamera.get_north_angle(doc)
except Exception: except Exception:
info["northAngle"] = 0 info["northAngle"] = 0
# Letzte ueber Topbar gesetzte Ansicht (fuer Active-Highlight) # Letzte ueber Topbar sete Ansicht (fuer Active-Highlight)
info["lastSetView"] = self._last_set_view info["lastSetView"] = self._last_set_view
# Referenzlinien-Sichtbarkeit fuer den Oberleiste-Toggle: alle # Referenzlinien-Sichtbarkeit fuer den Oberleiste-Toggle: alle
# Ebenen mit Name 'Referenz...' (keyword-driven, bauteil-uebergreifend) # Ebenen mit Name 'Referenz...' (keyword-driven, bauteil-uebergreifend)
@@ -2101,7 +2101,7 @@ class OberleisteBridge(panel_base.BaseBridge):
self.send("STATE", info) self.send("STATE", info)
def _check_pending_launcher_signals(self): def _check_pending_launcher_signals(self):
"""Pollt dossier_settings.json auf vom Launcher gesetzte Pending-Flags. """Pollt dossier_settings.json auf vom Launcher sete Pending-Flags.
Aktuell: pendingApplyLayout, pendingApplyViewColors, Aktuell: pendingApplyLayout, pendingApplyViewColors,
pendingImportDisplayModes. Loescht das jeweilige Flag nach pendingImportDisplayModes. Loescht das jeweilige Flag nach
Verarbeitung damit es nicht jeden Idle erneut feuert.""" Verarbeitung damit es nicht jeden Idle erneut feuert."""
@@ -2112,7 +2112,7 @@ class OberleisteBridge(panel_base.BaseBridge):
pend_layout = cfg.get("pendingApplyLayout") pend_layout = cfg.get("pendingApplyLayout")
if isinstance(pend_layout, str) and pend_layout: if isinstance(pend_layout, str) and pend_layout:
# Wenn der Cold-Start-Apply (_on_ready) DIESE Session schon # Wenn der Cold-Start-Apply (_on_ready) DIESE Session schon
# das Layout gesetzt hat (sticky=True), skippen wir den # das Layout set hat (sticky=True), skippen wir den
# Re-Apply vom Launcher-Pending — sonst triggert es eine # Re-Apply vom Launcher-Pending — sonst triggert es eine
# zweite Re-Mount-Welle ALLER Panels. # zweite Re-Mount-Welle ALLER Panels.
if sc.sticky.get("_dossier_layout_applied"): if sc.sticky.get("_dossier_layout_applied"):
@@ -2127,7 +2127,7 @@ class OberleisteBridge(panel_base.BaseBridge):
if cfg.get("pendingApplyViewColors"): if cfg.get("pendingApplyViewColors"):
if _apply_viewport_colors(cfg): if _apply_viewport_colors(cfg):
print("[OBERLEISTE] pendingApplyViewColors: angewendet") print("[OBERLEISTE] pendingApplyViewColors: applied")
cfg["pendingApplyViewColors"] = False cfg["pendingApplyViewColors"] = False
mutated = True mutated = True
+1 -1
View File
@@ -469,7 +469,7 @@ def show_welcome(force=False):
WICHTIG: UI muss auf Main-Thread laufen (Mac Cocoa) Rhino-Idle-Event WICHTIG: UI muss auf Main-Thread laufen (Mac Cocoa) Rhino-Idle-Event
feuert dort, deshalb defern wir die Anzeige.""" feuert dort, deshalb defern wir die Anzeige."""
if not force and _has_optout(): if not force and _has_optout():
print("[WELCOME] optout aktiv ({}) — skip".format(_WELCOME_OPTOUT)) print("[WELCOME] optout active ({}) — skip".format(_WELCOME_OPTOUT))
return return
print("[WELCOME] geplant — Anzeige nach Splash (>{:.1f}s)".format(_SPLASH_MIN_DELAY_SEC)) print("[WELCOME] geplant — Anzeige nach Splash (>{:.1f}s)".format(_SPLASH_MIN_DELAY_SEC))