syd 0.1.6__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.
@@ -23,6 +23,10 @@ class _NoUpdate:
23
23
  cls._instance = super().__new__(cls)
24
24
  return cls._instance
25
25
 
26
+ def __eq__(self, other):
27
+ """This makes sure all comparisons of _NoUpdate objects return True"""
28
+ return isinstance(other, _NoUpdate)
29
+
26
30
 
27
31
  # Create the singleton instance
28
32
  _NO_UPDATE = _NoUpdate()
@@ -32,7 +36,7 @@ def validate_parameter_operation(
32
36
  operation: str, parameter_type: Union[ParameterType, ActionType]
33
37
  ) -> Callable:
34
38
  """
35
- Decorator that validates parameter operations for the InteractiveViewer class.
39
+ Decorator that validates parameter operations for the viewer class.
36
40
 
37
41
  This decorator ensures that:
38
42
  1. The operation type matches the method name (add/update)
@@ -72,7 +76,7 @@ def validate_parameter_operation(
72
76
  )
73
77
 
74
78
  @wraps(func)
75
- def wrapper(self: "InteractiveViewer", name: Any, *args, **kwargs):
79
+ def wrapper(self: "Viewer", name: Any, *args, **kwargs):
76
80
  # Validate parameter name is a string
77
81
  if not isinstance(name, str):
78
82
  if operation == "add":
@@ -112,9 +116,9 @@ def validate_parameter_operation(
112
116
  return func(self, name, *args, **kwargs)
113
117
  except Exception as e:
114
118
  if operation == "add":
115
- raise ParameterAddError(name, parameter_type.name, str(e)) from e
119
+ raise ParameterAddError(name, parameter_type.name, str(e))
116
120
  elif operation == "update":
117
- raise ParameterUpdateError(name, parameter_type.name, str(e)) from e
121
+ raise ParameterUpdateError(name, parameter_type.name, str(e))
118
122
  else:
119
123
  raise e
120
124
 
@@ -123,7 +127,7 @@ def validate_parameter_operation(
123
127
  return decorator
124
128
 
125
129
 
126
- class InteractiveViewer:
130
+ class Viewer:
127
131
  """
128
132
  Base class for creating interactive matplotlib figures with GUI controls.
129
133
 
@@ -138,7 +142,7 @@ class InteractiveViewer:
138
142
 
139
143
  Examples
140
144
  --------
141
- >>> class MyViewer(InteractiveViewer):
145
+ >>> class MyViewer(Viewer):
142
146
  ... def plot(self, state: Dict[str, Any]):
143
147
  ... fig = plt.figure()
144
148
  ... plt.plot([0, state['x']])
@@ -154,7 +158,6 @@ class InteractiveViewer:
154
158
 
155
159
  parameters: Dict[str, Parameter]
156
160
  callbacks: Dict[str, List[Callable]]
157
- state: Dict[str, Any]
158
161
  _app_deployed: bool
159
162
  _in_callbacks: bool
160
163
 
@@ -162,15 +165,16 @@ class InteractiveViewer:
162
165
  instance = super().__new__(cls)
163
166
  instance.parameters = {}
164
167
  instance.callbacks = {}
165
- instance.state = {}
166
168
  instance._app_deployed = False
167
169
  instance._in_callbacks = False
168
170
  return instance
169
171
 
170
- def get_state(self) -> Dict[str, Any]:
172
+ @property
173
+ def state(self) -> Dict[str, Any]:
171
174
  """
172
175
  Get the current values of all parameters.
173
176
 
177
+
174
178
  Returns
175
179
  -------
176
180
  dict
@@ -180,10 +184,14 @@ class InteractiveViewer:
180
184
  --------
181
185
  >>> viewer.add_float('x', value=1.0, min_value=0, max_value=10)
182
186
  >>> viewer.add_text('label', value='data')
183
- >>> viewer.get_state()
187
+ >>> viewer.state
184
188
  {'x': 1.0, 'label': 'data'}
185
189
  """
186
- return {name: param.value for name, param in self.parameters.items()}
190
+ return {
191
+ name: param.value
192
+ for name, param in self.parameters.items()
193
+ if not param._is_action
194
+ }
187
195
 
188
196
  def plot(self, state: Dict[str, Any]) -> Figure:
189
197
  """Create and return a matplotlib figure.
@@ -192,14 +200,14 @@ class InteractiveViewer:
192
200
 
193
201
  1. Call set_plot() with your plotting function
194
202
  This will look like this:
195
- >>> def plot(viewer, state):
203
+ >>> def plot(state):
196
204
  >>> ... generate figure, plot stuff ...
197
205
  >>> return fig
198
206
  >>> viewer.set_plot(plot))
199
207
 
200
- 2. Subclass InteractiveViewer and override this method
208
+ 2. Subclass Viewer and override this method
201
209
  This will look like this:
202
- >>> class YourViewer(InteractiveViewer):
210
+ >>> class YourViewer(Viewer):
203
211
  >>> def plot(self, state):
204
212
  >>> ... generate figure, plot stuff ...
205
213
  >>> return fig
@@ -223,7 +231,7 @@ class InteractiveViewer:
223
231
  """
