syd 0.1.7__py3-none-any.whl → 1.0.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.
@@ -1,258 +0,0 @@
1
- from typing import Dict, Any, Optional
2
- from dataclasses import dataclass
3
- from contextlib import contextmanager
4
- import ipywidgets as widgets
5
- from IPython.display import display
6
- import matplotlib.pyplot as plt
7
- import warnings
8
- from ..parameters import ParameterUpdateWarning
9
-
10
- from ..viewer import Viewer
11
- from .widgets import BaseWidget, create_widget
12
-
13
-
14
- @contextmanager
15
- def _plot_context():
16
- plt.ioff()
17
- try:
18
- yield
19
- finally:
20
- plt.ion()
21
-
22
-
23
- @dataclass
24
- class LayoutConfig:
25
- """Configuration for the viewer layout."""
26
-
27
- controls_position: str = "left" # Options are: 'left', 'top'
28
- figure_width: float = 8.0
29
- figure_height: float = 6.0
30
- controls_width_percent: int = 30
31
-
32
- def __post_init__(self):
33
- valid_positions = ["left", "top"]
34
- if self.controls_position not in valid_positions:
35
- raise ValueError(
36
- f"Invalid controls position: {self.controls_position}. Must be one of {valid_positions}"
37
- )
38
-
39
- @property
40
- def is_horizontal(self) -> bool:
41
- return self.controls_position == "left"
42
-
43
-
44
- class NotebookDeployer:
45
- """
46
- A deployment system for Viewer in Jupyter notebooks using ipywidgets.
47
- Built around the parameter widget system for clean separation of concerns.
48
- """
49
-
50
- def __init__(
51
- self,
52
- viewer: Viewer,
53
- layout_config: Optional[LayoutConfig] = None,
54
- continuous: bool = False,
55
- suppress_warnings: bool = False,
56
- ):
57
- if not isinstance(viewer, Viewer): # type: ignore
58
- raise TypeError(f"viewer must be an Viewer, got {type(viewer).__name__}")
59
-
60
- self.viewer = viewer
61
- self.config = layout_config or LayoutConfig()
62
- self.continuous = continuous
63
- self.suppress_warnings = suppress_warnings
64
-
65
- # Initialize containers
66
- self.parameter_widgets: Dict[str, BaseWidget] = {}
67
- self.layout_widgets = self._create_layout_controls()
68
- self.plot_output = widgets.Output()
69
- self._canvas_widget = None
70
-
71
- # Store current figure
72
- self._current_figure = None
73
- # Flag to prevent circular updates
74
- self._updating = False
75
-
76
- def _create_layout_controls(self) -> Dict[str, widgets.Widget]:
77
- """Create widgets for controlling the layout."""
78
- controls: Dict[str, widgets.Widget] = {}
79
-
80
- # Controls width slider for horizontal layouts
81
- if self.config.is_horizontal:
82
- controls["controls_width"] = widgets.IntSlider(
83
- value=self.config.controls_width_percent,
84
- min=20,
85
- max=80,
86
- description="Controls Width %",
87
- continuous=True,
88
- layout=widgets.Layout(width="95%"),
89
- style={"description_width": "initial"},
90
- )
91
- controls["controls_width"].observe(
92
- self._handle_container_width_change, names="value"
93
- )
94
-
95
- return controls
96
-
97
- def _create_parameter_widgets(self) -> None:
98
- """Create widget instances for all parameters."""
99
- for name, param in self.viewer.parameters.items():
100
- widget = create_widget(
101
- param,
102
- continuous=self.continuous,
103
- )
104
-
105
- # Store in widget dict
106
- self.parameter_widgets[name] = widget
107
-
108
- def _handle_widget_engagement(self, name: str) -> None:
109
- """Handle engagement with an interactive widget."""
110
- if self._updating:
111
- print(
112
- "Already updating -- there's a circular dependency!"
113
- "This is probably caused by failing to disable callbacks for a parameter."
114
- "It's a bug --- tell the developer on github issues please."
115
- )
116
- return
117
-
118
- try:
119
- self._updating = True
120
-
121
- # Optionally suppress warnings during parameter updates
122
- with warnings.catch_warnings():
123
- if self.suppress_warnings:
124
- warnings.filterwarnings("ignore", category=ParameterUpdateWarning)
125
-
126
- widget = self.parameter_widgets[name]
127
-
128
- if widget._is_action:
129
- parameter = self.viewer.parameters[name]
130
- parameter.callback(self.viewer.state)
131
- else:
132
- self.viewer.set_parameter_value(name, widget.value)
133
-
134
- # Update any widgets that changed due to dependencies
135
- self._sync_widgets_with_state(exclude=name)
136
-
137
- # Update the plot
138
- self._update_plot()
139
-
140
- finally:
141
- self._updating = False
142
-
143
- def _handle_action(self, name: str) -> None:
144
- """Handle actions for parameter widgets."""
145
-
146
- def _sync_widgets_with_state(self, exclude: Optional[str] = None) -> None:
147
- """Sync widget values with viewer state."""
148
- for name, parameter in self.viewer.parameters.items():
149
- if name == exclude:
150
- continue
151
-
152
- widget = self.parameter_widgets[name]
153
- if not widget.matches_parameter(parameter):
154
- widget.update_from_parameter(parameter)
155
-
156
- def _handle_figure_size_change(self, change: Dict[str, Any]) -> None:
157
- """Handle changes to figure dimensions."""
158
- if self._current_figure is None:
159
- return
160
-
161
- self._redraw_plot()
162
-
163
- def _handle_container_width_change(self, change: Dict[str, Any]) -> None:
164
- """Handle changes to container width proportions."""
165
- width_percent = self.layout_widgets["controls_width"].value
166
- self.config.controls_width_percent = width_percent
167
-
168
- # Update container widths
169
- self.widgets_container.layout.width = f"{width_percent}%"
170
- self.plot_container.layout.width = f"{100 - width_percent}%"
171
-
172
- def _update_plot(self) -> None:
173
- """Update the plot with current state."""
174
- state = self.viewer.state
175
-
176
- with _plot_context():
177
- new_fig = self.viewer.plot(state)
178
- plt.close(self._current_figure) # Close old figure
179
- self._current_figure = new_fig
180
-
181
- # Clear previous output and display new figure
182
- self.plot_output.clear_output(wait=True)
183
- with self.plot_output:
184
- # Make sure the canvas is created and displayed
185
- if self._canvas_widget is None:
186
- self._canvas_widget = self._current_figure.canvas
187
- display(self._current_figure.canvas)
188
-
189
- def _redraw_plot(self) -> None:
190
- """Clear and redraw the plot in the output widget."""
191
- if self._canvas_widget is not None:
192
- self._canvas_widget.draw()
193
-
194
- def _create_layout(self) -> widgets.Widget:
195
- """Create the main layout combining controls and plot."""
196
- # Create layout controls section
197
- layout_box = widgets.VBox(
198
- [widgets.HTML("<b>Layout Controls</b>")]
199
- + list(self.layout_widgets.values()),
200
- layout=widgets.Layout(margin="10px 0px"),
201
- )
202
-
203
- # Set up parameter widgets with their observe callbacks
204
- for name, widget in self.parameter_widgets.items():
205
- widget.observe(lambda change, n=name: self._handle_widget_engagement(n))
206
-
207
- # Create parameter controls section
208
- param_box = widgets.VBox(
209
- [widgets.HTML("<b>Parameters</b>")]
210
- + [w.widget for w in self.parameter_widgets.values()],
211
- layout=widgets.Layout(margin="10px 0px"),
212
- )
213
-
214
- # Combine all controls
215
- self.widgets_container = widgets.VBox(
216
- [param_box, layout_box],
217
- layout=widgets.Layout(
218
- width=(
219
- f"{self.config.controls_width_percent}%"
220
- if self.config.is_horizontal
221
- else "100%"
222
- ),
223
- padding="10px",
224
- overflow_y="auto",
225
- ),
226
- )
227
-
228
- # Create plot container
229
- self.plot_container = widgets.VBox(
230
- [self.plot_output],
231
- layout=widgets.Layout(
232
- width=(
233
- f"{100 - self.config.controls_width_percent}%"
234
- if self.config.is_horizontal
235
- else "100%"
236
- ),
237
- padding="10px",
238
- ),
239
- )
240
-
241
- # Create final layout based on configuration
242
- if self.config.controls_position == "left":
243
- return widgets.HBox([self.widgets_container, self.plot_container])
244
- else:
245
- return widgets.VBox([self.widgets_container, self.plot_container])
246
-
247
- def deploy(self) -> None:
248
- """Deploy the interactive viewer with proper state management."""
249
- with self.viewer._deploy_app():
250
- # Create widgets
251
- self._create_parameter_widgets()
252
-
253
- # Create and display layout
254
- layout = self._create_layout()
255
- display(layout)
256
-
257
- # Create initial plot
258
- self._update_plot()
@@ -1 +0,0 @@
1
- from .deployer import PlotlyDeployer