Startup-Performance: WindowLayout-Apply skippen bei Quick-Restart

Diagnose-Log zeigte: 3046 ms (= 93% der 3.3s Cold-Start-Zeit) gehen
allein in den _-WindowLayout RunScript-Call beim ersten oberleiste-
_on_ready. Rhino dockt dabei alle Panels neu — teuer.

Optimierung: Marker-File-basiertes Skip
- Nach erfolgreichem Apply: ~/Library/Application Support/
  ch.gabrielevarano.Dossier/layout_marker.json mit Timestamp + Name
- Bei _on_ready: wenn Marker < 10 min alt UND derselbe Layout-Name →
  skip (Rhino "remembers" die Panel-Positionen meistens noch im
  internen State, nur unzuverlaessig zwischen LANGEN Pausen)
- Sonst (kalter Tag, langer Reboot, anderer Name): apply normal

Effekt fuer typischen Dev-Workflow (haeufige Quick-Restarts):
- Erster Restart heute: 3s Apply (wie bisher) + Marker
- Naechste Restarts < 10 min spaeter: ~0ms (skip)
- Nach Pause > 10 min: erneuter Apply

Helpers _is_layout_recently_applied + _mark_layout_applied. Marker
wird auch geschrieben wenn User manuell apply (z.B. via Settings-
Dialog oder Launcher pendingApplyLayout) → konsistent.

Falls Layout doch falsch ist nach Skip: Oberleiste-Settings →
"Jetzt anwenden"-Button erzwingt Apply.
This commit is contained in:
2026-05-27 19:24:02 +02:00
parent 6fee7bd143
commit edaf83229b
+63 -3
View File
@@ -760,21 +760,69 @@ def _layout_name_to_guid(name):
return None return None
_LAYOUT_MARKER_PATH = os.path.expanduser(
"~/Library/Application Support/ch.gabrielevarano.Dossier/layout_marker.json")
def _is_layout_recently_applied(name, max_age_sec=600):
"""True wenn das gegebene Layout vor < max_age_sec Sekunden via DOSSIER
appliziert wurde (Marker-File). Wird benutzt um den teuren
`_-WindowLayout`-RunScript bei Quick-Restarts zu skippen — Rhino haelt
die Panel-Positionen dann meistens noch in seinem internen State."""
try:
if not os.path.isfile(_LAYOUT_MARKER_PATH): return False
import time as _t, json as _j
with open(_LAYOUT_MARKER_PATH, "rb") as f:
data = _j.loads(f.read().decode("utf-8"))
if not isinstance(data, dict): return False
if (data.get("layoutName") or "") != (name or ""): return False
try: age = _t.time() - float(data.get("appliedAt", 0))
except Exception: return False
return 0 <= age <= max_age_sec
except Exception as ex:
print("[OBERLEISTE] layout-marker read:", ex)
return False
def _mark_layout_applied(name):
"""Schreibt den Marker nach erfolgreichem Apply."""
try:
import time as _t, json as _j
d = os.path.dirname(_LAYOUT_MARKER_PATH)
if not os.path.isdir(d):
try: os.makedirs(d)
except Exception: pass
with open(_LAYOUT_MARKER_PATH, "wb") as f:
f.write(_j.dumps({
"layoutName": name,
"appliedAt": _t.time(),
}, ensure_ascii=False).encode("utf-8"))
except Exception as ex:
print("[OBERLEISTE] layout-marker write:", ex)
def _apply_window_layout(name): def _apply_window_layout(name):
"""Wendet ein benanntes Window-Layout an. Probiert mehrere Wege weil """Wendet ein benanntes Window-Layout an. Probiert mehrere Wege weil
Mac Rhino 8 keine offizielle Python-API dafuer exponiert und die Mac Rhino 8 keine offizielle Python-API dafuer exponiert und die
Scripted-Commands je nach Rhino-Version variieren. STOP on success — Scripted-Commands je nach Rhino-Version variieren. STOP on success —
pruefen via RunScript-Return-Value oder via fehlerfreiem API-Call.""" pruefen via RunScript-Return-Value oder via fehlerfreiem API-Call.
Schreibt nach erfolgreichem Apply einen Marker fuer Quick-Restart-Skip
(siehe _is_layout_recently_applied).
"""
if not name: if not name:
print("[OBERLEISTE] apply_window_layout: leerer Name") print("[OBERLEISTE] apply_window_layout: leerer Name")
return False return False
import time as _t_wl import time as _t_wl
_t_wl_start = _t_wl.time() _t_wl_start = _t_wl.time()
_ret = False
try: try:
_ret = _apply_window_layout_impl(name) _ret = _apply_window_layout_impl(name)
finally: finally:
try: panel_base._t_mark("window_layout", name, _t_wl_start) try: panel_base._t_mark("window_layout", name, _t_wl_start)
except Exception: pass except Exception: pass
if _ret:
try: _mark_layout_applied(name)
except Exception: pass
return _ret return _ret
@@ -1305,14 +1353,26 @@ class OberleisteBridge(panel_base.BaseBridge):
# Default-Window-Layout anwenden, wenn aktiviert und noch nicht in # Default-Window-Layout anwenden, wenn aktiviert und noch nicht in
# DIESER Rhino-Session geschehen (sticky-flag = process-lifetime). # DIESER Rhino-Session geschehen (sticky-flag = process-lifetime).
# Mac Rhino persistiert die Window-Anordnung zwischen Sessions # Mac Rhino persistiert die Window-Anordnung zwischen Sessions
# NICHT zuverlaessig — der Cold-Start-Apply muss jedes Mal laufen. # NICHT zuverlaessig — der Cold-Start-Apply lief bisher jedes Mal
# (= ~3s _-WindowLayout RunScript-Blocker).
# Optimierung: Marker-File mit Timestamp + Name. Wenn der letzte
# erfolgreiche Apply < 10 min her ist UND derselbe Layout-Name → skip
# (Rhino "remembers" wahrscheinlich noch). Bei langem Abstand
# (Pause, neuer Tag, Reboot) → apply normal.
try: try:
cfg = _settings_load() cfg = _settings_load()
if not sc.sticky.get("_dossier_layout_applied"): if not sc.sticky.get("_dossier_layout_applied"):
layout_name = cfg.get("windowLayout") or cfg.get("defaultLayout") layout_name = cfg.get("windowLayout") or cfg.get("defaultLayout")
if cfg.get("autoApplyLayout") and layout_name: if cfg.get("autoApplyLayout") and layout_name:
sc.sticky["_dossier_layout_applied"] = True sc.sticky["_dossier_layout_applied"] = True
_apply_window_layout(layout_name) if _is_layout_recently_applied(layout_name, max_age_sec=600):
print("[OBERLEISTE] Layout '{}' wurde vor < 10min "
"appliziert — skip (~3s gespart). Manuell neu "
"anwenden via Oberleiste-Einstellungen wenn noetig.".format(
layout_name))
else:
_apply_window_layout(layout_name)
_mark_layout_applied(layout_name)
# Viewport-Colors einmalig pro Session auto-applien (wenn aktiviert) # Viewport-Colors einmalig pro Session auto-applien (wenn aktiviert)
if (cfg.get("autoApplyViewColors") and if (cfg.get("autoApplyViewColors") and
not sc.sticky.get("_dossier_view_colors_applied")): not sc.sticky.get("_dossier_view_colors_applied")):