syd 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,14 +14,14 @@ from ..parameters import (
14
14
  FloatRangeParameter,
15
15
  UnboundedIntegerParameter,
16
16
  UnboundedFloatParameter,
17
- ButtonParameter,
17
+ ButtonAction,
18
18
  )
19
19
 
20
20
  T = TypeVar("T", bound=Parameter[Any])
21
21
  W = TypeVar("W", bound=widgets.Widget)
22
22
 
23
23
 
24
- class BaseParameterWidget(Generic[T, W], ABC):
24
+ class BaseWidget(Generic[T, W], ABC):
25
25
  """
26
26
  Abstract base class for all parameter widgets.
27
27
 
@@ -31,15 +31,32 @@ class BaseParameterWidget(Generic[T, W], ABC):
31
31
 
32
32
  _widget: W
33
33
  _callbacks: List[Dict[str, Union[Callable, Union[str, List[str]]]]]
34
-
35
- def __init__(self, parameter: T, continuous_update: bool = False):
36
- self._widget = self._create_widget(parameter, continuous_update)
34
+ _is_action: bool = False
35
+
36
+ def __init__(
37
+ self,
38
+ parameter: T,
39
+ continuous: bool = False,
40
+ width: str = "auto",
41
+ margin: str = "3px 0px",
42
+ description_width: str = "initial",
43
+ ):
44
+ self._widget = self._create_widget(
45
+ parameter, continuous, width, margin, description_width
46
+ )
37
47
  self._updating = False # Flag to prevent circular updates
38
48
  # List of callbacks to remember for quick disabling/enabling
39
49
  self._callbacks = []
40
50
 
41
51
  @abstractmethod
42
- def _create_widget(self, parameter: T, continuous_update: bool) -> W:
52
+ def _create_widget(
53
+ self,
54
+ parameter: T,
55
+ continuous: bool,
56
+ width: str = "auto",
57
+ margin: str = "3px 0px",
58
+ description_width: str = "initial",
59
+ ) -> W:
43
60
  """Create and return the appropriate ipywidget."""
44
61
  pass
45
62
 
@@ -100,42 +117,62 @@ class BaseParameterWidget(Generic[T, W], ABC):
100
117
  self._widget.unobserve(**callback)
101
118
 
102
119
 
103
- class TextParameterWidget(BaseParameterWidget[TextParameter, widgets.Text]):
120
+ class TextWidget(BaseWidget[TextParameter, widgets.Text]):
104
121
  """Widget for text parameters."""
105
122
 
106
123
  def _create_widget(
107
- self, parameter: TextParameter, continuous_update: bool
124
+ self,
125
+ parameter: TextParameter,
126
+ continuous: bool,
127
+ width: str = "auto",
128
+ margin: str = "3px 0px",
129
+ description_width: str = "initial",
108
130
  ) -> widgets.Text:
109
131
  return widgets.Text(
110
132
  value=parameter.value,
111
133
  description=parameter.name,
112
- continuous_update=continuous_update,
113
- layout=widgets.Layout(width="95%"),
134
+ continuous=continuous,
135
+ layout=widgets.Layout(width=width, margin=margin),
136
+ style={"description_width": description_width},
114
137
  )
115
138
 
116
139
 
117
- class BooleanParameterWidget(BaseParameterWidget[BooleanParameter, widgets.Checkbox]):
140
+ class BooleanWidget(BaseWidget[BooleanParameter, widgets.ToggleButton]):
118
141
  """Widget for boolean parameters."""
119
142
 
120
143
  def _create_widget(
121
- self, parameter: BooleanParameter, continuous_update: bool
122
- ) -> widgets.Checkbox:
123
- return widgets.Checkbox(value=parameter.value, description=parameter.name)
144
+ self,
145
+ parameter: BooleanParameter,
146
+ continuous: bool,
147
+ width: str = "auto",
148
+ margin: str = "3px 0px",
149
+ description_width: str = "initial",
150
+ ) -> widgets.ToggleButton:
151
+ return widgets.ToggleButton(
152
+ value=parameter.value,
153
+ description=parameter.name,
154
+ layout=widgets.Layout(width=width, margin=margin),
155
+ style={"description_width": description_width},
156
+ )
124
157
 
125
158
 
126
- class SelectionParameterWidget(
127
- BaseParameterWidget[SelectionParameter, widgets.Dropdown]
128
- ):
159
+ class SelectionWidget(BaseWidget[SelectionParameter, widgets.Dropdown]):
129
160
  """Widget for single selection parameters."""
130
161
 
131
162
  def _create_widget(
132
- self, parameter: SelectionParameter, continuous_update: bool
163
+ self,
164
+ parameter: SelectionParameter,
165
+ continuous: bool,
166
+ width: str = "auto",
167
+ margin: str = "3px 0px",
168
+ description_width: str = "initial",
133
169
  ) -> widgets.Dropdown:
134
170
  return widgets.Dropdown(
135
171
  value=parameter.value,
136
172
  options=parameter.options,
137
173
  description=parameter.name,
138
- layout=widgets.Layout(width="95%"),
174
+ layout=widgets.Layout(width=width, margin=margin),
175
+ style={"description_width": description_width},
139
176
  )
140
177
 
141
178
  def matches_parameter(self, parameter: SelectionParameter) -> bool:
@@ -153,20 +190,26 @@ class SelectionParameterWidget(
153
190
  self._widget.value = new_value
154
191
 
155
192
 
156
- class MultipleSelectionParameterWidget(
157
- BaseParameterWidget[MultipleSelectionParameter, widgets.SelectMultiple]
193
+ class MultipleSelectionWidget(
194
+ BaseWidget[MultipleSelectionParameter, widgets.SelectMultiple]
158
195
  ):
159
196
  """Widget for multiple selection parameters."""
160
197
 
161
198
  def _create_widget(
162
- self, parameter: MultipleSelectionParameter, continuous_update: bool
199
+ self,
200
+ parameter: MultipleSelectionParameter,
201
+ continuous: bool,
202
+ width: str = "auto",
203
+ margin: str = "3px 0px",
204
+ description_width: str = "initial",
163
205
  ) -> widgets.SelectMultiple:
164
206
  return widgets.SelectMultiple(
165
207
  value=parameter.value,
166
208
  options=parameter.options,
167
209
  description=parameter.name,
168
210
  rows=min(len(parameter.options), 4),
169
- layout=widgets.Layout(width="95%"),
211
+ layout=widgets.Layout(width=width, margin=margin),
212
+ style={"description_width": description_width},
170
213
  )
171
214
 
172
215
  def matches_parameter(self, parameter: MultipleSelectionParameter) -> bool:
@@ -186,20 +229,25 @@ class MultipleSelectionParameterWidget(
186
229
  self._widget.value = new_values
187
230
 
188
231
 
189
- class IntegerParameterWidget(BaseParameterWidget[IntegerParameter, widgets.IntSlider]):
232
+ class IntegerWidget(BaseWidget[IntegerParameter, widgets.IntSlider]):
190
233
  """Widget for integer parameters."""
191
234
 
192
235
  def _create_widget(
193
- self, parameter: IntegerParameter, continuous_update: bool
236
+ self,
237
+ parameter: IntegerParameter,
238
+ continuous: bool,
239
+ width: str = "auto",
240
+ margin: str = "3px 0px",
241
+ description_width: str = "initial",
194
242
  ) -> widgets.IntSlider:
195
243
  return widgets.IntSlider(
196
244
  value=parameter.value,
197
245
  min=parameter.min_value,
198
246
  max=parameter.max_value,
199
247
  description=parameter.name,
200
- continuous_update=continuous_update,
201
- layout=widgets.Layout(width="95%"),
202
- style={"description_width": "initial"},
248
+ continuous=continuous,
249
+ layout=widgets.Layout(width=width, margin=margin),
250
+ style={"description_width": description_width},
203
251
  )
204
252
 
205
253
  def matches_parameter(self, parameter: IntegerParameter) -> bool:
@@ -218,11 +266,16 @@ class IntegerParameterWidget(BaseParameterWidget[IntegerParameter, widgets.IntSl
218
266
  self.value = max(parameter.min_value, min(parameter.max_value, current_value))
219
267
 
220
268
 
221
- class FloatParameterWidget(BaseParameterWidget[FloatParameter, widgets.FloatSlider]):
269
+ class FloatWidget(BaseWidget[FloatParameter, widgets.FloatSlider]):
222
270
  """Widget for float parameters."""
223
271
 
224
272
  def _create_widget(
225
- self, parameter: FloatParameter, continuous_update: bool
273
+ self,
274
+ parameter: FloatParameter,
275
+ continuous: bool,
276
+ width: str = "auto",
277
+ margin: str = "3px 0px",
278
+ description_width: str = "initial",
226
279
  ) -> widgets.FloatSlider:
227
280
  return widgets.FloatSlider(
228
281
  value=parameter.value,
@@ -230,9 +283,9 @@ class FloatParameterWidget(BaseParameterWidget[FloatParameter, widgets.FloatSlid
230
283
  max=parameter.max_value,
231
284
  step=parameter.step,
232
285
  description=parameter.name,
233
- continuous_update=continuous_update,
234
- layout=widgets.Layout(width="95%"),
235
- style={"description_width": "initial"},
286
+ continuous=continuous,
287
+ layout=widgets.Layout(width=width, margin=margin),
288
+ style={"description_width": description_width},
236
289
  )
237
290
 
238
291
  def matches_parameter(self, parameter: FloatParameter) -> bool:
@@ -252,22 +305,25 @@ class FloatParameterWidget(BaseParameterWidget[FloatParameter, widgets.FloatSlid
252
305
  self.value = max(parameter.min_value, min(parameter.max_value, current_value))
253
306
 
254
307
 
255
- class IntegerRangeParameterWidget(
256
- BaseParameterWidget[IntegerRangeParameter, widgets.IntRangeSlider]
257
- ):
308
+ class IntegerRangeWidget(BaseWidget[IntegerRangeParameter, widgets.IntRangeSlider]):
258
309
  """Widget for integer range parameters."""
259
310
 
260
311
  def _create_widget(
261
- self, parameter: IntegerRangeParameter, continuous_update: bool
312
+ self,
313
+ parameter: IntegerRangeParameter,
314
+ continuous: bool,
315
+ width: str = "auto",
316
+ margin: str = "3px 0px",
317
+ description_width: str = "initial",
262
318
  ) -> widgets.IntRangeSlider:
263
319
  return widgets.IntRangeSlider(
264
320
  value=parameter.value,
265
321
  min=parameter.min_value,
266
322
  max=parameter.max_value,
267
323
  description=parameter.name,
268
- continuous_update=continuous_update,
269
- layout=widgets.Layout(width="95%"),
270
- style={"description_width": "initial"},
324
+ continuous=continuous,
325
+ layout=widgets.Layout(width=width, margin=margin),
326
+ style={"description_width": description_width},
271
327
  )
272
328
 
273
329
  def matches_parameter(self, parameter: IntegerRangeParameter) -> bool:
@@ -289,13 +345,16 @@ class IntegerRangeParameterWidget(
289
345
  self.value = (low, high)
290
346
 
291
347
 
292
- class FloatRangeParameterWidget(
293
- BaseParameterWidget[FloatRangeParameter, widgets.FloatRangeSlider]
294
- ):
348
+ class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]):
295
349
  """Widget for float range parameters."""
296
350
 
297
351
  def _create_widget(
298
- self, parameter: FloatRangeParameter, continuous_update: bool
352
+ self,
353
+ parameter: FloatRangeParameter,
354
+ continuous: bool,
355
+ width: str = "auto",
356
+ margin: str = "3px 0px",
357
+ description_width: str = "initial",
299
358
  ) -> widgets.FloatRangeSlider:
300
359
  return widgets.FloatRangeSlider(
301
360
  value=parameter.value,
@@ -303,9 +362,9 @@ class FloatRangeParameterWidget(
303
362
  max=parameter.max_value,
304
363
  step=parameter.step,
305
364
  description=parameter.name,
306
- continuous_update=continuous_update,
307
- layout=widgets.Layout(width="95%"),
308
- style={"description_width": "initial"},
365
+ continuous=continuous,
366
+ layout=widgets.Layout(width=width, margin=margin),
367
+ style={"description_width": description_width},
309
368
  )
310
369
 
311
370
  def matches_parameter(self, parameter: FloatRangeParameter) -> bool:
@@ -328,21 +387,22 @@ class FloatRangeParameterWidget(
328
387
  self.value = (low, high)
329
388
 
330
389
 
331
- class UnboundedIntegerParameterWidget(
332
- BaseParameterWidget[UnboundedIntegerParameter, widgets.BoundedIntText]
333
- ):
390
+ class UnboundedIntegerWidget(BaseWidget[UnboundedIntegerParameter, widgets.IntText]):
334
391
  """Widget for unbounded integer parameters."""
335
392
 
336
393
  def _create_widget(
337
- self, parameter: UnboundedIntegerParameter, continuous_update: bool
338
- ) -> widgets.BoundedIntText:
339
- return widgets.BoundedIntText(
394
+ self,
395
+ parameter: UnboundedIntegerParameter,
396
+ continuous: bool,
397
+ width: str = "auto",
398
+ margin: str = "3px 0px",
399
+ description_width: str = "initial",
400
+ ) -> widgets.IntText:
401
+ return widgets.IntText(
340
402
  value=parameter.value,
341
- min=parameter.min_value,
342
- max=parameter.max_value,
343
403
  description=parameter.name,
344
- layout=widgets.Layout(width="95%"),
345
- style={"description_width": "initial"},
404
+ layout=widgets.Layout(width=width, margin=margin),
405
+ style={"description_width": description_width},
346
406
  )
347
407
 
348
408
  def matches_parameter(self, parameter: UnboundedIntegerParameter) -> bool:
@@ -353,28 +413,26 @@ class UnboundedIntegerParameterWidget(
353
413
  self, parameter: UnboundedIntegerParameter
354
414
  ) -> None:
355
415
  """Extra updates from the parameter."""
356
- if parameter.min_value is not None:
357
- self._widget.min = parameter.min_value
358
- if parameter.max_value is not None:
359
- self._widget.max = parameter.max_value
416
+ pass
360
417
 
361
418
 
362
- class UnboundedFloatParameterWidget(
363
- BaseParameterWidget[UnboundedFloatParameter, widgets.BoundedFloatText]
364
- ):
419
+ class UnboundedFloatWidget(BaseWidget[UnboundedFloatParameter, widgets.FloatText]):
365
420
  """Widget for unbounded float parameters."""
366
421
 
367
422
  def _create_widget(
368
- self, parameter: UnboundedFloatParameter, continuous_update: bool
369
- ) -> widgets.BoundedFloatText:
370
- return widgets.BoundedFloatText(
423
+ self,
424
+ parameter: UnboundedFloatParameter,
425
+ continuous: bool,
426
+ width: str = "auto",
427
+ margin: str = "3px 0px",
428
+ description_width: str = "initial",
429
+ ) -> widgets.FloatText:
430
+ return widgets.FloatText(
371
431
  value=parameter.value,
372
- min=parameter.min_value,
373
- max=parameter.max_value,
374
432
  step=parameter.step,
375
433
  description=parameter.name,
376
- layout=widgets.Layout(width="95%"),
377
- style={"description_width": "initial"},
434
+ layout=widgets.Layout(width=width, margin=margin),
435
+ style={"description_width": description_width},
378
436
  )
379
437
 
380
438
  def matches_parameter(self, parameter: UnboundedFloatParameter) -> bool:
@@ -383,89 +441,111 @@ class UnboundedFloatParameterWidget(
383
441
 
384
442
  def extra_updates_from_parameter(self, parameter: UnboundedFloatParameter) -> None:
385
443
  """Extra updates from the parameter."""
386
- if parameter.min_value is not None:
387
- self._widget.min = parameter.min_value
388
- if parameter.max_value is not None:
389
- self._widget.max = parameter.max_value
390
444
  self._widget.step = parameter.step
391
445
 
392
446
 
393
- class ButtonParameterWidget(BaseParameterWidget[ButtonParameter, widgets.Button]):
447
+ class ButtonWidget(BaseWidget[ButtonAction, widgets.Button]):
394
448
  """Widget for button parameters."""
395
449
 
396
- _is_button: bool = True
450
+ _is_action: bool = True
397
451
 
398
452
  def _create_widget(
399
- self, parameter: ButtonParameter, continuous_update: bool
453
+ self,
454
+ parameter: ButtonAction,
455
+ continuous: bool,
456
+ width: str = "auto",
457
+ margin: str = "3px 0px",
458
+ description_width: str = "initial",
400
459
  ) -> widgets.Button:
401
460
  button = widgets.Button(
402
461
  description=parameter.label,
403
- layout=widgets.Layout(width="auto"),
404
- style={"description_width": "initial"},
462
+ layout=widgets.Layout(width=width, margin=margin),
463
+ style={"description_width": description_width},
405
464
  )
406
- button.on_click(parameter.callback)
407
465
  return button
408
466
 
409
- def matches_parameter(self, parameter: ButtonParameter) -> bool:
467
+ def matches_parameter(self, parameter: ButtonAction) -> bool:
410
468
  """Check if the widget matches the parameter."""
411
469
  return self._widget.description == parameter.label
412
470
 
413
- def extra_updates_from_parameter(self, parameter: ButtonParameter) -> None:
471
+ def extra_updates_from_parameter(self, parameter: ButtonAction) -> None:
414
472
  """Extra updates from the parameter."""
473
+ # Callbacks are handled in the deployer, so the only relevant update is the label
415
474
  self._widget.description = parameter.label
416
- # Update click handler
417
- self._widget.on_click(parameter.callback, remove=True) # Remove old handler
418
- self._widget.on_click(parameter.callback) # Add new handler
419
475
 
420
476
  def observe(self, callback: Callable) -> None:
421
477
  """Observe the widget and call the callback when the value changes."""
478
+ if self._callbacks:
479
+ raise ValueError("ButtonWidget already has a callback!")
422
480
  self._widget.on_click(callback)
423
- self._callbacks.append(callback)
481
+ self._callbacks = callback
424
482
 
425
- def unobserve(self, callback: Callable[[Any], None]) -> None:
483
+ def unobserve(self, callback: Callable) -> None:
426
484
  """Unobserve the widget and stop calling the callback when the value changes."""
427
- self._widget.on_click(None)
428
- self._callbacks.remove(callback)
485
+ self._widget.on_click(callback, remove=True)
486
+ self._callbacks = []
429
487
 
430
488
  def reenable_callbacks(self) -> None:
431
489
  """Reenable all callbacks from the widget."""
432
- for callback in self._callbacks:
433
- self._widget.on_click(callback)
490
+ self._widget.on_click(self._callbacks)
434
491
 
435
492
  def disable_callbacks(self) -> None:
436
493
  """Disable all callbacks from the widget."""
437
- for callback in self._callbacks:
438
- self._widget.on_click(None)
494
+ self._widget.on_click(self._callbacks, remove=True)
439
495
 
440
496
 
441
- def create_parameter_widget(
442
- parameter: Parameter[Any],
443
- continuous_update: bool = False,
444
- ) -> BaseParameterWidget[Parameter[Any], widgets.Widget]:
497
+ def create_widget(
498
+ parameter: Union[Parameter[Any], ButtonAction],
499
+ continuous: bool = False,
500
+ width: str = "auto",
501
+ margin: str = "3px 0px",
502
+ description_width: str = "initial",
503
+ ) -> BaseWidget[Union[Parameter[Any], ButtonAction], widgets.Widget]:
445
504
  """Create and return the appropriate widget for the given parameter.
