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.
@@ -33,14 +33,30 @@ class BaseWidget(Generic[T, W], ABC):
33
33
  _callbacks: List[Dict[str, Union[Callable, Union[str, List[str]]]]]
34
34
  _is_action: bool = False
35
35
 
36
- def __init__(self, parameter: T, continuous: bool = False):
37
- self._widget = self._create_widget(parameter, continuous)
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
+ )
38
47
  self._updating = False # Flag to prevent circular updates
39
48
  # List of callbacks to remember for quick disabling/enabling
40
49
  self._callbacks = []
41
50
 
42
51
  @abstractmethod
43
- def _create_widget(self, parameter: T, continuous: 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:
44
60
  """Create and return the appropriate ipywidget."""
45
61
  pass
46
62
 
@@ -105,36 +121,58 @@ class TextWidget(BaseWidget[TextParameter, widgets.Text]):
105
121
  """Widget for text parameters."""
106
122
 
107
123
  def _create_widget(
108
- self, parameter: TextParameter, continuous: bool
124
+ self,
125
+ parameter: TextParameter,
126
+ continuous: bool,
127
+ width: str = "auto",
128
+ margin: str = "3px 0px",
129
+ description_width: str = "initial",
109
130
  ) -> widgets.Text:
110
131
  return widgets.Text(
111
132
  value=parameter.value,
112
133
  description=parameter.name,
113
134
  continuous=continuous,
114
- layout=widgets.Layout(width="95%"),
135
+ layout=widgets.Layout(width=width, margin=margin),
136
+ style={"description_width": description_width},
115
137
  )
116
138
 
117
139
 
118
- class BooleanWidget(BaseWidget[BooleanParameter, widgets.Checkbox]):
140
+ class BooleanWidget(BaseWidget[BooleanParameter, widgets.ToggleButton]):
119
141
  """Widget for boolean parameters."""
120
142
 
121
143
  def _create_widget(
122
- self, parameter: BooleanParameter, continuous: bool
123
- ) -> widgets.Checkbox:
124
- 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
+ )
125
157
 
126
158
 
127
159
  class SelectionWidget(BaseWidget[SelectionParameter, widgets.Dropdown]):
128
160
  """Widget for single selection parameters."""
129
161
 
130
162
  def _create_widget(
131
- self, parameter: SelectionParameter, continuous: bool
163
+ self,
164
+ parameter: SelectionParameter,
165
+ continuous: bool,
166
+ width: str = "auto",
167
+ margin: str = "3px 0px",
168
+ description_width: str = "initial",
132
169
  ) -> widgets.Dropdown:
133
170
  return widgets.Dropdown(
134
171
  value=parameter.value,
135
172
  options=parameter.options,
136
173
  description=parameter.name,
137
- layout=widgets.Layout(width="95%"),
174
+ layout=widgets.Layout(width=width, margin=margin),
175
+ style={"description_width": description_width},
138
176
  )
139
177
 
140
178
  def matches_parameter(self, parameter: SelectionParameter) -> bool:
