syd 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- syd/__init__.py +11 -1
- syd/flask_deployment/__init__.py +0 -0
- syd/flask_deployment/components.py +497 -0
- syd/flask_deployment/deployer.py +338 -0
- syd/flask_deployment/static/css/styles.css +39 -0
- syd/flask_deployment/static/js/components.js +51 -0
- syd/flask_deployment/static/js/viewer.js +0 -0
- syd/flask_deployment/templates/base.html +26 -0
- syd/flask_deployment/templates/viewer.html +97 -0
- syd/interactive_viewer.py +1314 -87
- syd/notebook_deployment/__init__.py +1 -0
- syd/notebook_deployment/deployer.py +254 -0
- syd/notebook_deployment/widgets.py +478 -0
- syd/parameters.py +1421 -157
- syd-0.1.6.dist-info/METADATA +106 -0
- syd-0.1.6.dist-info/RECORD +18 -0
- syd/notebook_deploy.py +0 -277
- syd-0.1.4.dist-info/METADATA +0 -33
- syd-0.1.4.dist-info/RECORD +0 -8
- {syd-0.1.4.dist-info → syd-0.1.6.dist-info}/WHEEL +0 -0
- {syd-0.1.4.dist-info → syd-0.1.6.dist-info}/licenses/LICENSE +0 -0
syd/interactive_viewer.py
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
|
-
from typing import List, Any, Callable, Dict, Tuple
|
|
2
|
-
from functools import wraps
|
|
1
|
+
from typing import List, Any, Callable, Dict, Tuple, Union, Optional
|
|
2
|
+
from functools import wraps, partial
|
|
3
|
+
import inspect
|
|
3
4
|
from contextlib import contextmanager
|
|
4
5
|
from matplotlib.figure import Figure
|
|
5
6
|
|
|
6
|
-
from .parameters import
|
|
7
|
+
from .parameters import (
|
|
8
|
+
ParameterType,
|
|
9
|
+
ActionType,
|
|
10
|
+
Parameter,
|
|
11
|
+
ParameterAddError,
|
|
12
|
+
ParameterUpdateError,
|
|
13
|
+
)
|
|
7
14
|
|
|
8
15
|
|
|
9
|
-
|
|
16
|
+
class _NoUpdate:
|
|
17
|
+
"""Singleton class to represent a non-update in parameter operations."""
|
|
18
|
+
|
|
19
|
+
_instance = None
|
|
20
|
+
|
|
21
|
+
def __new__(cls):
|
|
22
|
+
if cls._instance is None:
|
|
23
|
+
cls._instance = super().__new__(cls)
|
|
24
|
+
return cls._instance
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Create the singleton instance
|
|
28
|
+
_NO_UPDATE = _NoUpdate()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_parameter_operation(
|
|
32
|
+
operation: str, parameter_type: Union[ParameterType, ActionType]
|
|
33
|
+
) -> Callable:
|
|
10
34
|
"""
|
|
11
35
|
Decorator that validates parameter operations for the InteractiveViewer class.
|
|
12
36
|
|
|
@@ -36,25 +60,63 @@ def validate_parameter_operation(operation: str, parameter_type: ParameterType)
|
|
|
36
60
|
"""
|
|
37
61
|
|
|
38
62
|
def decorator(func: Callable) -> Callable:
|
|
63
|
+
if operation not in ["add", "update"]:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
"Incorrect use of validate_parameter_operation decorator. Must be called with 'add' or 'update' as the first argument."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Validate operation matches method name (add/update)
|
|
69
|
+
if not func.__name__.startswith(operation):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Invalid operation type specified ({operation}) for method {func.__name__}"
|
|
72
|
+
)
|
|
73
|
+
|
|
39
74
|
@wraps(func)
|
|
40
|
-
def wrapper(self: "InteractiveViewer", name:
|
|
41
|
-
# Validate
|
|
42
|
-
if not
|
|
43
|
-
|
|
75
|
+
def wrapper(self: "InteractiveViewer", name: Any, *args, **kwargs):
|
|
76
|
+
# Validate parameter name is a string
|
|
77
|
+
if not isinstance(name, str):
|
|
78
|
+
if operation == "add":
|
|
79
|
+
raise ParameterAddError(
|
|
80
|
+
name, parameter_type.name, "Parameter name must be a string"
|
|
81
|
+
)
|
|
82
|
+
elif operation == "update":
|
|
83
|
+
raise ParameterUpdateError(
|
|
84
|
+
name, parameter_type.name, "Parameter name must be a string"
|
|
85
|
+
)
|
|
44
86
|
|
|
45
87
|
# Validate deployment state
|
|
46
88
|
if operation == "add" and self._app_deployed:
|
|
47
|
-
raise RuntimeError(
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
"The app is currently deployed, cannot add a new parameter right now."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if operation == "add":
|
|
94
|
+
if name in self.parameters:
|
|
95
|
+
raise ParameterAddError(
|
|
96
|
+
name, parameter_type.name, "Parameter already exists!"
|
|
97
|
+
)
|
|
48
98
|
|
|
49
99
|
# For updates, validate parameter existence and type
|
|
50
100
|
if operation == "update":
|
|
51
101
|
if name not in self.parameters:
|
|
52
|
-
raise
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
102
|
+
raise ParameterUpdateError(
|
|
103
|
+
name,
|
|
104
|
+
parameter_type.name,
|
|
105
|
+
"Parameter not found - you can only update registered parameters!",
|
|
106
|
+
)
|
|
107
|
+
if not isinstance(self.parameters[name], parameter_type.value):
|
|
108
|
+
msg = f"Parameter called {name} was found but is registered as a different parameter type ({type(self.parameters[name])}). Expecting {parameter_type.value}."
|
|
109
|
+
raise ParameterUpdateError(name, parameter_type.name, msg)
|
|
56
110
|
|
|
57
|
-
|
|
111
|
+
try:
|
|
112
|
+
return func(self, name, *args, **kwargs)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
if operation == "add":
|
|
115
|
+
raise ParameterAddError(name, parameter_type.name, str(e)) from e
|
|
116
|
+
elif operation == "update":
|
|
117
|
+
raise ParameterUpdateError(name, parameter_type.name, str(e)) from e
|
|
118
|
+
else:
|
|
119
|
+
raise e
|
|
58
120
|
|
|
59
121
|
return wrapper
|
|
60
122
|
|
|
@@ -62,28 +124,129 @@ def validate_parameter_operation(operation: str, parameter_type: ParameterType)
|
|
|
62
124
|
|
|
63
125
|
|
|
64
126
|
class InteractiveViewer:
|
|
127
|
+
"""
|
|
128
|
+
Base class for creating interactive matplotlib figures with GUI controls.
|
|
129
|
+
|
|
130
|
+
This class helps you create interactive visualizations by adding GUI elements
|
|
131
|
+
(like sliders, dropdowns, etc.) that update your plot in real-time. To use it:
|
|
132
|
+
|
|
133
|
+
1. Create a subclass and implement the plot() method
|
|
134
|
+
2. Add parameters using add_* methods before deploying
|
|
135
|
+
3. Use on_change() to make parameters update the plot
|
|
136
|
+
4. Use update_* methods to update parameter values and properties
|
|
137
|
+
5. Deploy the app to show the interactive figure
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> class MyViewer(InteractiveViewer):
|
|
142
|
+
... def plot(self, state: Dict[str, Any]):
|
|
143
|
+
... fig = plt.figure()
|
|
144
|
+
... plt.plot([0, state['x']])
|
|
145
|
+
... return fig
|
|
146
|
+
...
|
|
147
|
+
... def update_based_on_x(self, state: Dict[str, Any]):
|
|
148
|
+
... self.update_float('x', value=state['x'])
|
|
149
|
+
...
|
|
150
|
+
>>> viewer = MyViewer()
|
|
151
|
+
>>> viewer.add_float('x', value=1.0, min_value=0, max_value=10)
|
|
152
|
+
>>> viewer.on_change('x', viewer.update_based_on_x)
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
parameters: Dict[str, Parameter]
|
|
156
|
+
callbacks: Dict[str, List[Callable]]
|
|
157
|
+
state: Dict[str, Any]
|
|
158
|
+
_app_deployed: bool
|
|
159
|
+
_in_callbacks: bool
|
|
160
|
+
|
|
65
161
|
def __new__(cls, *args, **kwargs):
|
|
66
162
|
instance = super().__new__(cls)
|
|
67
163
|
instance.parameters = {}
|
|
68
164
|
instance.callbacks = {}
|
|
69
165
|
instance.state = {}
|
|
70
166
|
instance._app_deployed = False
|
|
167
|
+
instance._in_callbacks = False
|
|
71
168
|
return instance
|
|
72
169
|
|
|
73
|
-
def __init__(self):
|
|
74
|
-
self.parameters: Dict[str, Parameter] = {}
|
|
75
|
-
self.callbacks: Dict[str, List[Callable]] = {}
|
|
76
|
-
self.state = {}
|
|
77
|
-
self._app_deployed = False
|
|
78
|
-
|
|
79
170
|
def get_state(self) -> Dict[str, Any]:
|
|
171
|
+
"""
|
|
172
|
+
Get the current values of all parameters.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
dict
|
|
177
|
+
Dictionary mapping parameter names to their current values
|
|
178
|
+
|
|
179
|
+
Examples
|
|
180
|
+
--------
|
|
181
|
+
>>> viewer.add_float('x', value=1.0, min_value=0, max_value=10)
|
|
182
|
+
>>> viewer.add_text('label', value='data')
|
|
183
|
+
>>> viewer.get_state()
|
|
184
|
+
{'x': 1.0, 'label': 'data'}
|
|
185
|
+
"""
|
|
80
186
|
return {name: param.value for name, param in self.parameters.items()}
|
|
81
187
|
|
|
82
|
-
def plot(self,
|
|
83
|
-
|
|
188
|
+
def plot(self, state: Dict[str, Any]) -> Figure:
|
|
189
|
+
"""Create and return a matplotlib figure.
|
|
190
|
+
|
|
191
|
+
This is a placeholder. You must either:
|
|
192
|
+
|
|
193
|
+
1. Call set_plot() with your plotting function
|
|
194
|
+
This will look like this:
|
|
195
|
+
>>> def plot(viewer, state):
|
|
196
|
+
>>> ... generate figure, plot stuff ...
|
|
197
|
+
>>> return fig
|
|
198
|
+
>>> viewer.set_plot(plot))
|
|
199
|
+
|
|
200
|
+
2. Subclass InteractiveViewer and override this method
|
|
201
|
+
This will look like this:
|
|
202
|
+
>>> class YourViewer(InteractiveViewer):
|
|
203
|
+
>>> def plot(self, state):
|
|
204
|
+
>>> ... generate figure, plot stuff ...
|
|
205
|
+
>>> return fig
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
state : dict
|
|
210
|
+
Current parameter values
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
matplotlib.figure.Figure
|
|
215
|
+
The figure to display
|
|
216
|
+
|
|
217
|
+
Notes
|
|
218
|
+
-----
|
|
219
|
+
- Create a new figure each time, don't reuse old ones
|
|
220
|
+
- Access parameter values using state['param_name']
|
|
221
|
+
- Access your viewer class using self (or viewer for the set_plot() method)
|
|
222
|
+
- Return the figure object, don't call plt.show()!
|
|
223
|
+
"""
|
|
224
|
+
raise NotImplementedError(
|
|
225
|
+
"Plot method not implemented. Either subclass "
|
|
226
|
+
"InteractiveViewer and override plot(), or use "
|
|
227
|
+
"set_plot() to provide a plotting function."
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def set_plot(self, func: Callable) -> None:
|
|
231
|
+
"""Set the plot method for the viewer"""
|
|
232
|
+
self.plot = self._prepare_function(func, context="Setting plot:")
|
|
233
|
+
|
|
234
|
+
def deploy(self, env: str = "notebook", **kwargs):
|
|
235
|
+
"""Deploy the app in a notebook or standalone environment"""
|
|
236
|
+
if env == "notebook":
|
|
237
|
+
from .notebook_deployment import NotebookDeployment
|
|
238
|
+
|
|
239
|
+
deployer = NotebookDeployment(self, **kwargs)
|
|
240
|
+
deployer.deploy()
|
|
241
|
+
|
|
242
|
+
return self
|
|
243
|
+
else:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
f"Unsupported environment: {env}, only 'notebook' is supported right now."
|
|
246
|
+
)
|
|
84
247
|
|
|
85
248
|
@contextmanager
|
|
86
|
-
def
|
|
249
|
+
def _deploy_app(self):
|
|
87
250
|
"""Internal context manager to control app deployment state"""
|
|
88
251
|
self._app_deployed = True
|
|
89
252
|
try:
|
|
@@ -91,23 +254,208 @@ class InteractiveViewer:
|
|
|
91
254
|
finally:
|
|
92
255
|
self._app_deployed = False
|
|
93
256
|
|
|
257
|
+
def _prepare_function(
|
|
258
|
+
self,
|
|
259
|
+
func: Callable,
|
|
260
|
+
context: Optional[str] = "",
|
|
261
|
+
) -> Callable:
|
|
262
|
+
# Check if func is Callable
|
|
263
|
+
if not callable(func):
|
|
264
|
+
raise ValueError(f"Function {func} is not callable")
|
|
265
|
+
|
|
266
|
+
# Handle partial functions
|
|
267
|
+
if isinstance(func, partial):
|
|
268
|
+
# Create new partial with self as first arg if not already there
|
|
269
|
+
get_self = (
|
|
270
|
+
lambda func: hasattr(func.func, "__self__") and func.func.__self__
|
|
271
|
+
)
|
|
272
|
+
get_name = lambda func: func.func.__name__
|
|
273
|
+
else:
|
|
274
|
+
get_self = lambda func: hasattr(func, "__self__") and func.__self__
|
|
275
|
+
get_name = lambda func: func.__name__
|
|
276
|
+
|
|
277
|
+
# Check if it's a class method
|
|
278
|
+
class_method = get_self(func) is self.__class__
|
|
279
|
+
if class_method:
|
|
280
|
+
raise ValueError(context + "Class methods are not supported.")
|
|
281
|
+
|
|
282
|
+
# Check if it's a bound method to another instance other than this one
|
|
283
|
+
if get_self(func) and get_self(func) is not self:
|
|
284
|
+
raise ValueError(
|
|
285
|
+
context + "Bound methods to other instances are not supported."
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Get function signature
|
|
289
|
+
try:
|
|
290
|
+
params = list(inspect.signature(func).parameters.values())
|
|
291
|
+
except ValueError:
|
|
292
|
+
# Handle built-ins or other objects without signatures
|
|
293
|
+
raise ValueError(context + f"Cannot inspect function signature for {func}")
|
|
294
|
+
|
|
295
|
+
# Look through params and check if there are two positional parameters (including self for bound methods)
|
|
296
|
+
bound_method = get_self(func) is self
|
|
297
|
+
positional_params = 0 + bound_method
|
|
298
|
+
optional_part = ""
|
|
299
|
+
for param in params:
|
|
300
|
+
# Check if it's a positional parameter. If it is, count it.
|
|
301
|
+
# As long as we have less than 2 positional parameters, we're good.
|
|
302
|
+
# When we already have 2 positional parameters, we need to make sure any other positional parameters have defaults.
|
|
303
|
+
if param.kind in (
|
|
304
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
305
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
306
|
+
):
|
|
307
|
+
if positional_params < 2:
|
|
308
|
+
positional_params += 1
|
|
309
|
+
else:
|
|
310
|
+
if param.default == inspect.Parameter.empty:
|
|
311
|
+
positional_params += 1
|
|
312
|
+
else:
|
|
313
|
+
optional_part += f", {param.name}={param.default!r}"
|
|
314
|
+
elif param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
315
|
+
optional_part += f", **{param.name}"
|
|
316
|
+
elif param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
317
|
+
optional_part += (
|
|
318
|
+
f", {param.name}={param.default!r}"
|
|
319
|
+
if param.default != inspect.Parameter.empty
|
|
320
|
+
else f", {param.name}"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
if positional_params != 2:
|
|
324
|
+
func_name = get_name(func)
|
|
325
|
+
if isinstance(func, partial):
|
|
326
|
+
func_sig = str(inspect.signature(func))
|
|
327
|
+
if bound_method:
|
|
328
|
+
func_sig = "(" + "self, " + func_sig[1:]
|
|
329
|
+
msg = (
|
|
330
|
+
context
|
|
331
|
+
+ "\n"
|
|
332
|
+
+ f"Your partial function '{func_name}' has an incorrect signature.\n"
|
|
333
|
+
"Partial functions must have exactly two positional parameters.\n"
|
|
334
|
+
"The first parameter should be self -- it corresponds to the viewer.\n"
|
|
335
|
+
"The second parameter should be state -- a dictionary of the current state of the viewer.\n"
|
|
336
|
+
"\nYour partial function effectivelylooks like this:\n"
|
|
337
|
+
f"def {func_name}{func_sig}:\n"
|
|
338
|
+
" ... your function code ..."
|
|
339
|
+
)
|
|
340
|
+
raise ValueError(msg)
|
|
341
|
+
|
|
342
|
+
if bound_method:
|
|
343
|
+
original_method = getattr(get_self(func).__class__, get_name(func))
|
|
344
|
+
func_sig = str(inspect.signature(original_method))
|
|
345
|
+
|
|
346
|
+
msg = (
|
|
347
|
+
context + "\n"
|
|
348
|
+
f"Your bound method '{func_name}{func_sig}' has an incorrect signature.\n"
|
|
349
|
+
"Bound methods must have exactly one positional parameter in addition to self.\n"
|
|
350
|
+
"The first parameter should be self -- it corresponds to the viewer.\n"
|
|
351
|
+
"The second parameter should be state -- a dictionary of the current state of the viewer.\n"
|
|
352
|
+
"\nYour method looks like this:\n"
|
|
353
|
+
"class YourViewer(InteractiveViewer):\n"
|
|
354
|
+
f" def {func_name}{func_sig}:\n"
|
|
355
|
+
" ... your function code ...\n"
|
|
356
|
+
"\nIt should look like this:\n"
|
|
357
|
+
"class YourViewer(InteractiveViewer):\n"
|
|
358
|
+
f" def {func_name}(self, state{optional_part}):\n"
|
|
359
|
+
" ... your function code ..."
|
|
360
|
+
)
|
|
361
|
+
raise ValueError(msg)
|
|
362
|
+
else:
|
|
363
|
+
func_sig = str(inspect.signature(func))
|
|
364
|
+
|
|
365
|
+
msg = (
|
|
366
|
+
context + "\n"
|
|
367
|
+
f"Your function '{func_name}{func_sig}' has an incorrect signature.\n"
|
|
368
|
+
"External functions must have exactly two positional parameters.\n"
|
|
369
|
+
"The first parameter should be viewer -- it corresponds to the viewer.\n"
|
|
370
|
+
"The second parameter should be state -- a dictionary of the current state of the viewer.\n"
|
|
371
|
+
"\nYour function looks like this:\n"
|
|
372
|
+
f"def {func_name}{func_sig}:\n"
|
|
373
|
+
" ... your function code ...\n"
|
|
374
|
+
"\nIt should look like this:\n"
|
|
375
|
+
f"def {func_name}(viewer, state{optional_part}):\n"
|
|
376
|
+
" ... your function code ..."
|
|
377
|
+
)
|
|
378
|
+
raise ValueError(msg)
|
|
379
|
+
|
|
380
|
+
# If not a bound method, wrap it to inject self when called with just the state
|
|
381
|
+
if bound_method:
|
|
382
|
+
return func
|
|
383
|
+
|
|
384
|
+
@wraps(func)
|
|
385
|
+
def func_with_self(*args, **kwargs):
|
|
386
|
+
return func(self, *args, **kwargs)
|
|
387
|
+
|
|
388
|
+
return func_with_self
|
|
389
|
+
|
|
94
390
|
def perform_callbacks(self, name: str) -> bool:
|
|
95
391
|
"""Perform callbacks for all parameters that have changed"""
|
|
96
|
-
if
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
392
|
+
if self._in_callbacks:
|
|
393
|
+
return
|
|
394
|
+
try:
|
|
395
|
+
self._in_callbacks = True
|
|
396
|
+
if name in self.callbacks:
|
|
397
|
+
state = self.get_state()
|
|
398
|
+
for callback in self.callbacks[name]:
|
|
399
|
+
callback(state)
|
|
400
|
+
finally:
|
|
401
|
+
self._in_callbacks = False
|
|
402
|
+
|
|
403
|
+
def on_change(self, parameter_name: Union[str, List[str]], callback: Callable):
|
|
404
|
+
"""
|
|
405
|
+
Register a function to run when parameters change.
|
|
406
|
+
|
|
407
|
+
The callback function will receive a dictionary of all current parameter
|
|
408
|
+
values whenever any of the specified parameters change.
|
|
409
|
+
|
|
410
|
+
Parameters
|
|
411
|
+
----------
|
|
412
|
+
parameter_name : str or list of str
|
|
413
|
+
Name(s) of parameters to watch for changes
|
|
414
|
+
callback : callable
|
|
415
|
+
Function to call when changes occur. Should accept a single dict argument
|
|
416
|
+
containing the current state.
|
|
417
|
+
|
|
418
|
+
Examples
|
|
419
|
+
--------
|
|
420
|
+
>>> def update_plot(state):
|
|
421
|
+
... print(f"x changed to {state['x']}")
|
|
422
|
+
>>> viewer.on_change('x', update_plot)
|
|
423
|
+
>>> viewer.on_change(['x', 'y'], lambda s: viewer.plot()) # Update on either change
|
|
424
|
+
"""
|
|
425
|
+
if isinstance(parameter_name, str):
|
|
426
|
+
parameter_name = [parameter_name]
|
|
427
|
+
|
|
428
|
+
callback = self._prepare_function(
|
|
429
|
+
callback,
|
|
430
|
+
context="Setting on_change callback:",
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
for param_name in parameter_name:
|
|
434
|
+
if param_name not in self.parameters:
|
|
435
|
+
raise ValueError(f"Parameter '{param_name}' is not registered!")
|
|
436
|
+
if param_name not in self.callbacks:
|
|
437
|
+
self.callbacks[param_name] = []
|
|
438
|
+
self.callbacks[param_name].append(callback)
|
|
108
439
|
|
|
109
440
|
def set_parameter_value(self, name: str, value: Any) -> None:
|
|
110
|
-
"""
|
|
441
|
+
"""
|
|
442
|
+
Update a parameter's value and trigger any callbacks.
|
|
443
|
+
|
|
444
|
+
This is a lower-level method - usually you'll want to use the update_*
|
|
445
|
+
methods instead (e.g., update_float, update_text, etc.).
|
|
446
|
+
|
|
447
|
+
Parameters
|
|
448
|
+
----------
|
|
449
|
+
name : str
|
|
450
|
+
Name of the parameter to update
|
|
451
|
+
value : Any
|
|
452
|
+
New value for the parameter
|
|
453
|
+
|
|
454
|
+
Raises
|
|
455
|
+
------
|
|
456
|
+
ValueError
|
|
457
|
+
If the parameter doesn't exist or the value is invalid
|
|
458
|
+
"""
|
|
111
459
|
if name not in self.parameters:
|
|
112
460
|
raise ValueError(f"Parameter {name} not found")
|
|
113
461
|
|
|
@@ -119,92 +467,971 @@ class InteractiveViewer:
|
|
|
119
467
|
|
|
120
468
|
# -------------------- parameter registration methods --------------------
|
|
121
469
|
@validate_parameter_operation("add", ParameterType.text)
|
|
122
|
-
def add_text(self, name: str,
|
|
123
|
-
|
|
470
|
+
def add_text(self, name: str, *, value: str) -> None:
|
|
471
|
+
"""
|
|
472
|
+
Add a text input parameter to the viewer.
|
|
473
|
+
|
|
474
|
+
Creates a text box in the GUI that accepts any string input.
|
|
475
|
+
See :class:`~syd.parameters.TextParameter` for details.
|
|
476
|
+
|
|
477
|
+
Parameters
|
|
478
|
+
----------
|
|
479
|
+
name : str
|
|
480
|
+
Name of the parameter (used as label in GUI)
|
|
481
|
+
value : str
|
|
482
|
+
Initial text value
|
|
483
|
+
|
|
484
|
+
Examples
|
|
485
|
+
--------
|
|
486
|
+
>>> viewer.add_text('title', value='My Plot')
|
|
487
|
+
>>> viewer.get_state()['title']
|
|
488
|
+
'My Plot'
|
|
489
|
+
"""
|
|
490
|
+
try:
|
|
491
|
+
new_param = ParameterType.text.value(name, value)
|
|
492
|
+
except Exception as e:
|
|
493
|
+
raise ParameterAddError(name, "text", str(e)) from e
|
|
494
|
+
else:
|
|
495
|
+
self.parameters[name] = new_param
|
|
496
|
+
|
|
497
|
+
@validate_parameter_operation("add", ParameterType.boolean)
|
|
498
|
+
def add_boolean(self, name: str, *, value: bool) -> None:
|
|
499
|
+
"""
|
|
500
|
+
Add a boolean parameter to the viewer.
|
|
501
|
+
|
|
502
|
+
Creates a checkbox in the GUI that can be toggled on/off.
|
|
503
|
+
See :class:`~syd.parameters.BooleanParameter` for details.
|
|
504
|
+
|
|
505
|
+
Parameters
|
|
506
|
+
----------
|
|
507
|
+
name : str
|
|
508
|
+
Name of the parameter (used as label in GUI)
|
|
509
|
+
value : bool
|
|
510
|
+
Initial state (True=checked, False=unchecked)
|
|
511
|
+
|
|
512
|
+
Examples
|
|
513
|
+
--------
|
|
514
|
+
>>> viewer.add_boolean('show_grid', value=True)
|
|
515
|
+
>>> viewer.get_state()['show_grid']
|
|
516
|
+
True
|
|
517
|
+
"""
|
|
518
|
+
try:
|
|
519
|
+
new_param = ParameterType.boolean.value(name, value)
|
|
520
|
+
except Exception as e:
|
|
521
|
+
raise ParameterAddError(name, "boolean", str(e)) from e
|
|
522
|
+
else:
|
|
523
|
+
self.parameters[name] = new_param
|
|
124
524
|
|
|
125
525
|
@validate_parameter_operation("add", ParameterType.selection)
|
|
126
|
-
def add_selection(self, name: str,
|
|
127
|
-
|
|
526
|
+
def add_selection(self, name: str, *, value: Any, options: List[Any]) -> None:
|
|
527
|
+
"""
|
|
528
|
+
Add a single-selection parameter to the viewer.
|
|
529
|
+
|
|
530
|
+
Creates a dropdown menu in the GUI where users can select one option.
|
|
531
|
+
See :class:`~syd.parameters.SelectionParameter` for details.
|
|
532
|
+
|
|
533
|
+
Parameters
|
|
534
|
+
----------
|
|
535
|
+
name : str
|
|
536
|
+
Name of the parameter (used as label in GUI)
|
|
537
|
+
value : Any
|
|
538
|
+
Initially selected value (must be one of the options)
|
|
539
|
+
options : list
|
|
540
|
+
List of values that can be selected
|
|
541
|
+
|
|
542
|
+
Examples
|
|
543
|
+
--------
|
|
544
|
+
>>> viewer.add_selection('color', value='red',
|
|
545
|
+
... options=['red', 'green', 'blue'])
|
|
546
|
+
>>> viewer.get_state()['color']
|
|
547
|
+
'red'
|
|
548
|
+
"""
|
|
549
|
+
try:
|
|
550
|
+
new_param = ParameterType.selection.value(name, value, options)
|
|
551
|
+
except Exception as e:
|
|
552
|
+
raise ParameterAddError(name, "selection", str(e)) from e
|
|
553
|
+
else:
|
|
554
|
+
self.parameters[name] = new_param
|
|
128
555
|
|
|
129
556
|
@validate_parameter_operation("add", ParameterType.multiple_selection)
|
|
130
|
-
def add_multiple_selection(
|
|
131
|
-
self
|
|
557
|
+
def add_multiple_selection(
|
|
558
|
+
self, name: str, *, value: List[Any], options: List[Any]
|
|
559
|
+
) -> None:
|
|
560
|
+
"""
|
|
561
|
+
Add a multiple-selection parameter to the viewer.
|
|
132
562
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
563
|
+
Creates a set of checkboxes or a multi-select dropdown in the GUI where
|
|
564
|
+
users can select any number of options.
|
|
565
|
+
See :class:`~syd.parameters.MultipleSelectionParameter` for details.
|
|
566
|
+
|
|
567
|
+
Parameters
|
|
568
|
+
----------
|
|
569
|
+
name : str
|
|
570
|
+
Name of the parameter (used as label in GUI)
|
|
571
|
+
value : list
|
|
572
|
+
Initially selected values (must all be in options)
|
|
573
|
+
options : list
|
|
574
|
+
List of values that can be selected
|
|
575
|
+
|
|
576
|
+
Examples
|
|
577
|
+
--------
|
|
578
|
+
>>> viewer.add_multiple_selection('toppings',
|
|
579
|
+
... value=['cheese'],
|
|
580
|
+
... options=['cheese', 'pepperoni', 'mushrooms'])
|
|
581
|
+
>>> viewer.get_state()['toppings']
|
|
582
|
+
['cheese']
|
|
583
|
+
"""
|
|
584
|
+
try:
|
|
585
|
+
new_param = ParameterType.multiple_selection.value(name, value, options)
|
|
586
|
+
except Exception as e:
|
|
587
|
+
raise ParameterAddError(name, "multiple_selection", str(e)) from e
|
|
588
|
+
else:
|
|
589
|
+
self.parameters[name] = new_param
|
|
136
590
|
|
|
137
591
|
@validate_parameter_operation("add", ParameterType.integer)
|
|
138
|
-
def add_integer(
|
|
139
|
-
self
|
|
592
|
+
def add_integer(
|
|
593
|
+
self,
|
|
594
|
+
name: str,
|
|
595
|
+
*,
|
|
596
|
+
value: Union[float, int],
|
|
597
|
+
min_value: Union[float, int],
|
|
598
|
+
max_value: Union[float, int],
|
|
599
|
+
) -> None:
|
|
600
|
+
"""
|
|
601
|
+
Add an integer parameter to the viewer.
|
|
602
|
+
|
|
603
|
+
Creates a slider in the GUI that lets users select whole numbers between
|
|
604
|
+
min_value and max_value. Values will be clamped to stay within bounds.
|
|
605
|
+
See :class:`~syd.parameters.IntegerParameter` for details.
|
|
606
|
+
|
|
607
|
+
Parameters
|
|
608
|
+
----------
|
|
609
|
+
name : str
|
|
610
|
+
Name of the parameter (used as label in GUI)
|
|
611
|
+
value : int
|
|
612
|
+
Initial value (will be clamped between min_value and max_value)
|
|
613
|
+
min_value : int
|
|
614
|
+
Minimum allowed value
|
|
615
|
+
max_value : int
|
|
616
|
+
Maximum allowed value
|
|
617
|
+
|
|
618
|
+
Examples
|
|
619
|
+
--------
|
|
620
|
+
>>> viewer.add_integer('count', value=5, min_value=0, max_value=10)
|
|
621
|
+
>>> viewer.get_state()['count']
|
|
622
|
+
5
|
|
623
|
+
>>> viewer.update_integer('count', value=15) # Will be clamped to 10
|
|
624
|
+
>>> viewer.get_state()['count']
|
|
625
|
+
10
|
|
626
|
+
"""
|
|
627
|
+
try:
|
|
628
|
+
new_param = ParameterType.integer.value(
|
|
629
|
+
name,
|
|
630
|
+
value,
|
|
631
|
+
min_value,
|
|
632
|
+
max_value,
|
|
633
|
+
)
|
|
634
|
+
except Exception as e:
|
|
635
|
+
raise ParameterAddError(name, "number", str(e)) from e
|
|
636
|
+
else:
|
|
637
|
+
self.parameters[name] = new_param
|
|
140
638
|
|
|
141
639
|
@validate_parameter_operation("add", ParameterType.float)
|
|
142
|
-
def add_float(
|
|
143
|
-
self
|
|
640
|
+
def add_float(
|
|
641
|
+
self,
|
|
642
|
+
name: str,
|
|
643
|
+
*,
|
|
644
|
+
value: Union[float, int],
|
|
645
|
+
min_value: Union[float, int],
|
|
646
|
+
max_value: Union[float, int],
|
|
647
|
+
step: float = 0.1,
|
|
648
|
+
) -> None:
|
|
649
|
+
"""
|
|
650
|
+
Add a decimal number parameter to the viewer.
|
|
651
|
+
|
|
652
|
+
Creates a slider in the GUI that lets users select numbers between
|
|
653
|
+
min_value and max_value. Values will be rounded to the nearest step
|
|
654
|
+
and clamped to stay within bounds.
|
|
655
|
+
|
|
656
|
+
Parameters
|
|
657
|
+
----------
|
|
658
|
+
name : str
|
|
659
|
+
Name of the parameter (used as label in GUI)
|
|
660
|
+
value : float
|
|
661
|
+
Initial value (will be clamped between min_value and max_value)
|
|
662
|
+
min_value : float
|
|
663
|
+
Minimum allowed value
|
|
664
|
+
max_value : float
|
|
665
|
+
Maximum allowed value
|
|
666
|
+
step : float, optional
|
|
667
|
+
Size of each increment (default: 0.1)
|
|
144
668
|
|
|
145
|
-
|
|
146
|
-
|
|
669
|
+
Examples
|
|
670
|
+
--------
|
|
671
|
+
>>> viewer.add_float('temperature', value=20.0,
|
|
672
|
+
... min_value=0.0, max_value=100.0, step=0.5)
|
|
673
|
+
>>> viewer.get_state()['temperature']
|
|
674
|
+
20.0
|
|
675
|
+
>>> viewer.update_float('temperature', value=20.7) # Will round to 20.5
|
|
676
|
+
>>> viewer.get_state()['temperature']
|
|
677
|
+
20.5
|
|
678
|
+
"""
|
|
679
|
+
try:
|
|
680
|
+
new_param = ParameterType.float.value(
|
|
681
|
+
name,
|
|
682
|
+
value,
|
|
683
|
+
min_value,
|
|
684
|
+
max_value,
|
|
685
|
+
step,
|
|
686
|
+
)
|
|
687
|
+
except Exception as e:
|
|
688
|
+
raise ParameterAddError(name, "number", str(e)) from e
|
|
689
|
+
else:
|
|
690
|
+
self.parameters[name] = new_param
|
|
691
|
+
|
|
692
|
+
@validate_parameter_operation("add", ParameterType.integer_range)
|
|
693
|
+
def add_integer_range(
|
|
147
694
|
self,
|
|
148
695
|
name: str,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
696
|
+
*,
|
|
697
|
+
value: Tuple[Union[float, int], Union[float, int]],
|
|
698
|
+
min_value: Union[float, int],
|
|
699
|
+
max_value: Union[float, int],
|
|
152
700
|
) -> None:
|
|
153
|
-
|
|
701
|
+
"""
|
|
702
|
+
Add a range parameter for whole numbers to the viewer.
|
|
703
|
+
|
|
704
|
+
Creates a range slider in the GUI that lets users select a range of integers
|
|
705
|
+
between min_value and max_value. The range is specified as (low, high) and
|
|
706
|
+
both values will be clamped to stay within bounds.
|
|
707
|
+
See :class:`~syd.parameters.IntegerRangeParameter` for details.
|
|
708
|
+
|
|
709
|
+
Parameters
|
|
710
|
+
----------
|
|
711
|
+
name : str
|
|
712
|
+
Name of the parameter (used as label in GUI)
|
|
713
|
+
value : tuple[int, int]
|
|
714
|
+
Initial (low, high) values
|
|
715
|
+
min_value : int
|
|
716
|
+
Minimum allowed value for both low and high
|
|
717
|
+
max_value : int
|
|
718
|
+
Maximum allowed value for both low and high
|
|
719
|
+
|
|
720
|
+
Examples
|
|
721
|
+
--------
|
|
722
|
+
>>> viewer.add_integer_range('age_range',
|
|
723
|
+
... value=(25, 35),
|
|
724
|
+
... min_value=18, max_value=100)
|
|
725
|
+
>>> viewer.get_state()['age_range']
|
|
726
|
+
(25, 35)
|
|
727
|
+
>>> # Values will be swapped if low > high
|
|
728
|
+
>>> viewer.update_integer_range('age_range', value=(40, 30))
|
|
729
|
+
>>> viewer.get_state()['age_range']
|
|
730
|
+
(30, 40)
|
|
731
|
+
"""
|
|
732
|
+
try:
|
|
733
|
+
new_param = ParameterType.integer_range.value(
|
|
734
|
+
name,
|
|
735
|
+
value,
|
|
736
|
+
min_value,
|
|
737
|
+
max_value,
|
|
738
|
+
)
|
|
739
|
+
except Exception as e:
|
|
740
|
+
raise ParameterAddError(name, "integer_range", str(e)) from e
|
|
741
|
+
else:
|
|
742
|
+
self.parameters[name] = new_param
|
|
154
743
|
|
|
155
|
-
@validate_parameter_operation("add", ParameterType.
|
|
156
|
-
def
|
|
744
|
+
@validate_parameter_operation("add", ParameterType.float_range)
|
|
745
|
+
def add_float_range(
|
|
157
746
|
self,
|
|
158
747
|
name: str,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
748
|
+
*,
|
|
749
|
+
value: Tuple[Union[float, int], Union[float, int]],
|
|
750
|
+
min_value: Union[float, int],
|
|
751
|
+
max_value: Union[float, int],
|
|
162
752
|
step: float = 0.1,
|
|
163
753
|
) -> None:
|
|
164
|
-
|
|
754
|
+
"""
|
|
755
|
+
Add a range parameter for decimal numbers to the viewer.
|
|
756
|
+
|
|
757
|
+
Creates a range slider in the GUI that lets users select a range of numbers
|
|
758
|
+
between min_value and max_value. The range is specified as (low, high) and
|
|
759
|
+
both values will be rounded to the nearest step and clamped to stay within bounds.
|
|
760
|
+
See :class:`~syd.parameters.FloatRangeParameter` for details.
|
|
761
|
+
|
|
762
|
+
Parameters
|
|
763
|
+
----------
|
|
764
|
+
name : str
|
|
765
|
+
Name of the parameter (used as label in GUI)
|
|
766
|
+
value : tuple[float, float]
|
|
767
|
+
Initial (low, high) values
|
|
768
|
+
min_value : float
|
|
769
|
+
Minimum allowed value for both low and high
|
|
770
|
+
max_value : float
|
|
771
|
+
Maximum allowed value for both low and high
|
|
772
|
+
step : float, optional
|
|
773
|
+
Size of each increment (default: 0.1)
|
|
774
|
+
|
|
775
|
+
Examples
|
|
776
|
+
--------
|
|
777
|
+
>>> viewer.add_float_range('price_range',
|
|
778
|
+
... value=(10.0, 20.0),
|
|
779
|
+
... min_value=0.0, max_value=100.0, step=0.5)
|
|
780
|
+
>>> viewer.get_state()['price_range']
|
|
781
|
+
(10.0, 20.0)
|
|
782
|
+
>>> # Values will be rounded to nearest step
|
|
783
|
+
>>> viewer.update_float_range('price_range', value=(10.7, 19.2))
|
|
784
|
+
>>> viewer.get_state()['price_range']
|
|
785
|
+
(10.5, 19.0)
|
|
786
|
+
"""
|
|
787
|
+
try:
|
|
788
|
+
new_param = ParameterType.float_range.value(
|
|
789
|
+
name,
|
|
790
|
+
value,
|
|
791
|
+
min_value,
|
|
792
|
+
max_value,
|
|
793
|
+
step,
|
|
794
|
+
)
|
|
795
|
+
except Exception as e:
|
|
796
|
+
raise ParameterAddError(name, "float_range", str(e)) from e
|
|
797
|
+
else:
|
|
798
|
+
self.parameters[name] = new_param
|
|
799
|
+
|
|
800
|
+
@validate_parameter_operation("add", ParameterType.unbounded_integer)
|
|
801
|
+
def add_unbounded_integer(
|
|
802
|
+
self,
|
|
803
|
+
name: str,
|
|
804
|
+
*,
|
|
805
|
+
value: Union[float, int],
|
|
806
|
+
min_value: Optional[Union[float, int]] = None,
|
|
807
|
+
max_value: Optional[Union[float, int]] = None,
|
|
808
|
+
) -> None:
|
|
809
|
+
"""
|
|
810
|
+
Add an unbounded integer parameter to the viewer.
|
|
811
|
+
|
|
812
|
+
Creates a text input box in the GUI for entering whole numbers. Unlike
|
|
813
|
+
add_integer(), this allows very large numbers and optionally no minimum
|
|
814
|
+
or maximum bounds.
|
|
815
|
+
See :class:`~syd.parameters.UnboundedIntegerParameter` for details.
|
|
816
|
+
|
|
817
|
+
Parameters
|
|
818
|
+
----------
|
|
819
|
+
name : str
|
|
820
|
+
Name of the parameter (used as label in GUI)
|
|
821
|
+
value : int
|
|
822
|
+
Initial value
|
|
823
|
+
min_value : int, optional
|
|
824
|
+
Minimum allowed value (or None for no minimum)
|
|
825
|
+
max_value : int, optional
|
|
826
|
+
Maximum allowed value (or None for no maximum)
|
|
827
|
+
|
|
828
|
+
Examples
|
|
829
|
+
--------
|
|
830
|
+
>>> viewer.add_unbounded_integer('population',
|
|
831
|
+
... value=1000000,
|
|
832
|
+
... min_value=0) # No maximum
|
|
833
|
+
>>> viewer.get_state()['population']
|
|
834
|
+
1000000
|
|
835
|
+
>>> # Values below minimum will be clamped
|
|
836
|
+
>>> viewer.update_unbounded_integer('population', value=-5)
|
|
837
|
+
>>> viewer.get_state()['population']
|
|
838
|
+
0
|
|
839
|
+
"""
|
|
840
|
+
try:
|
|
841
|
+
new_param = ParameterType.unbounded_integer.value(
|
|
842
|
+
name,
|
|
843
|
+
value,
|
|
844
|
+
min_value,
|
|
845
|
+
max_value,
|
|
846
|
+
)
|
|
847
|
+
except Exception as e:
|
|
848
|
+
raise ParameterAddError(name, "unbounded_integer", str(e)) from e
|
|
849
|
+
else:
|
|
850
|
+
self.parameters[name] = new_param
|
|
851
|
+
|
|
852
|
+
@validate_parameter_operation("add", ParameterType.unbounded_float)
|
|
853
|
+
def add_unbounded_float(
|
|
854
|
+
self,
|
|
855
|
+
name: str,
|
|
856
|
+
*,
|
|
857
|
+
value: Union[float, int],
|
|
858
|
+
min_value: Optional[Union[float, int]] = None,
|
|
859
|
+
max_value: Optional[Union[float, int]] = None,
|
|
860
|
+
step: Optional[float] = None,
|
|
861
|
+
) -> None:
|
|
862
|
+
"""
|
|
863
|
+
Add an unbounded decimal number parameter to the viewer.
|
|
864
|
+
|
|
865
|
+
Creates a text input box in the GUI for entering numbers. Unlike add_float(),
|
|
866
|
+
this allows very large or precise numbers and optionally no minimum or
|
|
867
|
+
maximum bounds. Values can optionally be rounded to a step size.
|
|
868
|
+
See :class:`~syd.parameters.UnboundedFloatParameter` for details.
|
|
869
|
+
|
|
870
|
+
Parameters
|
|
871
|
+
----------
|
|
872
|
+
name : str
|
|
873
|
+
Name of the parameter (used as label in GUI)
|
|
874
|
+
value : float
|
|
875
|
+
Initial value
|
|
876
|
+
min_value : float, optional
|
|
877
|
+
Minimum allowed value (or None for no minimum)
|
|
878
|
+
max_value : float, optional
|
|
879
|
+
Maximum allowed value (or None for no maximum)
|
|
880
|
+
step : float, optional
|
|
881
|
+
Size of each increment (or None for no rounding)
|
|
882
|
+
|
|
883
|
+
Examples
|
|
884
|
+
--------
|
|
885
|
+
>>> viewer.add_unbounded_float('wavelength',
|
|
886
|
+
... value=550e-9, # Nanometers
|
|
887
|
+
... min_value=0.0,
|
|
888
|
+
... step=1e-9) # Round to nearest nanometer
|
|
889
|
+
>>> viewer.get_state()['wavelength']
|
|
890
|
+
5.5e-07
|
|
891
|
+
>>> # Values will be rounded if step is provided
|
|
892
|
+
>>> viewer.update_unbounded_float('wavelength', value=550.7e-9)
|
|
893
|
+
>>> viewer.get_state()['wavelength']
|
|
894
|
+
5.51e-07
|
|
895
|
+
"""
|
|
896
|
+
try:
|
|
897
|
+
new_param = ParameterType.unbounded_float.value(
|
|
898
|
+
name,
|
|
899
|
+
value,
|
|
900
|
+
min_value,
|
|
901
|
+
max_value,
|
|
902
|
+
step,
|
|
903
|
+
)
|
|
904
|
+
except Exception as e:
|
|
905
|
+
raise ParameterAddError(name, "unbounded_float", str(e)) from e
|
|
906
|
+
else:
|
|
907
|
+
self.parameters[name] = new_param
|
|
908
|
+
|
|
909
|
+
@validate_parameter_operation("add", ActionType.button)
|
|
910
|
+
def add_button(
|
|
911
|
+
self,
|
|
912
|
+
name: str,
|
|
913
|
+
*,
|
|
914
|
+
label: str,
|
|
915
|
+
callback: Callable[[], None],
|
|
916
|
+
) -> None:
|
|
917
|
+
"""
|
|
918
|
+
Add a button parameter to the viewer.
|
|
919
|
+
|
|
920
|
+
Creates a clickable button in the GUI that triggers the provided callback function
|
|
921
|
+
when clicked. The button's display text can be different from its parameter name.
|
|
922
|
+
See :class:`~syd.parameters.ButtonParameter` for details.
|
|
923
|
+
|
|
924
|
+
Parameters
|
|
925
|
+
----------
|
|
926
|
+
name : str
|
|
927
|
+
Name of the parameter (internal identifier)
|
|
928
|
+
label : str
|
|
929
|
+
Text to display on the button
|
|
930
|
+
callback : callable
|
|
931
|
+
Function to call when the button is clicked (takes no arguments)
|
|
932
|
+
|
|
933
|
+
Examples
|
|
934
|
+
--------
|
|
935
|
+
>>> def reset_plot():
|
|
936
|
+
... print("Resetting plot...")
|
|
937
|
+
>>> viewer.add_button('reset', label='Reset Plot', callback=reset_plot)
|
|
938
|
+
"""
|
|
939
|
+
try:
|
|
940
|
+
|
|
941
|
+
callback = self._prepare_function(
|
|
942
|
+
callback,
|
|
943
|
+
context="Setting button callback:",
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
new_param = ActionType.button.value(name, label, callback)
|
|
947
|
+
except Exception as e:
|
|
948
|
+
raise ParameterAddError(name, "button", str(e)) from e
|
|
949
|
+
else:
|
|
950
|
+
self.parameters[name] = new_param
|
|
165
951
|
|
|
166
952
|
# -------------------- parameter update methods --------------------
|
|
167
953
|
@validate_parameter_operation("update", ParameterType.text)
|
|
168
|
-
def update_text(
|
|
169
|
-
self
|
|
954
|
+
def update_text(
|
|
955
|
+
self, name: str, *, value: Union[str, _NoUpdate] = _NO_UPDATE
|
|
956
|
+
) -> None:
|
|
957
|
+
"""
|
|
958
|
+
Update a text parameter's value.
|
|
959
|
+
|
|
960
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_text`.
|
|
961
|
+
See :class:`~syd.parameters.TextParameter` for details about value validation.
|
|
962
|
+
|
|
963
|
+
Parameters
|
|
964
|
+
----------
|
|
965
|
+
name : str
|
|
966
|
+
Name of the text parameter to update
|
|
967
|
+
value : str, optional
|
|
968
|
+
New text value (if not provided, no change)
|
|
969
|
+
|
|
970
|
+
Examples
|
|
971
|
+
--------
|
|
972
|
+
>>> viewer.add_text('title', value='Original Title')
|
|
973
|
+
>>> viewer.update_text('title', value='New Title')
|
|
974
|
+
>>> viewer.get_state()['title']
|
|
975
|
+
'New Title'
|
|
976
|
+
"""
|
|
977
|
+
updates = {}
|
|
978
|
+
if value is not _NO_UPDATE:
|
|
979
|
+
updates["value"] = value
|
|
980
|
+
if updates:
|
|
981
|
+
self.parameters[name].update(updates)
|
|
982
|
+
|
|
983
|
+
@validate_parameter_operation("update", ParameterType.boolean)
|
|
984
|
+
def update_boolean(
|
|
985
|
+
self, name: str, *, value: Union[bool, _NoUpdate] = _NO_UPDATE
|
|
986
|
+
) -> None:
|
|
987
|
+
"""
|
|
988
|
+
Update a boolean parameter's value.
|
|
989
|
+
|
|
990
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_boolean`.
|
|
991
|
+
See :class:`~syd.parameters.BooleanParameter` for details about value validation.
|
|
992
|
+
|
|
993
|
+
Parameters
|
|
994
|
+
----------
|
|
995
|
+
name : str
|
|
996
|
+
Name of the boolean parameter to update
|
|
997
|
+
value : bool, optional
|
|
998
|
+
New state (True/False) (if not provided, no change)
|
|
999
|
+
|
|
1000
|
+
Examples
|
|
1001
|
+
--------
|
|
1002
|
+
>>> viewer.add_boolean('show_grid', value=True)
|
|
1003
|
+
>>> viewer.update_boolean('show_grid', value=False)
|
|
1004
|
+
>>> viewer.get_state()['show_grid']
|
|
1005
|
+
False
|
|
1006
|
+
"""
|
|
1007
|
+
updates = {}
|
|
1008
|
+
if value is not _NO_UPDATE:
|
|
1009
|
+
updates["value"] = value
|
|
1010
|
+
if updates:
|
|
1011
|
+
self.parameters[name].update(updates)
|
|
170
1012
|
|
|
171
1013
|
@validate_parameter_operation("update", ParameterType.selection)
|
|
172
|
-
def update_selection(
|
|
173
|
-
self
|
|
1014
|
+
def update_selection(
|
|
1015
|
+
self,
|
|
1016
|
+
name: str,
|
|
1017
|
+
*,
|
|
1018
|
+
value: Union[Any, _NoUpdate] = _NO_UPDATE,
|
|
1019
|
+
options: Union[List[Any], _NoUpdate] = _NO_UPDATE,
|
|
1020
|
+
) -> None:
|
|
1021
|
+
"""
|
|
1022
|
+
Update a selection parameter's value and/or options.
|
|
1023
|
+
|
|
1024
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_selection`.
|
|
1025
|
+
See :class:`~syd.parameters.SelectionParameter` for details about value validation.
|
|
1026
|
+
|
|
1027
|
+
Parameters
|
|
1028
|
+
----------
|
|
1029
|
+
name : str
|
|
1030
|
+
Name of the selection parameter to update
|
|
1031
|
+
value : Any, optional
|
|
1032
|
+
New selected value (must be in options) (if not provided, no change)
|
|
1033
|
+
options : list, optional
|
|
1034
|
+
New list of selectable options (if not provided, no change)
|
|
1035
|
+
|
|
1036
|
+
Examples
|
|
1037
|
+
--------
|
|
1038
|
+
>>> viewer.add_selection('color', value='red',
|
|
1039
|
+
... options=['red', 'green', 'blue'])
|
|
1040
|
+
>>> # Update just the value
|
|
1041
|
+
>>> viewer.update_selection('color', value='blue')
|
|
1042
|
+
>>> # Update options and value together
|
|
1043
|
+
>>> viewer.update_selection('color',
|
|
1044
|
+
... options=['purple', 'orange'],
|
|
1045
|
+
... value='purple')
|
|
1046
|
+
"""
|
|
1047
|
+
updates = {}
|
|
1048
|
+
if value is not _NO_UPDATE:
|
|
1049
|
+
updates["value"] = value
|
|
1050
|
+
if options is not _NO_UPDATE:
|
|
1051
|
+
updates["options"] = options
|
|
1052
|
+
if updates:
|
|
1053
|
+
self.parameters[name].update(updates)
|
|
174
1054
|
|
|
175
1055
|
@validate_parameter_operation("update", ParameterType.multiple_selection)
|
|
176
|
-
def update_multiple_selection(
|
|
177
|
-
self
|
|
1056
|
+
def update_multiple_selection(
|
|
1057
|
+
self,
|
|
1058
|
+
name: str,
|
|
1059
|
+
*,
|
|
1060
|
+
value: Union[List[Any], _NoUpdate] = _NO_UPDATE,
|
|
1061
|
+
options: Union[List[Any], _NoUpdate] = _NO_UPDATE,
|
|
1062
|
+
) -> None:
|
|
1063
|
+
"""
|
|
1064
|
+
Update a multiple selection parameter's values and/or options.
|
|
178
1065
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
1066
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_multiple_selection`.
|
|
1067
|
+
See :class:`~syd.parameters.MultipleSelectionParameter` for details about value validation.
|
|
1068
|
+
|
|
1069
|
+
Parameters
|
|
1070
|
+
----------
|
|
1071
|
+
name : str
|
|
1072
|
+
Name of the multiple selection parameter to update
|
|
1073
|
+
value : list, optional
|
|
1074
|
+
New list of selected values (all must be in options) (if not provided, no change)
|
|
1075
|
+
options : list, optional
|
|
1076
|
+
New list of selectable options (if not provided, no change)
|
|
1077
|
+
|
|
1078
|
+
Examples
|
|
1079
|
+
--------
|
|
1080
|
+
>>> viewer.add_multiple_selection('toppings',
|
|
1081
|
+
... value=['cheese'],
|
|
1082
|
+
... options=['cheese', 'pepperoni', 'mushrooms'])
|
|
1083
|
+
>>> # Update selected values
|
|
1084
|
+
>>> viewer.update_multiple_selection('toppings',
|
|
1085
|
+
... value=['cheese', 'mushrooms'])
|
|
1086
|
+
>>> # Update options (will reset value if current selections not in new options)
|
|
1087
|
+
>>> viewer.update_multiple_selection('toppings',
|
|
1088
|
+
... options=['cheese', 'bacon', 'olives'],
|
|
1089
|
+
... value=['cheese', 'bacon'])
|
|
1090
|
+
"""
|
|
1091
|
+
updates = {}
|
|
1092
|
+
if value is not _NO_UPDATE:
|
|
1093
|
+
updates["value"] = value
|
|
1094
|
+
if options is not _NO_UPDATE:
|
|
1095
|
+
updates["options"] = options
|
|
1096
|
+
if updates:
|
|
1097
|
+
self.parameters[name].update(updates)
|
|
182
1098
|
|
|
183
1099
|
@validate_parameter_operation("update", ParameterType.integer)
|
|
184
|
-
def update_integer(
|
|
185
|
-
self
|
|
1100
|
+
def update_integer(
|
|
1101
|
+
self,
|
|
1102
|
+
name: str,
|
|
1103
|
+
*,
|
|
1104
|
+
value: Union[int, _NoUpdate] = _NO_UPDATE,
|
|
1105
|
+
min_value: Union[int, _NoUpdate] = _NO_UPDATE,
|
|
1106
|
+
max_value: Union[int, _NoUpdate] = _NO_UPDATE,
|
|
1107
|
+
) -> None:
|
|
1108
|
+
"""
|
|
1109
|
+
Update an integer parameter's value and/or bounds.
|
|
1110
|
+
|
|
1111
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_integer`.
|
|
1112
|
+
See :class:`~syd.parameters.IntegerParameter` for details about value validation.
|
|
1113
|
+
|
|
1114
|
+
Parameters
|
|
1115
|
+
----------
|
|
1116
|
+
name : str
|
|
1117
|
+
Name of the integer parameter to update
|
|
1118
|
+
value : int, optional
|
|
1119
|
+
New value (will be clamped to bounds) (if not provided, no change)
|
|
1120
|
+
min_value : int, optional
|
|
1121
|
+
New minimum value (if not provided, no change)
|
|
1122
|
+
max_value : int, optional
|
|
1123
|
+
New maximum value (if not provided, no change)
|
|
1124
|
+
|
|
1125
|
+
Examples
|
|
1126
|
+
--------
|
|
1127
|
+
>>> viewer.add_integer('count', value=5, min_value=0, max_value=10)
|
|
1128
|
+
>>> # Update just the value
|
|
1129
|
+
>>> viewer.update_integer('count', value=8)
|
|
1130
|
+
>>> # Update bounds (current value will be clamped if needed)
|
|
1131
|
+
>>> viewer.update_integer('count', min_value=7, max_value=15)
|
|
1132
|
+
"""
|
|
1133
|
+
updates = {}
|
|
1134
|
+
if value is not _NO_UPDATE:
|
|
1135
|
+
updates["value"] = value
|
|
1136
|
+
if min_value is not _NO_UPDATE:
|
|
1137
|
+
updates["min_value"] = min_value
|
|
1138
|
+
if max_value is not _NO_UPDATE:
|
|
1139
|
+
updates["max_value"] = max_value
|
|
1140
|
+
if updates:
|
|
1141
|
+
self.parameters[name].update(updates)
|
|
186
1142
|
|
|
187
1143
|
@validate_parameter_operation("update", ParameterType.float)
|
|
188
|
-
def update_float(
|
|
189
|
-
self
|
|
1144
|
+
def update_float(
|
|
1145
|
+
self,
|
|
1146
|
+
name: str,
|
|
1147
|
+
*,
|
|
1148
|
+
value: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1149
|
+
min_value: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1150
|
+
max_value: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1151
|
+
step: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1152
|
+
) -> None:
|
|
1153
|
+
"""
|
|
1154
|
+
Update a float parameter's value, bounds, and/or step size.
|
|
1155
|
+
|
|
1156
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_float`.
|
|
1157
|
+
See :class:`~syd.parameters.FloatParameter` for details about value validation.
|
|
1158
|
+
|
|
1159
|
+
Parameters
|
|
1160
|
+
----------
|
|
1161
|
+
name : str
|
|
1162
|
+
Name of the float parameter to update
|
|
1163
|
+
value : float, optional
|
|
1164
|
+
New value (will be rounded and clamped) (if not provided, no change)
|
|
1165
|
+
min_value : float, optional
|
|
1166
|
+
New minimum value (if not provided, no change)
|
|
1167
|
+
max_value : float, optional
|
|
1168
|
+
New maximum value (if not provided, no change)
|
|
1169
|
+
step : float, optional
|
|
1170
|
+
New step size (if not provided, no change)
|
|
1171
|
+
|
|
1172
|
+
Examples
|
|
1173
|
+
--------
|
|
1174
|
+
>>> viewer.add_float('temperature', value=20.0,
|
|
1175
|
+
... min_value=0.0, max_value=100.0, step=0.5)
|
|
1176
|
+
>>> # Update just the value (will round to step)
|
|
1177
|
+
>>> viewer.update_float('temperature', value=20.7) # Becomes 20.5
|
|
1178
|
+
>>> # Update bounds and step size
|
|
1179
|
+
>>> viewer.update_float('temperature',
|
|
1180
|
+
... min_value=15.0, max_value=30.0, step=0.1)
|
|
1181
|
+
"""
|
|
1182
|
+
updates = {}
|
|
1183
|
+
if value is not _NO_UPDATE:
|
|
1184
|
+
updates["value"] = value
|
|
1185
|
+
if min_value is not _NO_UPDATE:
|
|
1186
|
+
updates["min_value"] = min_value
|
|
1187
|
+
if max_value is not _NO_UPDATE:
|
|
1188
|
+
updates["max_value"] = max_value
|
|
1189
|
+
if step is not _NO_UPDATE:
|
|
1190
|
+
updates["step"] = step
|
|
1191
|
+
if updates:
|
|
1192
|
+
self.parameters[name].update(updates)
|
|
190
1193
|
|
|
191
|
-
@validate_parameter_operation("update", ParameterType.
|
|
192
|
-
def
|
|
1194
|
+
@validate_parameter_operation("update", ParameterType.integer_range)
|
|
1195
|
+
def update_integer_range(
|
|
193
1196
|
self,
|
|
194
1197
|
name: str,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
1198
|
+
*,
|
|
1199
|
+
value: Union[Tuple[int, int], _NoUpdate] = _NO_UPDATE,
|
|
1200
|
+
min_value: Union[int, _NoUpdate] = _NO_UPDATE,
|
|
1201
|
+
max_value: Union[int, _NoUpdate] = _NO_UPDATE,
|
|
198
1202
|
) -> None:
|
|
199
|
-
|
|
1203
|
+
"""
|
|
1204
|
+
Update an integer range parameter's values and/or bounds.
|
|
200
1205
|
|
|
201
|
-
|
|
202
|
-
|
|
1206
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_integer_range`.
|
|
1207
|
+
See :class:`~syd.parameters.IntegerRangeParameter` for details about value validation.
|
|
1208
|
+
|
|
1209
|
+
Parameters
|
|
1210
|
+
----------
|
|
1211
|
+
name : str
|
|
1212
|
+
Name of the integer range parameter to update
|
|
1213
|
+
value : tuple[int, int], optional
|
|
1214
|
+
New (low, high) values (will be clamped) (if not provided, no change)
|
|
1215
|
+
min_value : int, optional
|
|
1216
|
+
New minimum value for both low and high (if not provided, no change)
|
|
1217
|
+
max_value : int, optional
|
|
1218
|
+
New maximum value for both low and high (if not provided, no change)
|
|
1219
|
+
|
|
1220
|
+
Examples
|
|
1221
|
+
--------
|
|
1222
|
+
>>> viewer.add_integer_range('age_range',
|
|
1223
|
+
... value=(25, 35),
|
|
1224
|
+
... min_value=18, max_value=100)
|
|
1225
|
+
>>> # Update just the range (values will be swapped if needed)
|
|
1226
|
+
>>> viewer.update_integer_range('age_range', value=(40, 30)) # Becomes (30, 40)
|
|
1227
|
+
>>> # Update bounds (current values will be clamped if needed)
|
|
1228
|
+
>>> viewer.update_integer_range('age_range', min_value=20, max_value=80)
|
|
1229
|
+
"""
|
|
1230
|
+
updates = {}
|
|
1231
|
+
if value is not _NO_UPDATE:
|
|
1232
|
+
updates["value"] = value
|
|
1233
|
+
if min_value is not _NO_UPDATE:
|
|
1234
|
+
updates["min_value"] = min_value
|
|
1235
|
+
if max_value is not _NO_UPDATE:
|
|
1236
|
+
updates["max_value"] = max_value
|
|
1237
|
+
if updates:
|
|
1238
|
+
self.parameters[name].update(updates)
|
|
1239
|
+
|
|
1240
|
+
@validate_parameter_operation("update", ParameterType.float_range)
|
|
1241
|
+
def update_float_range(
|
|
203
1242
|
self,
|
|
204
1243
|
name: str,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
1244
|
+
*,
|
|
1245
|
+
value: Union[Tuple[float, float], _NoUpdate] = _NO_UPDATE,
|
|
1246
|
+
min_value: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1247
|
+
max_value: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1248
|
+
step: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1249
|
+
) -> None:
|
|
1250
|
+
"""
|
|
1251
|
+
Update a float range parameter's values, bounds, and/or step size.
|
|
1252
|
+
|
|
1253
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_float_range`.
|
|
1254
|
+
See :class:`~syd.parameters.FloatRangeParameter` for details about value validation.
|
|
1255
|
+
|
|
1256
|
+
Parameters
|
|
1257
|
+
----------
|
|
1258
|
+
name : str
|
|
1259
|
+
Name of the float range parameter to update
|
|
1260
|
+
value : tuple[float, float], optional
|
|
1261
|
+
New (low, high) values (will be rounded and clamped) (if not provided, no change)
|
|
1262
|
+
min_value : float, optional
|
|
1263
|
+
New minimum value for both low and high (if not provided, no change)
|
|
1264
|
+
max_value : float, optional
|
|
1265
|
+
New maximum value for both low and high (if not provided, no change)
|
|
1266
|
+
step : float, optional
|
|
1267
|
+
New step size for rounding values (if not provided, no change)
|
|
1268
|
+
|
|
1269
|
+
Examples
|
|
1270
|
+
--------
|
|
1271
|
+
>>> viewer.add_float_range('price_range',
|
|
1272
|
+
... value=(10.0, 20.0),
|
|
1273
|
+
... min_value=0.0, max_value=100.0, step=0.5)
|
|
1274
|
+
>>> # Update just the range (values will be rounded and swapped if needed)
|
|
1275
|
+
>>> viewer.update_float_range('price_range', value=(15.7, 14.2)) # Becomes (14.0, 15.5)
|
|
1276
|
+
>>> # Update bounds and step size
|
|
1277
|
+
>>> viewer.update_float_range('price_range',
|
|
1278
|
+
... min_value=5.0, max_value=50.0, step=0.1)
|
|
1279
|
+
"""
|
|
1280
|
+
updates = {}
|
|
1281
|
+
if value is not _NO_UPDATE:
|
|
1282
|
+
updates["value"] = value
|
|
1283
|
+
if min_value is not _NO_UPDATE:
|
|
1284
|
+
updates["min_value"] = min_value
|
|
1285
|
+
if max_value is not _NO_UPDATE:
|
|
1286
|
+
updates["max_value"] = max_value
|
|
1287
|
+
if step is not _NO_UPDATE:
|
|
1288
|
+
updates["step"] = step
|
|
1289
|
+
if updates:
|
|
1290
|
+
self.parameters[name].update(updates)
|
|
1291
|
+
|
|
1292
|
+
@validate_parameter_operation("update", ParameterType.unbounded_integer)
|
|
1293
|
+
def update_unbounded_integer(
|
|
1294
|
+
self,
|
|
1295
|
+
name: str,
|
|
1296
|
+
*,
|
|
1297
|
+
value: Union[int, _NoUpdate] = _NO_UPDATE,
|
|
1298
|
+
min_value: Union[Optional[int], _NoUpdate] = _NO_UPDATE,
|
|
1299
|
+
max_value: Union[Optional[int], _NoUpdate] = _NO_UPDATE,
|
|
209
1300
|
) -> None:
|
|
210
|
-
|
|
1301
|
+
"""
|
|
1302
|
+
Update an unbounded integer parameter's value and/or bounds.
|
|
1303
|
+
|
|
1304
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_unbounded_integer`.
|
|
1305
|
+
See :class:`~syd.parameters.UnboundedIntegerParameter` for details about value validation.
|
|
1306
|
+
|
|
1307
|
+
Parameters
|
|
1308
|
+
----------
|
|
1309
|
+
name : str
|
|
1310
|
+
Name of the unbounded integer parameter to update
|
|
1311
|
+
value : int, optional
|
|
1312
|
+
New value (will be clamped to any bounds) (if not provided, no change)
|
|
1313
|
+
min_value : int or None, optional
|
|
1314
|
+
New minimum value, or None for no minimum (if not provided, no change)
|
|
1315
|
+
max_value : int or None, optional
|
|
1316
|
+
New maximum value, or None for no maximum (if not provided, no change)
|
|
1317
|
+
|
|
1318
|
+
Examples
|
|
1319
|
+
--------
|
|
1320
|
+
>>> viewer.add_unbounded_integer('population',
|
|
1321
|
+
... value=1000000,
|
|
1322
|
+
... min_value=0) # No maximum
|
|
1323
|
+
>>> # Update just the value
|
|
1324
|
+
>>> viewer.update_unbounded_integer('population', value=2000000)
|
|
1325
|
+
>>> # Add a maximum bound (current value will be clamped if needed)
|
|
1326
|
+
>>> viewer.update_unbounded_integer('population', max_value=1500000)
|
|
1327
|
+
>>> # Remove the minimum bound
|
|
1328
|
+
>>> viewer.update_unbounded_integer('population', min_value=None)
|
|
1329
|
+
"""
|
|
1330
|
+
updates = {}
|
|
1331
|
+
if value is not _NO_UPDATE:
|
|
1332
|
+
updates["value"] = value
|
|
1333
|
+
if min_value is not _NO_UPDATE:
|
|
1334
|
+
updates["min_value"] = min_value
|
|
1335
|
+
if max_value is not _NO_UPDATE:
|
|
1336
|
+
updates["max_value"] = max_value
|
|
1337
|
+
if updates:
|
|
1338
|
+
self.parameters[name].update(updates)
|
|
1339
|
+
|
|
1340
|
+
@validate_parameter_operation("update", ParameterType.unbounded_float)
|
|
1341
|
+
def update_unbounded_float(
|
|
1342
|
+
self,
|
|
1343
|
+
name: str,
|
|
1344
|
+
*,
|
|
1345
|
+
value: Union[float, _NoUpdate] = _NO_UPDATE,
|
|
1346
|
+
min_value: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
|
|
1347
|
+
max_value: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
|
|
1348
|
+
step: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
|
|
1349
|
+
) -> None:
|
|
1350
|
+
"""
|
|
1351
|
+
Update an unbounded float parameter's value, bounds, and/or step size.
|
|
1352
|
+
|
|
1353
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_unbounded_float`.
|
|
1354
|
+
See :class:`~syd.parameters.UnboundedFloatParameter` for details about value validation.
|
|
1355
|
+
|
|
1356
|
+
Parameters
|
|
1357
|
+
----------
|
|
1358
|
+
name : str
|
|
1359
|
+
Name of the unbounded float parameter to update
|
|
1360
|
+
value : float, optional
|
|
1361
|
+
New value (will be rounded if step is set) (if not provided, no change)
|
|
1362
|
+
min_value : float or None, optional
|
|
1363
|
+
New minimum value, or None for no minimum (if not provided, no change)
|
|
1364
|
+
max_value : float or None, optional
|
|
1365
|
+
New maximum value, or None for no maximum (if not provided, no change)
|
|
1366
|
+
step : float or None, optional
|
|
1367
|
+
New step size for rounding, or None for no rounding (if not provided, no change)
|
|
1368
|
+
|
|
1369
|
+
Examples
|
|
1370
|
+
--------
|
|
1371
|
+
>>> viewer.add_unbounded_float('wavelength',
|
|
1372
|
+
... value=550e-9, # Nanometers
|
|
1373
|
+
... min_value=0.0,
|
|
1374
|
+
... step=1e-9)
|
|
1375
|
+
>>> # Update value (will be rounded if step is set)
|
|
1376
|
+
>>> viewer.update_unbounded_float('wavelength', value=632.8e-9) # HeNe laser
|
|
1377
|
+
>>> # Change step size and add maximum
|
|
1378
|
+
>>> viewer.update_unbounded_float('wavelength',
|
|
1379
|
+
... step=0.1e-9, # Finer control
|
|
1380
|
+
... max_value=1000e-9) # Infrared limit
|
|
1381
|
+
>>> # Remove step size (allow any precision)
|
|
1382
|
+
>>> viewer.update_unbounded_float('wavelength', step=None)
|
|
1383
|
+
"""
|
|
1384
|
+
updates = {}
|
|
1385
|
+
if value is not _NO_UPDATE:
|
|
1386
|
+
updates["value"] = value
|
|
1387
|
+
if min_value is not _NO_UPDATE:
|
|
1388
|
+
updates["min_value"] = min_value
|
|
1389
|
+
if max_value is not _NO_UPDATE:
|
|
1390
|
+
updates["max_value"] = max_value
|
|
1391
|
+
if step is not _NO_UPDATE:
|
|
1392
|
+
updates["step"] = step
|
|
1393
|
+
if updates:
|
|
1394
|
+
self.parameters[name].update(updates)
|
|
1395
|
+
|
|
1396
|
+
@validate_parameter_operation("update", ActionType.button)
|
|
1397
|
+
def update_button(
|
|
1398
|
+
self,
|
|
1399
|
+
name: str,
|
|
1400
|
+
*,
|
|
1401
|
+
label: Union[str, _NoUpdate] = _NO_UPDATE,
|
|
1402
|
+
callback: Union[Callable[[], None], _NoUpdate] = _NO_UPDATE,
|
|
1403
|
+
) -> None:
|
|
1404
|
+
"""
|
|
1405
|
+
Update a button parameter's label and/or callback function.
|
|
1406
|
+
|
|
1407
|
+
Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_button`.
|
|
1408
|
+
See :class:`~syd.parameters.ButtonAction` for details.
|
|
1409
|
+
|
|
1410
|
+
Parameters
|
|
1411
|
+
----------
|
|
1412
|
+
name : str
|
|
1413
|
+
Name of the button parameter to update
|
|
1414
|
+
label : str, optional
|
|
1415
|
+
New text to display on the button (if not provided, no change)
|
|
1416
|
+
callback : callable, optional
|
|
1417
|
+
New function to call when clicked (if not provided, no change)
|
|
1418
|
+
|
|
1419
|
+
Examples
|
|
1420
|
+
--------
|
|
1421
|
+
>>> def new_callback():
|
|
1422
|
+
... print("New action...")
|
|
1423
|
+
>>> viewer.update_button('reset',
|
|
1424
|
+
... label='Clear Plot',
|
|
1425
|
+
... callback=new_callback)
|
|
1426
|
+
"""
|
|
1427
|
+
updates = {}
|
|
1428
|
+
if label is not _NO_UPDATE:
|
|
1429
|
+
updates["label"] = label
|
|
1430
|
+
if callback is not _NO_UPDATE:
|
|
1431
|
+
callback = self._prepare_function(
|
|
1432
|
+
callback,
|
|
1433
|
+
context="Updating button callback:",
|
|
1434
|
+
)
|
|
1435
|
+
updates["callback"] = callback
|
|
1436
|
+
if updates:
|
|
1437
|
+
self.parameters[name].update(updates)
|