syd 0.1.7__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.
@@ -0,0 +1 @@
1
+ # This file exists to make the directory a proper Python package
@@ -0,0 +1,34 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Syd Viewer</title>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
8
+ </head>
9
+ <body>
10
+ <div class="viewer-container" data-controls-position="{{ config.controls_position }}">
11
+ <div class="controls-container" data-width-percent="{{ config.controls_width_percent }}"
12
+ {% if config.is_horizontal %}style="width: {{ config.controls_width_percent }}%;"{% else %}style="height: {{ config.controls_width_percent }}%;"{% endif %}>
13
+ <div id="controls-container">
14
+ <!-- Controls will be dynamically generated via JavaScript -->
15
+ </div>
16
+ </div>
17
+
18
+ <div class="plot-container"
19
+ {% if config.is_horizontal %}style="width: calc(100% - {{ config.controls_width_percent }}%);"{% else %}style="height: calc(100% - {{ config.controls_width_percent }}%);"{% endif %}>
20
+ <img id="plot-image" width="100%" height="100%">
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Store config as data attributes for JS to access -->
25
+ <div id="viewer-config"
26
+ data-figure-width="{{ config.figure_width }}"
27
+ data-figure-height="{{ config.figure_height }}"
28
+ data-controls-position="{{ config.controls_position }}"
29
+ data-controls-width-percent="{{ config.controls_width_percent }}"
30
+ style="display:none;"></div>
31
+
32
+ <script src="{{ url_for('static', filename='js/viewer.js') }}"></script>
33
+ </body>
34
+ </html>
@@ -57,7 +57,7 @@ class TestFlaskDeployerComponents(unittest.TestCase):
57
57
  def test_parameter_update_state_sync():
58
58
  # Create real viewer with test parameters
59
59
  viewer = Viewer()
60
- viewer.add_float('test_param', value=1.0, min_value=0, max_value=10)
60
+ viewer.add_float('test_param', value=1.0, min=0, max=10)
61
61
 
62
62
  # Create deployer with this viewer
63
63
  deployer = FlaskDeployer(viewer)
