syd 0.1.6__py3-none-any.whl → 0.2.0__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/parameters.py CHANGED
@@ -1,124 +1,23 @@
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
- )
1
+ from typing import List, Any, Tuple, Generic, TypeVar, Optional, Dict, Callable, Union
13
2
  from dataclasses import dataclass, field
14
3
  from abc import ABC, abstractmethod
15
4
  from enum import Enum
16
5
  from copy import deepcopy
17
6
  from warnings import warn
18
- import numpy as np
19
-
20
- T = TypeVar("T")
21
-
22
-
23
- # Keep original Parameter class and exceptions unchanged
24
- class ParameterAddError(Exception):
25
- """
26
- Exception raised when there is an error creating a new parameter.
27
-
28
- Parameters
29
- ----------
30
- parameter_name : str
31
- Name of the parameter that failed to be created
32
- parameter_type : str
33
- Type of the parameter that failed to be created
34
- message : str, optional
35
- Additional error details
36
- """
37
-
38
- def __init__(self, parameter_name: str, parameter_type: str, message: str = None):
39
- self.parameter_name = parameter_name
40
- self.parameter_type = parameter_type
41
- super().__init__(
42
- f"Failed to create {parameter_type} parameter '{parameter_name}'"
43
- + (f": {message}" if message else "")
44
- )
45
-
46
-
47
- class ParameterUpdateError(Exception):
48
- """
49
- Exception raised when there is an error updating an existing parameter.
50
-
51
- Parameters
52
- ----------
53
- parameter_name : str
54
- Name of the parameter that failed to update
55
- parameter_type : str
56
- Type of the parameter that failed to update
57
- message : str, optional
58
- Additional error details
59
- """
60
-
61
- def __init__(self, parameter_name: str, parameter_type: str, message: str = None):
62
- self.parameter_name = parameter_name
63
- self.parameter_type = parameter_type
64
- super().__init__(
65
- f"Failed to update {parameter_type} parameter '{parameter_name}'"
66
- + (f": {message}" if message else "")
67
- )
68
-
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
-
93
- def get_parameter_attributes(param_class) -> List[str]:
94
- """
95
- Get all valid attributes for a parameter class.
96
-
97
- Parameters
98
- ----------
99
- param_class : class
100
- The parameter class to inspect
101
7
 
102
- Returns
103
- -------
104
- list of str
105
- Names of all valid attributes for the parameter class
106
- """
107
- attributes = []
108
-
109
- # Walk through class hierarchy in reverse (most specific to most general)
110
- for cls in reversed(param_class.__mro__):
111
- if hasattr(cls, "__annotations__"):
112
- # Only add annotations that haven't been specified by a more specific class
113
- for name in cls.__annotations__:
114
- if not name.startswith("_"):
115
- attributes.append(name)
8
+ from .support import (
9
+ NoInitialValue,
10
+ ParameterMeta,
11
+ ParameterUpdateError,
12
+ ParameterUpdateWarning,
13
+ get_parameter_attributes,
14
+ )
116
15
 
117
- return attributes
16
+ T = TypeVar("T")
118
17
 
119
18
 
120
19
  @dataclass
121
- class Parameter(Generic[T], ABC):
20
+ class Parameter(Generic[T], ABC, metaclass=ParameterMeta):
122
21
  """
123
22
  Base class for all parameter types. Parameters are the building blocks
124
23
  for creating interactive GUI elements.
@@ -196,8 +95,8 @@ class Parameter(Generic[T], ABC):
196
95
 
197
96
  Examples
198
97
  --------
199
- >>> param = FloatParameter("temperature", 20.0, min_value=0, max_value=100)
200
- >>> param.update({"value": 25.0, "max_value": 150})
98
+ >>> param = FloatParameter("temperature", 20.0, min=0, max=100)
99
+ >>> param.update({"value": 25.0, "max": 150})
201
100
  """
202
101
  param_copy = deepcopy(self)
203
102
 
@@ -265,14 +164,14 @@ class TextParameter(Parameter[str]):
265
164
  Parameter for text input.
266
165
 
267
166
  Creates a text box in the GUI that accepts any string input.
268
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_text` and
269
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_text` for usage.
167
+ See :meth:`~syd.viewer.Viewer.add_text` and
168
+ :meth:`~syd.viewer.Viewer.update_text` for usage.
270
169
 
271
170
  Parameters
272
171
  ----------
273
172
  name : str
274
173
  The name of the parameter
275
- value : str
174
+ value : Union[str, NoInitialValue]
276
175
  The initial text value
277
176
 
278
177
  Examples
@@ -285,8 +184,10 @@ class TextParameter(Parameter[str]):
285
184
  'Bob'
286
185
  """
287
186
 
288
- def __init__(self, name: str, value: str):
187
+ def __init__(self, name: str, value: Union[str, NoInitialValue]):
289
188
  self.name = name
189
+ if isinstance(value, NoInitialValue):
190
+ value = ""
290
191
  self._value = self._validate(value)
291
192
 
292
193
  def _validate(self, new_value: Any) -> str:
@@ -308,14 +209,14 @@ class BooleanParameter(Parameter[bool]):
308
209
  Parameter for boolean values.
309
210
 
310
211
  Creates a checkbox in the GUI that can be toggled on/off.
311
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_boolean` and
312
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_boolean` for usage.
212
+ See :meth:`~syd.viewer.Viewer.add_boolean` and
213
+ :meth:`~syd.viewer.Viewer.update_boolean` for usage.
313
214
 
314
215
  Parameters
315
216
  ----------
316
217
  name : str
317
218
  The name of the parameter
318
- value : bool, optional
219
+ value : Union[bool, NoInitialValue]
319
220
  The initial state (default is True)
320
221
 
321
222
  Examples
@@ -328,8 +229,10 @@ class BooleanParameter(Parameter[bool]):
328
229
  False
329
230
  """
330
231
 
331
- def __init__(self, name: str, value: bool = True):
232
+ def __init__(self, name: str, value: Union[bool, NoInitialValue]):
332
233
  self.name = name
234
+ if isinstance(value, NoInitialValue):
235
+ value = True
333
236
  self._value = self._validate(value)