224
232
  raise NotImplementedError(
225
233
  "Plot method not implemented. Either subclass "
226
- "InteractiveViewer and override plot(), or use "
234
+ "Viewer and override plot(), or use "
227
235
  "set_plot() to provide a plotting function."
228
236
  )
229
237
 
@@ -234,15 +242,35 @@ class InteractiveViewer:
234
242
  def deploy(self, env: str = "notebook", **kwargs):
235
243
  """Deploy the app in a notebook or standalone environment"""
236
244
  if env == "notebook":
237
- from .notebook_deployment import NotebookDeployment
245
+ from .notebook_deployment import NotebookDeployer
238
246
 
239
- deployer = NotebookDeployment(self, **kwargs)
247
+ deployer = NotebookDeployer(self, **kwargs)
240
248
  deployer.deploy()
249
+ return self
250
+
251
+ elif env == "plotly":
252
+ from .plotly_deployment import PlotlyDeployer
253
+
254
+ deployer = PlotlyDeployer(self, **kwargs)
255
+ deployer.deploy(mode="server")
256
+ return self
257
+
258
+ elif env == "plotly-inline":
259
+ from .plotly_deployment import PlotlyDeployer
241
260
 
261
+ deployer = PlotlyDeployer(self, **kwargs)
262
+ deployer.deploy(mode="notebook")
263
+ return self
264
+
265
+ elif env == "flask":
266
+ from .flask_deployment import FlaskDeployer
267
+
268
+ deployer = FlaskDeployer(self, **kwargs)
269
+ deployer.deploy()
242
270
  return self
243
271
  else:
244
272
  raise ValueError(
245
- f"Unsupported environment: {env}, only 'notebook' is supported right now."
273
+ f"Unsupported environment: {env}, only 'notebook', 'plotly', 'plotly-inline', and 'flask' are supported right now."
246
274
  )
247
275
 
248
276
  @contextmanager
@@ -265,7 +293,6 @@ class InteractiveViewer:
265
293
 
266
294
  # Handle partial functions
267
295
  if isinstance(func, partial):
268
- # Create new partial with self as first arg if not already there
269
296
  get_self = (
270
297
  lambda func: hasattr(func.func, "__self__") and func.func.__self__
271
298
  )
@@ -274,17 +301,6 @@ class InteractiveViewer:
274
301
  get_self = lambda func: hasattr(func, "__self__") and func.__self__
275
302
  get_name = lambda func: func.__name__
276
303
 
277
- # Check if it's a class method
278
- class_method = get_self(func) is self.__class__
279
- if class_method:
280
- raise ValueError(context + "Class methods are not supported.")
281
-
282
- # Check if it's a bound method to another instance other than this one
283
- if get_self(func) and get_self(func) is not self:
284
- raise ValueError(
285
- context + "Bound methods to other instances are not supported."
286
- )
287
-
288
304
  # Get function signature
289
305
  try:
290
306
  params = list(inspect.signature(func).parameters.values())
@@ -294,17 +310,18 @@ class InteractiveViewer:
294
310
 
295
311
  # Look through params and check if there are two positional parameters (including self for bound methods)
296
312
  bound_method = get_self(func) is self
297
- positional_params = 0 + bound_method
313
+ positional_params = 0
314
+ required_kwargs = 0
298
315
  optional_part = ""
299
316
  for param in params:
300
317
  # Check if it's a positional parameter. If it is, count it.
301
- # As long as we have less than 2 positional parameters, we're good.
302
- # When we already have 2 positional parameters, we need to make sure any other positional parameters have defaults.
318
+ # We need at least 1 positional parameter. When we already have 1,
319
+ # we need to make sure any other positional parameters have defaults.
303
320
  if param.kind in (
304
321
  inspect.Parameter.POSITIONAL_OR_KEYWORD,
305
322
  inspect.Parameter.POSITIONAL_ONLY,
306
323
  ):
307
- if positional_params < 2:
324
+ if positional_params < 1:
308
325
  positional_params += 1
309
326
  else:
310
327
  if param.default == inspect.Parameter.empty:
@@ -317,10 +334,12 @@ class InteractiveViewer:
317
334
  optional_part += (
318
335
  f", {param.name}={param.default!r}"
319
336
  if param.default != inspect.Parameter.empty
320
- else f", {param.name}"
337
+ else f""
321
338
  )
339
+ if param.default == inspect.Parameter.empty:
340
+ required_kwargs += 1
322
341
 
323
- if positional_params != 2:
342
+ if positional_params != 1 or required_kwargs != 0:
324
343
  func_name = get_name(func)
325
344
  if isinstance(func, partial):
326
345
  func_sig = str(inspect.signature(func))
@@ -330,9 +349,8 @@ class InteractiveViewer:
330
349
  context
331
350
  + "\n"
332
351
  + f"Your partial function '{func_name}' has an incorrect signature.\n"