@@ -82,7 +82,7 @@ def test_parameter_update_state_sync():
82
82
  ```python
83
83
  def test_update_parameter_endpoint():
84
84
  viewer = Viewer()
85
- viewer.add_float('test_param', value=1.0, min_value=0, max_value=10)
85
+ viewer.add_float('test_param', value=1.0, min=0, max=10)
86
86
  deployer = FlaskDeployer(viewer)
87
87
  app = deployer.app
88
88
 
@@ -218,8 +218,8 @@ def standard_test_viewer():
218
218
  return fig
219
219
 
220
220
  viewer.set_plot(plot)
221
- viewer.add_float('amplitude', value=1.0, min_value=0, max_value=2)
222
- viewer.add_float('frequency', value=1.0, min_value=0.1, max_value=5)
221
+ viewer.add_float('amplitude', value=1.0, min=0, max=2)
222
+ viewer.add_float('frequency', value=1.0, min=0.1, max=5)
223
223
 
224
224
  return viewer
225
225
 
@@ -67,7 +67,7 @@ class LayoutConfig:
67
67
  controls_position: str = "left" # Options are: 'left', 'top', 'right', 'bottom'
68
68
  figure_width: float = 8.0
69
69
  figure_height: float = 6.0
70
- controls_width_percent: int = 30
70
+ controls_width_percent: int = 20
71
71
 
72
72
  def __post_init__(self):
73
73
  valid_positions = ["left", "top", "right", "bottom"]
@@ -93,7 +93,7 @@ class NotebookDeployer:
93
93
  controls_position: str = "left",
94
94
  figure_width: float = 8.0,
95
95
  figure_height: float = 6.0,
96
- controls_width_percent: int = 30,
96
+ controls_width_percent: int = 20,
97
97
  continuous: bool = False,
98
98
  suppress_warnings: bool = False,
99
99
  ):
@@ -111,7 +111,7 @@ class NotebookDeployer:
111
111
  self.backend_type = get_backend_type()
112
112
  if self.backend_type not in ["inline", "widget"]:
113
113
  warnings.warn(
114
- "The current backend is not supported. Please use %matplotlib widget or %matplotlib inline.\n"
114
+ f"The current backend ({self.backend_type}) is not supported. Please use %matplotlib widget or %matplotlib inline.\n"
115
115
  "The behavior of the viewer will almost definitely not work as expected."
116
116
  )
117
117
  self.parameter_widgets: Dict[str, BaseWidget] = {}
@@ -134,8 +134,8 @@ class NotebookDeployer:
134
134
  if self.config.is_horizontal:
135
135
  controls["controls_width"] = widgets.IntSlider(
136
136
  value=self.config.controls_width_percent,
137
- min=20,
138
- max=80,
137
+ min=10,
138
+ max=50,
139
139
  description="Controls Width %",
140
140
  continuous=True,
141
141
  layout=widgets.Layout(width="95%"),
@@ -227,6 +227,9 @@ class NotebookDeployer:
227
227
  with _plot_context():
228
228
  figure = self.viewer.plot(state)
229
229
 
230
+ # Update widgets if plot function updated a parameter
231
+ self._sync_widgets_with_state()
232
+
230
233
  # Close the last figure if it exists to keep matplotlib clean
231
234
  # (just moved this from after clear_output.... noting!)
232
235
  if self._last_figure is not None:
@@ -269,6 +272,13 @@ class NotebookDeployer:
269
272
  + list(self.layout_widgets.values()),
270
273
  layout=widgets.Layout(margin="10px 0px"),
271
274
  )
275
+
276
+ # Register the controls_width slider's observer
277
+ if "controls_width" in self.layout_widgets:
278
+ self.layout_widgets["controls_width"].observe(
279
+ self._handle_container_width_change, names="value"
280
+ )
281
+
272
282
  widgets_elements = [param_box, layout_box]
273
283
  else:
274
284
  widgets_elements = [param_box]
@@ -240,30 +240,33 @@ class IntegerWidget(BaseWidget[IntegerParameter, widgets.IntSlider]):
240
240
  margin: str = "3px 0px",
241
241
  description_width: str = "initial",
242
242
  ) -> widgets.IntSlider:
243
+ """Create the integer slider widget."""
243
244
  return widgets.IntSlider(
244
245
  value=parameter.value,
245
- min=parameter.min_value,
246
- max=parameter.max_value,
246
+ min=parameter.min,
247
+ max=parameter.max,
248
+ step=1,
247
249
  description=parameter.name,
248
- continuous=continuous,
249
- layout=widgets.Layout(width=width, margin=margin),
250
+ continuous_update=continuous,
250
251
  style={"description_width": description_width},
252
+ layout=widgets.Layout(width=width, margin=margin),
251
253
  )
252
254
 
253
255
  def matches_parameter(self, parameter: IntegerParameter) -> bool:
254
- """Check if the widget matches the parameter."""
256
+ """Check if the widget values match the parameter."""
255
257
  return (
256
- self.value == parameter.value
257
- and self._widget.min == parameter.min_value
258
- 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
259
262
  )
260
263
 
261
264
  def extra_updates_from_parameter(self, parameter: IntegerParameter) -> None:
262
- """Extra updates from the parameter."""
265
+ """Update the widget attributes from the parameter."""
263
266
  current_value = self._widget.value
264
- self._widget.min = parameter.min_value
265
- self._widget.max = parameter.max_value
266
- 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))
267
270
 
268
271
 
269
272
  class FloatWidget(BaseWidget[FloatParameter, widgets.FloatSlider]):
@@ -277,32 +280,35 @@ class FloatWidget(BaseWidget[FloatParameter, widgets.FloatSlider]):
277
280
  margin: str = "3px 0px",
278
281
  description_width: str = "initial",
279
282
  ) -> widgets.FloatSlider:
283
+ """Create the float slider widget."""
280
284
  return widgets.FloatSlider(
281
285
  value=parameter.value,
282
- min=parameter.min_value,
283
- max=parameter.max_value,
286
+ min=parameter.min,
287
+ max=parameter.max,
284
288
  step=parameter.step,
285
289
  description=parameter.name,
286
- continuous=continuous,
287
- layout=widgets.Layout(width=width, margin=margin),
290
+ continuous_update=continuous,
288
291
  style={"description_width": description_width},
292
+ layout=widgets.Layout(width=width, margin=margin),
289
293
  )
290
294
 
291
295
  def matches_parameter(self, parameter: FloatParameter) -> bool:
292
- """Check if the widget matches the parameter."""
296
+ """Check if the widget values match the parameter."""
293
297
  return (
294
- self.value == parameter.value
295
- and self._widget.min == parameter.min_value
296
- 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
297
303
  )
298
304
 
299
305
  def extra_updates_from_parameter(self, parameter: FloatParameter) -> None:
300
- """Extra updates from the parameter."""
306
+ """Update the widget attributes from the parameter."""
301
307
  current_value = self._widget.value
302
- self._widget.min = parameter.min_value
303
- self._widget.max = parameter.max_value
308
+ self._widget.min = parameter.min
309
+ self._widget.max = parameter.max
304
310
  self._widget.step = parameter.step
305
- self.value = max(parameter.min_value, min(parameter.max_value, current_value))
311
+ self.value = max(parameter.min, min(parameter.max, current_value))
306
312
 
307
313
 
308
314
  class IntegerRangeWidget(BaseWidget[IntegerRangeParameter, widgets.IntRangeSlider]):
@@ -316,33 +322,39 @@ class IntegerRangeWidget(BaseWidget[IntegerRangeParameter, widgets.IntRangeSlide
316
322
  margin: str = "3px 0px",
317
323
  description_width: str = "initial",
318
324
  ) -> widgets.IntRangeSlider:
325
+ """Create the integer range slider widget."""
326
+ low, high = parameter.value
319
327
  return widgets.IntRangeSlider(
320
- value=parameter.value,
321
- min=parameter.min_value,
322
- max=parameter.max_value,
328
+ value=[low, high],
329
+ min=parameter.min,
330
+ max=parameter.max,
331
+ step=1,
323
332
  description=parameter.name,
324
- continuous=continuous,
325
- layout=widgets.Layout(width=width, margin=margin),
333
+ continuous_update=continuous,
326
334
  style={"description_width": description_width},
335
+ layout=widgets.Layout(width=width, margin=margin),
327
336
  )
328
337
 
329
338
  def matches_parameter(self, parameter: IntegerRangeParameter) -> bool:
330
- """Check if the widget matches the parameter."""
339
+ """Check if the widget values match the parameter."""
340
+ low, high = parameter.value
331
341
  return (
332
- self.value == parameter.value
333
- and self._widget.min == parameter.min_value
334
- 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
335
347
  )
336
348
 
337
349
  def extra_updates_from_parameter(self, parameter: IntegerRangeParameter) -> None:
338
- """Extra updates from the parameter."""
339
- current_value = self._widget.value
340
- self._widget.min = parameter.min_value
341
- self._widget.max = parameter.max_value
342
- low, high = current_value
343
- low = max(parameter.min_value, min(parameter.max_value, low))
344
- high = max(parameter.min_value, min(parameter.max_value, high))
345
- 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]
346
358
 
347
359
 
348
360
  class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]):
@@ -356,35 +368,41 @@ class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]
356
368
  margin: str = "3px 0px",
357
369
  description_width: str = "initial",
358
370
  ) -> widgets.FloatRangeSlider:
371
+ """Create the float range slider widget."""
372
+ low, high = parameter.value
359
373
  return widgets.FloatRangeSlider(
360
- value=parameter.value,
361
- min=parameter.min_value,
362
- max=parameter.max_value,
374
+ value=[low, high],
375
+ min=parameter.min,
376
+ max=parameter.max,
363
377
  step=parameter.step,
364
378
  description=parameter.name,
365
- continuous=continuous,
366
- layout=widgets.Layout(width=width, margin=margin),
379
+ continuous_update=continuous,
367
380
  style={"description_width": description_width},
381
+ layout=widgets.Layout(width=width, margin=margin),
368
382
  )
369
383
 
370
384
  def matches_parameter(self, parameter: FloatRangeParameter) -> bool:
371
- """Check if the widget matches the parameter."""
385
+ """Check if the widget values match the parameter."""
386
+ low, high = parameter.value
372
387
  return (
373
- self.value == parameter.value
374
- and self._widget.min == parameter.min_value
375
- 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
376
394
  )
377
395
 
378
396
  def extra_updates_from_parameter(self, parameter: FloatRangeParameter) -> None:
379
- """Extra updates from the parameter."""
380
- current_value = self._widget.value
381
- self._widget.min = parameter.min_value
382
- 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
383
401
  self._widget.step = parameter.step
384
- low, high = current_value
385
- low = max(parameter.min_value, min(parameter.max_value, low))
386
- high = max(parameter.min_value, min(parameter.max_value, high))
387
- 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]
388
406
 
389
407
 
390
408
  class UnboundedIntegerWidget(BaseWidget[UnboundedIntegerParameter, widgets.IntText]):