334
237
 
335
238
  def _validate(self, new_value: Any) -> bool:
@@ -351,14 +254,14 @@ class SelectionParameter(Parameter[Any]):
351
254
  Parameter for single selection from a list of options.
352
255
 
353
256
  Creates a dropdown menu in the GUI where users can select one option.
354
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_selection` and
355
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_selection` for usage.
257
+ See :meth:`~syd.viewer.Viewer.add_selection` and
258
+ :meth:`~syd.viewer.Viewer.update_selection` for usage.
356
259
 
357
260
  Parameters
358
261
  ----------
359
262
  name : str
360
263
  The name of the parameter
361
- value : Any
264
+ value : Union[Any, NoInitialValue]
362
265
  The initially selected value (must be one of the options)
363
266
  options : sequence
364
267
  List, tuple, or 1D numpy array of valid choices that can be selected
@@ -381,9 +284,13 @@ class SelectionParameter(Parameter[Any]):
381
284
 
382
285
  options: List[Any]
383
286
 
384
- def __init__(self, name: str, value: Any, options: Union[List, Tuple]):
287
+ def __init__(
288
+ self, name: str, value: Union[Any, NoInitialValue], options: Union[List, Tuple]
289
+ ):
385
290
  self.name = name
386
291
  self.options = self._validate_options(options)
292
+ if isinstance(value, NoInitialValue):
293
+ value = self.options[0]
387
294
  self._value = self._validate(value)
388
295
 
389
296
  def _validate_options(self, options: Any) -> List[Any]:
@@ -412,6 +319,9 @@ class SelectionParameter(Parameter[Any]):
412
319
  f"Options for parameter {self.name} must be a list or tuple"
413
320
  )
414
321
 
322
+ if not options:
323
+ raise ValueError(f"Options for parameter {self.name} must not be empty")
324
+
415
325
  # Verify all options are hashable (needed for comparison)
416
326
  try:
417
327
  for opt in options:
@@ -435,9 +345,42 @@ class SelectionParameter(Parameter[Any]):
435
345
  Raises:
436
346
  ValueError: If value is not in options list
437
347
  """
438
- if new_value not in self.options:
439
- raise ValueError(f"Value {new_value} not in options: {self.options}")
440
- return new_value
348
+ # Direct check for non-float values or when new_value is exactly in options
349
+ if new_value in self.options:
350
+ return new_value
351
+
352
+ # Special handling for numeric values to account for type mismatches
353
+ if isinstance(new_value, (int, float)):
354
+ for option in self.options:
355
+ # For numeric options, compare as floats
356
+ if (
357
+ isinstance(option, (int, float))
358
+ and abs(float(new_value) - float(option)) < 1e-10
359
+ ):
360
+ return option
361
+ # Also try string conversion for numeric strings
362
+ elif isinstance(option, str):
363
+ try:
364
+ if abs(float(new_value) - float(option)) < 1e-10:
365
+ return option
366
+ except ValueError:
367
+ pass
368
+
369
+ # Handle string conversion - when new_value is a string but options might be numeric
370
+ if isinstance(new_value, str):
371
+ try:
372
+ # Try to convert to float if possible
373
+ float_value = float(new_value)
374
+ for option in self.options:
375
+ if (
376
+ isinstance(option, (int, float))
377
+ and abs(float_value - float(option)) < 1e-10
378
+ ):
379
+ return option
380
+ except ValueError:
381
+ pass
382
+
383
+ raise ValueError(f"Value {new_value} not in options: {self.options}")
441
384
 
442
385
  def _validate_update(self) -> None:
443
386
  """
@@ -450,12 +393,51 @@ class SelectionParameter(Parameter[Any]):
450
393
  TypeError: If options is not a list or tuple
451
394
  """
452
395
  self.options = self._validate_options(self.options)
453
- if self.value not in self.options:
396
+
397
+ # Check if value is directly in options
398
+ if self.value in self.options:
399
+ return
400
+
401
+ # For numeric values, try flexible comparison
402
+ value_found = False
403
+ if isinstance(self.value, (int, float)):
404
+ for option in self.options:
405
+ if (
406
+ isinstance(option, (int, float))
407
+ and abs(float(self.value) - float(option)) < 1e-10
408
+ ):
409
+ # Don't update self.value here as we want to keep the original type if possible
410
+ value_found = True
411
+ break
412
+ elif isinstance(option, str):
413
+ try:
414
+ if abs(float(self.value) - float(option)) < 1e-10:
415
+ value_found = True
416
+ break
417
+ except ValueError:
418
+ pass
419
+
420
+ # For string values that might be numeric
421
+ if not value_found and isinstance(self.value, str):
422
+ try:
423
+ float_value = float(self.value)
424
+ for option in self.options:
425
+ if (
426
+ isinstance(option, (int, float))
427
+ and abs(float_value - float(option)) < 1e-10
428
+ ):
429
+ value_found = True
430
+ break
431
+ except ValueError:
432
+ pass
433
+
434
+ # If value is not found after all checks, reset to first option
435
+ if not value_found:
454
436
  warn(
455
437
  ParameterUpdateWarning(
456
438
  self.name,
457
439
  type(self).__name__,
458
- f"Value {self.value} not in options: {self.options}, setting to first option",
440
+ f"Value {self.value} not in options, setting to first option ({self.options[0]})",
459
441
  )
460
442
  )
461
443
  self.value = self.options[0]
@@ -467,15 +449,15 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
467
449
  Parameter for multiple selections from a list of options.
468
450
 
469
451
  Creates a set of checkboxes or multi-select dropdown in the GUI.
470
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_multiple_selection` and
471
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_multiple_selection` for usage.
452
+ See :meth:`~syd.viewer.Viewer.add_multiple_selection` and
453
+ :meth:`~syd.viewer.Viewer.update_multiple_selection` for usage.
472
454
 
473
455
  Parameters
474
456
  ----------
475
457
  name : str
476
458
  The name of the parameter
477
- value : list
478
- List of initially selected values (must all be from options)
459
+ value : Union[List[Any], NoInitialValue]
460
+ List of initially selected values (must all be from options, can be empty)
479
461
  options : sequence
480
462
  List, tuple, or 1D numpy array of valid choices that can be selected
481
463
 
@@ -497,9 +479,16 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
497
479
 
498
480
  options: List[Any]
499
481
 
500
- def __init__(self, name: str, value: List[Any], options: Union[List, Tuple]):
482
+ def __init__(
483
+ self,
484
+ name: str,
485
+ value: Union[List[Any], NoInitialValue],
486
+ options: Union[List, Tuple],
487
+ ):
501
488
  self.name = name
502
489
  self.options = self._validate_options(options)
490
+ if isinstance(value, NoInitialValue):
491
+ value = []
503
492
  self._value = self._validate(value)
504
493
 
505
494
  def _validate_options(self, options: Any) -> List[Any]:
@@ -525,9 +514,12 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
525
514
  """