333
- "Partial functions must have exactly two positional parameters.\n"
334
- "The first parameter should be self -- it corresponds to the viewer.\n"
335
- "The second parameter should be state -- a dictionary of the current state of the viewer.\n"
352
+ "Partial functions must have exactly one positional parameter\n"
353
+ "which corresponds to a dictionary of the current state of the viewer.\n"
336
354
  "\nYour partial function effectivelylooks like this:\n"
337
355
  f"def {func_name}{func_sig}:\n"
338
356
  " ... your function code ..."
@@ -347,45 +365,44 @@ class InteractiveViewer:
347
365
  context + "\n"
348
366
  f"Your bound method '{func_name}{func_sig}' has an incorrect signature.\n"
349
367
  "Bound methods must have exactly one positional parameter in addition to self.\n"
350
- "The first parameter should be self -- it corresponds to the viewer.\n"
368
+ "The first parameter should be self (required for bound methods).\n"
351
369
  "The second parameter should be state -- a dictionary of the current state of the viewer.\n"
352
370
  "\nYour method looks like this:\n"
353
- "class YourViewer(InteractiveViewer):\n"
371
+ "class YourViewer(Viewer):\n"
354
372
  f" def {func_name}{func_sig}:\n"
355
373
  " ... your function code ...\n"
356
374
  "\nIt should look like this:\n"
357
- "class YourViewer(InteractiveViewer):\n"
375
+ "class YourViewer(Viewer):\n"
358
376
  f" def {func_name}(self, state{optional_part}):\n"
359
377
  " ... your function code ..."
360
378
  )
361
379
  raise ValueError(msg)
362
380
  else:
363
381
  func_sig = str(inspect.signature(func))
364
-
382
+ bound_elsewhere = get_self(func) and get_self(func) is not self
383
+ if bound_elsewhere:
384
+ func_name = f"self.{func_name}"
385
+ func_sig = f"(self, {func_sig[1:]})"
386
+ add_self = True
387
+ else:
388
+ add_self = False
365
389
  msg = (
366
390
  context + "\n"
367
391
  f"Your function '{func_name}{func_sig}' has an incorrect signature.\n"
368
- "External functions must have exactly two positional parameters.\n"
369
- "The first parameter should be viewer -- it corresponds to the viewer.\n"
370
- "The second parameter should be state -- a dictionary of the current state of the viewer.\n"
392
+ "Functions must have exactly one positional parameter\n"
393
+ "which corresponds to a dictionary of the current state of the viewer.\n"
371
394
  "\nYour function looks like this:\n"
372
395
  f"def {func_name}{func_sig}:\n"
373
396
  " ... your function code ...\n"
374
397
  "\nIt should look like this:\n"
375
- f"def {func_name}(viewer, state{optional_part}):\n"
398
+ f"def {func_name}({'self, ' if add_self else ''}state{optional_part}):\n"
376
399
  " ... your function code ..."
377
400
  )
378
401
  raise ValueError(msg)
379
402
 
380
- # If not a bound method, wrap it to inject self when called with just the state
381
- if bound_method:
382
- return func
383
-
384
- @wraps(func)
385
- def func_with_self(*args, **kwargs):
386
- return func(self, *args, **kwargs)
387
-
388
- return func_with_self
403
+ # If we've made it here, the function has exactly one required positional parameter
404
+ # which means it's callable by the viewer.
405
+ return func
389
406
 
390
407
  def perform_callbacks(self, name: str) -> bool:
391
408
  """Perform callbacks for all parameters that have changed"""
@@ -394,7 +411,7 @@ class InteractiveViewer:
394
411
  try:
395
412
  self._in_callbacks = True
396
413
  if name in self.callbacks:
397
- state = self.get_state()
414
+ state = self.state
398
415
  for callback in self.callbacks[name]:
399
416
  callback(state)
400
417
  finally:
@@ -484,7 +501,7 @@ class InteractiveViewer:
484
501
  Examples
485
502
  --------
486
503
  >>> viewer.add_text('title', value='My Plot')
487
- >>> viewer.get_state()['title']
504
+ >>> viewer.state['title']
488
505
  'My Plot'
489
506
  """
490
507
  try:
@@ -512,7 +529,7 @@ class InteractiveViewer:
512
529
  Examples
513
530
  --------
514
531
  >>> viewer.add_boolean('show_grid', value=True)
515
- >>> viewer.get_state()['show_grid']
532
+ >>> viewer.state['show_grid']
516
533
  True
517
534
  """
518
535
  try:
@@ -543,7 +560,7 @@ class InteractiveViewer:
543
560
  --------
544
561
  >>> viewer.add_selection('color', value='red',
545
562
  ... options=['red', 'green', 'blue'])
546
- >>> viewer.get_state()['color']
563
+ >>> viewer.state['color']
547
564
  'red'
