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