526
515
  if not isinstance(options, (list, tuple)):
527
516
  raise TypeError(
528
- f"Options for parameter {self.name} must be a list or tuple"
517
+ f"Options for parameter {self.name} must be a list or tuple, received {type(options)}"
529
518
  )
530
519
 
520
+ if not options:
521
+ raise ValueError(f"Options for parameter {self.name} must not be empty")
522
+
531
523
  # Verify all options are hashable (needed for comparison)
532
524
  try:
533
525
  for opt in options:
@@ -580,7 +572,7 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
580
572
  ParameterUpdateWarning(
581
573
  self.name,
582
574
  type(self).__name__,
583
- f"For parameter {self.name}, value {self.value} contains invalid options: {invalid}. Setting to empty list.",
575
+ f"For parameter {self.name}, value {self.value} contains invalid selections: {invalid}. Setting to empty list.",
584
576
  )
585
577
  )
586
578
  self.value = []
@@ -595,46 +587,48 @@ class IntegerParameter(Parameter[int]):
595
587
  Parameter for bounded integer values.
596
588
 
597
589
  Creates a slider in the GUI for selecting whole numbers between bounds.
598
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_integer` and
599
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_integer` for usage.
590
+ See :meth:`~syd.viewer.Viewer.add_integer` and
591
+ :meth:`~syd.viewer.Viewer.update_integer` for usage.
600
592
 
601
593
  Parameters
602
594
  ----------
603
595
  name : str
604
596
  The name of the parameter
605
- value : int
606
- Initial value (will be clamped to fit between min_value and max_value)
607
- min_value : int
597
+ value : Union[int, NoInitialValue]
598
+ Initial value (will be clamped to fit between min and max)
599
+ min : int
608
600
  Minimum allowed value
609
- max_value : int
601
+ max : int
610
602
  Maximum allowed value
611
603
 
612
604
  Examples
613
605
  --------
614
- >>> age = IntegerParameter("age", value=25, min_value=0, max_value=120)
606
+ >>> age = IntegerParameter("age", value=25, min=0, max=120)
615
607
  >>> age.value
616
608
  25
617
- >>> age.update({"value": 150}) # Will be clamped to max_value
609
+ >>> age.update({"value": 150}) # Will be clamped to max
618
610
  >>> age.value
619
611
  120
620
- >>> age.update({"value": -10}) # Will be clamped to min_value
612
+ >>> age.update({"value": -10}) # Will be clamped to min
621
613
  >>> age.value
622
614
  0
623
615
  """
624
616
 
625
- min_value: int
626
- max_value: int
617
+ min: int
618
+ max: int
627
619
 
628
620
  def __init__(
629
621
  self,
630
622
  name: str,
631
- value: int,
632
- min_value: int,
633
- max_value: int,
623
+ value: Union[int, NoInitialValue],
624
+ min: int,
625
+ max: int,
634
626
  ):
635
627
  self.name = name
636
- self.min_value = self._validate(min_value, compare_to_range=False)
637
- self.max_value = self._validate(max_value, compare_to_range=False)
628
+ self.min = self._validate(min, compare_to_range=False)
629
+ self.max = self._validate(max, compare_to_range=False)
630
+ if isinstance(value, NoInitialValue):
631
+ value = self.min
638
632
  self._value = self._validate(value)
639
633
 
640
634
  def _validate(self, new_value: Any, compare_to_range: bool = True) -> int:
@@ -657,43 +651,43 @@ class IntegerParameter(Parameter[int]):
657
651
  raise ValueError(f"Value {new_value} cannot be converted to int")
658
652
 
659
653
  if compare_to_range:
660
- if new_value < self.min_value:
654
+ if new_value < self.min:
661
655
  warn(
662
656
  ParameterUpdateWarning(
663
657
  self.name,
664
658
  type(self).__name__,
665
- f"Value {new_value} below minimum {self.min_value}, clamping",
659
+ f"Value {new_value} below minimum {self.min}, clamping",
666
660
  )
667
661
  )
668
- new_value = self.min_value
669
- if new_value > self.max_value:
662
+ new_value = self.min
663
+ if new_value > self.max:
670
664
  warn(
671
665
  ParameterUpdateWarning(
672
666
  self.name,
673
667
  type(self).__name__,
674
- f"Value {new_value} above maximum {self.max_value}, clamping",
668
+ f"Value {new_value} above maximum {self.max}, clamping",
675
669
  )
676
670
  )
677
- new_value = self.max_value
671
+ new_value = self.max
678
672
  return int(new_value)
679
673
 
680
674
  def _validate_update(self) -> None:
681
675
  """
682
676
  Validate complete parameter state after updates.
683
677
 
684
- Ensures min_value <= max_value, swapping if needed.
678
+ Ensures min <= max, swapping if needed.
685
679
  Re-validates current value against potentially updated bounds.
686
680
 
687
681
  Raises:
688
682
  ParameterUpdateError: If bounds are invalid (e.g. None when required)
