harishaseebat92 commited on
Commit
8be052c
·
1 Parent(s): aa49145

include geometry previews in EM_embedded

Browse files
Files changed (1) hide show
  1. em_embedded.py +77 -80
em_embedded.py CHANGED
@@ -42,116 +42,108 @@ except ModuleNotFoundError:
42
  pv.OFF_SCREEN = True
43
 
44
 
45
- class PrefixedStateProxy:
46
- """State wrapper that automatically namespaces EM fields with a prefix."""
47
-
48
- __slots__ = ("_prefix", "_state", "_pending_updates", "_pending_changes", "_cache")
49
-
50
- def __init__(self, prefix: str):
51
- object.__setattr__(self, "_prefix", prefix)
52
- object.__setattr__(self, "_state", None)
53
- object.__setattr__(self, "_pending_updates", {})
54
- object.__setattr__(self, "_pending_changes", [])
55
- object.__setattr__(self, "_cache", {})
56
-
57
- # --- Binding ---
58
- def bind(self, state):
59
- object.__setattr__(self, "_state", state)
60
- if self._pending_updates:
61
- prefixed = {f"{self._prefix}{k}": v for k, v in self._pending_updates.items()}
62
- state.update(prefixed)
63
- self._pending_updates.clear()
64
- for name, value in self._cache.items():
65
- setattr(state, f"{self._prefix}{name}", value)
66
- self._cache.clear()
67
- for names, kwargs, func in self._pending_changes:
68
- prefixed = tuple(f"{self._prefix}{n}" for n in names)
69
- state.change(*prefixed, **kwargs)(func)
 
 
70
  self._pending_changes.clear()
71
 
72
  @property
73
- def bound(self) -> bool:
74
  return self._state is not None
75
 
76
- # --- Core API ---
77
- def update(self, values: dict):
78
- if self._state is None:
79
- self._pending_updates.update(values)
80
  else:
81
- prefixed = {f"{self._prefix}{k}": v for k, v in values.items()}
82
- self._state.update(prefixed)
83
-
84
- def change(self, *names, **kwargs):
85
- prefix = self._prefix
86
 
 
 
 
 
 
87
  def decorator(func):
88
- # Wrap the user's callback to translate prefixed kwargs back to unprefixed
89
- def wrapper(**kw):
90
- translated = {}
91
- for k, v in kw.items():
92
- if k.startswith(prefix):
93
- translated[k[len(prefix):]] = v
94
- else:
95
- translated[k] = v
96
- return func(**translated)
97
-
98
  if self._state is not None:
99
- prefixed = tuple(f"{prefix}{n}" for n in names)
100
- return self._state.change(*prefixed, **kwargs)(wrapper)
101
- self._pending_changes.append((names, kwargs, wrapper))
 
 
102
  return func
103
-
104
  return decorator
105
 
106
- # --- Attribute proxying ---
107
  def __getattr__(self, name):
108
- if name in {"_prefix", "_state", "_pending_updates", "_pending_changes", "_cache", "bind", "bound", "update", "change"}:
109
- return object.__getattribute__(self, name)
110
- if self._state is not None:
111
- return getattr(self._state, f"{self._prefix}{name}")
112
- if name in self._pending_updates:
113
- return self._pending_updates[name]
114
- if name in self._cache:
115
- return self._cache[name]
116
- raise AttributeError(name)
117
 
118
  def __setattr__(self, name, value):
119
- if name in {"_prefix", "_state", "_pending_updates", "_pending_changes", "_cache"}:
120
  object.__setattr__(self, name, value)
121
  elif self._state is not None:
122
- setattr(self._state, f"{self._prefix}{name}", value)
123
  else:
124
- self._cache[name] = value
 
125
 
126
 
127
- class ControllerProxy:
128
- """Controller proxy that queues attribute bindings until a server is set."""
129
-
130
- __slots__ = ("_ctrl", "_pending")
 
131
 
132
  def __init__(self):
133
- object.__setattr__(self, "_ctrl", None)
134
- object.__setattr__(self, "_pending", {})
135
 
136
- def bind(self, ctrl):
137
- object.__setattr__(self, "_ctrl", ctrl)
 
138
  for name, value in self._pending.items():
139
- setattr(ctrl, name, value)
140
  self._pending.clear()
141
 
142
  @property
143
- def bound(self) -> bool:
144
  return self._ctrl is not None
145
 
146
  def __getattr__(self, name):
147
- if name in {"_ctrl", "_pending", "bind", "bound"}:
148
- return object.__getattribute__(self, name)
149
  if self._ctrl is None:
150
- raise AttributeError(f"Controller not set; cannot access '{name}'")
151
  return getattr(self._ctrl, name)
152
 
153
  def __setattr__(self, name, value):
154
- if name in {"_ctrl", "_pending", "bind", "bound"}:
155
  object.__setattr__(self, name, value)
156
  elif self._ctrl is not None:
157
  setattr(self._ctrl, name, value)
@@ -159,9 +151,10 @@ class ControllerProxy:
159
  self._pending[name] = value
160
 
161
 
 
162
  _server = None
163
- state = PrefixedStateProxy("em_")
164
- ctrl = ControllerProxy()
165
 
166
 
167
  def _noop(*_, **__):