548
565
  """
549
566
  try:
@@ -578,7 +595,7 @@ class InteractiveViewer:
578
595
  >>> viewer.add_multiple_selection('toppings',
579
596
  ... value=['cheese'],
580
597
  ... options=['cheese', 'pepperoni', 'mushrooms'])
581
- >>> viewer.get_state()['toppings']
598
+ >>> viewer.state['toppings']
582
599
  ['cheese']
583
600
  """
584
601
  try:
@@ -618,10 +635,10 @@ class InteractiveViewer:
618
635
  Examples
619
636
  --------
620
637
  >>> viewer.add_integer('count', value=5, min_value=0, max_value=10)
621
- >>> viewer.get_state()['count']
638
+ >>> viewer.state['count']
622
639
  5
623
640
  >>> viewer.update_integer('count', value=15) # Will be clamped to 10
624
- >>> viewer.get_state()['count']
641
+ >>> viewer.state['count']
625
642
  10
626
643
  """
627
644
  try:
@@ -632,7 +649,7 @@ class InteractiveViewer:
632
649
  max_value,
633
650
  )
634
651
  except Exception as e:
635
- raise ParameterAddError(name, "number", str(e)) from e
652
+ raise ParameterAddError(name, "number", str(e))
636
653
  else:
637
654
  self.parameters[name] = new_param
638
655
 
@@ -644,11 +661,12 @@ class InteractiveViewer:
644
661
  value: Union[float, int],
645
662
  min_value: Union[float, int],
646
663
  max_value: Union[float, int],
647
- step: float = 0.1,
664
+ step: float = 0.01,
648
665
  ) -> None:
649
666
  """
650
667
  Add a decimal number parameter to the viewer.
651
668
 
669
+
652
670
  Creates a slider in the GUI that lets users select numbers between
653
671
  min_value and max_value. Values will be rounded to the nearest step
654
672
  and clamped to stay within bounds.
@@ -664,16 +682,16 @@ class InteractiveViewer:
664
682
  max_value : float
665
683
  Maximum allowed value
666
684
  step : float, optional
667
- Size of each increment (default: 0.1)
685
+ Size of each increment (default: 0.01)
668
686
 
669
687
  Examples
670
688
  --------
671
689
  >>> viewer.add_float('temperature', value=20.0,
672
690
  ... min_value=0.0, max_value=100.0, step=0.5)
673
- >>> viewer.get_state()['temperature']
691
+ >>> viewer.state['temperature']
674
692
  20.0
675
693
  >>> viewer.update_float('temperature', value=20.7) # Will round to 20.5
676
- >>> viewer.get_state()['temperature']
694
+ >>> viewer.state['temperature']
677
695
  20.5
678
696
  """
679
697
  try:
@@ -722,11 +740,11 @@ class InteractiveViewer:
722
740
  >>> viewer.add_integer_range('age_range',
723
741
  ... value=(25, 35),
724
742
  ... min_value=18, max_value=100)
725
- >>> viewer.get_state()['age_range']
743
+ >>> viewer.state['age_range']
726
744
  (25, 35)
727
745
  >>> # Values will be swapped if low > high
728
746
  >>> viewer.update_integer_range('age_range', value=(40, 30))
729
- >>> viewer.get_state()['age_range']
747
+ >>> viewer.state['age_range']
730
748
  (30, 40)
731
749
  """
732
750
  try:
@@ -749,7 +767,7 @@ class InteractiveViewer:
749
767
  value: Tuple[Union[float, int], Union[float, int]],
750
768
  min_value: Union[float, int],
751
769
  max_value: Union[float, int],
752
- step: float = 0.1,
770
+ step: float = 0.01,
753
771
  ) -> None:
754
772
  """
755
773
  Add a range parameter for decimal numbers to the viewer.
@@ -770,18 +788,18 @@ class InteractiveViewer:
770
788
  max_value : float
771
789
  Maximum allowed value for both low and high
772
790
  step : float, optional
773
- Size of each increment (default: 0.1)
791
+ Size of each increment (default: 0.01)
774
792
 
775
793
  Examples
776
794
  --------
777
795
  >>> viewer.add_float_range('price_range',
778
796
  ... value=(10.0, 20.0),
779
797
  ... min_value=0.0, max_value=100.0, step=0.5)
780
- >>> viewer.get_state()['price_range']
798
+ >>> viewer.state['price_range']
781
799
  (10.0, 20.0)
782
800
  >>> # Values will be rounded to nearest step
783
801
  >>> viewer.update_float_range('price_range', value=(10.7, 19.2))
784
- >>> viewer.get_state()['price_range']
802
+ >>> viewer.state['price_range']
785
803
  (10.5, 19.0)
786
804
  """
787
805
  try:
@@ -803,15 +821,12 @@ class InteractiveViewer:
803
821
  name: str,
804
822
  *,
805
823
  value: Union[float, int],
806
- min_value: Optional[Union[float, int]] = None,
807
- max_value: Optional[Union[float, int]] = None,
808
824
  ) -> None:
809
825
  """
810
826
  Add an unbounded integer parameter to the viewer.
811
827
 
812
828
  Creates a text input box in the GUI for entering whole numbers. Unlike
813
- add_integer(), this allows very large numbers and optionally no minimum
814
- or maximum bounds.
829
+ add_integer(), this allows very large numbers without bounds.
815
830
  See :class:`~syd.parameters.UnboundedIntegerParameter` for details.
816
831
 
817
832
  Parameters
@@ -820,29 +835,17 @@ class InteractiveViewer:
820
835
  Name of the parameter (used as label in GUI)
821
836
  value : int
822
837
  Initial value
823
- min_value : int, optional
824
- Minimum allowed value (or None for no minimum)
825
- max_value : int, optional
826
- Maximum allowed value (or None for no maximum)
827
838
 
828
839
  Examples
829
840
  --------
830
- >>> viewer.add_unbounded_integer('population',
831
- ... value=1000000,
832
- ... min_value=0) # No maximum
833
- >>> viewer.get_state()['population']
841
+ >>> viewer.add_unbounded_integer('population', value=1000000)
842
+ >>> viewer.state['population']
834
843
  1000000
835
- >>> # Values below minimum will be clamped
836
- >>> viewer.update_unbounded_integer('population', value=-5)
837
- >>> viewer.get_state()['population']
838
- 0
839
844
  """
840
845
  try:
841
846
  new_param = ParameterType.unbounded_integer.value(
842
847
  name,
843
848
  value,
844
- min_value,
845
- max_value,
846
849
  )
847
850
  except Exception as e:
848
851
  raise ParameterAddError(name, "unbounded_integer", str(e)) from e
@@ -855,16 +858,14 @@ class InteractiveViewer:
855
858
  name: str,
856
859
  *,
857
860
  value: Union[float, int],
858
- min_value: Optional[Union[float, int]] = None,
859
- max_value: Optional[Union[float, int]] = None,
860
861
  step: Optional[float] = None,
861
862
  ) -> None:
862
863
  """
863
864
  Add an unbounded decimal number parameter to the viewer.
864
865
 
865
866
  Creates a text input box in the GUI for entering numbers. Unlike add_float(),
866
- this allows very large or precise numbers and optionally no minimum or
867
- maximum bounds. Values can optionally be rounded to a step size.
867
+ this allows very large or precise numbers without bounds. Values can optionally
868
+ be rounded to a step size.
868
869
  See :class:`~syd.parameters.UnboundedFloatParameter` for details.
869
870
 
870
871
  Parameters
@@ -873,32 +874,23 @@ class InteractiveViewer:
873
874
  Name of the parameter (used as label in GUI)
874
875
  value : float
875
876
  Initial value
876
- min_value : float, optional
877
- Minimum allowed value (or None for no minimum)
878
- max_value : float, optional
879
- Maximum allowed value (or None for no maximum)
880
877
  step : float, optional
881
878
  Size of each increment (or None for no rounding)
882
879
 
883
880
  Examples
884
881
  --------
885
- >>> viewer.add_unbounded_float('wavelength',
886
- ... value=550e-9, # Nanometers
887
- ... min_value=0.0,
888
- ... step=1e-9) # Round to nearest nanometer
889
- >>> viewer.get_state()['wavelength']
882
+ >>> viewer.add_unbounded_float('wavelength', value=550e-9, step=1e-9)
883
+ >>> viewer.state['wavelength']
890
884
  5.5e-07
891
885
  >>> # Values will be rounded if step is provided
892
886
  >>> viewer.update_unbounded_float('wavelength', value=550.7e-9)
893
- >>> viewer.get_state()['wavelength']
887
+ >>> viewer.state['wavelength']
894
888
  5.51e-07
895
889
  """
896
890
  try:
897
891
  new_param = ParameterType.unbounded_float.value(
898
892
  name,
899
893
  value,
900
- min_value,
901
- max_value,
902
894
  step,
903
895
  )
904
896
  except Exception as e:
@@ -928,16 +920,19 @@ class InteractiveViewer:
928
920
  label : str
929
921
  Text to display on the button
930
922
  callback : callable
931
- Function to call when the button is clicked (takes no arguments)
923
+ Function to call when the button is clicked (takes state as a single argument)
932
924
 
933
925
  Examples
934
926
  --------
935
- >>> def reset_plot():
927
+ >>> def reset_plot(state):
936
928
  ... print("Resetting plot...")
937
929
  >>> viewer.add_button('reset', label='Reset Plot', callback=reset_plot)
930
+
931
+ >>> def print_plot_info(state):
932
+ ... print(f"Current plot info: {state['plot_info']}")
933
+ >>> viewer.add_button('print_info', label='Print Plot Info', callback=print_plot_info)
938
934
  """
939
935
  try:
940
-
941
936
  callback = self._prepare_function(
942
937
  callback,
943
938
  context="Setting button callback:",
@@ -957,7 +952,7 @@ class InteractiveViewer:
957
952
  """
958
953
  Update a text parameter's value.
959
954
 
960
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_text`.
955
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_text`.
961
956
  See :class:`~syd.parameters.TextParameter` for details about value validation.
962
957
 
963
958
  Parameters