689
683
  """
690
- if self.min_value is None or self.max_value is None:
684
+ if self.min is None or self.max is None:
691
685
  raise ParameterUpdateError(
692
686
  self.name,
693
687
  type(self).__name__,
694
- "IntegerParameter must have both min_value and max_value bounds",
688
+ "IntegerParameter must have both min and max bounds",
695
689
  )
696
- if self.min_value > self.max_value:
690
+ if self.min > self.max:
697
691
  warn(
698
692
  ParameterUpdateWarning(
699
693
  self.name,
@@ -701,7 +695,7 @@ class IntegerParameter(Parameter[int]):
701
695
  f"Min value greater than max value, swapping",
702
696
  )
703
697
  )
704
- self.min_value, self.max_value = self.max_value, self.min_value
698
+ self.min, self.max = self.max, self.min
705
699
  self.value = self._validate(self.value)
706
700
 
707
701
 
@@ -711,32 +705,32 @@ class FloatParameter(Parameter[float]):
711
705
  Parameter for bounded decimal numbers.
712
706
 
713
707
  Creates a slider in the GUI for selecting numbers between bounds.
714
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_float` and
715
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_float` for usage.
708
+ See :meth:`~syd.viewer.Viewer.add_float` and
709
+ :meth:`~syd.viewer.Viewer.update_float` for usage.
716
710
 
717
711
  Parameters
718
712
  ----------
719
713
  name : str
720
714
  The name of the parameter
721
- value : float
722
- Initial value (will be clamped to fit between min_value and max_value)
723
- min_value : float
715
+ value : Union[float, NoInitialValue]
716
+ Initial value (will be clamped to fit between min and max)
717
+ min : float
724
718
  Minimum allowed value
725
- max_value : float
719
+ max : float
726
720
  Maximum allowed value
727
721
  step : float, optional
728
- Size of each increment (default is 0.1)
722
+ Size of each increment (default is 0.001)
729
723
 
730
724
  Examples
731
725
  --------
732
726
  >>> temp = FloatParameter("temperature", value=98.6,
733
- ... min_value=95.0, max_value=105.0, step=0.1)
727
+ ... min=95.0, max=105.0, step=0.1)
734
728
  >>> temp.value
735
729
  98.6
736
730
  >>> temp.update({"value": 98.67}) # Will be rounded to nearest step
737
731
  >>> temp.value
738
732
  98.7
739
- >>> temp.update({"value": 110.0}) # Will be clamped to max_value
733
+ >>> temp.update({"value": 110.0}) # Will be clamped to max
740
734
  >>> temp.value
741
735
  105.0
742
736
 
@@ -748,22 +742,24 @@ class FloatParameter(Parameter[float]):
748
742
  - step=5.0 allows values like 0.0, 5.0, 10.0, etc.
749
743
  """
750
744
 
751
- min_value: float
752
- max_value: float
745
+ min: float
746
+ max: float
753
747
  step: float
754
748
 
755
749
  def __init__(
756
750
  self,
757
751
  name: str,
758
- value: float,
759
- min_value: float,
760
- max_value: float,
761
- step: float = 0.1,
752
+ value: Union[float, NoInitialValue],
753
+ min: float,
754
+ max: float,
755
+ step: float = 0.001,
762
756
  ):
763
757
  self.name = name
764
758
  self.step = step
765
- self.min_value = self._validate(min_value, compare_to_range=False)
766
- self.max_value = self._validate(max_value, compare_to_range=False)
759
+ self.min = self._validate(min, compare_to_range=False)
760
+ self.max = self._validate(max, compare_to_range=False)
761
+ if isinstance(value, NoInitialValue):
762
+ value = self.min
767
763
  self._value = self._validate(value)
768
764
 
769
765
  def _validate(self, new_value: Any, compare_to_range: bool = True) -> float:
@@ -791,24 +787,24 @@ class FloatParameter(Parameter[float]):
791
787
  new_value = round(new_value / self.step) * self.step
792
788
 
793
789
  if compare_to_range:
794
- if new_value < self.min_value:
790
+ if new_value < self.min:
795
791
  warn(
796
792
  ParameterUpdateWarning(
797
793
  self.name,
798
794
  type(self).__name__,
799
- f"Value {new_value} below minimum {self.min_value}, clamping",
795
+ f"Value {new_value} below minimum {self.min}, clamping",
800
796
  )
801
797
  )
802
- new_value = self.min_value
803
- if new_value > self.max_value:
798
+ new_value = self.min
799
+ if new_value > self.max:
804
800
  warn(
805
801
  ParameterUpdateWarning(
806
802
  self.name,
807
803
  type(self).__name__,
808
- f"Value {new_value} above maximum {self.max_value}, clamping",
804
+ f"Value {new_value} above maximum {self.max}, clamping",
809
805
  )
810
806
  )
811
- new_value = self.max_value
807
+ new_value = self.max
812
808
 
813
809
  return float(new_value)
814
810
 
@@ -816,19 +812,19 @@ class FloatParameter(Parameter[float]):
816
812
  """
817
813
  Validate complete parameter state after updates.
818
814
 
819
- Ensures min_value <= max_value, swapping if needed.
815
+ Ensures min <= max, swapping if needed.
820
816
  Re-validates current value against potentially updated bounds.
821
817
 
822
818
  Raises:
823
819
  ParameterUpdateError: If bounds are invalid (e.g. None when required)
824
820
  """
825
- if self.min_value is None or self.max_value is None:
821
+ if self.min is None or self.max is None:
826
822
  raise ParameterUpdateError(
827
823
  self.name,
828
824
  type(self).__name__,
829
- "FloatParameter must have both min_value and max_value bounds",
825
+ "FloatParameter must have both min and max bounds",
830
826
  )
831
- if self.min_value > self.max_value:
827
+ if self.min > self.max:
832
828
  warn(
833
829
  ParameterUpdateWarning(
834
830
  self.name,
@@ -836,7 +832,7 @@ class FloatParameter(Parameter[float]):
836
832
  f"Min value greater than max value, swapping",
837
833
  )
838
834
  )
839
- self.min_value, self.max_value = self.max_value, self.min_value
835
+ self.min, self.max = self.max, self.min
840
836
  self.value = self._validate(self.value)
841
837
 
842
838
 
@@ -846,24 +842,24 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
846
842
  Parameter for a range of bounded integer values.
847
843
 