@@ -193,6 +186,11 @@ def init_state(force: bool = False):
193
  return
194
  if force:
195
  reset_to_defaults()
 
 
 
 
 
196
 
197
  # Cache for QPU Plotly export/selection (Python-side)
198
  qpu_ts_cache = {
@@ -3797,5 +3795,4 @@ def on_qpu_plot_filter_change(qpu_plot_filter, **kwargs):
3797
  # No-op: updates handled by controller bound to the VSelect to avoid double refresh
3798
  return
3799
 
3800
- # --- Initial Setup Call ---
3801
- update_initial_state_preview()
 
42
  pv.OFF_SCREEN = True
43
 
44
 
45
+ # ============================================================================
46
+ # DEFERRED STATE/CONTROLLER PROXY CLASSES
47
+ # These allow using state.update(), @state.change(), and ctrl.xxx at module
48
+ # load time, then applying them when set_server() is called.
49
+ # ============================================================================
50
+
51
+ class _DeferredStateProxy:
52
+ """
53
+ A proxy that collects state defaults and change decorators at module load time,
54
+ then applies them to the real server.state when bind() is called.
55
+ """
56
+
57
+ def __init__(self):
58
+ self._state = None
59
+ self._defaults = {}
60
+ self._pending_changes = [] # list of (keys, func)
61
+
62
+ def bind(self, real_state):
63
+ """Bind to the real state object and apply all pending operations."""
64
+ self._state = real_state
65
+ # Apply all collected defaults
66
+ if self._defaults:
67
+ real_state.update(self._defaults)
68
+ self._defaults.clear()
69
+ # Apply all pending @state.change decorators
70
+ for keys, func in self._pending_changes:
71
+ real_state.change(*keys)(func)
72
  self._pending_changes.clear()
73
 
74
  @property
75
+ def bound(self):
76
  return self._state is not None
77
 
78
+ def update(self, d):
79
+ """Collect defaults; apply immediately if bound."""
80
+ if self._state is not None:
81
+ self._state.update(d)
82
  else:
83
+ self._defaults.update(d)
 
 
 
 
84
 
85
+ def change(self, *keys):
86
+ """
87
+ Decorator factory that mimics @state.change("key1", "key2").
88
+ If already bound, apply immediately. Otherwise, queue for later.
89
+ """
90
  def decorator(func):
 
 
 
 
 
 
 
 
 
 
91
  if self._state is not None:
92
+ # Already bound - register directly
93
+ self._state.change(*keys)(func)
94
+ else:
95
+ # Queue for later
96
+ self._pending_changes.append((keys, func))
97
  return func
 
98
  return decorator
99
 
 
100
  def __getattr__(self, name):
101
+ if name.startswith("_"):
102
+ raise AttributeError(name)
103
+ if self._state is None:
104
+ raise AttributeError(f"State not bound yet; cannot access '{name}'")
105
+ return getattr(self._state, name)
 
 
 
 
106
 
107
  def __setattr__(self, name, value):
108
+ if name.startswith("_"):
109
  object.__setattr__(self, name, value)
110
  elif self._state is not None:
111
+ setattr(self._state, name, value)
112
  else:
113
+ # Store as a default
114
+ self._defaults[name] = value
115
 
116
 
117
+ class _DeferredControllerProxy:
118
+ """
119
+ A proxy that collects controller attribute assignments at module load time,
120
+ then applies them when bind() is called.
121
+ """
122
 
123
  def __init__(self):
124
+ self._ctrl = None
125
+ self._pending = {}
126
 
127
+ def bind(self, real_ctrl):
128
+ """Bind to the real controller and apply pending attributes."""
129
+ self._ctrl = real_ctrl
130
  for name, value in self._pending.items():
131
+ setattr(real_ctrl, name, value)
132
  self._pending.clear()
133
 
134
  @property
135
+ def bound(self):
136
  return self._ctrl is not None
137
 
138
  def __getattr__(self, name):
139
+ if name.startswith("_"):
140
+ raise AttributeError(name)
141
  if self._ctrl is None:
142
+ raise AttributeError(f"Controller not bound yet; cannot access '{name}'")
143
  return getattr(self._ctrl, name)
144
 
145
  def __setattr__(self, name, value):
146
+ if name.startswith("_"):
147
  object.__setattr__(self, name, value)
148
  elif self._ctrl is not None:
149
  setattr(self._ctrl, name, value)
 
151
  self._pending[name] = value
152
 
153
 
154
+ # Module-level proxies (will be bound when set_server is called)
155
  _server = None
156
+ state = _DeferredStateProxy()
157
+ ctrl = _DeferredControllerProxy()
158
 
159
 
160
  def _noop(*_, **__):
 
186
  return
187
  if force:
188
  reset_to_defaults()
189
+ # Initialize preview after state is bound
190
+ try:
191
+ update_initial_state_preview()
192
+ except Exception:
193
+ pass # Ignore if preview can't be initialized yet
194
 
195
  # Cache for QPU Plotly export/selection (Python-side)
196
  qpu_ts_cache = {
 
3795
  # No-op: updates handled by controller bound to the VSelect to avoid double refresh
3796
  return
3797
 
3798
+ # Note: update_initial_state_preview() is called from init_state() after the server is bound