@@ -158,14 +196,20 @@ class MultipleSelectionWidget(
158
196
  """Widget for multiple selection parameters."""
159
197
 
160
198
  def _create_widget(
161
- self, parameter: MultipleSelectionParameter, continuous: bool
199
+ self,
200
+ parameter: MultipleSelectionParameter,
201
+ continuous: bool,
202
+ width: str = "auto",
203
+ margin: str = "3px 0px",
204
+ description_width: str = "initial",
162
205
  ) -> widgets.SelectMultiple:
163
206
  return widgets.SelectMultiple(
164
207
  value=parameter.value,
165
208
  options=parameter.options,
166
209
  description=parameter.name,
167
210
  rows=min(len(parameter.options), 4),
168
- layout=widgets.Layout(width="95%"),
211
+ layout=widgets.Layout(width=width, margin=margin),
212
+ style={"description_width": description_width},
169
213
  )
170
214
 
171
215
  def matches_parameter(self, parameter: MultipleSelectionParameter) -> bool:
@@ -189,155 +233,194 @@ class IntegerWidget(BaseWidget[IntegerParameter, widgets.IntSlider]):
189
233
  """Widget for integer parameters."""
190
234
 
191
235
  def _create_widget(
192
- self, parameter: IntegerParameter, continuous: bool
236
+ self,
237
+ parameter: IntegerParameter,
238
+ continuous: bool,
239
+ width: str = "auto",
240
+ margin: str = "3px 0px",
241
+ description_width: str = "initial",
193
242
  ) -> widgets.IntSlider:
243
+ """Create the integer slider widget."""
194
244
  return widgets.IntSlider(
195
245
  value=parameter.value,
196
- min=parameter.min_value,
197
- max=parameter.max_value,
246
+ min=parameter.min,
247
+ max=parameter.max,
248
+ step=1,
198
249
  description=parameter.name,
199
- continuous=continuous,
200
- layout=widgets.Layout(width="95%"),
201
- style={"description_width": "initial"},
250
+ continuous_update=continuous,
251
+ style={"description_width": description_width},
252
+ layout=widgets.Layout(width=width, margin=margin),
202
253
  )
203
254
 
204
255
  def matches_parameter(self, parameter: IntegerParameter) -> bool:
205
- """Check if the widget matches the parameter."""
256
+ """Check if the widget values match the parameter."""
206
257
  return (
207
- self.value == parameter.value
208
- and self._widget.min == parameter.min_value
209
- and self._widget.max == parameter.max_value
258
+ self._widget.description == parameter.name
259
+ and self._widget.value == parameter.value
260
+ and self._widget.min == parameter.min
261
+ and self._widget.max == parameter.max
210
262
  )
211
263
 
212
264
  def extra_updates_from_parameter(self, parameter: IntegerParameter) -> None:
213
- """Extra updates from the parameter."""
265
+ """Update the widget attributes from the parameter."""
214
266
  current_value = self._widget.value
215
- self._widget.min = parameter.min_value
216
- self._widget.max = parameter.max_value
217
- self.value = max(parameter.min_value, min(parameter.max_value, current_value))
267
+ self._widget.min = parameter.min
268
+ self._widget.max = parameter.max
269
+ self.value = max(parameter.min, min(parameter.max, current_value))
218
270
 
219
271
 
220
272
  class FloatWidget(BaseWidget[FloatParameter, widgets.FloatSlider]):
221
273
  """Widget for float parameters."""
222
274
 
223
275
  def _create_widget(
224
- self, parameter: FloatParameter, continuous: bool
276
+ self,
277
+ parameter: FloatParameter,
278
+ continuous: bool,
279
+ width: str = "auto",
280
+ margin: str = "3px 0px",
281
+ description_width: str = "initial",
225
282
  ) -> widgets.FloatSlider:
283
+ """Create the float slider widget."""
226
284
  return widgets.FloatSlider(
227
285
  value=parameter.value,
228
- min=parameter.min_value,
229
- max=parameter.max_value,
286
+ min=parameter.min,
287
+ max=parameter.max,
230
288
  step=parameter.step,
231
289
  description=parameter.name,
232
- continuous=continuous,
233
- layout=widgets.Layout(width="95%"),
234
- style={"description_width": "initial"},
290
+ continuous_update=continuous,
291
+ style={"description_width": description_width},
292
+ layout=widgets.Layout(width=width, margin=margin),
235
293
  )
236
294
 
237
295
  def matches_parameter(self, parameter: FloatParameter) -> bool:
238
- """Check if the widget matches the parameter."""
296
+ """Check if the widget values match the parameter."""
239
297
  return (
240
- self.value == parameter.value
241
- and self._widget.min == parameter.min_value
242
- and self._widget.max == parameter.max_value
298
+ self._widget.description == parameter.name
299
+ and self._widget.value == parameter.value
300
+ and self._widget.min == parameter.min
301
+ and self._widget.max == parameter.max
302
+ and self._widget.step == parameter.step
243
303
  )
244
304
 
245
305
  def extra_updates_from_parameter(self, parameter: FloatParameter) -> None:
246
- """Extra updates from the parameter."""
306
+ """Update the widget attributes from the parameter."""
247
307
  current_value = self._widget.value
248
- self._widget.min = parameter.min_value
249
- self._widget.max = parameter.max_value
308
+ self._widget.min = parameter.min
309
+ self._widget.max = parameter.max
250
310
  self._widget.step = parameter.step
251
- self.value = max(parameter.min_value, min(parameter.max_value, current_value))
311
+ self.value = max(parameter.min, min(parameter.max, current_value))
252
312
 
253
313
 
254
314
  class IntegerRangeWidget(BaseWidget[IntegerRangeParameter, widgets.IntRangeSlider]):
255
315
  """Widget for integer range parameters."""
256
316
 
257
317
  def _create_widget(
258
- self, parameter: IntegerRangeParameter, continuous: bool
318
+ self,
319
+ parameter: IntegerRangeParameter,
320
+ continuous: bool,
321
+ width: str = "auto",
322
+ margin: str = "3px 0px",
323
+ description_width: str = "initial",
259
324
  ) -> widgets.IntRangeSlider:
325
+ """Create the integer range slider widget."""
326
+ low, high = parameter.value
260
327
  return widgets.IntRangeSlider(
261
- value=parameter.value,
262
- min=parameter.min_value,
263
- max=parameter.max_value,
328
+ value=[low, high],
329
+ min=parameter.min,
330
+ max=parameter.max,
331
+ step=1,
264
332
  description=parameter.name,
265
- continuous=continuous,
266
- layout=widgets.Layout(width="95%"),
267
- style={"description_width": "initial"},
333
+ continuous_update=continuous,
334
+ style={"description_width": description_width},
335
+ layout=widgets.Layout(width=width, margin=margin),
268
336
  )
269
337
 
270
338
  def matches_parameter(self, parameter: IntegerRangeParameter) -> bool:
271
- """Check if the widget matches the parameter."""
339
+ """Check if the widget values match the parameter."""
340
+ low, high = parameter.value
272
341
  return (
273
- self.value == parameter.value
274
- and self._widget.min == parameter.min_value
275
- and self._widget.max == parameter.max_value
342
+ self._widget.description == parameter.name
343
+ and self._widget.value[0] == low
344
+ and self._widget.value[1] == high
345
+ and self._widget.min == parameter.min
346
+ and self._widget.max == parameter.max
276
347
  )
277
348
 
278
349
  def extra_updates_from_parameter(self, parameter: IntegerRangeParameter) -> None:
279
- """Extra updates from the parameter."""
280
- current_value = self._widget.value
281
- self._widget.min = parameter.min_value
282
- self._widget.max = parameter.max_value
283
- low, high = current_value
284
- low = max(parameter.min_value, min(parameter.max_value, low))
285
- high = max(parameter.min_value, min(parameter.max_value, high))
286
- self.value = (low, high)
350
+ """Update the widget attributes from the parameter."""
351
+ low, high = self._widget.value
352
+ self._widget.min = parameter.min
353
+ self._widget.max = parameter.max
354
+ # Ensure values stay within bounds
355
+ low = max(parameter.min, min(parameter.max, low))
356
+ high = max(parameter.min, min(parameter.max, high))
357
+ self.value = [low, high]
287
358
 
288
359
 
289
360
  class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]):