848
844
  Creates a range slider in the GUI for selecting a range of whole numbers.
849
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_integer_range` and
850
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_integer_range` for usage.
845
+ See :meth:`~syd.viewer.Viewer.add_integer_range` and
846
+ :meth:`~syd.viewer.Viewer.update_integer_range` for usage.
851
847
 
852
848
  Parameters
853
849
  ----------
854
850
  name : str
855
851
  The name of the parameter
856
- value : tuple[int, int]
852
+ value : Union[Tuple[int, int], NoInitialValue]
857
853
  Initial (low, high) values
858
- min_value : int
854
+ min : int
859
855
  Minimum allowed value for both low and high
860
- max_value : int
856
+ max : int
861
857
  Maximum allowed value for both low and high
862
858
 
863
859
  Examples
864
860
  --------
865
861
  >>> age_range = IntegerRangeParameter("age_range",
866
- ... value=(25, 35), min_value=18, max_value=100)
862
+ ... value=(25, 35), min=18, max=100)
867
863
  >>> age_range.value
868
864
  (25, 35)
869
865
  >>> age_range.update({"value": (35, 25)}) # Values will be swapped
@@ -874,22 +870,24 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
874
870
  (18, 40)
875
871
  """
876
872
 
877
- min_value: int
878
- max_value: int
873
+ min: int
874
+ max: int
879
875
 
880
876
  def __init__(
881
877
  self,
882
878
  name: str,
883
- value: Tuple[int, int],
884
- min_value: int,
885
- max_value: int,
879
+ value: Union[Tuple[int, int], NoInitialValue],
880
+ min: int,
881
+ max: int,
886
882
  ):
887
883
  self.name = name
888
- self.min_value = self._validate_single(min_value)
889
- self.max_value = self._validate_single(max_value)
884
+ self.min = self._validate_single(min, context="min")
885
+ self.max = self._validate_single(max, context="max")
886
+ if isinstance(value, NoInitialValue):
887
+ value = (self.min, self.max)
890
888
  self._value = self._validate(value)
891
889
 
892
- def _validate_single(self, new_value: Any) -> int:
890
+ def _validate_single(self, new_value: Any, context: Optional[str] = None) -> int:
893
891
  """
894
892
  Validate and convert a single numeric value.
895
893
 
@@ -907,8 +905,11 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
907
905
  """
908
906
  try:
909
907
  return int(new_value)
910
- except ValueError:
911
- raise ValueError(f"Value {new_value} cannot be converted to int")
908
+ except Exception:
909
+ msg = f"Value {new_value} cannot be converted to int"
910
+ if context:
911
+ msg += f" for {context}"
912
+ raise ValueError(msg)
912
913
 
913
914
  def _validate(self, new_value: Any) -> Tuple[int, int]:
914
915
  """
@@ -940,24 +941,24 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
940
941
  )
941
942
  low, high = high, low
942
943
 
943
- if low < self.min_value:
944
+ if low < self.min:
944
945
  warn(
945
946
  ParameterUpdateWarning(
946
947
  self.name,
947
948
  type(self).__name__,
948
- f"Low value {low} below minimum {self.min_value}, clamping",
949
+ f"Low value {low} below minimum {self.min}, clamping",
949
950
  )
950
951
  )
951
- low = self.min_value
952
- if high > self.max_value:
952
+ low = self.min
953
+ if high > self.max:
953
954
  warn(
954
955
  ParameterUpdateWarning(
955
956
  self.name,
956
957
  type(self).__name__,
957
- f"High value {high} above maximum {self.max_value}, clamping",
958
+ f"High value {high} above maximum {self.max}, clamping",
958
959
  )
959
960
  )
960
- high = self.max_value
961
+ high = self.max
961
962
 
962
963
  return (low, high)
963
964
 
@@ -965,19 +966,19 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
965
966
  """
966
967
  Validate complete parameter state after updates.
967
968
 
968
- Ensures min_value <= max_value, swapping if needed.
969
+ Ensures min <= max, swapping if needed.
969
970
  Re-validates current value against potentially updated bounds.
970
971
 
971
972
  Raises:
972
973
  ParameterUpdateError: If bounds are invalid (e.g. None when required)
973
974
  """
974
- if self.min_value is None or self.max_value is None:
975
+ if self.min is None or self.max is None:
975
976
  raise ParameterUpdateError(
976
977
  self.name,
977
978
  type(self).__name__,
978
- "IntegerRangeParameter must have both min_value and max_value bounds",
979
+ "IntegerRangeParameter must have both min and max bounds",
979
980
  )
980
- if self.min_value > self.max_value:
981
+ if self.min > self.max:
981
982
  warn(
982
983
  ParameterUpdateWarning(
983
984
  self.name,
@@ -985,7 +986,7 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
985
986
  f"Min value greater than max value, swapping",
986
987
  )
987
988
  )
988
- self.min_value, self.max_value = self.max_value, self.min_value
989
+ self.min, self.max = self.max, self.min
989
990
  self.value = self._validate(self.value)
990
991
 
991
992
 
@@ -995,26 +996,26 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
995
996
  Parameter for a range of bounded decimal numbers.
996
997
 
997
998
  Creates a range slider in the GUI for selecting a range of numbers.
998
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_float_range` and
999
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_float_range` for usage.
999
+ See :meth:`~syd.viewer.Viewer.add_float_range` and
1000
+ :meth:`~syd.viewer.Viewer.update_float_range` for usage.
1000
1001
 
1001
1002
  Parameters
1002
1003
  ----------
1003
1004
  name : str
1004
1005
  The name of the parameter
1005
- value : tuple[float, float]
1006
+ value : Union[Tuple[float, float], NoInitialValue]
1006
1007
  Initial (low, high) values
1007
- min_value : float
1008
+ min : float
1008
1009
  Minimum allowed value for both low and high
1009
- max_value : float
1010
+ max : float
1010
1011
  Maximum allowed value for both low and high
1011
1012
  step : float, optional
1012
- Size of each increment (default is 0.1)
1013
+ Size of each increment (default is 0.001)
1013
1014
 
1014
1015
  Examples
1015
1016
  --------