@@ -971,11 +966,11 @@ class InteractiveViewer:
971
966
  --------
972
967
  >>> viewer.add_text('title', value='Original Title')
973
968
  >>> viewer.update_text('title', value='New Title')
974
- >>> viewer.get_state()['title']
969
+ >>> viewer.state['title']
975
970
  'New Title'
976
971
  """
977
972
  updates = {}
978
- if value is not _NO_UPDATE:
973
+ if not value == _NO_UPDATE:
979
974
  updates["value"] = value
980
975
  if updates:
981
976
  self.parameters[name].update(updates)
@@ -987,7 +982,7 @@ class InteractiveViewer:
987
982
  """
988
983
  Update a boolean parameter's value.
989
984
 
990
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_boolean`.
985
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_boolean`.
991
986
  See :class:`~syd.parameters.BooleanParameter` for details about value validation.
992
987
 
993
988
  Parameters
@@ -1001,11 +996,11 @@ class InteractiveViewer:
1001
996
  --------
1002
997
  >>> viewer.add_boolean('show_grid', value=True)
1003
998
  >>> viewer.update_boolean('show_grid', value=False)
1004
- >>> viewer.get_state()['show_grid']
999
+ >>> viewer.state['show_grid']
1005
1000
  False
1006
1001
  """
1007
1002
  updates = {}
1008
- if value is not _NO_UPDATE:
1003
+ if not value == _NO_UPDATE:
1009
1004
  updates["value"] = value
1010
1005
  if updates:
1011
1006
  self.parameters[name].update(updates)
@@ -1021,7 +1016,7 @@ class InteractiveViewer:
1021
1016
  """
1022
1017
  Update a selection parameter's value and/or options.
1023
1018
 
1024
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_selection`.
1019
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_selection`.
1025
1020
  See :class:`~syd.parameters.SelectionParameter` for details about value validation.
1026
1021
 
1027
1022
  Parameters
@@ -1045,9 +1040,9 @@ class InteractiveViewer:
1045
1040
  ... value='purple')
1046
1041
  """
1047
1042
  updates = {}
1048
- if value is not _NO_UPDATE:
1043
+ if not value == _NO_UPDATE:
1049
1044
  updates["value"] = value
1050
- if options is not _NO_UPDATE:
1045
+ if not options == _NO_UPDATE:
1051
1046
  updates["options"] = options
1052
1047
  if updates:
1053
1048
  self.parameters[name].update(updates)
@@ -1063,7 +1058,7 @@ class InteractiveViewer:
1063
1058
  """
1064
1059
  Update a multiple selection parameter's values and/or options.
1065
1060
 
1066
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_multiple_selection`.
1061
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_multiple_selection`.
1067
1062
  See :class:`~syd.parameters.MultipleSelectionParameter` for details about value validation.
1068
1063
 
1069
1064
  Parameters
@@ -1089,9 +1084,9 @@ class InteractiveViewer:
1089
1084
  ... value=['cheese', 'bacon'])
1090
1085
  """
1091
1086
  updates = {}
1092
- if value is not _NO_UPDATE:
1087
+ if not value == _NO_UPDATE:
1093
1088
  updates["value"] = value
1094
- if options is not _NO_UPDATE:
1089
+ if not options == _NO_UPDATE:
1095
1090
  updates["options"] = options
1096
1091
  if updates:
1097
1092
  self.parameters[name].update(updates)
@@ -1108,7 +1103,7 @@ class InteractiveViewer:
1108
1103
  """
1109
1104
  Update an integer parameter's value and/or bounds.
1110
1105
 
1111
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_integer`.
1106
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_integer`.
1112
1107
  See :class:`~syd.parameters.IntegerParameter` for details about value validation.
1113
1108
 
1114
1109
  Parameters
@@ -1131,11 +1126,11 @@ class InteractiveViewer:
1131
1126
  >>> viewer.update_integer('count', min_value=7, max_value=15)
1132
1127
  """
1133
1128
  updates = {}
1134
- if value is not _NO_UPDATE:
1129
+ if not value == _NO_UPDATE:
1135
1130
  updates["value"] = value
1136
- if min_value is not _NO_UPDATE:
1131
+ if not min_value == _NO_UPDATE:
1137
1132
  updates["min_value"] = min_value
1138
- if max_value is not _NO_UPDATE:
1133
+ if not max_value == _NO_UPDATE:
1139
1134
  updates["max_value"] = max_value
1140
1135
  if updates:
1141
1136
  self.parameters[name].update(updates)
@@ -1153,7 +1148,7 @@ class InteractiveViewer:
1153
1148
  """
1154
1149
  Update a float parameter's value, bounds, and/or step size.
1155
1150
 
1156
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_float`.
1151
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_float`.
1157
1152
  See :class:`~syd.parameters.FloatParameter` for details about value validation.
1158
1153
 
1159
1154
  Parameters
@@ -1180,13 +1175,13 @@ class InteractiveViewer:
1180
1175
  ... min_value=15.0, max_value=30.0, step=0.1)