290
361
  """Widget for float range parameters."""
291
362
 
292
363
  def _create_widget(
293
- self, parameter: FloatRangeParameter, continuous: bool
364
+ self,
365
+ parameter: FloatRangeParameter,
366
+ continuous: bool,
367
+ width: str = "auto",
368
+ margin: str = "3px 0px",
369
+ description_width: str = "initial",
294
370
  ) -> widgets.FloatRangeSlider:
371
+ """Create the float range slider widget."""
372
+ low, high = parameter.value
295
373
  return widgets.FloatRangeSlider(
296
- value=parameter.value,
297
- min=parameter.min_value,
298
- max=parameter.max_value,
374
+ value=[low, high],
375
+ min=parameter.min,
376
+ max=parameter.max,
299
377
  step=parameter.step,
300
378
  description=parameter.name,
301
- continuous=continuous,
302
- layout=widgets.Layout(width="95%"),
303
- style={"description_width": "initial"},
379
+ continuous_update=continuous,
380
+ style={"description_width": description_width},
381
+ layout=widgets.Layout(width=width, margin=margin),
304
382
  )
305
383
 
306
384
  def matches_parameter(self, parameter: FloatRangeParameter) -> bool:
307
- """Check if the widget matches the parameter."""
385
+ """Check if the widget values match the parameter."""
386
+ low, high = parameter.value
308
387
  return (
309
- self.value == parameter.value
310
- and self._widget.min == parameter.min_value
311
- and self._widget.max == parameter.max_value
388
+ self._widget.description == parameter.name
389
+ and self._widget.value[0] == low
390
+ and self._widget.value[1] == high
391
+ and self._widget.min == parameter.min
392
+ and self._widget.max == parameter.max
393
+ and self._widget.step == parameter.step
312
394
  )