1016
1017
  >>> temp_range = FloatRangeParameter("temperature_range",
1017
- ... value=(98.6, 100.4), min_value=95.0, max_value=105.0, step=0.1)
1018
+ ... value=(98.6, 100.4), min=95.0, max=105.0, step=0.1)
1018
1019
  >>> temp_range.value
1019
1020
  (98.6, 100.4)
1020
1021
  >>> temp_range.update({"value": (98.67, 100.0)}) # Low will be rounded
@@ -1032,25 +1033,27 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1032
1033
  - step=5.0 allows values like 0.0, 5.0, 10.0, etc.
1033
1034
  """
1034
1035
 
1035
- min_value: float
1036
- max_value: float
1036
+ min: float
1037
+ max: float
1037
1038
  step: float
1038
1039
 
1039
1040
  def __init__(
1040
1041
  self,
1041
1042
  name: str,
1042
- value: Tuple[float, float],
1043
- min_value: float,
1044
- max_value: float,
1045
- step: float = 0.1,
1043
+ value: Union[Tuple[float, float], NoInitialValue],
1044
+ min: float,
1045
+ max: float,
1046
+ step: float = 0.001,
1046
1047
  ):
1047
1048
  self.name = name
1048
1049
  self.step = step
1049
- self.min_value = self._validate_single(min_value)
1050
- self.max_value = self._validate_single(max_value)
1050
+ self.min = self._validate_single(min, context="min")
1051
+ self.max = self._validate_single(max, context="max")
1052
+ if isinstance(value, NoInitialValue):
1053
+ value = (self.min, self.max)
1051
1054
  self._value = self._validate(value)
1052
1055
 
1053
- def _validate_single(self, new_value: Any) -> float:
1056
+ def _validate_single(self, new_value: Any, context: Optional[str] = None) -> float:
1054
1057
  """
1055
1058
  Validate and convert a single numeric value.
1056
1059
 
@@ -1068,8 +1071,11 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1068
1071
  """
1069
1072
  try:
1070
1073
  new_value = float(new_value)
1071
- except ValueError:
1072
- raise ValueError(f"Value {new_value} cannot be converted to float")
1074
+ except Exception:
1075
+ msg = f"Value {new_value} cannot be converted to float"
1076
+ if context:
1077
+ msg += f" for {context}"
1078
+ raise ValueError(msg)
1073
1079
 
1074
1080
  # Round to the nearest step
1075
1081
  new_value = round(new_value / self.step) * self.step
@@ -1105,24 +1111,24 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1105
1111
  )
1106
1112
  low, high = high, low
1107
1113
 
1108
- if low < self.min_value:
1114
+ if low < self.min:
1109
1115
  warn(
1110
1116
  ParameterUpdateWarning(
1111
1117
  self.name,
1112
1118
  type(self).__name__,
1113
- f"Low value {low} below minimum {self.min_value}, clamping",
1119
+ f"Low value {low} below minimum {self.min}, clamping",
1114
1120
  )
1115
1121
  )
1116
- low = self.min_value
1117
- if high > self.max_value:
1122
+ low = self.min
1123
+ if high > self.max:
1118
1124
  warn(
1119
1125
  ParameterUpdateWarning(
1120
1126
  self.name,
1121
1127
  type(self).__name__,
1122
- f"High value {high} above maximum {self.max_value}, clamping",
1128
+ f"High value {high} above maximum {self.max}, clamping",
1123
1129
  )
1124
1130
  )
1125
- high = self.max_value
1131
+ high = self.max
1126
1132
 
1127
1133
  return (low, high)
1128
1134
 
@@ -1130,19 +1136,19 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1130
1136
  """
1131
1137
  Validate complete parameter state after updates.
1132
1138
 
1133
- Ensures min_value <= max_value, swapping if needed.
1139
+ Ensures min <= max, swapping if needed.
1134
1140
  Re-validates current value against potentially updated bounds.
1135
1141
 
1136
1142
  Raises:
1137
1143
  ParameterUpdateError: If bounds are invalid (e.g. None when required)
1138
1144
  """
1139
- if self.min_value is None or self.max_value is None:
1145
+ if self.min is None or self.max is None:
1140
1146
  raise ParameterUpdateError(
1141
1147
  self.name,
1142
1148
  type(self).__name__,
1143
- "FloatRangeParameter must have both min_value and max_value bounds",
1149
+ "FloatRangeParameter must have both min and max bounds",
1144
1150
  )
1145
- if self.min_value > self.max_value:
1151
+ if self.min > self.max:
1146
1152
  warn(
1147
1153
  ParameterUpdateWarning(
1148
1154
  self.name,
@@ -1150,7 +1156,7 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1150
1156
  f"Min value greater than max value, swapping",
1151
1157
  )
1152
1158
  )
1153
- self.min_value, self.max_value = self.max_value, self.min_value
1159
+ self.min, self.max = self.max, self.min
1154
1160
  self.value = self._validate(self.value)
1155
1161
 
1156
1162
 
@@ -1160,28 +1166,21 @@ class UnboundedIntegerParameter(Parameter[int]):
1160
1166
  Parameter for optionally bounded integer values.
1161
1167
 
1162
1168
  Creates a text input box in the GUI for entering whole numbers.
1163
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_unbounded_integer` and
1164
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_unbounded_integer` for usage.
1169
+ See :meth:`~syd.viewer.Viewer.add_unbounded_integer` and
1170
+ :meth:`~syd.viewer.Viewer.update_unbounded_integer` for usage.
1165
1171
 
1166
1172
  Parameters
1167
1173
  ----------
1168
1174
  name : str
1169
1175
  The name of the parameter
1170
- value : int
1176
+ value : Union[int, NoInitialValue]
1171
1177
  Initial value
1172
- min_value : int, optional
1173
- Minimum allowed value (or None for no minimum)
1174
- max_value : int, optional
1175
- Maximum allowed value (or None for no maximum)
1176
1178
 
1177
1179
  Examples
1178
1180
  --------
1179
- >>> count = UnboundedIntegerParameter("count", value=10, min_value=0)
1181
+ >>> count = UnboundedIntegerParameter("count", value=10)
1180
1182
  >>> count.value
1181
1183
  10
1182
- >>> count.update({"value": -5}) # Will be clamped to min_value
1183
- >>> count.value
1184
- 0
1185
1184
  >>> count.update({"value": 1000000}) # No maximum, so this is allowed
1186
1185
  >>> count.value
1187
1186
  1000000
@@ -1189,43 +1188,26 @@ class UnboundedIntegerParameter(Parameter[int]):
1189
1188
  Notes
1190
1189
  -----
1191
1190
  Use this instead of IntegerParameter when you:
1192
- - Don't know a reasonable maximum value
1193
- - Only want to enforce a minimum or maximum, but not both
1191
+ - Don't have any reason to bound the value
1194
1192
  - Need to allow very large numbers that would be impractical with a slider
1195
1193
  """