1181
1176
  """
1182
1177
  updates = {}
1183
- if value is not _NO_UPDATE:
1178
+ if not value == _NO_UPDATE:
1184
1179
  updates["value"] = value
1185
- if min_value is not _NO_UPDATE:
1180
+ if not min_value == _NO_UPDATE:
1186
1181
  updates["min_value"] = min_value
1187
- if max_value is not _NO_UPDATE:
1182
+ if not max_value == _NO_UPDATE:
1188
1183
  updates["max_value"] = max_value
1189
- if step is not _NO_UPDATE:
1184
+ if not step == _NO_UPDATE:
1190
1185
  updates["step"] = step
1191
1186
  if updates:
1192
1187
  self.parameters[name].update(updates)
@@ -1203,7 +1198,7 @@ class InteractiveViewer:
1203
1198
  """
1204
1199
  Update an integer range parameter's values and/or bounds.
1205
1200
 
1206
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_integer_range`.
1201
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_integer_range`.
1207
1202
  See :class:`~syd.parameters.IntegerRangeParameter` for details about value validation.
1208
1203
 
1209
1204
  Parameters
@@ -1228,11 +1223,11 @@ class InteractiveViewer:
1228
1223
  >>> viewer.update_integer_range('age_range', min_value=20, max_value=80)
1229
1224
  """
1230
1225
  updates = {}
1231
- if value is not _NO_UPDATE:
1226
+ if not value == _NO_UPDATE:
1232
1227
  updates["value"] = value
1233
- if min_value is not _NO_UPDATE:
1228
+ if not min_value == _NO_UPDATE:
1234
1229
  updates["min_value"] = min_value
1235
- if max_value is not _NO_UPDATE:
1230
+ if not max_value == _NO_UPDATE:
1236
1231
  updates["max_value"] = max_value
1237
1232
  if updates:
1238
1233
  self.parameters[name].update(updates)
@@ -1250,7 +1245,7 @@ class InteractiveViewer:
1250
1245
  """
1251
1246
  Update a float range parameter's values, bounds, and/or step size.
1252
1247
 
1253
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_float_range`.
1248
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_float_range`.
1254
1249
  See :class:`~syd.parameters.FloatRangeParameter` for details about value validation.
1255
1250
 
1256
1251
  Parameters
@@ -1278,13 +1273,13 @@ class InteractiveViewer:
1278
1273
  ... min_value=5.0, max_value=50.0, step=0.1)
1279
1274
  """
1280
1275
  updates = {}
1281
- if value is not _NO_UPDATE:
1276
+ if not value == _NO_UPDATE:
1282
1277
  updates["value"] = value
1283
- if min_value is not _NO_UPDATE:
1278
+ if not min_value == _NO_UPDATE:
1284
1279
  updates["min_value"] = min_value
1285
- if max_value is not _NO_UPDATE:
1280
+ if not max_value == _NO_UPDATE:
1286
1281
  updates["max_value"] = max_value
1287
- if step is not _NO_UPDATE:
1282
+ if not step == _NO_UPDATE:
1288
1283
  updates["step"] = step
1289
1284
  if updates:
1290
1285
  self.parameters[name].update(updates)
@@ -1295,13 +1290,11 @@ class InteractiveViewer:
1295
1290
  name: str,
1296
1291
  *,
1297
1292
  value: Union[int, _NoUpdate] = _NO_UPDATE,
1298
- min_value: Union[Optional[int], _NoUpdate] = _NO_UPDATE,
1299
- max_value: Union[Optional[int], _NoUpdate] = _NO_UPDATE,
1300
1293
  ) -> None:
1301
1294
  """
1302
1295
  Update an unbounded integer parameter's value and/or bounds.
1303
1296
 
1304
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_unbounded_integer`.
1297
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_unbounded_integer`.
1305
1298
  See :class:`~syd.parameters.UnboundedIntegerParameter` for details about value validation.
1306
1299
 
1307
1300
  Parameters
@@ -1309,31 +1302,17 @@ class InteractiveViewer:
1309
1302
  name : str
1310
1303
  Name of the unbounded integer parameter to update
1311
1304
  value : int, optional
1312
- New value (will be clamped to any bounds) (if not provided, no change)
1313
- min_value : int or None, optional
1314
- New minimum value, or None for no minimum (if not provided, no change)
1315
- max_value : int or None, optional
1316
- New maximum value, or None for no maximum (if not provided, no change)
1305
+ New value (if not provided, no change)
1317
1306
 
1318
1307
  Examples
1319
1308
  --------
1320
- >>> viewer.add_unbounded_integer('population',
1321
- ... value=1000000,
1322
- ... min_value=0) # No maximum
1309
+ >>> viewer.add_unbounded_integer('population', value=1000000)
1323
1310
  >>> # Update just the value
1324
1311
  >>> viewer.update_unbounded_integer('population', value=2000000)