446
505
 
447
506
  Args:
448
507
  parameter: The parameter to create a widget for
449
- continuous_update: Whether to update the widget value continuously during user interaction
508
+ continuous: Whether to update the widget value continuously during user interaction
509
+ width: Width of the widget
510
+ margin: Margin of the widget
511
+ description_width: Width of the description label
450
512
  """
451
513
  widget_map = {
452
- TextParameter: TextParameterWidget,
453
- SelectionParameter: SelectionParameterWidget,
454
- MultipleSelectionParameter: MultipleSelectionParameterWidget,
455
- BooleanParameter: BooleanParameterWidget,
456
- IntegerParameter: IntegerParameterWidget,
457
- FloatParameter: FloatParameterWidget,
458
- IntegerRangeParameter: IntegerRangeParameterWidget,
459
- FloatRangeParameter: FloatRangeParameterWidget,
460
- UnboundedIntegerParameter: UnboundedIntegerParameterWidget,
461
- UnboundedFloatParameter: UnboundedFloatParameterWidget,
462
- ButtonParameter: ButtonParameterWidget,
514
+ TextParameter: TextWidget,
515
+ SelectionParameter: SelectionWidget,
516
+ MultipleSelectionParameter: MultipleSelectionWidget,
517
+ BooleanParameter: BooleanWidget,
518
+ IntegerParameter: IntegerWidget,
519
+ FloatParameter: FloatWidget,
520
+ IntegerRangeParameter: IntegerRangeWidget,
521
+ FloatRangeParameter: FloatRangeWidget,
522
+ UnboundedIntegerParameter: UnboundedIntegerWidget,
523
+ UnboundedFloatParameter: UnboundedFloatWidget,
524
+ ButtonAction: ButtonWidget,
463
525
  }
464
526
 
527
+ # Try direct type lookup first
465
528
  widget_class = widget_map.get(type(parameter))
529
+
530
+ # If that fails, try matching by class name
531
+ if widget_class is None:
532
+ param_type_name = type(parameter).__name__
533
+ for key_class, value_class in widget_map.items():
534
+ if key_class.__name__ == param_type_name:
535
+ widget_class = value_class
536
+ break
537
+
466
538
  if widget_class is None:
467
539
  raise ValueError(
468
- f"No widget implementation for parameter type: {type(parameter)}"
540
+ f"No widget implementation for parameter type: {type(parameter)}\n"
541
+ f"Parameter type name: {type(parameter).__name__}\n"
542
+ f"Available types: {[k.__name__ for k in widget_map.keys()]}"
469
543
  )
470
544
 
471
- return widget_class(parameter, continuous_update)
545
+ return widget_class(
546
+ parameter,
547
+ continuous,
548
+ width=width,
549
+ margin=margin,
550
+ description_width=description_width,
551
+ )