1196
1194
 
1197
- min_value: Optional[int]
1198
- max_value: Optional[int]
1199
-
1200
1195
  def __init__(
1201
1196
  self,
1202
1197
  name: str,
1203
- value: int,
1204
- min_value: Optional[int] = None,
1205
- max_value: Optional[int] = None,
1198
+ value: Union[int, NoInitialValue],
1206
1199
  ):
1207
1200
  self.name = name
1208
- self.min_value = (
1209
- self._validate(min_value, compare_to_range=False)
1210
- if min_value is not None
1211
- else None
1212
- )
1213
- self.max_value = (
1214
- self._validate(max_value, compare_to_range=False)
1215
- if max_value is not None
1216
- else None
1217
- )
1201
+ if isinstance(value, NoInitialValue):
1202
+ value = 0
1218
1203
  self._value = self._validate(value)
1219
1204
 
1220
- def _validate(self, new_value: Any, compare_to_range: bool = True) -> int:
1205
+ def _validate(self, new_value: Any) -> int:
1221
1206
  """
1222
- Validate and convert value to integer, optionally checking bounds.
1223
-
1224
- Handles None min/max values by skipping those bound checks.
1207
+ Validate and convert value to integer.
1225
1208
 
1226
1209
  Args:
1227
1210
  new_value: Value to validate
1228
- compare_to_range: If True, clamps value to any defined min/max bounds
1229
1211
 
1230
1212
  Returns:
1231
1213
  Validated integer value
@@ -1238,50 +1220,15 @@ class UnboundedIntegerParameter(Parameter[int]):
1238
1220
  except ValueError:
1239
1221
  raise ValueError(f"Value {new_value} cannot be converted to int")
1240
1222
 
1241
- if compare_to_range:
1242
- if self.min_value is not None and new_value < self.min_value:
1243
- warn(
1244
- ParameterUpdateWarning(
1245
- self.name,
1246
- type(self).__name__,
1247
- f"Value {new_value} below minimum {self.min_value}, clamping",
1248
- )
1249
- )
1250
- new_value = self.min_value
1251
- if self.max_value is not None and new_value > self.max_value:
1252
- warn(
1253
- ParameterUpdateWarning(
1254
- self.name,
1255
- type(self).__name__,
1256
- f"Value {new_value} above maximum {self.max_value}, clamping",
1257
- )
1258
- )
1259
- new_value = self.max_value
1260
1223
  return int(new_value)
1261
1224
 
1262
1225
  def _validate_update(self) -> None:
1263
1226
  """
1264
1227
  Validate complete parameter state after updates.
1265
1228
 
1266
- Ensures min_value <= max_value, swapping if needed.
1267
- Re-validates current value against potentially updated bounds.
1268
-
1269
1229
  Raises:
1270
1230
  ParameterUpdateError: If bounds are invalid (e.g. None when required)
1271
1231
  """
1272
- if (
1273
- self.min_value is not None
1274
- and self.max_value is not None
1275
- and self.min_value > self.max_value
1276
- ):
1277
- warn(
1278
- ParameterUpdateWarning(
1279
- self.name,
1280
- type(self).__name__,
1281
- f"Min value greater than max value, swapping",
1282
- )
1283
- )
1284
- self.min_value, self.max_value = self.max_value, self.min_value
1285
1232
  self.value = self._validate(self.value)
1286
1233
 
1287
1234
 
@@ -1291,30 +1238,23 @@ class UnboundedFloatParameter(Parameter[float]):
1291
1238
  Parameter for optionally bounded decimal numbers.
1292
1239
 
1293
1240
  Creates a text input box in the GUI for entering numbers.
1294
- See :meth:`~syd.interactive_viewer.InteractiveViewer.add_unbounded_float` and
1295
- :meth:`~syd.interactive_viewer.InteractiveViewer.update_unbounded_float` for usage.
1241
+ See :meth:`~syd.viewer.Viewer.add_unbounded_float` and
1242
+ :meth:`~syd.viewer.Viewer.update_unbounded_float` for usage.
1296
1243
 
1297
1244
  Parameters
1298
1245
  ----------
1299
1246
  name : str
1300
1247
  The name of the parameter
1301
- value : float
1248
+ value : Union[float, NoInitialValue]
1302
1249
  Initial value
1303
- min_value : float, optional
1304
- Minimum allowed value (or None for no minimum)
1305
- max_value : float, optional
1306
- Maximum allowed value (or None for no maximum)
1307
1250
  step : float, optional
1308
1251
  Size of each increment (default is None, meaning no rounding)
1309
1252
 
1310
1253
  Examples
1311
1254
  --------
1312
- >>> price = UnboundedFloatParameter("price", value=19.99, min_value=0.0, step=0.01)
1255
+ >>> price = UnboundedFloatParameter("price", value=19.99)
1313
1256
  >>> price.value
1314
1257
  19.99
1315
- >>> price.update({"value": -5.0}) # Will be clamped to min_value
1316
- >>> price.value
1317
- 0.0
1318
1258
  >>> price.update({"value": 19.987}) # Will be rounded to step
1319
1259
  >>> price.value
1320
1260
  19.99
@@ -1323,7 +1263,6 @@ class UnboundedFloatParameter(Parameter[float]):
1323
1263
  -----
1324
1264
  Use this instead of FloatParameter when you:
1325
1265
  - Don't know a reasonable maximum value
1326
- - Only want to enforce a minimum or maximum, but not both
1327
1266
  - Need to allow very large or precise numbers that would be impractical with a slider
1328
1267
 
1329
1268
  If step is provided, values will be rounded:
@@ -1332,42 +1271,27 @@ class UnboundedFloatParameter(Parameter[float]):
1332
1271
  - step=5.0 rounds to 0.0, 5.0, 10.0, etc.
1333
1272
  """