1325
- >>> # Add a maximum bound (current value will be clamped if needed)
1326
- >>> viewer.update_unbounded_integer('population', max_value=1500000)
1327
- >>> # Remove the minimum bound
1328
- >>> viewer.update_unbounded_integer('population', min_value=None)
1329
1312
  """
1330
1313
  updates = {}
1331
- if value is not _NO_UPDATE:
1314
+ if not value == _NO_UPDATE:
1332
1315
  updates["value"] = value
1333
- if min_value is not _NO_UPDATE:
1334
- updates["min_value"] = min_value
1335
- if max_value is not _NO_UPDATE:
1336
- updates["max_value"] = max_value
1337
1316
  if updates:
1338
1317
  self.parameters[name].update(updates)
1339
1318
 
@@ -1343,14 +1322,12 @@ class InteractiveViewer:
1343
1322
  name: str,
1344
1323
  *,
1345
1324
  value: Union[float, _NoUpdate] = _NO_UPDATE,
1346
- min_value: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
1347
- max_value: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
1348
1325
  step: Union[Optional[float], _NoUpdate] = _NO_UPDATE,
1349
1326
  ) -> None:
1350
1327
  """
1351
1328
  Update an unbounded float parameter's value, bounds, and/or step size.
1352
1329
 
1353
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_unbounded_float`.
1330
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_unbounded_float`.
1354
1331
  See :class:`~syd.parameters.UnboundedFloatParameter` for details about value validation.
1355
1332
 
1356
1333
  Parameters
@@ -1359,36 +1336,23 @@ class InteractiveViewer:
1359
1336
  Name of the unbounded float parameter to update
1360
1337
  value : float, optional
1361
1338
  New value (will be rounded if step is set) (if not provided, no change)
1362
- min_value : float or None, optional
1363
- New minimum value, or None for no minimum (if not provided, no change)
1364
- max_value : float or None, optional
1365
- New maximum value, or None for no maximum (if not provided, no change)
1366
1339
  step : float or None, optional
1367
1340
  New step size for rounding, or None for no rounding (if not provided, no change)
1368
1341
 
1369
1342
  Examples
1370
1343
  --------
1371
- >>> viewer.add_unbounded_float('wavelength',
1372
- ... value=550e-9, # Nanometers
1373
- ... min_value=0.0,
1374
- ... step=1e-9)
1344
+ >>> viewer.add_unbounded_float('wavelength', value=550e-9, step=1e-9)
1375
1345
  >>> # Update value (will be rounded if step is set)
1376
- >>> viewer.update_unbounded_float('wavelength', value=632.8e-9) # HeNe laser
1377
- >>> # Change step size and add maximum
1378
- >>> viewer.update_unbounded_float('wavelength',
1379
- ... step=0.1e-9, # Finer control
1380
- ... max_value=1000e-9) # Infrared limit
1346
+ >>> viewer.update_unbounded_float('wavelength', value=632.8e-9)
1347
+ >>> # Change step size
1348
+ >>> viewer.update_unbounded_float('wavelength', step=0.1e-9)
1381
1349
  >>> # Remove step size (allow any precision)
1382
1350
  >>> viewer.update_unbounded_float('wavelength', step=None)
1383
1351
  """
1384
1352
  updates = {}
1385
- if value is not _NO_UPDATE:
1353
+ if not value == _NO_UPDATE:
1386
1354
  updates["value"] = value
1387
- if min_value is not _NO_UPDATE:
1388
- updates["min_value"] = min_value
1389
- if max_value is not _NO_UPDATE:
1390
- updates["max_value"] = max_value
1391
- if step is not _NO_UPDATE:
1355
+ if not step == _NO_UPDATE:
1392
1356
  updates["step"] = step
1393
1357
  if updates:
1394
1358
  self.parameters[name].update(updates)
@@ -1404,7 +1368,7 @@ class InteractiveViewer:
1404
1368
  """
1405
1369
  Update a button parameter's label and/or callback function.
1406
1370
 
1407
- Updates a parameter created by :meth:`~syd.interactive_viewer.InteractiveViewer.add_button`.
1371
+ Updates a parameter created by :meth:`~syd.viewer.Viewer.add_button`.
1408
1372
  See :class:`~syd.parameters.ButtonAction` for details.
1409
1373
 
1410
1374
  Parameters
@@ -1418,16 +1382,16 @@ class InteractiveViewer:
1418
1382
 
1419
1383
  Examples
1420
1384
  --------
1421
- >>> def new_callback():
1385
+ >>> def new_callback(state):
1422
1386
  ... print("New action...")
1423
1387
  >>> viewer.update_button('reset',
1424
- ... label='Clear Plot',
1388
+ ... label='New Action!',
1425
1389
  ... callback=new_callback)
1426
1390
  """
1427
1391
  updates = {}
1428
- if label is not _NO_UPDATE:
1392
+ if not label == _NO_UPDATE:
1429
1393
  updates["label"] = label
1430
- if callback is not _NO_UPDATE:
1394
+ if not callback == _NO_UPDATE:
1431
1395
  callback = self._prepare_function(
1432
1396
  callback,
1433
1397
  context="Updating button callback:",