313
395
 
314
396
  def extra_updates_from_parameter(self, parameter: FloatRangeParameter) -> None:
315
- """Extra updates from the parameter."""
316
- current_value = self._widget.value
317
- self._widget.min = parameter.min_value
318
- self._widget.max = parameter.max_value
397
+ """Update the widget attributes from the parameter."""
398
+ low, high = self._widget.value
399
+ self._widget.min = parameter.min
400
+ self._widget.max = parameter.max
319
401
  self._widget.step = parameter.step
320
- low, high = current_value
321
- low = max(parameter.min_value, min(parameter.max_value, low))
322
- high = max(parameter.min_value, min(parameter.max_value, high))
323
- self.value = (low, high)
402
+ # Ensure values stay within bounds
403
+ low = max(parameter.min, min(parameter.max, low))
404
+ high = max(parameter.min, min(parameter.max, high))
405
+ self.value = [low, high]
324
406
 
325
407
 
326
- class UnboundedIntegerWidget(
327
- BaseWidget[UnboundedIntegerParameter, widgets.BoundedIntText]
328
- ):
408
+ class UnboundedIntegerWidget(BaseWidget[UnboundedIntegerParameter, widgets.IntText]):
329
409
  """Widget for unbounded integer parameters."""
330
410
 
331
411
  def _create_widget(
332
- self, parameter: UnboundedIntegerParameter, continuous: bool
333
- ) -> widgets.BoundedIntText:
334
- return widgets.BoundedIntText(
412
+ self,
413
+ parameter: UnboundedIntegerParameter,
414
+ continuous: bool,
415
+ width: str = "auto",
416
+ margin: str = "3px 0px",
417
+ description_width: str = "initial",
418
+ ) -> widgets.IntText:
419
+ return widgets.IntText(
335
420
  value=parameter.value,
336
- min=parameter.min_value,
337
- max=parameter.max_value,
338
421
  description=parameter.name,
339
- layout=widgets.Layout(width="95%"),
340
- style={"description_width": "initial"},
422
+ layout=widgets.Layout(width=width, margin=margin),
423
+ style={"description_width": description_width},
341
424
  )
342
425
 
343
426
  def matches_parameter(self, parameter: UnboundedIntegerParameter) -> bool:
@@ -348,28 +431,26 @@ class UnboundedIntegerWidget(
348
431
  self, parameter: UnboundedIntegerParameter
349
432
  ) -> None:
350
433
  """Extra updates from the parameter."""
351
- if parameter.min_value is not None:
352
- self._widget.min = parameter.min_value
353
- if parameter.max_value is not None:
354
- self._widget.max = parameter.max_value
434
+ pass
355
435
 
356
436
 
357
- class UnboundedFloatWidget(
358
- BaseWidget[UnboundedFloatParameter, widgets.BoundedFloatText]
359
- ):
437
+ class UnboundedFloatWidget(BaseWidget[UnboundedFloatParameter, widgets.FloatText]):
360
438
  """Widget for unbounded float parameters."""
361
439
 
362
440
  def _create_widget(
363
- self, parameter: UnboundedFloatParameter, continuous: bool
364
- ) -> widgets.BoundedFloatText:
365
- return widgets.BoundedFloatText(
441
+ self,
442
+ parameter: UnboundedFloatParameter,
443
+ continuous: bool,
444
+ width: str = "auto",
445
+ margin: str = "3px 0px",
446
+ description_width: str = "initial",
447
+ ) -> widgets.FloatText:
448
+ return widgets.FloatText(
366
449
  value=parameter.value,
367
- min=parameter.min_value,
368
- max=parameter.max_value,
369
450
  step=parameter.step,
370
451
  description=parameter.name,
371
- layout=widgets.Layout(width="95%"),
372
- style={"description_width": "initial"},
452
+ layout=widgets.Layout(width=width, margin=margin),
453
+ style={"description_width": description_width},
373
454
  )
374
455
 
375
456
  def matches_parameter(self, parameter: UnboundedFloatParameter) -> bool:
@@ -378,10 +459,6 @@ class UnboundedFloatWidget(
378
459
 
379
460
  def extra_updates_from_parameter(self, parameter: UnboundedFloatParameter) -> None:
380
461
  """Extra updates from the parameter."""
381
- if parameter.min_value is not None:
382
- self._widget.min = parameter.min_value
383
- if parameter.max_value is not None:
384
- self._widget.max = parameter.max_value
385
462
  self._widget.step = parameter.step
386
463
 
387
464
 
@@ -391,14 +468,18 @@ class ButtonWidget(BaseWidget[ButtonAction, widgets.Button]):
391
468
  _is_action: bool = True
392
469
 
393
470
  def _create_widget(
394
- self, parameter: ButtonAction, continuous: bool
471
+ self,
472
+ parameter: ButtonAction,
473
+ continuous: bool,
474
+ width: str = "auto",
475
+ margin: str = "3px 0px",
476
+ description_width: str = "initial",
395
477
  ) -> widgets.Button:
396
478
  button = widgets.Button(
397
479
  description=parameter.label,
398
- layout=widgets.Layout(width="auto"),
399
- style={"description_width": "initial"},
480
+ layout=widgets.Layout(width=width, margin=margin),
481
+ style={"description_width": description_width},
400
482
  )
401
- button.on_click(parameter.callback)
402
483
  return button
403
484
 
404
485
  def matches_parameter(self, parameter: ButtonAction) -> bool:
@@ -407,41 +488,45 @@ class ButtonWidget(BaseWidget[ButtonAction, widgets.Button]):
407
488
 
408
489
  def extra_updates_from_parameter(self, parameter: ButtonAction) -> None:
409
490
  """Extra updates from the parameter."""
491
+ # Callbacks are handled in the deployer, so the only relevant update is the label
410
492
  self._widget.description = parameter.label
411
- # Update click handler
412
- self._widget.on_click(parameter.callback, remove=True) # Remove old handler
413
- self._widget.on_click(parameter.callback) # Add new handler
414
493
 
415
494
  def observe(self, callback: Callable) -> None:
416
495
  """Observe the widget and call the callback when the value changes."""
496
+ if self._callbacks:
497
+ raise ValueError("ButtonWidget already has a callback!")
417
498
  self._widget.on_click(callback)
418
- self._callbacks.append(callback)
499
+ self._callbacks = callback
419
500
 
420
- def unobserve(self, callback: Callable[[Any], None]) -> None:
501
+ def unobserve(self, callback: Callable) -> None:
421
502
  """Unobserve the widget and stop calling the callback when the value changes."""
422
- self._widget.on_click(None)
423
- self._callbacks.remove(callback)
503
+ self._widget.on_click(callback, remove=True)
504
+ self._callbacks = []
424
505
 
425
506
  def reenable_callbacks(self) -> None:
426
507
  """Reenable all callbacks from the widget."""
427
- for callback in self._callbacks:
428
- self._widget.on_click(callback)
508
+ self._widget.on_click(self._callbacks)
429
509
 
430
510
  def disable_callbacks(self) -> None:
431
511
  """Disable all callbacks from the widget."""
432
- for callback in self._callbacks:
433
- self._widget.on_click(None)
512
+ self._widget.on_click(self._callbacks, remove=True)
434
513
 
435
514
 
436
515
  def create_widget(
437
516
  parameter: Union[Parameter[Any], ButtonAction],
438
517
  continuous: bool = False,
518
+ width: str = "auto",
519
+ margin: str = "3px 0px",
520
+ description_width: str = "initial",
439
521
  ) -> BaseWidget[Union[Parameter[Any], ButtonAction], widgets.Widget]:
440
522
  """Create and return the appropriate widget for the given parameter.
441
523
 
442
524
  Args:
443
525
  parameter: The parameter to create a widget for
444
526
  continuous: Whether to update the widget value continuously during user interaction
527
+ width: Width of the widget
528
+ margin: Margin of the widget
529
+ description_width: Width of the description label
445
530
  """
446
531
  widget_map = {
447
532
  TextParameter: TextWidget,
@@ -475,4 +560,10 @@ def create_widget(
475
560
  f"Available types: {[k.__name__ for k in widget_map.keys()]}"
476
561
  )
477
562
 
478
- return widget_class(parameter, continuous)
563
+ return widget_class(
564
+ parameter,
565
+ continuous,
566
+ width=width,
567
+ margin=margin,
568
+ description_width=description_width,
569
+ )