syd 0.1.5__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- syd/__init__.py +6 -10
- syd/flask_deployment/__init__.py +0 -0
- syd/flask_deployment/components.py +497 -0
- syd/flask_deployment/deployer.py +338 -0
- syd/flask_deployment/static/css/styles.css +39 -0
- syd/flask_deployment/static/js/components.js +51 -0
- syd/flask_deployment/static/js/viewer.js +0 -0
- syd/flask_deployment/templates/base.html +26 -0
- syd/flask_deployment/templates/viewer.html +97 -0
- syd/interactive_viewer.py +200 -31
- syd/{notebook_deploy → notebook_deployment}/deployer.py +39 -22
- syd/{notebook_deploy → notebook_deployment}/widgets.py +69 -62
- syd/parameters.py +356 -73
- {syd-0.1.5.dist-info → syd-0.1.6.dist-info}/METADATA +68 -3
- syd-0.1.6.dist-info/RECORD +18 -0
- syd-0.1.5.dist-info/RECORD +0 -10
- /syd/{notebook_deploy → notebook_deployment}/__init__.py +0 -0
- {syd-0.1.5.dist-info → syd-0.1.6.dist-info}/WHEEL +0 -0
- {syd-0.1.5.dist-info → syd-0.1.6.dist-info}/licenses/LICENSE +0 -0
syd/parameters.py
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
from typing import
|
|
2
|
-
|
|
1
|
+
from typing import (
|
|
2
|
+
List,
|
|
3
|
+
Any,
|
|
4
|
+
Tuple,
|
|
5
|
+
Generic,
|
|
6
|
+
TypeVar,
|
|
7
|
+
Optional,
|
|
8
|
+
Dict,
|
|
9
|
+
Callable,
|
|
10
|
+
Union,
|
|
11
|
+
Sequence,
|
|
12
|
+
)
|
|
13
|
+
from dataclasses import dataclass, field
|
|
3
14
|
from abc import ABC, abstractmethod
|
|
4
15
|
from enum import Enum
|
|
5
16
|
from copy import deepcopy
|
|
6
17
|
from warnings import warn
|
|
18
|
+
import numpy as np
|
|
7
19
|
|
|
8
20
|
T = TypeVar("T")
|
|
9
21
|
|
|
@@ -55,6 +67,29 @@ class ParameterUpdateError(Exception):
|
|
|
55
67
|
)
|
|
56
68
|
|
|
57
69
|
|
|
70
|
+
class ParameterUpdateWarning(Warning):
|
|
71
|
+
"""
|
|
72
|
+
Warning raised when there is a non-critical issue updating a parameter.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
parameter_name : str
|
|
77
|
+
Name of the parameter that had the warning
|
|
78
|
+
parameter_type : str
|
|
79
|
+
Type of the parameter
|
|
80
|
+
message : str, optional
|
|
81
|
+
Additional warning details
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, parameter_name: str, parameter_type: str, message: str = None):
|
|
85
|
+
self.parameter_name = parameter_name
|
|
86
|
+
self.parameter_type = parameter_type
|
|
87
|
+
super().__init__(
|
|
88
|
+
f"Warning updating {parameter_type} parameter '{parameter_name}'"
|
|
89
|
+
+ (f": {message}" if message else "")
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
58
93
|
def get_parameter_attributes(param_class) -> List[str]:
|
|
59
94
|
"""
|
|
60
95
|
Get all valid attributes for a parameter class.
|
|
@@ -106,6 +141,7 @@ class Parameter(Generic[T], ABC):
|
|
|
106
141
|
|
|
107
142
|
name: str
|
|
108
143
|
value: T
|
|
144
|
+
_is_action: bool = False
|
|
109
145
|
|
|
110
146
|
@abstractmethod
|
|
111
147
|
def __init__(self, name: str, value: T):
|
|
@@ -324,8 +360,8 @@ class SelectionParameter(Parameter[Any]):
|
|
|
324
360
|
The name of the parameter
|
|
325
361
|
value : Any
|
|
326
362
|
The initially selected value (must be one of the options)
|
|
327
|
-
options :
|
|
328
|
-
List of valid choices that can be selected
|
|
363
|
+
options : sequence
|
|
364
|
+
List, tuple, or 1D numpy array of valid choices that can be selected
|
|
329
365
|
|
|
330
366
|
Examples
|
|
331
367
|
--------
|
|
@@ -336,15 +372,56 @@ class SelectionParameter(Parameter[Any]):
|
|
|
336
372
|
>>> color.value
|
|
337
373
|
'blue'
|
|
338
374
|
>>> color.update({"value": "yellow"}) # This will raise an error
|
|
375
|
+
>>> # With numpy array
|
|
376
|
+
>>> import numpy as np
|
|
377
|
+
>>> numbers = SelectionParameter("number", 1, options=np.array([1, 2, 3]))
|
|
378
|
+
>>> numbers.value
|
|
379
|
+
1
|
|
339
380
|
"""
|
|
340
381
|
|
|
341
382
|
options: List[Any]
|
|
342
383
|
|
|
343
|
-
def __init__(self, name: str, value: Any, options: List
|
|
384
|
+
def __init__(self, name: str, value: Any, options: Union[List, Tuple]):
|
|
344
385
|
self.name = name
|
|
345
|
-
self.options = options
|
|
386
|
+
self.options = self._validate_options(options)
|
|
346
387
|
self._value = self._validate(value)
|
|
347
388
|
|
|
389
|
+
def _validate_options(self, options: Any) -> List[Any]:
|
|
390
|
+
"""
|
|
391
|
+
Validate options and convert to list if necessary.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
options : list or tuple
|
|
396
|
+
The options to validate
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
list
|
|
401
|
+
Validated list of options
|
|
402
|
+
|
|
403
|
+
Raises
|
|
404
|
+
------
|
|
405
|
+
TypeError
|
|
406
|
+
If options is not a list or tuple
|
|
407
|
+
ValueError
|
|
408
|
+
If any option is not hashable
|
|
409
|
+
"""
|
|
410
|
+
if not isinstance(options, (list, tuple)):
|
|
411
|
+
raise TypeError(
|
|
412
|
+
f"Options for parameter {self.name} must be a list or tuple"
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Verify all options are hashable (needed for comparison)
|
|
416
|
+
try:
|
|
417
|
+
for opt in options:
|
|
418
|
+
hash(opt)
|
|
419
|
+
except TypeError as e:
|
|
420
|
+
raise ValueError(
|
|
421
|
+
f"All options for parameter {self.name} must be hashable: {str(e)}"
|
|
422
|
+
)
|
|
423
|
+
return list(options)
|
|
424
|
+
|
|
348
425
|
def _validate(self, new_value: Any) -> Any:
|
|
349
426
|
"""
|
|
350
427
|
Validate that value is one of the allowed options.
|
|
@@ -372,13 +449,14 @@ class SelectionParameter(Parameter[Any]):
|
|
|
372
449
|
Raises:
|
|
373
450
|
TypeError: If options is not a list or tuple
|
|
374
451
|
"""
|
|
375
|
-
|
|
376
|
-
raise TypeError(
|
|
377
|
-
f"Options for parameter {self.name} are not a list or tuple: {self.options}"
|
|
378
|
-
)
|
|
452
|
+
self.options = self._validate_options(self.options)
|
|
379
453
|
if self.value not in self.options:
|
|
380
454
|
warn(
|
|
381
|
-
|
|
455
|
+
ParameterUpdateWarning(
|
|
456
|
+
self.name,
|
|
457
|
+
type(self).__name__,
|
|
458
|
+
f"Value {self.value} not in options: {self.options}, setting to first option",
|
|
459
|
+
)
|
|
382
460
|
)
|
|
383
461
|
self.value = self.options[0]
|
|
384
462
|
|
|
@@ -398,8 +476,8 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
398
476
|
The name of the parameter
|
|
399
477
|
value : list
|
|
400
478
|
List of initially selected values (must all be from options)
|
|
401
|
-
options :
|
|
402
|
-
List of valid choices that can be selected
|
|
479
|
+
options : sequence
|
|
480
|
+
List, tuple, or 1D numpy array of valid choices that can be selected
|
|
403
481
|
|
|
404
482
|
Examples
|
|
405
483
|
--------
|
|
@@ -408,18 +486,58 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
408
486
|
... options=["cheese", "mushrooms", "pepperoni", "olives"])
|
|
409
487
|
>>> toppings.value
|
|
410
488
|
['cheese', 'mushrooms']
|
|
411
|
-
>>>
|
|
412
|
-
>>>
|
|
413
|
-
|
|
489
|
+
>>> # With numpy array
|
|
490
|
+
>>> import numpy as np
|
|
491
|
+
>>> numbers = MultipleSelectionParameter("numbers",
|
|
492
|
+
... value=[1, 3],
|
|
493
|
+
... options=np.array([1, 2, 3, 4]))
|
|
494
|
+
>>> numbers.value
|
|
495
|
+
[1, 3]
|
|
414
496
|
"""
|
|
415
497
|
|
|
416
498
|
options: List[Any]
|
|
417
499
|
|
|
418
|
-
def __init__(self, name: str, value: List[Any], options: List
|
|
500
|
+
def __init__(self, name: str, value: List[Any], options: Union[List, Tuple]):
|
|
419
501
|
self.name = name
|
|
420
|
-
self.options = options
|
|
502
|
+
self.options = self._validate_options(options)
|
|
421
503
|
self._value = self._validate(value)
|
|
422
504
|
|
|
505
|
+
def _validate_options(self, options: Any) -> List[Any]:
|
|
506
|
+
"""
|
|
507
|
+
Validate options and convert to list if necessary.
|
|
508
|
+
|
|
509
|
+
Parameters
|
|
510
|
+
----------
|
|
511
|
+
options : list or tuple
|
|
512
|
+
The options to validate
|
|
513
|
+
|
|
514
|
+
Returns
|
|
515
|
+
-------
|
|
516
|
+
list
|
|
517
|
+
Validated list of options
|
|
518
|
+
|
|
519
|
+
Raises
|
|
520
|
+
------
|
|
521
|
+
TypeError
|
|
522
|
+
If options is not a list or tuple
|
|
523
|
+
ValueError
|
|
524
|
+
If any option is not hashable
|
|
525
|
+
"""
|
|
526
|
+
if not isinstance(options, (list, tuple)):
|
|
527
|
+
raise TypeError(
|
|
528
|
+
f"Options for parameter {self.name} must be a list or tuple"
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
# Verify all options are hashable (needed for comparison)
|
|
532
|
+
try:
|
|
533
|
+
for opt in options:
|
|
534
|
+
hash(opt)
|
|
535
|
+
except TypeError as e:
|
|
536
|
+
raise ValueError(
|
|
537
|
+
f"All options for parameter {self.name} must be hashable: {str(e)}"
|
|
538
|
+
)
|
|
539
|
+
return list(options)
|
|
540
|
+
|
|
423
541
|
def _validate(self, new_value: Any) -> List[Any]:
|
|
424
542
|
"""
|
|
425
543
|
Validate list of selected values against options.
|
|
@@ -446,19 +564,24 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
|
446
564
|
return [x for x in self.options if x in new_value]
|
|
447
565
|
|
|
448
566
|
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
|
-
)
|
|
567
|
+
self.options = self._validate_options(self.options)
|
|
453
568
|
if not isinstance(self.value, (list, tuple)):
|
|
454
569
|
warn(
|
|
455
|
-
|
|
570
|
+
ParameterUpdateWarning(
|
|
571
|
+
self.name,
|
|
572
|
+
type(self).__name__,
|
|
573
|
+
f"For parameter {self.name}, value {self.value} is not a list or tuple. Setting to empty list.",
|
|
574
|
+
)
|
|
456
575
|
)
|
|
457
576
|
self.value = []
|
|
458
577
|
if not all(val in self.options for val in self.value):
|
|
459
578
|
invalid = [val for val in self.value if val not in self.options]
|
|
460
579
|
warn(
|
|
461
|
-
|
|
580
|
+
ParameterUpdateWarning(
|
|
581
|
+
self.name,
|
|
582
|
+
type(self).__name__,
|
|
583
|
+
f"For parameter {self.name}, value {self.value} contains invalid options: {invalid}. Setting to empty list.",
|
|
584
|
+
)
|
|
462
585
|
)
|
|
463
586
|
self.value = []
|
|
464
587
|
# Keep only unique values while preserving order based on self.options
|
|
@@ -535,10 +658,22 @@ class IntegerParameter(Parameter[int]):
|
|
|
535
658
|
|
|
536
659
|
if compare_to_range:
|
|
537
660
|
if new_value < self.min_value:
|
|
538
|
-
warn(
|
|
661
|
+
warn(
|
|
662
|
+
ParameterUpdateWarning(
|
|
663
|
+
self.name,
|
|
664
|
+
type(self).__name__,
|
|
665
|
+
f"Value {new_value} below minimum {self.min_value}, clamping",
|
|
666
|
+
)
|
|
667
|
+
)
|
|
539
668
|
new_value = self.min_value
|
|
540
669
|
if new_value > self.max_value:
|
|
541
|
-
warn(
|
|
670
|
+
warn(
|
|
671
|
+
ParameterUpdateWarning(
|
|
672
|
+
self.name,
|
|
673
|
+
type(self).__name__,
|
|
674
|
+
f"Value {new_value} above maximum {self.max_value}, clamping",
|
|
675
|
+
)
|
|
676
|
+
)
|
|
542
677
|
new_value = self.max_value
|
|
543
678
|
return int(new_value)
|
|
544
679
|
|
|
@@ -559,7 +694,13 @@ class IntegerParameter(Parameter[int]):
|
|
|
559
694
|
"IntegerParameter must have both min_value and max_value bounds",
|
|
560
695
|
)
|
|
561
696
|
if self.min_value > self.max_value:
|
|
562
|
-
warn(
|
|
697
|
+
warn(
|
|
698
|
+
ParameterUpdateWarning(
|
|
699
|
+
self.name,
|
|
700
|
+
type(self).__name__,
|
|
701
|
+
f"Min value greater than max value, swapping",
|
|
702
|
+
)
|
|
703
|
+
)
|
|
563
704
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
564
705
|
self.value = self._validate(self.value)
|
|
565
706
|
|
|
@@ -651,10 +792,22 @@ class FloatParameter(Parameter[float]):
|
|
|
651
792
|
|
|
652
793
|
if compare_to_range:
|
|
653
794
|
if new_value < self.min_value:
|
|
654
|
-
warn(
|
|
795
|
+
warn(
|
|
796
|
+
ParameterUpdateWarning(
|
|
797
|
+
self.name,
|
|
798
|
+
type(self).__name__,
|
|
799
|
+
f"Value {new_value} below minimum {self.min_value}, clamping",
|
|
800
|
+
)
|
|
801
|
+
)
|
|
655
802
|
new_value = self.min_value
|
|
656
803
|
if new_value > self.max_value:
|
|
657
|
-
warn(
|
|
804
|
+
warn(
|
|
805
|
+
ParameterUpdateWarning(
|
|
806
|
+
self.name,
|
|
807
|
+
type(self).__name__,
|
|
808
|
+
f"Value {new_value} above maximum {self.max_value}, clamping",
|
|
809
|
+
)
|
|
810
|
+
)
|
|
658
811
|
new_value = self.max_value
|
|
659
812
|
|
|
660
813
|
return float(new_value)
|
|
@@ -676,7 +829,13 @@ class FloatParameter(Parameter[float]):
|
|
|
676
829
|
"FloatParameter must have both min_value and max_value bounds",
|
|
677
830
|
)
|
|
678
831
|
if self.min_value > self.max_value:
|
|
679
|
-
warn(
|
|
832
|
+
warn(
|
|
833
|
+
ParameterUpdateWarning(
|
|
834
|
+
self.name,
|
|
835
|
+
type(self).__name__,
|
|
836
|
+
f"Min value greater than max value, swapping",
|
|
837
|
+
)
|
|
838
|
+
)
|
|
680
839
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
681
840
|
self.value = self._validate(self.value)
|
|
682
841
|
|
|
@@ -772,14 +931,32 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
772
931
|
high = self._validate_single(new_value[1])
|
|
773
932
|
|
|
774
933
|
if low > high:
|
|
775
|
-
warn(
|
|
934
|
+
warn(
|
|
935
|
+
ParameterUpdateWarning(
|
|
936
|
+
self.name,
|
|
937
|
+
type(self).__name__,
|
|
938
|
+
f"Low value {low} greater than high value {high}, swapping",
|
|
939
|
+
)
|
|
940
|
+
)
|
|
776
941
|
low, high = high, low
|
|
777
942
|
|
|
778
943
|
if low < self.min_value:
|
|
779
|
-
warn(
|
|
944
|
+
warn(
|
|
945
|
+
ParameterUpdateWarning(
|
|
946
|
+
self.name,
|
|
947
|
+
type(self).__name__,
|
|
948
|
+
f"Low value {low} below minimum {self.min_value}, clamping",
|
|
949
|
+
)
|
|
950
|
+
)
|
|
780
951
|
low = self.min_value
|
|
781
952
|
if high > self.max_value:
|
|
782
|
-
warn(
|
|
953
|
+
warn(
|
|
954
|
+
ParameterUpdateWarning(
|
|
955
|
+
self.name,
|
|
956
|
+
type(self).__name__,
|
|
957
|
+
f"High value {high} above maximum {self.max_value}, clamping",
|
|
958
|
+
)
|
|
959
|
+
)
|
|
783
960
|
high = self.max_value
|
|
784
961
|
|
|
785
962
|
return (low, high)
|
|
@@ -801,7 +978,13 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
|
|
|
801
978
|
"IntegerRangeParameter must have both min_value and max_value bounds",
|
|
802
979
|
)
|
|
803
980
|
if self.min_value > self.max_value:
|
|
804
|
-
warn(
|
|
981
|
+
warn(
|
|
982
|
+
ParameterUpdateWarning(
|
|
983
|
+
self.name,
|
|
984
|
+
type(self).__name__,
|
|
985
|
+
f"Min value greater than max value, swapping",
|
|
986
|
+
)
|
|
987
|
+
)
|
|
805
988
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
806
989
|
self.value = self._validate(self.value)
|
|
807
990
|
|
|
@@ -913,14 +1096,32 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
913
1096
|
high = self._validate_single(new_value[1])
|
|
914
1097
|
|
|
915
1098
|
if low > high:
|
|
916
|
-
warn(
|
|
1099
|
+
warn(
|
|
1100
|
+
ParameterUpdateWarning(
|
|
1101
|
+
self.name,
|
|
1102
|
+
type(self).__name__,
|
|
1103
|
+
f"Low value {low} greater than high value {high}, swapping",
|
|
1104
|
+
)
|
|
1105
|
+
)
|
|
917
1106
|
low, high = high, low
|
|
918
1107
|
|
|
919
1108
|
if low < self.min_value:
|
|
920
|
-
warn(
|
|
1109
|
+
warn(
|
|
1110
|
+
ParameterUpdateWarning(
|
|
1111
|
+
self.name,
|
|
1112
|
+
type(self).__name__,
|
|
1113
|
+
f"Low value {low} below minimum {self.min_value}, clamping",
|
|
1114
|
+
)
|
|
1115
|
+
)
|
|
921
1116
|
low = self.min_value
|
|
922
1117
|
if high > self.max_value:
|
|
923
|
-
warn(
|
|
1118
|
+
warn(
|
|
1119
|
+
ParameterUpdateWarning(
|
|
1120
|
+
self.name,
|
|
1121
|
+
type(self).__name__,
|
|
1122
|
+
f"High value {high} above maximum {self.max_value}, clamping",
|
|
1123
|
+
)
|
|
1124
|
+
)
|
|
924
1125
|
high = self.max_value
|
|
925
1126
|
|
|
926
1127
|
return (low, high)
|
|
@@ -942,7 +1143,13 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
|
|
|
942
1143
|
"FloatRangeParameter must have both min_value and max_value bounds",
|
|
943
1144
|
)
|
|
944
1145
|
if self.min_value > self.max_value:
|
|
945
|
-
warn(
|
|
1146
|
+
warn(
|
|
1147
|
+
ParameterUpdateWarning(
|
|
1148
|
+
self.name,
|
|
1149
|
+
type(self).__name__,
|
|
1150
|
+
f"Min value greater than max value, swapping",
|
|
1151
|
+
)
|
|
1152
|
+
)
|
|
946
1153
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
947
1154
|
self.value = self._validate(self.value)
|
|
948
1155
|
|
|
@@ -1033,10 +1240,22 @@ class UnboundedIntegerParameter(Parameter[int]):
|
|
|
1033
1240
|
|
|
1034
1241
|
if compare_to_range:
|
|
1035
1242
|
if self.min_value is not None and new_value < self.min_value:
|
|
1036
|
-
warn(
|
|
1243
|
+
warn(
|
|
1244
|
+
ParameterUpdateWarning(
|
|
1245
|
+
self.name,
|
|
1246
|
+
type(self).__name__,
|
|
1247
|
+
f"Value {new_value} below minimum {self.min_value}, clamping",
|
|
1248
|
+
)
|
|
1249
|
+
)
|
|
1037
1250
|
new_value = self.min_value
|
|
1038
1251
|
if self.max_value is not None and new_value > self.max_value:
|
|
1039
|
-
warn(
|
|
1252
|
+
warn(
|
|
1253
|
+
ParameterUpdateWarning(
|
|
1254
|
+
self.name,
|
|
1255
|
+
type(self).__name__,
|
|
1256
|
+
f"Value {new_value} above maximum {self.max_value}, clamping",
|
|
1257
|
+
)
|
|
1258
|
+
)
|
|
1040
1259
|
new_value = self.max_value
|
|
1041
1260
|
return int(new_value)
|
|
1042
1261
|
|
|
@@ -1055,7 +1274,13 @@ class UnboundedIntegerParameter(Parameter[int]):
|
|
|
1055
1274
|
and self.max_value is not None
|
|
1056
1275
|
and self.min_value > self.max_value
|
|
1057
1276
|
):
|
|
1058
|
-
warn(
|
|
1277
|
+
warn(
|
|
1278
|
+
ParameterUpdateWarning(
|
|
1279
|
+
self.name,
|
|
1280
|
+
type(self).__name__,
|
|
1281
|
+
f"Min value greater than max value, swapping",
|
|
1282
|
+
)
|
|
1283
|
+
)
|
|
1059
1284
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
1060
1285
|
self.value = self._validate(self.value)
|
|
1061
1286
|
|
|
@@ -1161,10 +1386,22 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1161
1386
|
|
|
1162
1387
|
if compare_to_range:
|
|
1163
1388
|
if self.min_value is not None and new_value < self.min_value:
|
|
1164
|
-
warn(
|
|
1389
|
+
warn(
|
|
1390
|
+
ParameterUpdateWarning(
|
|
1391
|
+
self.name,
|
|
1392
|
+
type(self).__name__,
|
|
1393
|
+
f"Value {new_value} below minimum {self.min_value}, clamping",
|
|
1394
|
+
)
|
|
1395
|
+
)
|
|
1165
1396
|
new_value = self.min_value
|
|
1166
1397
|
if self.max_value is not None and new_value > self.max_value:
|
|
1167
|
-
warn(
|
|
1398
|
+
warn(
|
|
1399
|
+
ParameterUpdateWarning(
|
|
1400
|
+
self.name,
|
|
1401
|
+
type(self).__name__,
|
|
1402
|
+
f"Value {new_value} above maximum {self.max_value}, clamping",
|
|
1403
|
+
)
|
|
1404
|
+
)
|
|
1168
1405
|
new_value = self.max_value
|
|
1169
1406
|
|
|
1170
1407
|
return float(new_value)
|
|
@@ -1184,19 +1421,66 @@ class UnboundedFloatParameter(Parameter[float]):
|
|
|
1184
1421
|
and self.max_value is not None
|
|
1185
1422
|
and self.min_value > self.max_value
|
|
1186
1423
|
):
|
|
1187
|
-
warn(
|
|
1424
|
+
warn(
|
|
1425
|
+
ParameterUpdateWarning(
|
|
1426
|
+
self.name,
|
|
1427
|
+
type(self).__name__,
|
|
1428
|
+
f"Min value greater than max value, swapping",
|
|
1429
|
+
)
|
|
1430
|
+
)
|
|
1188
1431
|
self.min_value, self.max_value = self.max_value, self.min_value
|
|
1189
1432
|
self.value = self._validate(self.value)
|
|
1190
1433
|
|
|
1191
1434
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1435
|
+
@dataclass(init=False)
|
|
1436
|
+
class ButtonAction(Parameter[None]):
|
|
1437
|
+
"""
|
|
1438
|
+
Parameter for creating clickable buttons with callbacks.
|
|
1439
|
+
|
|
1440
|
+
Creates a button in the GUI that executes a callback function when clicked.
|
|
1441
|
+
See :meth:`~syd.interactive_viewer.InteractiveViewer.add_button` and
|
|
1442
|
+
:meth:`~syd.interactive_viewer.InteractiveViewer.update_button` for usage.
|
|
1443
|
+
|
|
1444
|
+
Parameters
|
|
1445
|
+
----------
|
|
1446
|
+
name : str
|
|
1447
|
+
The name of the parameter
|
|
1448
|
+
label : str
|
|
1449
|
+
Text to display on the button
|
|
1450
|
+
callback : callable
|
|
1451
|
+
Function to execute when the button is clicked
|
|
1452
|
+
|
|
1453
|
+
Examples
|
|
1454
|
+
--------
|
|
1455
|
+
>>> def print_hello():
|
|
1456
|
+
... print("Hello!")
|
|
1457
|
+
>>> button = ButtonAction("greeting", label="Say Hello", callback=print_hello)
|
|
1458
|
+
>>> button.callback() # Simulates clicking the button
|
|
1459
|
+
Hello!
|
|
1460
|
+
>>> # Update the button's label and callback
|
|
1461
|
+
>>> def print_goodbye():
|
|
1462
|
+
... print("Goodbye!")
|
|
1463
|
+
>>> button.update({"label": "Say Goodbye", "callback": print_goodbye})
|
|
1464
|
+
>>> button.callback()
|
|
1465
|
+
Goodbye!
|
|
1466
|
+
|
|
1467
|
+
Notes
|
|
1468
|
+
-----
|
|
1469
|
+
Unlike other Parameter types, ButtonAction:
|
|
1470
|
+
- Has no value (always None)
|
|
1471
|
+
- Is marked as an action (_is_action = True)
|
|
1472
|
+
- Executes code directly rather than storing state
|
|
1473
|
+
- Cannot be updated through the value property
|
|
1474
|
+
"""
|
|
1194
1475
|
|
|
1195
|
-
|
|
1476
|
+
label: str
|
|
1477
|
+
callback: Callable
|
|
1478
|
+
value: None = field(default=None, repr=False)
|
|
1479
|
+
_is_action: bool = field(default=True, repr=False)
|
|
1196
1480
|
|
|
1197
|
-
def __init__(self, name: str, label: str, callback: Callable
|
|
1481
|
+
def __init__(self, name: str, label: str, callback: Callable):
|
|
1198
1482
|
"""
|
|
1199
|
-
Initialize a button
|
|
1483
|
+
Initialize a button.
|
|
1200
1484
|
|
|
1201
1485
|
Args:
|
|
1202
1486
|
name: Internal name of the parameter
|
|
@@ -1206,32 +1490,28 @@ class ButtonParameter(Parameter):
|
|
|
1206
1490
|
self.name = name
|
|
1207
1491
|
self.label = label
|
|
1208
1492
|
self.callback = callback
|
|
1209
|
-
self._value = None
|
|
1493
|
+
self._value = None
|
|
1210
1494
|
|
|
1211
|
-
def
|
|
1212
|
-
"""
|
|
1213
|
-
|
|
1214
|
-
self.label = updates["label"]
|
|
1215
|
-
if "callback" in updates:
|
|
1216
|
-
self.callback = updates["callback"]
|
|
1217
|
-
|
|
1218
|
-
@property
|
|
1219
|
-
def value(self) -> None:
|
|
1220
|
-
"""Buttons don't have a value, always returns None."""
|
|
1221
|
-
return self._value
|
|
1222
|
-
|
|
1223
|
-
@value.setter
|
|
1224
|
-
def value(self, _: Any) -> None:
|
|
1225
|
-
"""Buttons don't store values."""
|
|
1226
|
-
pass
|
|
1495
|
+
def _validate(self, new_value: Any) -> None:
|
|
1496
|
+
"""Validate the button's value."""
|
|
1497
|
+
return None
|
|
1227
1498
|
|
|
1228
1499
|
def _validate_update(self) -> None:
|
|
1229
|
-
"""
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1500
|
+
"""Validate the button's value after updates."""
|
|
1501
|
+
if not callable(self.callback):
|
|
1502
|
+
raise ParameterUpdateError(
|
|
1503
|
+
self.name,
|
|
1504
|
+
type(self).__name__,
|
|
1505
|
+
f"Callback {self.callback} is not callable",
|
|
1506
|
+
)
|
|
1507
|
+
try:
|
|
1508
|
+
str(self.label)
|
|
1509
|
+
except Exception:
|
|
1510
|
+
raise ParameterUpdateError(
|
|
1511
|
+
self.name,
|
|
1512
|
+
type(self).__name__,
|
|
1513
|
+
f"Label {self.label} doesn't have a string representation",
|
|
1514
|
+
)
|
|
1235
1515
|
|
|
1236
1516
|
|
|
1237
1517
|
class ParameterType(Enum):
|
|
@@ -1247,4 +1527,7 @@ class ParameterType(Enum):
|
|
|
1247
1527
|
float_range = FloatRangeParameter
|
|
1248
1528
|
unbounded_integer = UnboundedIntegerParameter
|
|
1249
1529
|
unbounded_float = UnboundedFloatParameter
|
|
1250
|
-
|
|
1530
|
+
|
|
1531
|
+
|
|
1532
|
+
class ActionType(Enum):
|
|
1533
|
+
button = ButtonAction
|