1334
1273
 
1335
- min_value: Optional[float]
1336
- max_value: Optional[float]
1337
- step: float
1274
+ step: Optional[float]
1338
1275
 
1339
1276
  def __init__(
1340
1277
  self,
1341
1278
  name: str,
1342
- value: float,
1343
- min_value: Optional[float] = None,
1344
- max_value: Optional[float] = None,
1279
+ value: Union[float, NoInitialValue],
1345
1280
  step: Optional[float] = None,
1346
1281
  ):
1347
1282
  self.name = name
1348
1283
  self.step = step
1349
- self.min_value = (
1350
- self._validate(min_value, compare_to_range=False)
1351
- if min_value is not None
1352
- else None
1353
- )
1354
- self.max_value = (
1355
- self._validate(max_value, compare_to_range=False)
1356
- if max_value is not None
1357
- else None
1358
- )
1284
+ if isinstance(value, NoInitialValue):
1285
+ value = 0
1359
1286
  self._value = self._validate(value)
1360
1287
 
1361
- def _validate(self, new_value: Any, compare_to_range: bool = True) -> float:
1288
+ def _validate(self, new_value: Any) -> float:
1362
1289
  """
1363
- Validate and convert value to float, optionally checking bounds.
1364
-
1365
- Handles None min/max values by skipping those bound checks.
1290
+ Validate and convert value to float.
1366
1291
  Only rounds to step if step is not None.
1367
1292
 
1368
1293
  Args:
1369
1294
  new_value: Value to validate
1370
- compare_to_range: If True, clamps value to any defined min/max bounds
1371
1295
 
1372
1296
  Returns:
1373
1297
  Validated and potentially rounded float value
@@ -1384,51 +1308,18 @@ class UnboundedFloatParameter(Parameter[float]):
1384
1308
  if self.step is not None:
1385
1309
  new_value = round(new_value / self.step) * self.step
1386
1310
 
1387
- if compare_to_range:
1388
- if self.min_value is not None and new_value < self.min_value:
1389
- warn(
1390
- ParameterUpdateWarning(
1391
- self.name,
1392
- type(self).__name__,
1393
- f"Value {new_value} below minimum {self.min_value}, clamping",
1394
- )
1395
- )
1396
- new_value = self.min_value
1397
- if self.max_value is not None and new_value > self.max_value:
1398
- warn(
1399
- ParameterUpdateWarning(
1400
- self.name,
1401
- type(self).__name__,
1402
- f"Value {new_value} above maximum {self.max_value}, clamping",
1403
- )
1404
- )
1405
- new_value = self.max_value
1406
-
1407
1311
  return float(new_value)
1408
1312
 
1409
1313
  def _validate_update(self) -> None:
1410
1314
  """
1411
1315
  Validate complete parameter state after updates.
1412
1316
 
1413
- Ensures min_value <= max_value, swapping if needed.
1317
+ Ensures min <= max, swapping if needed.
1414
1318
  Re-validates current value against potentially updated bounds.
1415
1319
 
1416
1320
  Raises:
1417
1321
  ParameterUpdateError: If bounds are invalid (e.g. None when required)
1418
1322
  """
1419
- if (
1420
- self.min_value is not None
1421
- and self.max_value is not None
1422
- and self.min_value > self.max_value
1423
- ):
1424
- warn(
1425
- ParameterUpdateWarning(
1426
- self.name,
1427
- type(self).__name__,
1428
- f"Min value greater than max value, swapping",
1429
- )
1430
- )
1431
- self.min_value, self.max_value = self.max_value, self.min_value
1432
1323
  self.value = self._validate(self.value)
1433
1324
 
1434
1325
 
@@ -1438,15 +1329,15 @@ class ButtonAction(Parameter[None]):
1438
1329
  Parameter for creating clickable buttons with callbacks.
1439
1330
 
1440
1331
  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.
1332
+ See :meth:`~syd.viewer.Viewer.add_button` and
1333
+ :meth:`~syd.viewer.Viewer.update_button` for usage.
1443
1334
 
1444
1335
  Parameters
1445
1336
  ----------
1446
1337
  name : str
1447
1338
  The name of the parameter
1448
- label : str
1449
- Text to display on the button
1339
+ label : Union[str, NoInitialValue]
1340
+ Text to display on the button (default is the button's name)
1450
1341
  callback : callable
1451
1342
  Function to execute when the button is clicked
1452
1343
 
@@ -1478,16 +1369,27 @@ class ButtonAction(Parameter[None]):
1478
1369
  value: None = field(default=None, repr=False)
1479
1370
  _is_action: bool = field(default=True, repr=False)
1480
1371
 
1481
- def __init__(self, name: str, label: str, callback: Callable):
1372
+ def __init__(
1373
+ self,
1374
+ name: str,
1375
+ label: Union[str, NoInitialValue],
1376
+ callback: Callable,
1377
+ ):
1482
1378
  """
1483
1379
  Initialize a button.
1484
1380
 
1485
- Args:
1486
- name: Internal name of the parameter
1487
- label: Text to display on the button
1488
- callback: Function to call when button is clicked
1381
+ Parameters
1382
+ ----------
1383
+ name : str
1384
+ The name of the parameter
1385
+ label : Union[str, NoInitialValue]
1386
+ Text to display on the button (default is the button's name)
1387
+ callback : callable
1388
+ Function to execute when the button is clicked
1489
1389
  """
1490
1390
  self.name = name
1391
+ if isinstance(label, NoInitialValue):
1392
+ label = name
1491
1393
  self.label = label
1492
1394
  self.callback = callback
1493
1395
  self._value = None