syd 0.1.5__py3-none-any.whl → 0.1.7__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 +7 -11
- syd/flask_deployment/__init__.py +1 -0
- syd/flask_deployment/components.py +510 -0
- syd/flask_deployment/deployer.py +302 -0
- syd/flask_deployment/static/css/viewer.css +82 -0
- syd/flask_deployment/static/js/viewer.js +174 -0
- syd/flask_deployment/templates/base.html +29 -0
- syd/flask_deployment/templates/viewer.html +51 -0
- syd/flask_deployment/testing_principles.md +300 -0
- syd/notebook_deployment/__init__.py +1 -0
- syd/{notebook_deploy/deployer.py → notebook_deployment/_ipympl_deployer.py} +57 -36
- syd/notebook_deployment/deployer.py +330 -0
- syd/{notebook_deploy → notebook_deployment}/widgets.py +192 -112
- syd/parameters.py +390 -194
- syd/plotly_deployment/__init__.py +1 -0
- syd/plotly_deployment/components.py +531 -0
- syd/plotly_deployment/deployer.py +376 -0
- syd/{interactive_viewer.py → viewer.py} +309 -176
- syd-0.1.7.dist-info/METADATA +120 -0
- syd-0.1.7.dist-info/RECORD +22 -0
- syd/notebook_deploy/__init__.py +0 -1
- syd-0.1.5.dist-info/METADATA +0 -41
- syd-0.1.5.dist-info/RECORD +0 -10
- {syd-0.1.5.dist-info → syd-0.1.7.dist-info}/WHEEL +0 -0
- {syd-0.1.5.dist-info → syd-0.1.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from typing import List, Any, Callable, Dict, Tuple, Union, Optional
|
|
2
|
-
from functools import wraps
|
|
2
|
+
from functools import wraps, partial
|
|
3
|
+
import inspect
|
|
3
4
|
from contextlib import contextmanager
|
|
4
|
-
from abc import ABC, abstractmethod
|
|
5
5
|
from matplotlib.figure import Figure
|
|
6
6
|
|
|
7
7
|
from .parameters import (
|
|
8
8
|
ParameterType,
|
|
9
|
+
ActionType,
|
|
9
10
|
Parameter,
|
|
10
11
|
ParameterAddError,
|
|
11
12
|
ParameterUpdateError,
|
|
@@ -22,16 +23,20 @@ class _NoUpdate:
|
|
|
22
23
|
cls._instance = super().__new__(cls)
|
|
23
24
|
return cls._instance
|
|
24
25
|
|
|
26
|
+
def __eq__(self, other):
|
|
27
|
+
"""This makes sure all comparisons of _NoUpdate objects return True"""
|
|
28
|
+
return isinstance(other, _NoUpdate)
|
|
29
|
+
|
|
25
30
|
|
|
26
31
|
# Create the singleton instance
|
|
27
32
|
_NO_UPDATE = _NoUpdate()
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
def validate_parameter_operation(
|
|
31
|
-
operation: str, parameter_type: ParameterType
|
|
36
|
+
operation: str, parameter_type: Union[ParameterType, ActionType]
|
|
32
37
|
) -> Callable:
|
|
33
38
|
"""
|
|
34
|
-
Decorator that validates parameter operations for the
|
|
39
|
+
Decorator that validates parameter operations for the viewer class.
|
|
35
40
|
|
|
36
41
|
This decorator ensures that:
|
|
37
42
|
1. The operation type matches the method name (add/update)
|
|
@@ -64,14 +69,14 @@ def validate_parameter_operation(
|
|
|
64
69
|
"Incorrect use of validate_parameter_operation decorator. Must be called with 'add' or 'update' as the first argument."
|
|
65
70
|
)
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
f"Invalid operation type specified ({operation}) for method {func.__name__}"
|
|
73
|
-
)
|
|
72
|
+
# Validate operation matches method name (add/update)
|
|
73
|
+
if not func.__name__.startswith(operation):
|
|
74
|
+
raise ValueError(
|
|
75
|
+
f"Invalid operation type specified ({operation}) for method {func.__name__}"
|
|
76
|
+
)
|
|
74
77
|
|
|
78
|
+
@wraps(func)
|
|
79
|
+
def wrapper(self: "Viewer", name: Any, *args, **kwargs):
|
|
75
80
|
# Validate parameter name is a string
|
|
76
81
|
if not isinstance(name, str):
|
|
77
82
|
if operation == "add":
|
|
@@ -103,17 +108,17 @@ def validate_parameter_operation(
|
|
|
103
108
|
parameter_type.name,
|
|
104
109
|
"Parameter not found - you can only update registered parameters!",
|
|
105
110
|
)
|
|
106
|
-
if
|
|
107
|
-
msg = f"Parameter called {name} was found but is registered as a different parameter type ({type(self.parameters[name])})"
|
|
111
|
+
if not isinstance(self.parameters[name], parameter_type.value):
|
|
112
|
+
msg = f"Parameter called {name} was found but is registered as a different parameter type ({type(self.parameters[name])}). Expecting {parameter_type.value}."
|
|
108
113
|
raise ParameterUpdateError(name, parameter_type.name, msg)
|
|
109
114
|
|
|
110
115
|
try:
|
|
111
116
|
return func(self, name, *args, **kwargs)
|
|
112
117
|
except Exception as e:
|
|
113
118
|
if operation == "add":
|
|
114
|
-
raise ParameterAddError(name, parameter_type.name, str(e))
|
|
119
|
+
raise ParameterAddError(name, parameter_type.name, str(e))
|
|
115
120
|
elif operation == "update":
|
|
116
|
-
raise ParameterUpdateError(name, parameter_type.name, str(e))
|
|
121
|
+
raise ParameterUpdateError(name, parameter_type.name, str(e))
|
|
117
122
|
else:
|
|
118
123
|
raise e
|
|
119
124
|
|
|
@@ -122,7 +127,7 @@ def validate_parameter_operation(
|
|
|
122
127
|
return decorator
|
|
123
128
|
|
|
124
129
|
|
|
125
|
-
class
|
|
130
|
+
class Viewer:
|
|
126
131
|
"""
|
|
127
132
|
Base class for creating interactive matplotlib figures with GUI controls.
|
|
128
133
|
|
|
@@ -137,7 +142,7 @@ class InteractiveViewer(ABC):
|
|
|
137
142
|
|
|
138
143
|
Examples
|
|
139
144
|
--------
|
|
140
|
-
>>> class MyViewer(
|
|
145
|
+
>>> class MyViewer(Viewer):
|
|
141
146
|
... def plot(self, state: Dict[str, Any]):
|
|
142
147
|
... fig = plt.figure()
|
|
143
148
|
... plt.plot([0, state['x']])
|
|
@@ -153,7 +158,6 @@ class InteractiveViewer(ABC):
|
|
|
153
158
|
|
|
154
159
|
parameters: Dict[str, Parameter]
|
|
155
160
|
callbacks: Dict[str, List[Callable]]
|
|
156
|
-
state: Dict[str, Any]
|
|
157
161
|
_app_deployed: bool
|
|
158
162
|
_in_callbacks: bool
|
|
159
163
|
|
|
@@ -161,15 +165,16 @@ class InteractiveViewer(ABC):
|
|
|
161
165
|
instance = super().__new__(cls)
|
|
162
166
|
instance.parameters = {}
|
|
163
167
|
instance.callbacks = {}
|
|
164
|
-
instance.state = {}
|
|
165
168
|
instance._app_deployed = False
|
|
166
169
|
instance._in_callbacks = False
|
|
167
170
|
return instance
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
@property
|
|
173
|
+
def state(self) -> Dict[str, Any]:
|
|
170
174
|
"""
|
|
171
175
|
Get the current values of all parameters.
|
|
172
176
|
|
|
177
|
+
|
|
173
178
|
Returns
|
|
174
179
|
-------
|
|
175
180
|
dict
|
|
@@ -179,18 +184,38 @@ class InteractiveViewer(ABC):
|
|
|
179
184
|
--------
|
|
180
185
|
>>> viewer.add_float('x', value=1.0, min_value=0, max_value=10)
|
|
181
186
|
>>> viewer.add_text('label', value='data')
|
|
182
|
-
>>> viewer.
|
|
187
|
+
>>> viewer.state
|
|
183
188
|
{'x': 1.0, 'label': 'data'}
|
|
184
189
|
"""
|
|
185
|
-
return {
|
|
190
|
+
return {
|
|
191
|
+
name: param.value
|
|
192
|
+
for name, param in self.parameters.items()
|
|
193
|
+
if not param._is_action
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
def plot(self, state: Dict[str, Any]) -> Figure:
|
|
197
|
+
"""Create and return a matplotlib figure.
|
|
198
|
+
|
|
199
|
+
This is a placeholder. You must either:
|
|
200
|
+
|
|
201
|
+
1. Call set_plot() with your plotting function
|
|
202
|
+
This will look like this:
|
|
203
|
+
>>> def plot(state):
|
|
204
|
+
>>> ... generate figure, plot stuff ...
|
|
205
|
+
>>> return fig
|
|
206
|
+
>>> viewer.set_plot(plot))
|
|
207
|
+
|
|
208
|
+
2. Subclass Viewer and override this method
|
|
209
|
+
This will look like this:
|
|
210
|
+
>>> class YourViewer(Viewer):
|
|
211
|
+
>>> def plot(self, state):
|
|
212
|
+
>>> ... generate figure, plot stuff ...
|
|
213
|
+
>>> return fig
|
|
186
214
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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.
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
state : dict
|
|
218
|
+
Current parameter values
|
|
194
219
|
|
|
195
220
|
Returns
|
|
196
221
|
-------
|
|
@@ -200,27 +225,56 @@ class InteractiveViewer(ABC):
|
|
|
200
225
|
Notes
|
|
201
226
|
-----
|
|
202
227
|
- Create a new figure each time, don't reuse old ones
|
|
203
|
-
- Access parameter values using
|
|
204
|
-
-
|
|
228
|
+
- Access parameter values using state['param_name']
|
|
229
|
+
- Access your viewer class using self (or viewer for the set_plot() method)
|
|
230
|
+
- Return the figure object, don't call plt.show()!
|
|
205
231
|
"""
|
|
206
|
-
raise NotImplementedError(
|
|
232
|
+
raise NotImplementedError(
|
|
233
|
+
"Plot method not implemented. Either subclass "
|
|
234
|
+
"Viewer and override plot(), or use "
|
|
235
|
+
"set_plot() to provide a plotting function."
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def set_plot(self, func: Callable) -> None:
|
|
239
|
+
"""Set the plot method for the viewer"""
|
|
240
|
+
self.plot = self._prepare_function(func, context="Setting plot:")
|
|
207
241
|
|
|
208
242
|
def deploy(self, env: str = "notebook", **kwargs):
|
|
209
243
|
"""Deploy the app in a notebook or standalone environment"""
|
|
210
244
|
if env == "notebook":
|
|
211
|
-
from .
|
|
245
|
+
from .notebook_deployment import NotebookDeployer
|
|
212
246
|
|
|
213
|
-
deployer =
|
|
247
|
+
deployer = NotebookDeployer(self, **kwargs)
|
|
214
248
|
deployer.deploy()
|
|
249
|
+
return self
|
|
250
|
+
|
|
251
|
+
elif env == "plotly":
|
|
252
|
+
from .plotly_deployment import PlotlyDeployer
|
|
215
253
|
|
|
216
|
-
|
|
254
|
+
deployer = PlotlyDeployer(self, **kwargs)
|
|
255
|
+
deployer.deploy(mode="server")
|
|
256
|
+
return self
|
|
257
|
+
|
|
258
|
+
elif env == "plotly-inline":
|
|
259
|
+
from .plotly_deployment import PlotlyDeployer
|
|
260
|
+
|
|
261
|
+
deployer = PlotlyDeployer(self, **kwargs)
|
|
262
|
+
deployer.deploy(mode="notebook")
|
|
263
|
+
return self
|
|
264
|
+
|
|
265
|
+
elif env == "flask":
|
|
266
|
+
from .flask_deployment import FlaskDeployer
|
|
267
|
+
|
|
268
|
+
deployer = FlaskDeployer(self, **kwargs)
|
|
269
|
+
deployer.deploy()
|
|
270
|
+
return self
|
|
217
271
|
else:
|
|
218
272
|
raise ValueError(
|
|
219
|
-
f"Unsupported environment: {env}, only 'notebook'
|
|
273
|
+
f"Unsupported environment: {env}, only 'notebook', 'plotly', 'plotly-inline', and 'flask' are supported right now."
|
|
220
274
|
)
|
|
221
275
|
|
|
222
276
|
@contextmanager
|
|
223
|
-
def
|
|
277
|
+
def _deploy_app(self):
|
|
224
278
|
"""Internal context manager to control app deployment state"""
|
|
225
279
|
self._app_deployed = True
|
|
226
280
|
try:
|
|
@@ -228,6 +282,128 @@ class InteractiveViewer(ABC):
|
|
|
228
282
|
finally:
|
|
229
283
|
self._app_deployed = False
|
|
230
284
|
|
|
285
|
+
def _prepare_function(
|
|
286
|
+
self,
|
|
287
|
+
func: Callable,
|
|
288
|
+
context: Optional[str] = "",
|
|
289
|
+
) -> Callable:
|
|
290
|
+
# Check if func is Callable
|
|
291
|
+
if not callable(func):
|
|
292
|
+
raise ValueError(f"Function {func} is not callable")
|
|
293
|
+
|
|
294
|
+
# Handle partial functions
|
|
295
|
+
if isinstance(func, partial):
|
|
296
|
+
get_self = (
|
|
297
|
+
lambda func: hasattr(func.func, "__self__") and func.func.__self__
|
|
298
|
+
)
|
|
299
|
+
get_name = lambda func: func.func.__name__
|
|
300
|
+
else:
|
|
301
|
+
get_self = lambda func: hasattr(func, "__self__") and func.__self__
|
|
302
|
+
get_name = lambda func: func.__name__
|
|
303
|
+
|
|
304
|
+
# Get function signature
|
|
305
|
+
try:
|
|
306
|
+
params = list(inspect.signature(func).parameters.values())
|
|
307
|
+
except ValueError:
|
|
308
|
+
# Handle built-ins or other objects without signatures
|
|
309
|
+
raise ValueError(context + f"Cannot inspect function signature for {func}")
|
|
310
|
+
|
|
311
|
+
# Look through params and check if there are two positional parameters (including self for bound methods)
|
|
312
|
+
bound_method = get_self(func) is self
|
|
313
|
+
positional_params = 0
|
|
314
|
+
required_kwargs = 0
|
|
315
|
+
optional_part = ""
|
|
316
|
+
for param in params:
|
|
317
|
+
# Check if it's a positional parameter. If it is, count it.
|
|
318
|
+
# We need at least 1 positional parameter. When we already have 1,
|
|
319
|
+
# we need to make sure any other positional parameters have defaults.
|
|
320
|
+
if param.kind in (
|
|
321
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
322
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
323
|
+
):
|
|
324
|
+
if positional_params < 1:
|
|
325
|
+
positional_params += 1
|
|
326
|
+
else:
|
|
327
|
+
if param.default == inspect.Parameter.empty:
|
|
328
|
+
positional_params += 1
|
|
329
|
+
else:
|
|
330
|
+
optional_part += f", {param.name}={param.default!r}"
|
|
331
|
+
elif param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
332
|
+
optional_part += f", **{param.name}"
|
|
333
|
+
elif param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
334
|
+
optional_part += (
|
|
335
|
+
f", {param.name}={param.default!r}"
|
|
336
|
+
if param.default != inspect.Parameter.empty
|
|
337
|
+
else f""
|
|
338
|
+
)
|
|
339
|
+
if param.default == inspect.Parameter.empty:
|
|
340
|
+
required_kwargs += 1
|
|
341
|
+
|
|
342
|
+
if positional_params != 1 or required_kwargs != 0:
|
|
343
|
+
func_name = get_name(func)
|
|
344
|
+
if isinstance(func, partial):
|
|
345
|
+
func_sig = str(inspect.signature(func))
|
|
346
|
+
if bound_method:
|
|
347
|
+
func_sig = "(" + "self, " + func_sig[1:]
|
|
348
|
+
msg = (
|
|
349
|
+
context
|
|
350
|
+
+ "\n"
|
|
351
|
+
+ f"Your partial function '{func_name}' has an incorrect signature.\n"
|
|
352
|
+
"Partial functions must have exactly one positional parameter\n"
|
|
353
|
+
"which corresponds to a dictionary of the current state of the viewer.\n"
|
|
354
|
+
"\nYour partial function effectivelylooks like this:\n"
|
|
355
|
+
f"def {func_name}{func_sig}:\n"
|
|
356
|
+
" ... your function code ..."
|
|
357
|
+
)
|
|
358
|
+
raise ValueError(msg)
|
|
359
|
+
|
|
360
|
+
if bound_method:
|
|
361
|
+
original_method = getattr(get_self(func).__class__, get_name(func))
|
|
362
|
+
func_sig = str(inspect.signature(original_method))
|
|
363
|
+
|
|
364
|
+
msg = (
|
|
365
|
+
context + "\n"
|
|
366
|
+
f"Your bound method '{func_name}{func_sig}' has an incorrect signature.\n"
|
|
367
|
+
"Bound methods must have exactly one positional parameter in addition to self.\n"
|
|
368
|
+
"The first parameter should be self (required for bound methods).\n"
|
|
369
|
+
"The second parameter should be state -- a dictionary of the current state of the viewer.\n"
|
|
370
|
+
"\nYour method looks like this:\n"
|
|
371
|
+
"class YourViewer(Viewer):\n"
|
|
372
|
+
f" def {func_name}{func_sig}:\n"
|
|
373
|
+
" ... your function code ...\n"
|
|
374
|
+
"\nIt should look like this:\n"
|
|
375
|
+
"class YourViewer(Viewer):\n"
|
|
376
|
+
f" def {func_name}(self, state{optional_part}):\n"
|
|
377
|
+
" ... your function code ..."
|
|
378
|
+
)
|
|
379
|
+
raise ValueError(msg)
|
|
380
|
+
else:
|
|
381
|
+
func_sig = str(inspect.signature(func))
|
|
382
|
+
bound_elsewhere = get_self(func) and get_self(func) is not self
|
|
383
|
+
if bound_elsewhere:
|
|
384
|
+
func_name = f"self.{func_name}"
|
|
385
|
+
func_sig = f"(self, {func_sig[1:]})"
|
|
386
|
+
add_self = True
|
|
387
|
+
else:
|
|
388
|
+
add_self = False
|
|
389
|
+
msg = (
|
|
390
|
+
context + "\n"
|
|
391
|
+
f"Your function '{func_name}{func_sig}' has an incorrect signature.\n"
|
|
392
|
+
"Functions must have exactly one positional parameter\n"
|
|
393
|
+
"which corresponds to a dictionary of the current state of the viewer.\n"
|
|
394
|
+
"\nYour function looks like this:\n"
|
|
395
|
+
f"def {func_name}{func_sig}:\n"
|
|
396
|
+
" ... your function code ...\n"
|
|
397
|
+
"\nIt should look like this:\n"
|
|
398
|
+
f"def {func_name}({'self, ' if add_self else ''}state{optional_part}):\n"
|
|
399
|
+
" ... your function code ..."
|
|
400
|
+
)
|
|
401
|
+
raise ValueError(msg)
|
|
402
|
+
|
|
403
|
+
# If we've made it here, the function has exactly one required positional parameter
|
|
404
|
+
# which means it's callable by the viewer.
|
|
405
|
+
return func
|
|
406
|
+
|
|
231
407
|
def perform_callbacks(self, name: str) -> bool:
|
|
232
408
|
"""Perform callbacks for all parameters that have changed"""
|
|
233
409
|
if self._in_callbacks:
|
|
@@ -235,7 +411,7 @@ class InteractiveViewer(ABC):
|
|
|
235
411
|
try:
|
|
236
412
|
self._in_callbacks = True
|
|
237
413
|
if name in self.callbacks:
|
|
238
|
-
state = self.
|
|
414
|
+
state = self.state
|
|
239
415
|
for callback in self.callbacks[name]:
|
|
240
416
|
callback(state)
|
|
241
417
|
finally:
|
|
@@ -266,6 +442,11 @@ class InteractiveViewer(ABC):
|
|
|
266
442
|
if isinstance(parameter_name, str):
|
|
267
443
|
parameter_name = [parameter_name]
|
|
268
444
|
|
|
445
|
+
callback = self._prepare_function(
|
|
446
|
+
callback,
|
|
447
|
+
context="Setting on_change callback:",
|
|
448
|
+
)
|
|
449
|
+
|
|
269
450
|
for param_name in parameter_name:
|
|
270
451
|
if param_name not in self.parameters:
|
|
271
452
|
raise ValueError(f"Parameter '{param_name}' is not registered!")
|
|
@@ -320,7 +501,7 @@ class InteractiveViewer(ABC):
|
|
|
320
501
|
Examples
|
|
321
502
|
--------
|
|
322
503
|
>>> viewer.add_text('title', value='My Plot')
|
|
323
|
-
>>> viewer.
|
|
504
|
+
>>> viewer.state['title']
|
|
324
505
|
'My Plot'
|
|
325
506
|
"""
|
|
326
507
|
try:
|
|
@@ -348,7 +529,7 @@ class InteractiveViewer(ABC):
|
|
|
348
529
|
Examples
|
|
349
530
|
--------
|
|
350
531
|
>>> viewer.add_boolean('show_grid', value=True)
|
|
351
|
-
>>> viewer.
|
|
532
|
+
>>> viewer.state['show_grid']
|
|
352
533
|
True
|
|
353
534
|
"""
|
|
354
535
|
try:
|
|
@@ -379,7 +560,7 @@ class InteractiveViewer(ABC):
|
|
|
379
560
|
--------
|
|
380
561
|
>>> viewer.add_selection('color', value='red',
|
|
381
562
|
... options=['red', 'green', 'blue'])
|
|
382
|
-
>>> viewer.
|
|
563
|
+
>>> viewer.state['color']
|
|
383
564
|
'red'
|
|
384
565
|
"""
|
|
385
566
|
try:
|
|
@@ -414,7 +595,7 @@ class InteractiveViewer(ABC):
|
|
|
414
595
|
>>> viewer.add_multiple_selection('toppings',
|
|
415
596
|
... value=['cheese'],
|
|
416
597
|
... options=['cheese', 'pepperoni', 'mushrooms'])
|
|
417
|
-
>>> viewer.
|
|
598
|
+
>>> viewer.state['toppings']
|
|
418
599
|
['cheese']
|
|
419
600
|
"""
|
|
420
601
|
try:
|
|
@@ -454,10 +635,10 @@ class InteractiveViewer(ABC):
|
|
|
454
635
|
Examples
|
|
455
636
|
--------
|
|
456
637
|
>>> viewer.add_integer('count', value=5, min_value=0, max_value=10)
|
|
457
|
-
>>> viewer.
|
|
638
|
+
>>> viewer.state['count']
|
|
458
639
|
5
|
|
459
640
|
>>> viewer.update_integer('count', value=15) # Will be clamped to 10
|
|
460
|
-
>>> viewer.
|
|
641
|
+
>>> viewer.state['count']
|
|
461
642
|
10
|
|
462
643
|
"""
|
|
463
644
|
try:
|
|
@@ -468,7 +649,7 @@ class InteractiveViewer(ABC):
|
|
|
468
649
|
max_value,
|
|
469
650
|
)
|
|
470
651
|
except Exception as e:
|
|
471
|
-
raise ParameterAddError(name, "number", str(e))
|
|
652
|
+
raise ParameterAddError(name, "number", str(e))
|
|
472
653
|
else:
|
|
473
654
|
self.parameters[name] = new_param
|
|
474
655
|
|
|
@@ -480,11 +661,12 @@ class InteractiveViewer(ABC):
|
|
|
480
661
|
value: Union[float, int],
|
|
481
662
|
min_value: Union[float, int],
|
|
482
663
|
max_value: Union[float, int],
|
|
483
|
-
step: float = 0.
|
|
664
|
+
step: float = 0.01,
|
|
484
665
|
) -> None:
|
|
485
666
|
"""
|
|
486
667
|
Add a decimal number parameter to the viewer.
|
|
487
668
|
|
|
669
|
+
|
|
488
670
|
Creates a slider in the GUI that lets users select numbers between
|
|
489
671
|
min_value and max_value. Values will be rounded to the nearest step
|
|
490
672
|
and clamped to stay within bounds.
|
|
@@ -500,16 +682,16 @@ class InteractiveViewer(ABC):
|
|
|
500
682
|
max_value : float
|
|
501
683
|
Maximum allowed value
|
|
502
684
|
step : float, optional
|
|
503
|
-
Size of each increment (default: 0.
|
|
685
|
+
Size of each increment (default: 0.01)
|
|
504
686
|
|
|
505
687
|
Examples
|
|
506
688
|
--------
|
|
507
689
|
>>> viewer.add_float('temperature', value=20.0,
|
|
508
690
|
... min_value=0.0, max_value=100.0, step=0.5)
|
|
509
|
-
>>> viewer.
|
|
691
|
+
>>> viewer.state['temperature']
|
|
510
692
|
20.0
|
|
511
693
|
>>> viewer.update_float('temperature', value=20.7) # Will round to 20.5
|
|
512
|
-
>>> viewer.
|
|
694
|
+
>>> viewer.state['temperature']
|
|
513
695
|
20.5
|
|
514
696
|
"""
|
|
515
697
|
try:
|
|
@@ -558,11 +740,11 @@ class InteractiveViewer(ABC):
|
|
|
558
740
|
>>> viewer.add_integer_range('age_range',
|
|
559
741
|
... value=(25, 35),
|
|
560
742
|
... min_value=18, max_value=100)
|
|
561
|
-
>>> viewer.
|
|
743
|
+
>>> viewer.state['age_range']
|
|
562
744
|
(25, 35)
|
|
563
745
|
>>> # Values will be swapped if low > high
|
|
564
746
|
>>> viewer.update_integer_range('age_range', value=(40, 30))
|
|
565
|
-
>>> viewer.
|
|
747
|
+
>>> viewer.state['age_range']
|
|
566
748
|
(30, 40)
|
|
567
749
|
"""
|
|
568
750
|
try:
|
|
@@ -585,7 +767,7 @@ class InteractiveViewer(ABC):
|
|
|
585
767
|
value: Tuple[Union[float, int], Union[float, int]],
|
|
586
768
|
min_value: Union[float, int],
|
|
587
769
|
max_value: Union[float, int],
|
|
588
|
-
step: float = 0.
|
|
770
|
+
step: float = 0.01,
|
|
589
771
|
) -> None:
|
|
590
772
|
"""
|
|
591
773
|
Add a range parameter for decimal numbers to the viewer.
|
|
@@ -606,18 +788,18 @@ class InteractiveViewer(ABC):
|
|
|
606
788
|
max_value : float
|
|
607
789
|
Maximum allowed value for both low and high
|
|
608
790
|
step : float, optional
|
|
609
|
-
Size of each increment (default: 0.
|
|
791
|
+
Size of each increment (default: 0.01)
|
|
610
792
|
|
|
611
793
|
Examples
|
|
612
794
|
--------
|
|
613
795
|
>>> viewer.add_float_range('price_range',
|
|
614
796
|
... value=(10.0, 20.0),
|
|
615
797
|
... min_value=0.0, max_value=100.0, step=0.5)
|
|
616
|
-
>>> viewer.
|
|
798
|
+
>>> viewer.state['price_range']
|
|
617
799
|
(10.0, 20.0)
|
|
618
800
|
>>> # Values will be rounded to nearest step
|
|
619
801
|
>>> viewer.update_float_range('price_range', value=(10.7, 19.2))
|
|
620
|
-
>>> viewer.
|
|
802
|
+
>>> viewer.state['price_range']
|
|
621
803
|
(10.5, 19.0)
|
|
622
804
|
"""
|
|
623
805
|
try:
|
|
@@ -639,15 +821,12 @@ class InteractiveViewer(ABC):
|
|
|
639
821
|
name: str,
|
|
640
822
|
*,
|
|
641
823
|
value: Union[float, int],
|
|
642
|
-
min_value: Optional[Union[float, int]] = None,
|
|
643
|
-
max_value: Optional[Union[float, int]] = None,
|
|
644
824
|
) -> None:
|
|
645
825
|
"""
|
|
646
826
|
Add an unbounded integer parameter to the viewer.
|
|
647
827
|
|
|
648
828
|
Creates a text input box in the GUI for entering whole numbers. Unlike
|
|
649
|
-
add_integer(), this allows very large numbers
|
|
650
|
-
or maximum bounds.
|
|
829
|
+
add_integer(), this allows very large numbers without bounds.
|
|
651
830
|
See :class:`~syd.parameters.UnboundedIntegerParameter` for details.
|
|
652
831
|
|
|
653
832
|
Parameters
|
|
@@ -656,29 +835,17 @@ class InteractiveViewer(ABC):
|
|
|
656
835
|
Name of the parameter (used as label in GUI)
|
|
657
836
|
value : int
|
|
658
837
|
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
838
|
|
|
664
839
|
Examples
|
|
665
840
|
--------
|
|
666
|
-
>>> viewer.add_unbounded_integer('population',
|
|
667
|
-
|
|
668
|
-
... min_value=0) # No maximum
|
|
669
|
-
>>> viewer.get_state()['population']
|
|
841
|
+
>>> viewer.add_unbounded_integer('population', value=1000000)
|
|
842
|
+
>>> viewer.state['population']
|
|
670
843
|
1000000
|
|
671
|
-
>>> # Values below minimum will be clamped
|
|
672
|
-
>>> viewer.update_unbounded_integer('population', value=-5)
|
|
673
|
-
>>> viewer.get_state()['population']
|
|
674
|
-
0
|
|
675
844
|
"""
|
|
676
845
|
try:
|
|
677
846
|
new_param = ParameterType.unbounded_integer.value(
|
|
678
847
|
name,
|
|
679
848
|
value,
|
|
680
|
-
min_value,
|
|
681
|
-
max_value,
|
|
682
849
|
)
|
|
683
850
|
except Exception as e:
|
|
684
851
|
raise ParameterAddError(name, "unbounded_integer", str(e)) from e
|
|
@@ -691,16 +858,14 @@ class InteractiveViewer(ABC):
|
|
|
691
858
|
name: str,
|
|
692
859
|
*,
|
|
693
860
|
value: Union[float, int],
|
|
694
|
-
min_value: Optional[Union[float, int]] = None,
|
|
695
|
-
max_value: Optional[Union[float, int]] = None,
|
|
696
861
|
step: Optional[float] = None,
|
|
697
862
|
) -> None:
|
|
698
863
|
"""
|
|
699
864
|
Add an unbounded decimal number parameter to the viewer.
|
|
700
865
|
|
|
701
866
|
Creates a text input box in the GUI for entering numbers. Unlike add_float(),
|
|
702
|
-
this allows very large or precise numbers
|
|
703
|
-
|
|
867
|
+
this allows very large or precise numbers without bounds. Values can optionally
|
|
868
|
+
be rounded to a step size.
|
|
704
869
|
See :class:`~syd.parameters.UnboundedFloatParameter` for details.
|
|
705
870
|
|
|
706
871
|
Parameters
|
|
@@ -709,32 +874,23 @@ class InteractiveViewer(ABC):
|
|
|
709
874
|
Name of the parameter (used as label in GUI)
|
|
710
875
|
value : float
|
|
711
876
|
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
877
|
step : float, optional
|
|
717
878
|
Size of each increment (or None for no rounding)
|
|
718
879
|
|
|
719
880
|
Examples
|
|
720
881
|
--------
|
|
721
|
-
>>> viewer.add_unbounded_float('wavelength',
|
|
722
|
-
|
|
723
|
-
... min_value=0.0,
|
|
724
|
-
... step=1e-9) # Round to nearest nanometer
|
|
725
|
-
>>> viewer.get_state()['wavelength']
|
|
882
|
+
>>> viewer.add_unbounded_float('wavelength', value=550e-9, step=1e-9)
|
|
883
|
+
>>> viewer.state['wavelength']
|
|
726
884
|
5.5e-07
|
|
727
885
|
>>> # Values will be rounded if step is provided
|
|
728
886
|
>>> viewer.update_unbounded_float('wavelength', value=550.7e-9)
|
|
729
|
-
>>> viewer.
|
|
887
|
+
>>> viewer.state['wavelength']
|
|
730
888
|
5.51e-07
|
|
731
889
|
"""
|
|
732
890
|
try:
|
|
733
891
|
new_param = ParameterType.unbounded_float.value(
|
|
734
892
|
name,
|
|
735
893
|
value,
|
|
736
|
-
min_value,
|
|
737
|
-
max_value,
|
|
738
894
|
step,
|
|
739
895
|
)
|
|
740
896
|
except Exception as e:
|
|
@@ -742,7 +898,7 @@ class InteractiveViewer(ABC):
|
|
|
742
898
|
else:
|
|
743
899
|
self.parameters[name] = new_param
|
|
744
900
|
|
|
745
|
-
@validate_parameter_operation("add",
|
|
901
|
+
@validate_parameter_operation("add", ActionType.button)
|
|
746
902
|
def add_button(
|
|
747
903
|
self,
|
|
748
904
|
name: str,
|
|
@@ -764,21 +920,25 @@ class InteractiveViewer(ABC):
|
|
|
764
920
|
label : str
|
|
765
921
|
Text to display on the button
|
|
766
922
|
callback : callable
|
|
767
|
-
Function to call when the button is clicked (takes
|
|
923
|
+
Function to call when the button is clicked (takes state as a single argument)
|
|
768
924
|
|
|
769
925
|
Examples
|
|
770
926
|
--------
|
|
771
|
-
>>> def reset_plot():
|
|
927
|
+
>>> def reset_plot(state):
|
|
772
928
|
... print("Resetting plot...")
|
|
773
929
|
>>> viewer.add_button('reset', label='Reset Plot', callback=reset_plot)
|
|
930
|
+
|
|
931
|
+
>>> def print_plot_info(state):
|
|
932
|
+
... print(f"Current plot info: {state['plot_info']}")
|
|
933
|
+
>>> viewer.add_button('print_info', label='Print Plot Info', callback=print_plot_info)
|
|
774
934
|
"""
|
|
775
935
|
try:
|
|
936
|
+
callback = self._prepare_function(
|
|
937
|
+
callback,
|
|
938
|
+
context="Setting button callback:",
|
|
939
|
+
)
|
|
776
940
|
|
|
777
|
-
|
|
778
|
-
def wrapped_callback(button):
|
|
779
|
-
callback(self.get_state())
|
|
780
|
-
|
|
781
|
-
new_param = ParameterType.button.value(name, label, wrapped_callback)
|
|
941
|
+
new_param = ActionType.button.value(name, label, callback)
|
|
782
942
|
except Exception as e:
|
|
783
943
|
raise ParameterAddError(name, "button", str(e)) from e
|
|
784
944
|
else:
|
|
@@ -792,7 +952,7 @@ class InteractiveViewer(ABC):
|
|
|
792
952
|
"""
|
|
793
953
|
Update a text parameter's value.
|
|
794
954
|
|
|
795
|
-
Updates a parameter created by :meth:`~syd.
|
|
955
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_text`.
|
|
796
956
|
See :class:`~syd.parameters.TextParameter` for details about value validation.
|
|
797
957
|
|
|
798
958
|
Parameters
|
|
@@ -806,11 +966,11 @@ class InteractiveViewer(ABC):
|
|
|
806
966
|
--------
|
|
807
967
|
>>> viewer.add_text('title', value='Original Title')
|
|
808
968
|
>>> viewer.update_text('title', value='New Title')
|
|
809
|
-
>>> viewer.
|
|
969
|
+
>>> viewer.state['title']
|
|
810
970
|
'New Title'
|
|
811
971
|
"""
|
|
812
972
|
updates = {}
|
|
813
|
-
if value
|
|
973
|
+
if not value == _NO_UPDATE:
|
|
814
974
|
updates["value"] = value
|
|
815
975
|
if updates:
|
|
816
976
|
self.parameters[name].update(updates)
|
|
@@ -822,7 +982,7 @@ class InteractiveViewer(ABC):
|
|
|
822
982
|
"""
|
|
823
983
|
Update a boolean parameter's value.
|
|
824
984
|
|
|
825
|
-
Updates a parameter created by :meth:`~syd.
|
|
985
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_boolean`.
|
|
826
986
|
See :class:`~syd.parameters.BooleanParameter` for details about value validation.
|
|
827
987
|
|
|
828
988
|
Parameters
|
|
@@ -836,11 +996,11 @@ class InteractiveViewer(ABC):
|
|
|
836
996
|
--------
|
|
837
997
|
>>> viewer.add_boolean('show_grid', value=True)
|
|
838
998
|
>>> viewer.update_boolean('show_grid', value=False)
|
|
839
|
-
>>> viewer.
|
|
999
|
+
>>> viewer.state['show_grid']
|
|
840
1000
|
False
|
|
841
1001
|
"""
|
|
842
1002
|
updates = {}
|
|
843
|
-
if value
|
|
1003
|
+
if not value == _NO_UPDATE:
|
|
844
1004
|
updates["value"] = value
|
|
845
1005
|
if updates:
|
|
846
1006
|
self.parameters[name].update(updates)
|
|
@@ -856,7 +1016,7 @@ class InteractiveViewer(ABC):
|
|
|
856
1016
|
"""
|
|
857
1017
|
Update a selection parameter's value and/or options.
|
|
858
1018
|
|
|
859
|
-
Updates a parameter created by :meth:`~syd.
|
|
1019
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_selection`.
|
|
860
1020
|
See :class:`~syd.parameters.SelectionParameter` for details about value validation.
|
|
861
1021
|
|
|
862
1022
|
Parameters
|
|
@@ -880,9 +1040,9 @@ class InteractiveViewer(ABC):
|
|
|
880
1040
|
... value='purple')
|
|
881
1041
|
"""
|
|
882
1042
|
updates = {}
|
|
883
|
-
if value
|
|
1043
|
+
if not value == _NO_UPDATE:
|
|
884
1044
|
updates["value"] = value
|
|
885
|
-
if options
|
|
1045
|
+
if not options == _NO_UPDATE:
|
|
886
1046
|
updates["options"] = options
|
|
887
1047
|
if updates:
|
|
888
1048
|
self.parameters[name].update(updates)
|
|
@@ -898,7 +1058,7 @@ class InteractiveViewer(ABC):
|
|
|
898
1058
|
"""
|
|
899
1059
|
Update a multiple selection parameter's values and/or options.
|
|
900
1060
|
|
|
901
|
-
Updates a parameter created by :meth:`~syd.
|
|
1061
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_multiple_selection`.
|
|
902
1062
|
See :class:`~syd.parameters.MultipleSelectionParameter` for details about value validation.
|
|
903
1063
|
|
|
904
1064
|
Parameters
|
|
@@ -924,9 +1084,9 @@ class InteractiveViewer(ABC):
|
|
|
924
1084
|
... value=['cheese', 'bacon'])
|
|
925
1085
|
"""
|
|
926
1086
|
updates = {}
|
|
927
|
-
if value
|
|
1087
|
+
if not value == _NO_UPDATE:
|
|
928
1088
|
updates["value"] = value
|
|
929
|
-
if options
|
|
1089
|
+
if not options == _NO_UPDATE:
|
|
930
1090
|
updates["options"] = options
|
|
931
1091
|
if updates:
|
|
932
1092
|
self.parameters[name].update(updates)
|
|
@@ -943,7 +1103,7 @@ class InteractiveViewer(ABC):
|
|
|
943
1103
|
"""
|
|
944
1104
|
Update an integer parameter's value and/or bounds.
|
|
945
1105
|
|
|
946
|
-
Updates a parameter created by :meth:`~syd.
|
|
1106
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_integer`.
|
|
947
1107
|
See :class:`~syd.parameters.IntegerParameter` for details about value validation.
|
|
948
1108
|
|
|
949
1109
|
Parameters
|
|
@@ -966,11 +1126,11 @@ class InteractiveViewer(ABC):
|
|
|
966
1126
|
>>> viewer.update_integer('count', min_value=7, max_value=15)
|
|
967
1127
|
"""
|
|
968
1128
|
updates = {}
|
|
969
|
-
if value
|
|
1129
|
+
if not value == _NO_UPDATE:
|
|
970
1130
|
updates["value"] = value
|
|
971
|
-
if min_value
|
|
1131
|
+
if not min_value == _NO_UPDATE:
|
|
972
1132
|
updates["min_value"] = min_value
|
|
973
|
-
if max_value
|
|
1133
|
+
if not max_value == _NO_UPDATE:
|
|
974
1134
|
updates["max_value"] = max_value
|
|
975
1135
|
if updates:
|
|
976
1136
|
self.parameters[name].update(updates)
|
|
@@ -988,7 +1148,7 @@ class InteractiveViewer(ABC):
|
|
|
988
1148
|
"""
|
|
989
1149
|
Update a float parameter's value, bounds, and/or step size.
|
|
990
1150
|
|
|
991
|
-
Updates a parameter created by :meth:`~syd.
|
|
1151
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_float`.
|
|
992
1152
|
See :class:`~syd.parameters.FloatParameter` for details about value validation.
|
|
993
1153
|
|
|
994
1154
|
Parameters
|
|
@@ -1015,13 +1175,13 @@ class InteractiveViewer(ABC):
|
|
|
1015
1175
|
... min_value=15.0, max_value=30.0, step=0.1)
|
|
1016
1176
|
"""
|
|
1017
1177
|
updates = {}
|
|
1018
|
-
if value
|
|
1178
|
+
if not value == _NO_UPDATE:
|
|
1019
1179
|
updates["value"] = value
|
|
1020
|
-
if min_value
|
|
1180
|
+
if not min_value == _NO_UPDATE:
|
|
1021
1181
|
updates["min_value"] = min_value
|
|
1022
|
-
if max_value
|
|
1182
|
+
if not max_value == _NO_UPDATE:
|
|
1023
1183
|
updates["max_value"] = max_value
|
|
1024
|
-
if step
|
|
1184
|
+
if not step == _NO_UPDATE:
|
|
1025
1185
|
updates["step"] = step
|
|
1026
1186
|
if updates:
|
|
1027
1187
|
self.parameters[name].update(updates)
|
|
@@ -1038,7 +1198,7 @@ class InteractiveViewer(ABC):
|
|
|
1038
1198
|
"""
|
|
1039
1199
|
Update an integer range parameter's values and/or bounds.
|
|
1040
1200
|
|
|
1041
|
-
Updates a parameter created by :meth:`~syd.
|
|
1201
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_integer_range`.
|
|
1042
1202
|
See :class:`~syd.parameters.IntegerRangeParameter` for details about value validation.
|
|
1043
1203
|
|
|
1044
1204
|
Parameters
|
|
@@ -1063,11 +1223,11 @@ class InteractiveViewer(ABC):
|
|
|
1063
1223
|
>>> viewer.update_integer_range('age_range', min_value=20, max_value=80)
|
|
1064
1224
|
"""
|
|
1065
1225
|
updates = {}
|
|
1066
|
-
if value
|
|
1226
|
+
if not value == _NO_UPDATE:
|
|
1067
1227
|
updates["value"] = value
|
|
1068
|
-
if min_value
|
|
1228
|
+
if not min_value == _NO_UPDATE:
|
|
1069
1229
|
updates["min_value"] = min_value
|
|
1070
|
-
if max_value
|
|
1230
|
+
if not max_value == _NO_UPDATE:
|
|
1071
1231
|
updates["max_value"] = max_value
|
|
1072
1232
|
if updates:
|
|
1073
1233
|
self.parameters[name].update(updates)
|
|
@@ -1085,7 +1245,7 @@ class InteractiveViewer(ABC):
|
|
|
1085
1245
|
"""
|
|
1086
1246
|
Update a float range parameter's values, bounds, and/or step size.
|
|
1087
1247
|
|
|
1088
|
-
Updates a parameter created by :meth:`~syd.
|
|
1248
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_float_range`.
|
|
1089
1249
|
See :class:`~syd.parameters.FloatRangeParameter` for details about value validation.
|
|
1090
1250
|
|
|
1091
1251
|
Parameters
|
|
@@ -1113,13 +1273,13 @@ class InteractiveViewer(ABC):
|
|
|
1113
1273
|
... min_value=5.0, max_value=50.0, step=0.1)
|
|
1114
1274
|
"""
|
|
1115
1275
|
updates = {}
|
|
1116
|
-
if value
|
|
1276
|
+
if not value == _NO_UPDATE:
|
|
1117
1277
|
updates["value"] = value
|
|
1118
|
-
if min_value
|
|
1278
|
+
if not min_value == _NO_UPDATE:
|
|
1119
1279
|
updates["min_value"] = min_value
|
|
1120
|
-
if max_value
|
|
1280
|
+
if not max_value == _NO_UPDATE:
|
|
1121
1281
|
updates["max_value"] = max_value
|
|
1122
|
-
if step
|
|
1282
|
+
if not step == _NO_UPDATE:
|
|
1123
1283
|
updates["step"] = step
|
|
1124
1284
|
if updates:
|
|
1125
1285
|
self.parameters[name].update(updates)
|
|
@@ -1130,13 +1290,11 @@ class InteractiveViewer(ABC):
|
|
|
1130
1290
|
name: str,
|
|
1131
1291
|
*,
|
|
1132
1292
|
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
1293
|
) -> None:
|
|
1136
1294
|
"""
|
|
1137
1295
|
Update an unbounded integer parameter's value and/or bounds.
|
|
1138
1296
|
|
|
1139
|
-
Updates a parameter created by :meth:`~syd.
|
|
1297
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_unbounded_integer`.
|
|
1140
1298
|
See :class:`~syd.parameters.UnboundedIntegerParameter` for details about value validation.
|
|
1141
1299
|
|
|
1142
1300
|
Parameters
|
|
@@ -1144,31 +1302,17 @@ class InteractiveViewer(ABC):
|
|
|
1144
1302
|
name : str
|
|
1145
1303
|
Name of the unbounded integer parameter to update
|
|
1146
1304
|
value : int, optional
|
|
1147
|
-
New value (
|
|
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)
|
|
1305
|
+
New value (if not provided, no change)
|
|
1152
1306
|
|
|
1153
1307
|
Examples
|
|
1154
1308
|
--------
|
|
1155
|
-
>>> viewer.add_unbounded_integer('population',
|
|
1156
|
-
... value=1000000,
|
|
1157
|
-
... min_value=0) # No maximum
|
|
1309
|
+
>>> viewer.add_unbounded_integer('population', value=1000000)
|
|
1158
1310
|
>>> # Update just the value
|
|
1159
1311
|
>>> 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
1312
|
"""
|
|
1165
1313
|
updates = {}
|
|
1166
|
-
if value
|
|
1314
|
+
if not value == _NO_UPDATE:
|
|
1167
1315
|
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
1316
|
if updates:
|
|
1173
1317
|
self.parameters[name].update(updates)
|
|
1174
1318
|
|
|
@@ -1178,14 +1322,12 @@ class InteractiveViewer(ABC):
|
|
|
1178
1322
|
name: str,
|
|
1179
1323
|
*,
|
|
1180
1324
|
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
1325
|
step: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
|
|
1184
1326
|
) -> None:
|
|
1185
1327
|
"""
|
|
1186
1328
|
Update an unbounded float parameter's value, bounds, and/or step size.
|
|
1187
1329
|
|
|
1188
|
-
Updates a parameter created by :meth:`~syd.
|
|
1330
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_unbounded_float`.
|
|
1189
1331
|
See :class:`~syd.parameters.UnboundedFloatParameter` for details about value validation.
|
|
1190
1332
|
|
|
1191
1333
|
Parameters
|
|
@@ -1194,41 +1336,28 @@ class InteractiveViewer(ABC):
|
|
|
1194
1336
|
Name of the unbounded float parameter to update
|
|
1195
1337
|
value : float, optional
|
|
1196
1338
|
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
1339
|
step : float or None, optional
|
|
1202
1340
|
New step size for rounding, or None for no rounding (if not provided, no change)
|
|
1203
1341
|
|
|
1204
1342
|
Examples
|
|
1205
1343
|
--------
|
|
1206
|
-
>>> viewer.add_unbounded_float('wavelength',
|
|
1207
|
-
... value=550e-9, # Nanometers
|
|
1208
|
-
... min_value=0.0,
|
|
1209
|
-
... step=1e-9)
|
|
1344
|
+
>>> viewer.add_unbounded_float('wavelength', value=550e-9, step=1e-9)
|
|
1210
1345
|
>>> # Update value (will be rounded if step is set)
|
|
1211
|
-
>>> viewer.update_unbounded_float('wavelength', value=632.8e-9)
|
|
1212
|
-
>>> # Change step size
|
|
1213
|
-
>>> viewer.update_unbounded_float('wavelength',
|
|
1214
|
-
... step=0.1e-9, # Finer control
|
|
1215
|
-
... max_value=1000e-9) # Infrared limit
|
|
1346
|
+
>>> viewer.update_unbounded_float('wavelength', value=632.8e-9)
|
|
1347
|
+
>>> # Change step size
|
|
1348
|
+
>>> viewer.update_unbounded_float('wavelength', step=0.1e-9)
|
|
1216
1349
|
>>> # Remove step size (allow any precision)
|
|
1217
1350
|
>>> viewer.update_unbounded_float('wavelength', step=None)
|
|
1218
1351
|
"""
|
|
1219
1352
|
updates = {}
|
|
1220
|
-
if value
|
|
1353
|
+
if not value == _NO_UPDATE:
|
|
1221
1354
|
updates["value"] = value
|
|
1222
|
-
if
|
|
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:
|
|
1355
|
+
if not step == _NO_UPDATE:
|
|
1227
1356
|
updates["step"] = step
|
|
1228
1357
|
if updates:
|
|
1229
1358
|
self.parameters[name].update(updates)
|
|
1230
1359
|
|
|
1231
|
-
@validate_parameter_operation("update",
|
|
1360
|
+
@validate_parameter_operation("update", ActionType.button)
|
|
1232
1361
|
def update_button(
|
|
1233
1362
|
self,
|
|
1234
1363
|
name: str,
|
|
@@ -1239,8 +1368,8 @@ class InteractiveViewer(ABC):
|
|
|
1239
1368
|
"""
|
|
1240
1369
|
Update a button parameter's label and/or callback function.
|
|
1241
1370
|
|
|
1242
|
-
Updates a parameter created by :meth:`~syd.
|
|
1243
|
-
See :class:`~syd.parameters.
|
|
1371
|
+
Updates a parameter created by :meth:`~syd.viewer.Viewer.add_button`.
|
|
1372
|
+
See :class:`~syd.parameters.ButtonAction` for details.
|
|
1244
1373
|
|
|
1245
1374
|
Parameters
|
|
1246
1375
|
----------
|
|
@@ -1253,16 +1382,20 @@ class InteractiveViewer(ABC):
|
|
|
1253
1382
|
|
|
1254
1383
|
Examples
|
|
1255
1384
|
--------
|
|
1256
|
-
>>> def new_callback():
|
|
1385
|
+
>>> def new_callback(state):
|
|
1257
1386
|
... print("New action...")
|
|
1258
1387
|
>>> viewer.update_button('reset',
|
|
1259
|
-
... label='
|
|
1388
|
+
... label='New Action!',
|
|
1260
1389
|
... callback=new_callback)
|
|
1261
1390
|
"""
|
|
1262
1391
|
updates = {}
|
|
1263
|
-
if label
|
|
1392
|
+
if not label == _NO_UPDATE:
|
|
1264
1393
|
updates["label"] = label
|
|
1265
|
-
if callback
|
|
1394
|
+
if not callback == _NO_UPDATE:
|
|
1395
|
+
callback = self._prepare_function(
|
|
1396
|
+
callback,
|
|
1397
|
+
context="Updating button callback:",
|
|
1398
|
+
)
|
|
1266
1399
|
updates["callback"] = callback
|
|
1267
1400
|
if updates:
|
|
1268
1401
|
self.parameters[name].update(updates)
|