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
syd/parameters.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from typing import List, Any, Tuple, Generic, TypeVar, Optional, Dict, Callable
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
1
|
+
from typing import List, Any, Tuple, Generic, TypeVar, Optional, Dict, Callable, Union
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from abc import ABC, ABCMeta, abstractmethod
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from copy import deepcopy
|
|
6
6
|
from warnings import warn
|
|
7
|
+
import numpy as np
|
|
7
8
|
|
|
8
9
|
T = TypeVar("T")
|
|
9
10
|
|
|
@@ -55,6 +56,29 @@ class ParameterUpdateError(Exception):
|
|
|
55
56
|
)
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
class ParameterUpdateWarning(Warning):
|
|
60
|
+
"""
|
|
61
|
+
Warning raised when there is a non-critical issue updating a parameter.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
parameter_name : str
|
|
66
|
+
Name of the parameter that had the warning
|
|
67
|
+
parameter_type : str
|
|
68
|
+
Type of the parameter
|
|
69
|
+
message : str, optional
|
|
70
|
+
Additional warning details
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, parameter_name: str, parameter_type: str, message: str = None):
|
|
74
|
+
self.parameter_name = parameter_name
|
|
75
|
+
self.parameter_type = parameter_type
|
|
76
|
+
super().__init__(
|
|
77
|
+
f"Warning updating {parameter_type} parameter '{parameter_name}'"
|
|
78
|
+
+ (f": {message}" if message else "")
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
58
82
|
def get_parameter_attributes(param_class) -> List[str]:
|
|
59
83
|
"""
|
|
60
84
|
Get all valid attributes for a parameter class.
|
|
@@ -82,8 +106,39 @@ def get_parameter_attributes(param_class) -> List[str]:
|
|
|
82
106
|
return attributes
|
|
83
107
|
|
|
84
108
|
|
|
109
|
+
class ParameterMeta(ABCMeta):
|
|
110
|
+
_parameter_types = {}
|
|
111
|
+
_parameter_ids = {} # Store unique identifiers for our parameter types
|
|
112
|
+
|
|
113
|
+
def __new__(cls, name, bases, namespace):
|
|
114
|
+
parameter_class = super().__new__(cls, name, bases, namespace)
|
|
115
|
+
if name != "Parameter":
|
|
116
|
+
# Generate a unique ID for this parameter type
|
|
117
|
+
type_id = f"syd.parameters.{name}" # Using fully qualified name
|
|
118
|
+
cls._parameter_ids[name] = type_id
|
|
119
|
+
|
|
120
|
+
# Add ID to the class
|
|
121
|
+
if not hasattr(parameter_class, "_parameter_type_id"):
|
|
122
|
+
setattr(parameter_class, "_parameter_type_id", type_id)
|
|
123
|
+
else:
|
|
124
|
+
if getattr(parameter_class, "_parameter_type_id") != type_id:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"Parameter type {name} has multiple IDs: {type_id} and {getattr(parameter_class, '_parameter_type_id')}"
|
|
127
|
+
)
|
|
128
|
+
cls._parameter_types[name] = parameter_class
|
|
129
|
+
return parameter_class
|
|
130
|
+
|
|
131
|
+
def __instancecheck__(cls, instance):
|
|
132
|
+
type_id = cls._parameter_ids.get(cls.__name__)
|
|
133
|
+
if not type_id:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
# Check if instance has our type ID
|
|
137
|
+
return getattr(instance.__class__, "_parameter_type_id", None) == type_id
|
|
138
|
+
|
|
139
|
+
|
|
85
140
|
@dataclass
|
|
86
|
-
class Parameter(Generic[T], ABC):
|
|
141
|
+
class Parameter(Generic[T], ABC, metaclass=ParameterMeta):
|
|
87
142
|
"""
|
|
88
143
|
Base class for all parameter types. Parameters are the building blocks
|
|
89
144
|
for creating interactive GUI elements.
|
|
@@ -106,6 +161,7 @@ class Parameter(Generic[T], ABC):
|
|
|
106
161
|
|
|
107
162
|
name: str
|
|
108
163
|
value: T
|
|
164
|
+
_is_action: bool = False
|
|
109
165
|
|
|
110
166
|
@abstractmethod
|
|
111
167
|
def __init__(self, name: str, value: T):
|
|
@@ -229,8 +285,8 @@ class TextParameter(Parameter[str]):
|
|
|
229
285
|
Parameter for text input.
|
|
230
286
|
|
|
231
287
|
Creates a text box in the GUI that accepts any string input.
|
|
232
|
-
See :meth:`~syd.
|
|
233
|
-
:meth:`~syd.
|
|
288
|
+
See :meth:`~syd.viewer.Viewer.add_text` and
|
|
289
|
+
:meth:`~syd.viewer.Viewer.update_text` for usage.
|
|
234
290
|
|
|
235
291
|
Parameters
|
|
236
292
|
----------
|
|
@@ -272,8 +328,8 @@ class BooleanParameter(Parameter[bool]):
|
|
|
272
328
|
Parameter for boolean values.
|
|
273
329
|
|
|
274
330
|
Creates a checkbox in the GUI that can be toggled on/off.
|
|
275
|
-
See :meth:`~syd.
|
|
276
|
-
:meth:`~syd.
|
|
331
|
+
See :meth:`~syd.viewer.Viewer.add_boolean` and
|
|
332
|
+
:meth:`~syd.viewer.Viewer.update_boolean` for usage.
|
|
277
333
|
|
|
278
334
|
Parameters
|
|
279
335
|
----------
|
|
@@ -315,8 +371,8 @@ class SelectionParameter(Parameter[Any]):
|
|
|
315
371
|
Parameter for single selection from a list of options.
|
|
316
372
|
|
|
317
373
|
Creates a dropdown menu in the GUI where users can select one option.
|
|
318
|
-
See :meth:`~syd.
|
|
319
|
-
:meth:`~syd.
|
|
374
|
+
See :meth:`~syd.viewer.Viewer.add_selection` and
|
|
375
|
+
:meth:`~syd.viewer.Viewer.update_selection` for usage.
|
|
320
376
|
|
|
321
377
|
Parameters
|
|
322
378
|
----------
|
|
@@ -324,8 +380,8 @@ class SelectionParameter(Parameter[Any]):
|
|
|
324
380
|
The name of the parameter
|
|
325
381
|
value : Any
|
|
326
382
|
The initially selected value (must be one of the options)
|
|
327
|
-
options :
|
|
328
|
-
List of valid choices that can be selected
|
|
383
|
+
options : sequence
|
|
384
|
+
List, tuple, or 1D numpy array of valid choices that can be selected
|
|
329
385
|
|
|
330
386
|
Examples
|
|
331
387
|
--------
|
|
@@ -336,15 +392,59 @@ class SelectionParameter(Parameter[Any]):
|
|
|
336
392
|
>>> color.value
|
|
337
393
|
'blue'
|
|
338
394
|
>>> color.update({"value": "yellow"}) # This will raise an error
|
|
395
|
+
>>> # With numpy array
|
|
396
|
+
>>> import numpy as np
|
|
397
|
+
>>> numbers = SelectionParameter("number", 1, options=np.array([1, 2, 3]))
|
|
398
|
+
>>> numbers.value
|
|
399
|
+
1
|
|
339
400
|
"""
|
|
340
401
|
|
|
341
402
|
options: List[Any]
|
|
342
403
|
|
|
343
|
-
def __init__(self, name: str, value: Any, options: List
|
|
404
|
+
def __init__(self, name: str, value: Any, options: Union[List, Tuple]):
|
|
344
405
|
self.name = name
|
|
345
|
-
self.options = options
|
|
406
|
+
self.options = self._validate_options(options)
|
|
346
407
|
self._value = self._validate(value)
|
|
347
408
|
|
|
409
|
+
def _validate_options(self, options: Any) -> List[Any]:
|
|
410
|
+
"""
|
|
411
|
+
Validate options and convert to list if necessary.
|
|
412
|
+
|
|
413
|
+
Parameters
|
|
414
|
+
----------
|
|
415
|
+
options : list or tuple
|
|
416
|
+
The options to validate
|
|
417
|
+
|
|
418
|
+
Returns
|
|
419
|
+
-------
|
|
420
|
+
list
|
|
421
|
+
Validated list of options
|
|
422
|
+
|
|
423
|
+
Raises
|
|
424
|
+
------
|
|
425
|
+
TypeError
|
|
426
|
+
If options is not a list or tuple
|
|
427
|
+
ValueError
|
|
428
|
+
If any option is not hashable
|
|
429
|
+
"""
|
|
430
|
+
if not isinstance(options, (list, tuple)):
|
|
431
|
+
raise TypeError(
|
|
432
|
+
f"Options for parameter {self.name} must be a list or tuple"
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
if not options:
|
|
436
|
+
raise ValueError(f"Options for parameter {self.name} must not be empty")
|
|
437
|
+
|
|
438
|
+
# Verify all options are hashable (needed for comparison)
|
|
439
|
+
try:
|
|
440
|
+
for opt in options:
|
|
441
|
+
hash(opt)
|
|
442
|
+
except TypeError as e:
|
|
443
|
+
raise ValueError(
|
|
444
|
+
f"All options for parameter {self.name} must be hashable: {str(e)}"
|
|
445
|
+
)
|
|
446
|
+
return list(options)
|
|
447
|
+
|
|
348
448
|
def _validate(self, new_value: Any) -> Any:
|
|
349
449
|
"""
|
|
350
450
|
Validate that value is one of the allowed options.
|
|
@@ -372,13 +472,14 @@ class SelectionParameter(Parameter[Any]):
|
|
|
372
472
|
Raises:
|
|
373
473
|
TypeError: If options is not a list or tuple
|
|
374
474
|
"""
|
|
375
|
-
|
|
376
|
-
raise TypeError(
|
|
377
|
-
f"Options for parameter {self.name} are not a list or tuple: {self.options}"
|
|
378
|
-
)
|
|
475
|
+
self.options = self._validate_options(self.options)
|
|
379
476
|
if self.value not in self.options:
|
|
380
477
|
warn(
|
|
381
|
-
|
|
478
|
+
ParameterUpdateWarning(
|
|
479
|
+
self.name,
|
|
480
|
+
type(self).__name__,
|
|
481
|
+
f"Value {self.value} not in options, setting to first option ({self.options[0]})",
|
|
482
|
+
)
|
|
382
483
|
)
|
|
383
484
|
self.value = self.options[0]
|
|
384
485
|
|
|
@@ -389,8 +490,8 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
389
490
|
Parameter for multiple selections from a list of options.
|
|
390
491
|
|
|
391
492
|
Creates a set of checkboxes or multi-select dropdown in the GUI.
|
|
392
|
-
See :meth:`~syd.
|
|
393
|
-
:meth:`~syd.
|
|
493
|
+
See :meth:`~syd.viewer.Viewer.add_multiple_selection` and
|
|
494
|
+
:meth:`~syd.viewer.Viewer.update_multiple_selection` for usage.
|
|
394
495
|
|
|
395
496
|
Parameters
|
|
396
497
|
----------
|
|
@@ -398,8 +499,8 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
398
499
|
The name of the parameter
|
|
399
500
|
value : list
|
|
400
501
|
List of initially selected values (must all be from options)
|
|
401
|
-
options :
|
|
402
|
-
List of valid choices that can be selected
|
|
502
|
+
options : sequence
|
|
503
|
+
List, tuple, or 1D numpy array of valid choices that can be selected
|
|
403
504
|
|
|
404
505
|
Examples
|
|
405
506
|
--------
|
|
@@ -408,18 +509,61 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
408
509
|
... options=["cheese", "mushrooms", "pepperoni", "olives"])
|
|
409
510
|
>>> toppings.value
|
|
410
511
|
['cheese', 'mushrooms']
|
|
411
|
-
>>>
|
|
412
|
-
>>>
|
|
413
|
-
|
|
512
|
+
>>> # With numpy array
|
|
513
|
+
>>> import numpy as np
|
|
514
|
+
>>> numbers = MultipleSelectionParameter("numbers",
|
|
515
|
+
... value=[1, 3],
|
|
516
|
+
... options=np.array([1, 2, 3, 4]))
|
|
517
|
+
>>> numbers.value
|
|
518
|
+
[1, 3]
|
|
414
519
|
"""
|
|
415
520
|
|
|
416
521
|
options: List[Any]
|
|
417
522
|
|
|
418
|
-
def __init__(self, name: str, value: List[Any], options: List
|
|
523
|
+
def __init__(self, name: str, value: List[Any], options: Union[List, Tuple]):
|
|
419
524
|
self.name = name
|
|
420
|
-
self.options = options
|
|
525
|
+
self.options = self._validate_options(options)
|
|
421
526
|
self._value = self._validate(value)
|
|
422
527
|
|
|
528
|
+
def _validate_options(self, options: Any) -> List[Any]:
|
|
529
|
+
"""
|
|
530
|
+
Validate options and convert to list if necessary.
|
|
531
|
+
|
|
532
|
+
Parameters
|
|
533
|
+
----------
|
|
534
|
+
options : list or tuple
|
|
535
|
+
The options to validate
|
|
536
|
+
|
|
537
|
+
Returns
|
|
538
|
+
-------
|
|
539
|
+
list
|
|
540
|
+
Validated list of options
|
|
541
|
+
|
|
542
|
+
Raises
|
|
543
|
+
------
|
|
544
|
+
TypeError
|
|
545
|
+
If options is not a list or tuple
|
|
546
|
+
ValueError
|
|
547
|
+
If any option is not hashable
|
|
548
|
+
"""
|
|
549
|
+
if not isinstance(options, (list, tuple)):
|
|
550
|
+
raise TypeError(
|
|
551
|
+
f"Options for parameter {self.name} must be a list or tuple, received {type(options)}"
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
if not options:
|
|
555
|
+
raise ValueError(f"Options for parameter {self.name} must not be empty")
|
|
556
|
+
|
|
557
|
+
# Verify all options are hashable (needed for comparison)
|
|
558
|
+
try:
|
|
559
|
+
for opt in options:
|
|
560
|
+
hash(opt)
|
|
561
|
+
except TypeError as e:
|
|
562
|
+
raise ValueError(
|
|
563
|
+
f"All options for parameter {self.name} must be hashable: {str(e)}"
|
|
564
|
+
)
|
|
565
|
+
return list(options)
|
|
566
|
+
|
|
423
567
|
def _validate(self, new_value: Any) -> List[Any]:
|
|
424
568
|
"""
|
|
425
569
|
Validate list of selected values against options.
|
|
@@ -446,19 +590,24 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
446
590
|
return [x for x in self.options if x in new_value]
|
|
447
591
|
|
|
448
592
|
def _validate_update(self) -> None:
|
|
449
|
-
|
|
450
|
-
raise TypeError(
|
|
451
|
-
f"Options for parameter {self.name} are not a list or tuple: {self.options}"
|
|
452
|
-
)
|
|
593
|
+
self.options = self._validate_options(self.options)
|
|
453
594
|
if not isinstance(self.value, (list, tuple)):
|
|
454
595
|
warn(
|
|
455
|
-
|
|
596
|
+
ParameterUpdateWarning(
|
|
597
|
+
self.name,
|
|
598
|
+
type(self).__name__,
|
|
599
|
+
f"For parameter {self.name}, value {self.value} is not a list or tuple. Setting to empty list.",
|
|
600
|
+
)
|
|
456
601
|
)
|
|
457
602
|
self.value = []
|
|
458
603
|
if not all(val in self.options for val in self.value):
|
|
459
604
|
invalid = [val for val in self.value if val not in self.options]
|
|
460
605
|
warn(
|
|
461
|
-
|
|
606
|
+
ParameterUpdateWarning(
|
|
607
|
+
self.name,
|
|
608
|
+
type(self).__name__,
|
|
609
|
+
f"For parameter {self.name}, value {self.value} contains invalid selections: {invalid}. Setting to empty list.",
|
|
610
|
+
)
|
|
462
611
|
)
|
|
463
612
|
self.value = []
|
|
464
613
|
# Keep only unique values while preserving order based on self.options
|
|
@@ -472,8 +621,8 @@ class IntegerParameter(Parameter[int]):
|
|
|
472
621
|
Parameter for bounded integer values.
|
|
473
622
|
|
|
474
623
|
Creates a slider in the GUI for selecting whole numbers between bounds.
|
|
475
|
-
See :meth:`~syd.
|
|
476
|
-
:meth:`~syd.
|
|
624
|
+
See :meth:`~syd.viewer.Viewer.add_integer` and
|
|
625
|
+
:meth:`~syd.viewer.Viewer.update_integer` for usage.
|
|
477
626
|
|
|
478
627
|
Parameters
|
|
479
628
|
----------
|
|
@@ -535,10 +684,22 @@ class IntegerParameter(Parameter[int]):
|
|
|
535
684
|
|
|
536
685
|
if compare_to_range:
|
|
537
686
|
if new_value < self.min_value:
|
|
538
|
-
warn(
|
|
687
|
+
warn(
|
|
688
|
+
ParameterUpdateWarning(
|
|
689
|
+
self.name,
|
|
690
|
+
type(self).__name__,
|
|
691
|
+
f"Value {new_value} below minimum {self.min_value}, clamping",
|
|
692
|
+
)
|
|
693
|
+
)
|
|
539
694
|
new_value = self.min_value
|
|
540
695
|
if new_value > self.max_value:
|
|
541
|
-
warn(
|
|
696
|
+
warn(
|
|
697
|
+
ParameterUpdateWarning(
|
|
698
|
+
self.name,
|
|
699
|
+
type(self).__name__,
|
|
700
|
+
f"Value {new_value} above maximum {self.max_value}, clamping",
|
|
701
|
+
)
|
|
702
|
+
)
|
|
542
703
|
new_value = self.max_value
|
|
543
704
|
return int(new_value)
|
|
544
705
|
|
|
@@ -559,7 +720,13 @@ class IntegerParameter(Parameter[int]):
|
|
|
559
720
|
"IntegerParameter must have both min_value and max_value bounds",
|
|
560
721
|
)
|
|
561
722
|
if self.min_value > self.max_value:
|
|
562
|
-
warn(
|
|
723
|
+
warn(
|
|
724
|
+
ParameterUpdateWarning(
|
|
725
|
+
self.name,
|
|
726
|
+
type(self).__name__,
|
|
727
|
+
f"Min value greater than max value, swapping",
|
|
728
|
+
)
|
|
729
|
+
)
|
|
563
730
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
564
731
|
self.value = self._validate(self.value)
|
|
565
732
|
|
|
@@ -570,8 +737,8 @@ class FloatParameter(Parameter[float]):
|
|
|
570
737
|
Parameter for bounded decimal numbers.
|
|
571
738
|
|
|
572
739
|
Creates a slider in the GUI for selecting numbers between bounds.
|
|
573
|
-
See :meth:`~syd.
|
|
574
|
-
:meth:`~syd.
|
|
740
|
+
See :meth:`~syd.viewer.Viewer.add_float` and
|
|
741
|
+
:meth:`~syd.viewer.Viewer.update_float` for usage.
|
|
575
742
|
|
|
576
743
|
Parameters
|
|
577
744
|
----------
|
|
@@ -584,7 +751,7 @@ class FloatParameter(Parameter[float]):
|
|
|
584
751
|
max_value : float
|
|
585
752
|
Maximum allowed value
|
|
586
753
|
step : float, optional
|
|
587
|
-
Size of each increment (default is 0.
|
|
754
|
+
Size of each increment (default is 0.001)
|
|
588
755
|
|
|
589
756
|
Examples
|
|
590
757
|
--------
|
|
@@ -617,7 +784,7 @@ class FloatParameter(Parameter[float]):
|
|
|
617
784
|
value: float,
|
|
618
785
|
min_value: float,
|
|
619
786
|
max_value: float,
|
|
620
|
-
step: float = 0.
|
|
787
|
+
step: float = 0.001,
|
|
621
788
|
):
|
|
622
789
|
self.name = name
|
|
623
790
|
self.step = step
|
|
@@ -651,10 +818,22 @@ class FloatParameter(Parameter[float]):
|
|
|
651
818
|
|
|
652
819
|
if compare_to_range:
|
|
653
820
|
if new_value < self.min_value:
|
|
654
|
-
warn(
|
|
821
|
+
warn(
|
|
822
|
+
ParameterUpdateWarning(
|
|
823
|
+
self.name,
|
|
824
|
+
type(self).__name__,
|
|
825
|
+
f"Value {new_value} below minimum {self.min_value}, clamping",
|
|
826
|
+
)
|
|
827
|
+
)
|
|
655
828
|
new_value = self.min_value
|
|
656
829
|
if new_value > self.max_value:
|
|
657
|
-
warn(
|
|
830
|
+
warn(
|
|
831
|
+
ParameterUpdateWarning(
|
|
832
|
+
self.name,
|
|
833
|
+
type(self).__name__,
|
|
834
|
+
f"Value {new_value} above maximum {self.max_value}, clamping",
|
|
835
|
+
)
|
|
836
|
+
)
|
|
658
837
|
new_value = self.max_value
|
|
659
838
|
|
|
660
839
|
return float(new_value)
|
|
@@ -676,7 +855,13 @@ class FloatParameter(Parameter[float]):
|
|
|
676
855
|
"FloatParameter must have both min_value and max_value bounds",
|
|
677
856
|
)
|
|
678
857
|
if self.min_value > self.max_value:
|
|
679
|
-
warn(
|
|
858
|
+
warn(
|
|
859
|
+
ParameterUpdateWarning(
|
|
860
|
+
self.name,
|
|
861
|
+
type(self).__name__,
|
|
862
|
+
f"Min value greater than max value, swapping",
|
|
863
|
+
)
|
|
864
|
+
)
|
|
680
865
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
681
866
|
self.value = self._validate(self.value)
|
|
682
867
|
|
|
@@ -687,8 +872,8 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
687
872
|
Parameter for a range of bounded integer values.
|
|
688
873
|
|
|
689
874
|
Creates a range slider in the GUI for selecting a range of whole numbers.
|
|
690
|
-
See :meth:`~syd.
|
|
691
|
-
:meth:`~syd.
|
|
875
|
+
See :meth:`~syd.viewer.Viewer.add_integer_range` and
|
|
876
|
+
:meth:`~syd.viewer.Viewer.update_integer_range` for usage.
|
|
692
877
|
|
|
693
878
|
Parameters
|
|
694
879
|
----------
|
|
@@ -726,11 +911,11 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
726
911
|
max_value: int,
|
|
727
912
|
):
|
|
728
913
|
self.name = name
|
|
729
|
-
self.min_value = self._validate_single(min_value)
|
|
730
|
-
self.max_value = self._validate_single(max_value)
|
|
914
|
+
self.min_value = self._validate_single(min_value, context="min_value")
|
|
915
|
+
self.max_value = self._validate_single(max_value, context="max_value")
|
|
731
916
|
self._value = self._validate(value)
|
|
732
917
|
|
|
733
|
-
def _validate_single(self, new_value: Any) -> int:
|
|
918
|
+
def _validate_single(self, new_value: Any, context: Optional[str] = None) -> int:
|
|
734
919
|
"""
|
|
735
920
|
Validate and convert a single numeric value.
|
|
736
921
|
|
|
@@ -748,8 +933,11 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
748
933
|
"""
|
|
749
934
|
try:
|
|
750
935
|
return int(new_value)
|
|
751
|
-
except
|
|
752
|
-
|
|
936
|
+
except Exception:
|
|
937
|
+
msg = f"Value {new_value} cannot be converted to int"
|
|
938
|
+
if context:
|
|
939
|
+
msg += f" for {context}"
|
|
940
|
+
raise ValueError(msg)
|
|
753
941
|
|
|
754
942
|
def _validate(self, new_value: Any) -> Tuple[int, int]:
|
|
755
943
|
"""
|
|
@@ -772,14 +960,32 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
772
960
|
high = self._validate_single(new_value[1])
|
|
773
961
|
|
|
774
962
|
if low > high:
|
|
775
|
-
warn(
|
|
963
|
+
warn(
|
|
964
|
+
ParameterUpdateWarning(
|
|
965
|
+
self.name,
|
|
966
|
+
type(self).__name__,
|
|
967
|
+
f"Low value {low} greater than high value {high}, swapping",
|
|
968
|
+
)
|
|
969
|
+
)
|
|
776
970
|
low, high = high, low
|
|
777
971
|
|
|
778
972
|
if low < self.min_value:
|
|
779
|
-
warn(
|
|
973
|
+
warn(
|
|
974
|
+
ParameterUpdateWarning(
|
|
975
|
+
self.name,
|
|
976
|
+
type(self).__name__,
|
|
977
|
+
f"Low value {low} below minimum {self.min_value}, clamping",
|
|
978
|
+
)
|
|
979
|
+
)
|
|
780
980
|
low = self.min_value
|
|
781
981
|
if high > self.max_value:
|
|
782
|
-
warn(
|
|
982
|
+
warn(
|
|
983
|
+
ParameterUpdateWarning(
|
|
984
|
+
self.name,
|
|
985
|
+
type(self).__name__,
|
|
986
|
+
f"High value {high} above maximum {self.max_value}, clamping",
|
|
987
|
+
)
|
|
988
|
+
)
|
|
783
989
|
high = self.max_value
|
|
784
990
|
|
|
785
991
|
return (low, high)
|
|
@@ -801,7 +1007,13 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
801
1007
|
"IntegerRangeParameter must have both min_value and max_value bounds",
|
|
802
1008
|
)
|
|
803
1009
|
if self.min_value > self.max_value:
|
|
804
|
-
warn(
|
|
1010
|
+
warn(
|
|
1011
|
+
ParameterUpdateWarning(
|
|
1012
|
+
self.name,
|
|
1013
|
+
type(self).__name__,
|
|
1014
|
+
f"Min value greater than max value, swapping",
|
|
1015
|
+
)
|
|
1016
|
+
)
|
|
805
1017
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
806
1018
|
self.value = self._validate(self.value)
|
|
807
1019
|
|
|
@@ -812,8 +1024,8 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
812
1024
|
Parameter for a range of bounded decimal numbers.
|
|
813
1025
|
|
|
814
1026
|
Creates a range slider in the GUI for selecting a range of numbers.
|
|
815
|
-
See :meth:`~syd.
|
|
816
|
-
:meth:`~syd.
|
|
1027
|
+
See :meth:`~syd.viewer.Viewer.add_float_range` and
|
|
1028
|
+
:meth:`~syd.viewer.Viewer.update_float_range` for usage.
|
|
817
1029
|
|
|
818
1030
|
Parameters
|
|
819
1031
|
----------
|
|
@@ -826,7 +1038,7 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
826
1038
|
max_value : float
|
|
827
1039
|
Maximum allowed value for both low and high
|
|
828
1040
|
step : float, optional
|
|
829
|
-
Size of each increment (default is 0.
|
|
1041
|
+
Size of each increment (default is 0.001)
|
|
830
1042
|
|
|
831
1043
|
Examples
|
|
832
1044
|
--------
|
|
@@ -859,15 +1071,15 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
859
1071
|
value: Tuple[float, float],
|
|
860
1072
|
min_value: float,
|
|
861
1073
|
max_value: float,
|
|
862
|
-
step: float = 0.
|
|
1074
|
+
step: float = 0.001,
|
|
863
1075
|
):
|
|
864
1076
|
self.name = name
|
|
865
1077
|
self.step = step
|
|
866
|
-
self.min_value = self._validate_single(min_value)
|
|
867
|
-
self.max_value = self._validate_single(max_value)
|
|
1078
|
+
self.min_value = self._validate_single(min_value, context="min_value")
|
|
1079
|
+
self.max_value = self._validate_single(max_value, context="max_value")
|
|
868
1080
|
self._value = self._validate(value)
|
|
869
1081
|
|
|
870
|
-
def _validate_single(self, new_value: Any) -> float:
|
|
1082
|
+
def _validate_single(self, new_value: Any, context: Optional[str] = None) -> float:
|
|
871
1083
|
"""
|
|
872
1084
|
Validate and convert a single numeric value.
|
|
873
1085
|
|
|
@@ -885,8 +1097,11 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
885
1097
|
"""
|
|
886
1098
|
try:
|
|
887
1099
|
new_value = float(new_value)
|
|
888
|
-
except
|
|
889
|
-
|
|
1100
|
+
except Exception:
|
|
1101
|
+
msg = f"Value {new_value} cannot be converted to float"
|
|
1102
|
+
if context:
|
|
1103
|
+
msg += f" for {context}"
|
|
1104
|
+
raise ValueError(msg)
|
|
890
1105
|
|
|
891
1106
|
# Round to the nearest step
|
|
892
1107
|
new_value = round(new_value / self.step) * self.step
|
|
@@ -913,14 +1128,32 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
913
1128
|
high = self._validate_single(new_value[1])
|
|
914
1129
|
|
|
915
1130
|
if low > high:
|
|
916
|
-
warn(
|
|
1131
|
+
warn(
|
|
1132
|
+
ParameterUpdateWarning(
|
|
1133
|
+
self.name,
|
|
1134
|
+
type(self).__name__,
|
|
1135
|
+
f"Low value {low} greater than high value {high}, swapping",
|
|
1136
|
+
)
|
|
1137
|
+
)
|
|
917
1138
|
low, high = high, low
|
|
918
1139
|
|
|
919
1140
|
if low < self.min_value:
|
|
920
|
-
warn(
|
|
1141
|
+
warn(
|
|
1142
|
+
ParameterUpdateWarning(
|
|
1143
|
+
self.name,
|
|
1144
|
+
type(self).__name__,
|
|
1145
|
+
f"Low value {low} below minimum {self.min_value}, clamping",
|
|
1146
|
+
)
|
|
1147
|
+
)
|
|
921
1148
|
low = self.min_value
|
|
922
1149
|
if high > self.max_value:
|
|
923
|
-
warn(
|
|
1150
|
+
warn(
|
|
1151
|
+
ParameterUpdateWarning(
|
|
1152
|
+
self.name,
|
|
1153
|
+
type(self).__name__,
|
|
1154
|
+
f"High value {high} above maximum {self.max_value}, clamping",
|
|
1155
|
+
)
|
|
1156
|
+
)
|
|
924
1157
|
high = self.max_value
|
|
925
1158
|
|
|
926
1159
|
return (low, high)
|
|
@@ -942,7 +1175,13 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
942
1175
|
"FloatRangeParameter must have both min_value and max_value bounds",
|
|
943
1176
|
)
|
|
944
1177
|
if self.min_value > self.max_value:
|
|
945
|
-
warn(
|
|
1178
|
+
warn(
|
|
1179
|
+
ParameterUpdateWarning(
|
|
1180
|
+
self.name,
|
|
1181
|
+
type(self).__name__,
|
|
1182
|
+
f"Min value greater than max value, swapping",
|
|
1183
|
+
)
|
|
1184
|
+
)
|
|
946
1185
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
947
1186
|
self.value = self._validate(self.value)
|
|
948
1187
|
|
|
@@ -953,8 +1192,8 @@ class UnboundedIntegerParameter(Parameter[int]):
|
|
|
953
1192
|
Parameter for optionally bounded integer values.
|
|
954
1193
|
|
|
955
1194
|
Creates a text input box in the GUI for entering whole numbers.
|
|
956
|
-
See :meth:`~syd.
|
|
957
|
-
:meth:`~syd.
|
|
1195
|
+
See :meth:`~syd.viewer.Viewer.add_unbounded_integer` and
|
|
1196
|
+
:meth:`~syd.viewer.Viewer.update_unbounded_integer` for usage.
|
|
958
1197
|
|
|
959
1198
|
Parameters
|
|
960
1199
|
----------
|
|
@@ -962,19 +1201,12 @@ class UnboundedIntegerParameter(Parameter[int]):
|
|
|
962
1201
|
The name of the parameter
|
|
963
1202
|
value : int
|
|
964
1203
|
Initial value
|
|
965
|
-
min_value : int, optional
|
|
966
|
-
Minimum allowed value (or None for no minimum)
|
|
967
|
-
max_value : int, optional
|
|
968
|
-
Maximum allowed value (or None for no maximum)
|
|
969
1204
|
|
|
970
1205
|
Examples
|
|
971
1206
|
--------
|
|
972
|
-
>>> count = UnboundedIntegerParameter("count", value=10
|
|
1207
|
+
>>> count = UnboundedIntegerParameter("count", value=10)
|
|
973
1208
|
>>> count.value
|
|
974
1209
|
10
|
|
975
|
-
>>> count.update({"value": -5}) # Will be clamped to min_value
|
|
976
|
-
>>> count.value
|
|
977
|
-
0
|
|
978
1210
|
>>> count.update({"value": 1000000}) # No maximum, so this is allowed
|
|
979
1211
|
>>> count.value
|
|
980
1212
|
1000000
|
|
@@ -982,43 +1214,24 @@ class UnboundedIntegerParameter(Parameter[int]):
|
|
|
982
1214
|
Notes
|
|
983
1215
|
-----
|
|
984
1216
|
Use this instead of IntegerParameter when you:
|
|
985
|
-
- Don't
|
|
986
|
-
- Only want to enforce a minimum or maximum, but not both
|
|
1217
|
+
- Don't have any reason to bound the value
|
|
987
1218
|
- Need to allow very large numbers that would be impractical with a slider
|
|
988
1219
|
"""
|
|
989
1220
|
|
|
990
|
-
min_value: Optional[int]
|
|
991
|
-
max_value: Optional[int]
|
|
992
|
-
|
|
993
1221
|
def __init__(
|
|
994
1222
|
self,
|
|
995
1223
|
name: str,
|
|
996
1224
|
value: int,
|
|
997
|
-
min_value: Optional[int] = None,
|
|
998
|
-
max_value: Optional[int] = None,
|
|
999
1225
|
):
|
|
1000
1226
|
self.name = name
|
|
1001
|
-
self.min_value = (
|
|
1002
|
-
self._validate(min_value, compare_to_range=False)
|
|
1003
|
-
if min_value is not None
|
|
1004
|
-
else None
|
|
1005
|
-
)
|
|
1006
|
-
self.max_value = (
|
|
1007
|
-
self._validate(max_value, compare_to_range=False)
|
|
1008
|
-
if max_value is not None
|
|
1009
|
-
else None
|
|
1010
|
-
)
|
|
1011
1227
|
self._value = self._validate(value)
|
|
1012
1228
|
|
|
1013
|
-
def _validate(self, new_value: Any
|
|
1229
|
+
def _validate(self, new_value: Any) -> int:
|
|
1014
1230
|
"""
|
|
1015
|
-
Validate and convert value to integer
|
|
1016
|
-
|
|
1017
|
-
Handles None min/max values by skipping those bound checks.
|
|
1231
|
+
Validate and convert value to integer.
|
|
1018
1232
|
|
|
1019
1233
|
Args:
|
|
1020
1234
|
new_value: Value to validate
|
|
1021
|
-
compare_to_range: If True, clamps value to any defined min/max bounds
|
|
1022
1235
|
|
|
1023
1236
|
Returns:
|
|
1024
1237
|
Validated integer value
|
|
@@ -1031,32 +1244,15 @@ class UnboundedIntegerParameter(Parameter[int]):
|
|
|
1031
1244
|
except ValueError:
|
|
1032
1245
|
raise ValueError(f"Value {new_value} cannot be converted to int")
|
|
1033
1246
|
|
|
1034
|
-
if compare_to_range:
|
|
1035
|
-
if self.min_value is not None and new_value < self.min_value:
|
|
1036
|
-
warn(f"Value {new_value} below minimum {self.min_value}, clamping")
|
|
1037
|
-
new_value = self.min_value
|
|
1038
|
-
if self.max_value is not None and new_value > self.max_value:
|
|
1039
|
-
warn(f"Value {new_value} above maximum {self.max_value}, clamping")
|
|
1040
|
-
new_value = self.max_value
|
|
1041
1247
|
return int(new_value)
|
|
1042
1248
|
|
|
1043
1249
|
def _validate_update(self) -> None:
|
|
1044
1250
|
"""
|
|
1045
1251
|
Validate complete parameter state after updates.
|
|
1046
1252
|
|
|
1047
|
-
Ensures min_value <= max_value, swapping if needed.
|
|
1048
|
-
Re-validates current value against potentially updated bounds.
|
|
1049
|
-
|
|
1050
1253
|
Raises:
|
|
1051
1254
|
ParameterUpdateError: If bounds are invalid (e.g. None when required)
|
|
1052
1255
|
"""
|
|
1053
|
-
if (
|
|
1054
|
-
self.min_value is not None
|
|
1055
|
-
and self.max_value is not None
|
|
1056
|
-
and self.min_value > self.max_value
|
|
1057
|
-
):
|
|
1058
|
-
warn(f"Min value greater than max value, swapping")
|
|
1059
|
-
self.min_value, self.max_value = self.max_value, self.min_value
|
|
1060
1256
|
self.value = self._validate(self.value)
|
|
1061
1257
|
|
|
1062
1258
|
|
|
@@ -1066,8 +1262,8 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1066
1262
|
Parameter for optionally bounded decimal numbers.
|
|
1067
1263
|
|
|
1068
1264
|
Creates a text input box in the GUI for entering numbers.
|
|
1069
|
-
See :meth:`~syd.
|
|
1070
|
-
:meth:`~syd.
|
|
1265
|
+
See :meth:`~syd.viewer.Viewer.add_unbounded_float` and
|
|
1266
|
+
:meth:`~syd.viewer.Viewer.update_unbounded_float` for usage.
|
|
1071
1267
|
|
|
1072
1268
|
Parameters
|
|
1073
1269
|
----------
|
|
@@ -1075,21 +1271,14 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1075
1271
|
The name of the parameter
|
|
1076
1272
|
value : float
|
|
1077
1273
|
Initial value
|
|
1078
|
-
min_value : float, optional
|
|
1079
|
-
Minimum allowed value (or None for no minimum)
|
|
1080
|
-
max_value : float, optional
|
|
1081
|
-
Maximum allowed value (or None for no maximum)
|
|
1082
1274
|
step : float, optional
|
|
1083
1275
|
Size of each increment (default is None, meaning no rounding)
|
|
1084
1276
|
|
|
1085
1277
|
Examples
|
|
1086
1278
|
--------
|
|
1087
|
-
>>> price = UnboundedFloatParameter("price", value=19.99
|
|
1279
|
+
>>> price = UnboundedFloatParameter("price", value=19.99)
|
|
1088
1280
|
>>> price.value
|
|
1089
1281
|
19.99
|
|
1090
|
-
>>> price.update({"value": -5.0}) # Will be clamped to min_value
|
|
1091
|
-
>>> price.value
|
|
1092
|
-
0.0
|
|
1093
1282
|
>>> price.update({"value": 19.987}) # Will be rounded to step
|
|
1094
1283
|
>>> price.value
|
|
1095
1284
|
19.99
|
|
@@ -1098,7 +1287,6 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1098
1287
|
-----
|
|
1099
1288
|
Use this instead of FloatParameter when you:
|
|
1100
1289
|
- Don't know a reasonable maximum value
|
|
1101
|
-
- Only want to enforce a minimum or maximum, but not both
|
|
1102
1290
|
- Need to allow very large or precise numbers that would be impractical with a slider
|
|
1103
1291
|
|
|
1104
1292
|
If step is provided, values will be rounded:
|
|
@@ -1107,42 +1295,25 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1107
1295
|
- step=5.0 rounds to 0.0, 5.0, 10.0, etc.
|
|
1108
1296
|
"""
|
|
1109
1297
|
|
|
1110
|
-
|
|
1111
|
-
max_value: Optional[float]
|
|
1112
|
-
step: float
|
|
1298
|
+
step: Optional[float]
|
|
1113
1299
|
|
|
1114
1300
|
def __init__(
|
|
1115
1301
|
self,
|
|
1116
1302
|
name: str,
|
|
1117
1303
|
value: float,
|
|
1118
|
-
min_value: Optional[float] = None,
|
|
1119
|
-
max_value: Optional[float] = None,
|
|
1120
1304
|
step: Optional[float] = None,
|
|
1121
1305
|
):
|
|
1122
1306
|
self.name = name
|
|
1123
1307
|
self.step = step
|
|
1124
|
-
self.min_value = (
|
|
1125
|
-
self._validate(min_value, compare_to_range=False)
|
|
1126
|
-
if min_value is not None
|
|
1127
|
-
else None
|
|
1128
|
-
)
|
|
1129
|
-
self.max_value = (
|
|
1130
|
-
self._validate(max_value, compare_to_range=False)
|
|
1131
|
-
if max_value is not None
|
|
1132
|
-
else None
|
|
1133
|
-
)
|
|
1134
1308
|
self._value = self._validate(value)
|
|
1135
1309
|
|
|
1136
|
-
def _validate(self, new_value: Any
|
|
1310
|
+
def _validate(self, new_value: Any) -> float:
|
|
1137
1311
|
"""
|
|
1138
|
-
Validate and convert value to float
|
|
1139
|
-
|
|
1140
|
-
Handles None min/max values by skipping those bound checks.
|
|
1312
|
+
Validate and convert value to float.
|
|
1141
1313
|
Only rounds to step if step is not None.
|
|
1142
1314
|
|
|
1143
1315
|
Args:
|
|
1144
1316
|
new_value: Value to validate
|
|
1145
|
-
compare_to_range: If True, clamps value to any defined min/max bounds
|
|
1146
1317
|
|
|
1147
1318
|
Returns:
|
|
1148
1319
|
Validated and potentially rounded float value
|
|
@@ -1159,14 +1330,6 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1159
1330
|
if self.step is not None:
|
|
1160
1331
|
new_value = round(new_value / self.step) * self.step
|
|
1161
1332
|
|
|
1162
|
-
if compare_to_range:
|
|
1163
|
-
if self.min_value is not None and new_value < self.min_value:
|
|
1164
|
-
warn(f"Value {new_value} below minimum {self.min_value}, clamping")
|
|
1165
|
-
new_value = self.min_value
|
|
1166
|
-
if self.max_value is not None and new_value > self.max_value:
|
|
1167
|
-
warn(f"Value {new_value} above maximum {self.max_value}, clamping")
|
|
1168
|
-
new_value = self.max_value
|
|
1169
|
-
|
|
1170
1333
|
return float(new_value)
|
|
1171
1334
|
|
|
1172
1335
|
def _validate_update(self) -> None:
|
|
@@ -1179,24 +1342,58 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1179
1342
|
Raises:
|
|
1180
1343
|
ParameterUpdateError: If bounds are invalid (e.g. None when required)
|
|
1181
1344
|
"""
|
|
1182
|
-
if (
|
|
1183
|
-
self.min_value is not None
|
|
1184
|
-
and self.max_value is not None
|
|
1185
|
-
and self.min_value > self.max_value
|
|
1186
|
-
):
|
|
1187
|
-
warn(f"Min value greater than max value, swapping")
|
|
1188
|
-
self.min_value, self.max_value = self.max_value, self.min_value
|
|
1189
1345
|
self.value = self._validate(self.value)
|
|
1190
1346
|
|
|
1191
1347
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1348
|
+
@dataclass(init=False)
|
|
1349
|
+
class ButtonAction(Parameter[None]):
|
|
1350
|
+
"""
|
|
1351
|
+
Parameter for creating clickable buttons with callbacks.
|
|
1352
|
+
|
|
1353
|
+
Creates a button in the GUI that executes a callback function when clicked.
|
|
1354
|
+
See :meth:`~syd.viewer.Viewer.add_button` and
|
|
1355
|
+
:meth:`~syd.viewer.Viewer.update_button` for usage.
|
|
1356
|
+
|
|
1357
|
+
Parameters
|
|
1358
|
+
----------
|
|
1359
|
+
name : str
|
|
1360
|
+
The name of the parameter
|
|
1361
|
+
label : str
|
|
1362
|
+
Text to display on the button
|
|
1363
|
+
callback : callable
|
|
1364
|
+
Function to execute when the button is clicked
|
|
1365
|
+
|
|
1366
|
+
Examples
|
|
1367
|
+
--------
|
|
1368
|
+
>>> def print_hello():
|
|
1369
|
+
... print("Hello!")
|
|
1370
|
+
>>> button = ButtonAction("greeting", label="Say Hello", callback=print_hello)
|
|
1371
|
+
>>> button.callback() # Simulates clicking the button
|
|
1372
|
+
Hello!
|
|
1373
|
+
>>> # Update the button's label and callback
|
|
1374
|
+
>>> def print_goodbye():
|
|
1375
|
+
... print("Goodbye!")
|
|
1376
|
+
>>> button.update({"label": "Say Goodbye", "callback": print_goodbye})
|
|
1377
|
+
>>> button.callback()
|
|
1378
|
+
Goodbye!
|
|
1379
|
+
|
|
1380
|
+
Notes
|
|
1381
|
+
-----
|
|
1382
|
+
Unlike other Parameter types, ButtonAction:
|
|
1383
|
+
- Has no value (always None)
|
|
1384
|
+
- Is marked as an action (_is_action = True)
|
|
1385
|
+
- Executes code directly rather than storing state
|
|
1386
|
+
- Cannot be updated through the value property
|
|
1387
|
+
"""
|
|
1194
1388
|
|
|
1195
|
-
|
|
1389
|
+
label: str
|
|
1390
|
+
callback: Callable
|
|
1391
|
+
value: None = field(default=None, repr=False)
|
|
1392
|
+
_is_action: bool = field(default=True, repr=False)
|
|
1196
1393
|
|
|
1197
|
-
def __init__(self, name: str, label: str, callback: Callable
|
|
1394
|
+
def __init__(self, name: str, label: str, callback: Callable):
|
|
1198
1395
|
"""
|
|
1199
|
-
Initialize a button
|
|
1396
|
+
Initialize a button.
|
|
1200
1397
|
|
|
1201
1398
|
Args:
|
|
1202
1399
|
name: Internal name of the parameter
|
|
@@ -1206,32 +1403,28 @@ class ButtonParameter(Parameter):
|
|
|
1206
1403
|
self.name = name
|
|
1207
1404
|
self.label = label
|
|
1208
1405
|
self.callback = callback
|
|
1209
|
-
self._value = None
|
|
1210
|
-
|
|
1211
|
-
def update(self, updates: Dict[str, Any]) -> None:
|
|
1212
|
-
"""Update the button's label and/or callback."""
|
|
1213
|
-
if "label" in updates:
|
|
1214
|
-
self.label = updates["label"]
|
|
1215
|
-
if "callback" in updates:
|
|
1216
|
-
self.callback = updates["callback"]
|
|
1406
|
+
self._value = None
|
|
1217
1407
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
return self._value
|
|
1222
|
-
|
|
1223
|
-
@value.setter
|
|
1224
|
-
def value(self, _: Any) -> None:
|
|
1225
|
-
"""Buttons don't store values."""
|
|
1226
|
-
pass
|
|
1408
|
+
def _validate(self, new_value: Any) -> None:
|
|
1409
|
+
"""Validate the button's value."""
|
|
1410
|
+
return None
|
|
1227
1411
|
|
|
1228
1412
|
def _validate_update(self) -> None:
|
|
1229
|
-
"""
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1413
|
+
"""Validate the button's value after updates."""
|
|
1414
|
+
if not callable(self.callback):
|
|
1415
|
+
raise ParameterUpdateError(
|
|
1416
|
+
self.name,
|
|
1417
|
+
type(self).__name__,
|
|
1418
|
+
f"Callback {self.callback} is not callable",
|
|
1419
|
+
)
|
|
1420
|
+
try:
|
|
1421
|
+
str(self.label)
|
|
1422
|
+
except Exception:
|
|
1423
|
+
raise ParameterUpdateError(
|
|
1424
|
+
self.name,
|
|
1425
|
+
type(self).__name__,
|
|
1426
|
+
f"Label {self.label} doesn't have a string representation",
|
|
1427
|
+
)
|
|
1235
1428
|
|
|
1236
1429
|
|
|
1237
1430
|
class ParameterType(Enum):
|
|
@@ -1247,4 +1440,7 @@ class ParameterType(Enum):
|
|
|
1247
1440
|
float_range = FloatRangeParameter
|
|
1248
1441
|
unbounded_integer = UnboundedIntegerParameter
|
|
1249
1442
|
unbounded_float = UnboundedFloatParameter
|
|
1250
|
-
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
class ActionType(Enum):
|
|
1446
|
+
button = ButtonAction
|