PyMieSim 3.6.0__cp313-cp313-win_amd64.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.
- PyMieSim/__init__.py +16 -0
- PyMieSim/__main__.py +9 -0
- PyMieSim/_version.py +21 -0
- PyMieSim/binary/__init__.py +0 -0
- PyMieSim/binary/interface_detector.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_detector.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_detector.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_detector.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_experiment.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_scatterer.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_sets.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp310-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp311-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp312-win_amd64.pyd +0 -0
- PyMieSim/binary/interface_source.cp313-win_amd64.pyd +0 -0
- PyMieSim/binary/libcpp_coordinates.a +0 -0
- PyMieSim/binary/libcpp_detector.a +0 -0
- PyMieSim/binary/libcpp_experiment.a +0 -0
- PyMieSim/binary/libcpp_fibonacci.a +0 -0
- PyMieSim/binary/libcpp_mode_field.a +0 -0
- PyMieSim/binary/libcpp_sets.a +0 -0
- PyMieSim/binary/libcpp_source.a +0 -0
- PyMieSim/directories.py +31 -0
- PyMieSim/experiment/__init__.py +1 -0
- PyMieSim/experiment/dataframe_subclass.py +220 -0
- PyMieSim/experiment/detector/__init__.py +2 -0
- PyMieSim/experiment/detector/base.py +169 -0
- PyMieSim/experiment/detector/coherent_mode.py +50 -0
- PyMieSim/experiment/detector/photodiode.py +52 -0
- PyMieSim/experiment/scatterer/__init__.py +4 -0
- PyMieSim/experiment/scatterer/base.py +98 -0
- PyMieSim/experiment/scatterer/core_shell.py +82 -0
- PyMieSim/experiment/scatterer/cylinder.py +63 -0
- PyMieSim/experiment/scatterer/sphere.py +66 -0
- PyMieSim/experiment/setup.py +356 -0
- PyMieSim/experiment/source/__init__.py +2 -0
- PyMieSim/experiment/source/base.py +85 -0
- PyMieSim/experiment/source/gaussian.py +60 -0
- PyMieSim/experiment/source/planewave.py +69 -0
- PyMieSim/experiment/utils.py +132 -0
- PyMieSim/gui/__init__.py +0 -0
- PyMieSim/gui/helper.py +60 -0
- PyMieSim/gui/interface.py +136 -0
- PyMieSim/gui/section.py +606 -0
- PyMieSim/mesh.py +368 -0
- PyMieSim/polarization.py +174 -0
- PyMieSim/single/__init__.py +48 -0
- PyMieSim/single/detector/__init__.py +2 -0
- PyMieSim/single/detector/base.py +271 -0
- PyMieSim/single/detector/coherent.py +99 -0
- PyMieSim/single/detector/uncoherent.py +105 -0
- PyMieSim/single/representations.py +734 -0
- PyMieSim/single/scatterer/__init__.py +4 -0
- PyMieSim/single/scatterer/base.py +405 -0
- PyMieSim/single/scatterer/core_shell.py +126 -0
- PyMieSim/single/scatterer/cylinder.py +113 -0
- PyMieSim/single/scatterer/sphere.py +108 -0
- PyMieSim/single/source/__init__.py +3 -0
- PyMieSim/single/source/base.py +7 -0
- PyMieSim/single/source/gaussian.py +137 -0
- PyMieSim/single/source/planewave.py +97 -0
- PyMieSim/special_functions.py +81 -0
- PyMieSim/units.py +130 -0
- PyMieSim/validation_data/bohren_huffman/figure_810.csv +245 -0
- PyMieSim/validation_data/bohren_huffman/figure_87.csv +2 -0
- PyMieSim/validation_data/bohren_huffman/figure_88.csv +2 -0
- PyMieSim/validation_data/pymiescatt/example_coreshell_0.csv +41 -0
- PyMieSim/validation_data/pymiescatt/example_coreshell_1.csv +401 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_0.csv +51 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_1.csv +801 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_2.csv +41 -0
- PyMieSim/validation_data/pymiescatt/example_shpere_3.csv +401 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_0.csv +51 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_1.csv +801 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_2.csv +41 -0
- PyMieSim/validation_data/pymiescatt/example_sphere_3.csv +401 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca.csv +800 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_1.csv +400 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca_coreshell_2.csv +400 -0
- PyMieSim/validation_data/pymiescatt/validation_Qsca_medium.csv +800 -0
- PyMieSim/validation_data/pymiescatt/validation_coreshell.csv +81 -0
- PyMieSim/validation_data/pymiescatt/validation_sphere.csv +801 -0
- lib/libZBessel.a +0 -0
- lib/lib_ZBessel.a +0 -0
- lib/libcpp_base_scatterer.a +0 -0
- lib/libcpp_coreshell.a +0 -0
- lib/libcpp_cylinder.a +0 -0
- lib/libcpp_sphere.a +0 -0
- pymiesim-3.6.0.dist-info/METADATA +246 -0
- pymiesim-3.6.0.dist-info/RECORD +101 -0
- pymiesim-3.6.0.dist-info/WHEEL +5 -0
- pymiesim-3.6.0.dist-info/licenses/LICENSE +21 -0
PyMieSim/gui/section.py
ADDED
@@ -0,0 +1,606 @@
|
|
1
|
+
from dash import html, dcc, State, Input, Output
|
2
|
+
from PyMieSim.units import nanometer, degree, milliwatt
|
3
|
+
from PyMieSim.gui.helper import parse_string_to_array_or_float
|
4
|
+
import numpy
|
5
|
+
|
6
|
+
length_units = nanometer
|
7
|
+
power_units = milliwatt
|
8
|
+
angle_units = degree
|
9
|
+
|
10
|
+
class Section:
|
11
|
+
"""
|
12
|
+
A base class to define shared functionality for different sections in the optical simulation interface.
|
13
|
+
|
14
|
+
This class provides methods for creating dropdown menus, input fields, and setting up callbacks for
|
15
|
+
interaction with the Dash application.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def create_call_backs(self):
|
19
|
+
"""
|
20
|
+
Create and set up callbacks for the section.
|
21
|
+
|
22
|
+
This method defines the `call_backs` attribute by combining the dropdown and input field callbacks
|
23
|
+
and calls the `update_callbacks` method to set up the necessary Dash callbacks.
|
24
|
+
|
25
|
+
Attributes
|
26
|
+
----------
|
27
|
+
call_backs : list
|
28
|
+
A list of Dash `Input` objects corresponding to the dropdown and input fields of the section.
|
29
|
+
"""
|
30
|
+
self.call_backs = [Input(f"{self.name}-dropdown", "value")] + [Input(id, "value") for id in self.input_id]
|
31
|
+
self.update_callbacks()
|
32
|
+
|
33
|
+
def create(self):
|
34
|
+
"""
|
35
|
+
Create the layout for the section.
|
36
|
+
|
37
|
+
This method generates the HTML layout for the section, including a title, a dropdown menu to select
|
38
|
+
the section type, and input fields for relevant parameters.
|
39
|
+
|
40
|
+
Returns
|
41
|
+
-------
|
42
|
+
tuple
|
43
|
+
A tuple containing:
|
44
|
+
- html.Div : The main layout for the section.
|
45
|
+
- html.Div : A hidden placeholder for storing section-related data.
|
46
|
+
"""
|
47
|
+
title = html.H2(self.title, style={'color': self.color})
|
48
|
+
type_dropdown = html.Div([
|
49
|
+
html.Label(f"Select {self.title} Type:"),
|
50
|
+
html.Div([dcc.Dropdown(id=self.dropdown_id, options=self.dropdown_options, value=self.default_value)])
|
51
|
+
],
|
52
|
+
style={'margin-bottom': '20px'}
|
53
|
+
)
|
54
|
+
|
55
|
+
layout = [
|
56
|
+
title,
|
57
|
+
type_dropdown,
|
58
|
+
]
|
59
|
+
|
60
|
+
for input in self.inputs.values():
|
61
|
+
user_option = html.Div([
|
62
|
+
html.Label(input['label'], style={'margin-right': '10px'}),
|
63
|
+
dcc.Input(id=input['id'], type='text', value=input['default'], style={'width': '200px'})
|
64
|
+
],
|
65
|
+
style={'margin-bottom': '10px'}
|
66
|
+
)
|
67
|
+
layout.append(user_option)
|
68
|
+
|
69
|
+
return (
|
70
|
+
html.Div(layout, style={'padding': '10px', 'border': '1px solid black', 'margin': '10px'}),
|
71
|
+
html.Div(id=f"{self.name}-dropdown-data", style={"display": "none"}) # Hidden placeholder for data
|
72
|
+
)
|
73
|
+
|
74
|
+
def update_callbacks(self):
|
75
|
+
"""
|
76
|
+
Set up callbacks to update the data dictionary and x-axis options.
|
77
|
+
"""
|
78
|
+
@self.app.callback(Output(f"{self.dropdown_id}-data", "children"), *self.call_backs)
|
79
|
+
def update_data(object_type, *inputs):
|
80
|
+
"""
|
81
|
+
Update the data dictionary and x-axis options based on user inputs.
|
82
|
+
|
83
|
+
Parameters
|
84
|
+
----------
|
85
|
+
object_type : str
|
86
|
+
The type of the source selected in the dropdown.
|
87
|
+
inputs : list
|
88
|
+
The values of the inputs from the source section.
|
89
|
+
|
90
|
+
Returns
|
91
|
+
-------
|
92
|
+
str
|
93
|
+
A message indicating the data has been updated.
|
94
|
+
"""
|
95
|
+
# Map the input values to their corresponding keys
|
96
|
+
input_values = dict(zip(self.inputs.keys(), inputs))
|
97
|
+
|
98
|
+
# Parse inputs and update _xaxis_options if input is an array
|
99
|
+
self._xaxis_options = []
|
100
|
+
for key, value in input_values.items():
|
101
|
+
try:
|
102
|
+
parsed_value = parse_string_to_array_or_float(value)
|
103
|
+
if isinstance(parsed_value, numpy.ndarray) and parsed_value.size > 1:
|
104
|
+
self._xaxis_options.append(f"{self.name}:{key}")
|
105
|
+
self._xaxis_options_length.append(len(parsed_value))
|
106
|
+
except ValueError:
|
107
|
+
pass # Ignore invalid inputs
|
108
|
+
|
109
|
+
# Update the data dictionary
|
110
|
+
self.data = {'type': object_type, **input_values}
|
111
|
+
|
112
|
+
return "Data Updated"
|
113
|
+
|
114
|
+
|
115
|
+
class SourceSection(Section):
|
116
|
+
"""
|
117
|
+
A class to manage the Source section of the optical simulation interface.
|
118
|
+
|
119
|
+
This class handles the inputs and callbacks related to the light source, such as its type,
|
120
|
+
wavelength, optical power, numerical aperture, and polarization.
|
121
|
+
|
122
|
+
Parameters
|
123
|
+
----------
|
124
|
+
app : Dash
|
125
|
+
The Dash application instance.
|
126
|
+
"""
|
127
|
+
|
128
|
+
name = 'source'
|
129
|
+
|
130
|
+
def __init__(self, app):
|
131
|
+
"""
|
132
|
+
Initialize the SourceSection class.
|
133
|
+
|
134
|
+
Parameters
|
135
|
+
----------
|
136
|
+
app : Dash
|
137
|
+
The Dash application instance.
|
138
|
+
"""
|
139
|
+
self.app = app
|
140
|
+
self.title = "Source"
|
141
|
+
self.color = "blue"
|
142
|
+
self.dropdown_options = [
|
143
|
+
{'label': 'Laser', 'value': 'laser'}
|
144
|
+
]
|
145
|
+
self.dropdown_id = f'{self.name}-dropdown'
|
146
|
+
self.default_value = 'laser'
|
147
|
+
|
148
|
+
self.inputs = {
|
149
|
+
"wavelength": {
|
150
|
+
"id": f"{self.name}-wavelength",
|
151
|
+
"label": f"Wavelength [{length_units}]",
|
152
|
+
"default": "650"
|
153
|
+
},
|
154
|
+
"optical_power": {
|
155
|
+
"id": f"{self.name}-optical_power",
|
156
|
+
"label": f"Power [{power_units}]",
|
157
|
+
"default": "5"
|
158
|
+
},
|
159
|
+
"NA": {
|
160
|
+
"id": f"{self.name}-NA",
|
161
|
+
"label": "Numerical aperture [NA]",
|
162
|
+
"default": "0.2"
|
163
|
+
},
|
164
|
+
"polarization": {
|
165
|
+
"id": f"{self.name}-polarization",
|
166
|
+
"label": f"Polarization [{angle_units}]",
|
167
|
+
"default": "0"
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
# Initialize the x-axis options attribute
|
172
|
+
self._xaxis_options = []
|
173
|
+
self._xaxis_options_length = []
|
174
|
+
|
175
|
+
self.call_backs = [Input(f"{self.name}-dropdown", "value")] + [
|
176
|
+
Input(input_data["id"], "value") for input_data in self.inputs.values()
|
177
|
+
]
|
178
|
+
|
179
|
+
self.update_callbacks()
|
180
|
+
|
181
|
+
|
182
|
+
class ScattererSection(Section):
|
183
|
+
"""
|
184
|
+
A class to manage the Scatterer section of the optical simulation interface.
|
185
|
+
|
186
|
+
This class handles the inputs and callbacks related to the scatterer, such as its type,
|
187
|
+
diameter, refractive index property, and medium refractive index.
|
188
|
+
|
189
|
+
Parameters
|
190
|
+
----------
|
191
|
+
app : Dash
|
192
|
+
The Dash application instance.
|
193
|
+
"""
|
194
|
+
|
195
|
+
name = 'scatterer'
|
196
|
+
|
197
|
+
def __init__(self, app):
|
198
|
+
"""
|
199
|
+
Initialize the ScattererSection class.
|
200
|
+
|
201
|
+
Parameters
|
202
|
+
----------
|
203
|
+
app : Dash
|
204
|
+
The Dash application instance.
|
205
|
+
"""
|
206
|
+
self.app = app
|
207
|
+
self.title = "Scatterer"
|
208
|
+
self.color = "green"
|
209
|
+
self.dropdown_options = [
|
210
|
+
{'label': 'Sphere', 'value': 'sphere'},
|
211
|
+
]
|
212
|
+
self.dropdown_id = 'scatterer-dropdown'
|
213
|
+
self.default_value = 'sphere'
|
214
|
+
|
215
|
+
self.inputs = {
|
216
|
+
"diameter": {
|
217
|
+
"id": f"{self.name}-diameter",
|
218
|
+
"label": f"Diameter [{length_units}]",
|
219
|
+
"default": "100:20000:200"
|
220
|
+
},
|
221
|
+
"property": {
|
222
|
+
"id": f"{self.name}-property",
|
223
|
+
"label": "Scatterer Property",
|
224
|
+
"default": "1.5, 1.6"
|
225
|
+
},
|
226
|
+
"medium_property": {
|
227
|
+
"id": f"{self.name}-medium-property",
|
228
|
+
"label": "Medium Property",
|
229
|
+
"default": "1.33"
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
# Initialize x-axis options
|
234
|
+
self._xaxis_options = []
|
235
|
+
self._xaxis_options_length = []
|
236
|
+
|
237
|
+
self.call_backs = [Input(f"{self.name}-dropdown", "value")] + [
|
238
|
+
Input(input_data["id"], "value") for input_data in self.inputs.values()
|
239
|
+
]
|
240
|
+
|
241
|
+
self.update_callbacks()
|
242
|
+
|
243
|
+
|
244
|
+
class DetectorSection(Section):
|
245
|
+
"""
|
246
|
+
A class to manage the Detector section of the optical simulation interface.
|
247
|
+
|
248
|
+
This class handles the inputs and callbacks related to the detector, such as numerical aperture,
|
249
|
+
offsets, sampling, and polarization filter.
|
250
|
+
|
251
|
+
Parameters
|
252
|
+
----------
|
253
|
+
app : Dash
|
254
|
+
The Dash application instance.
|
255
|
+
"""
|
256
|
+
|
257
|
+
name = 'detector'
|
258
|
+
|
259
|
+
def __init__(self, app):
|
260
|
+
"""
|
261
|
+
Initialize the DetectorSection class.
|
262
|
+
|
263
|
+
Parameters
|
264
|
+
----------
|
265
|
+
app : Dash
|
266
|
+
The Dash application instance.
|
267
|
+
"""
|
268
|
+
self.app = app
|
269
|
+
self.title = "Detector"
|
270
|
+
self.color = "red"
|
271
|
+
self.dropdown_options = [
|
272
|
+
{'label': 'Photodiode', 'value': 'photodiode'},
|
273
|
+
]
|
274
|
+
self.dropdown_id = 'detector-dropdown'
|
275
|
+
self.default_value = 'photodiode'
|
276
|
+
|
277
|
+
self.inputs = {
|
278
|
+
"NA": {
|
279
|
+
"id": f"{self.name}-na",
|
280
|
+
"label": "Numerical aperture",
|
281
|
+
"default": "0.9"
|
282
|
+
},
|
283
|
+
"phi_offset": {
|
284
|
+
"id": f"{self.name}-phi-offset",
|
285
|
+
"label": f"Phi offset [{angle_units}]",
|
286
|
+
"default": "0"
|
287
|
+
},
|
288
|
+
"gamma_offset": {
|
289
|
+
"id": f"{self.name}-gamma-offset",
|
290
|
+
"label": f"Gamma offset [{angle_units}]",
|
291
|
+
"default": "0"
|
292
|
+
},
|
293
|
+
"sampling": {
|
294
|
+
"id": f"{self.name}-sampling",
|
295
|
+
"label": "Sampling",
|
296
|
+
"default": "1000"
|
297
|
+
},
|
298
|
+
"polarization_filter": {
|
299
|
+
"id": f"{self.name}-polarization_filter",
|
300
|
+
"label": f"Polarization Filter [{angle_units}]",
|
301
|
+
"default": "None"
|
302
|
+
}
|
303
|
+
}
|
304
|
+
|
305
|
+
# Initialize x-axis options
|
306
|
+
self._xaxis_options = []
|
307
|
+
self._xaxis_options_length = []
|
308
|
+
|
309
|
+
self.call_backs = [Input(f"{self.name}-dropdown", "value")] + [
|
310
|
+
Input(input_data["id"], "value") for input_data in self.inputs.values()
|
311
|
+
]
|
312
|
+
|
313
|
+
self.update_callbacks()
|
314
|
+
|
315
|
+
|
316
|
+
class MeasureSection:
|
317
|
+
"""
|
318
|
+
A class to manage the Measure section of the optical simulation interface.
|
319
|
+
|
320
|
+
Parameters
|
321
|
+
----------
|
322
|
+
app : Dash
|
323
|
+
The Dash application instance.
|
324
|
+
scatterer_section : Section
|
325
|
+
The Scatterer section instance providing scatterer-related inputs.
|
326
|
+
source_section : Section
|
327
|
+
The Source section instance providing source-related inputs.
|
328
|
+
detector_section : Section
|
329
|
+
The Detector section instance providing detector-related inputs.
|
330
|
+
"""
|
331
|
+
|
332
|
+
def __init__(self, app, scatterer_section, source_section, detector_section):
|
333
|
+
self.app = app
|
334
|
+
self.scatterer_section = scatterer_section
|
335
|
+
self.source_section = source_section
|
336
|
+
self.detector_section = detector_section
|
337
|
+
self.dropdown_id = "measure-input"
|
338
|
+
self.plot_button_id = "generate-plot"
|
339
|
+
self.xaxis_input_id = "xaxis-input"
|
340
|
+
self.filename_input_id = "filename-input"
|
341
|
+
self.save_button_id = "save-data"
|
342
|
+
self.plot_ready_store_id = "plot-ready"
|
343
|
+
self.data = "Qsca" # Default measure value
|
344
|
+
self.download_id = "download-data"
|
345
|
+
|
346
|
+
def get_measure_dropdown(self) -> dcc.Dropdown:
|
347
|
+
"""
|
348
|
+
Creates a dropdown menu for selecting the measure type.
|
349
|
+
|
350
|
+
Returns
|
351
|
+
-------
|
352
|
+
dcc.Dropdown
|
353
|
+
A Dash dropdown component for selecting measures such as Qsca, Qext, etc.
|
354
|
+
"""
|
355
|
+
return dcc.Dropdown(
|
356
|
+
id=self.dropdown_id,
|
357
|
+
options=[
|
358
|
+
{'label': 'Qsca', 'value': 'Qsca'},
|
359
|
+
{'label': 'Qext', 'value': 'Qext'},
|
360
|
+
{'label': 'Qabs', 'value': 'Qabs'},
|
361
|
+
{'label': 'Qpr', 'value': 'Qpr'},
|
362
|
+
{'label': 'Csca', 'value': 'Csca'},
|
363
|
+
{'label': 'Cext', 'value': 'Cext'},
|
364
|
+
{'label': 'Cabs', 'value': 'Cabs'},
|
365
|
+
{'label': 'Cpr', 'value': 'Cpr'},
|
366
|
+
{'label': 'g', 'value': 'g'},
|
367
|
+
{'label': 'Coupling', 'value': 'coupling'}
|
368
|
+
],
|
369
|
+
value='Qsca',
|
370
|
+
style={'margin-right': '10px', 'margin-bottom': '0px', 'height': '36px', 'width': '200px'}
|
371
|
+
)
|
372
|
+
|
373
|
+
def get_xaxis_dropdown(self) -> dcc.Dropdown:
|
374
|
+
"""
|
375
|
+
Creates a dropdown menu for selecting the x-axis parameter.
|
376
|
+
|
377
|
+
Returns
|
378
|
+
-------
|
379
|
+
dcc.Dropdown
|
380
|
+
A Dash dropdown component populated dynamically with x-axis options.
|
381
|
+
"""
|
382
|
+
return dcc.Dropdown(
|
383
|
+
id=self.xaxis_input_id,
|
384
|
+
options=[],
|
385
|
+
value=None,
|
386
|
+
style={'margin-right': '10px', 'height': '36px', 'width': '300px'}
|
387
|
+
)
|
388
|
+
|
389
|
+
def get_filename_input(self) -> dcc.Input:
|
390
|
+
return dcc.Input(
|
391
|
+
id=self.filename_input_id,
|
392
|
+
type="text",
|
393
|
+
placeholder="Enter filename",
|
394
|
+
value="output.csv",
|
395
|
+
style={'margin-right': '10px', 'height': '36px', 'width': '200px'}
|
396
|
+
)
|
397
|
+
|
398
|
+
def get_plot_button(self) -> html.Button:
|
399
|
+
return html.Button("Generate Plot", id=self.plot_button_id, n_clicks=0, disabled=True, style={'height': '36px', 'background-color': 'grey', 'color': 'white'})
|
400
|
+
|
401
|
+
def get_save_button(self) -> html.Button:
|
402
|
+
return html.Button("Save Data", id=self.save_button_id, n_clicks=0, disabled=True, style={'height': '36px', 'background-color': 'grey', 'color': 'white'})
|
403
|
+
|
404
|
+
def create(self) -> html.Div:
|
405
|
+
"""
|
406
|
+
Creates the layout for the Measure section, including dropdowns and buttons.
|
407
|
+
|
408
|
+
Returns
|
409
|
+
-------
|
410
|
+
html.Div
|
411
|
+
A Dash HTML Div containing the Measure section components.
|
412
|
+
"""
|
413
|
+
return html.Div([
|
414
|
+
dcc.Store(id=self.plot_ready_store_id, data=False), # Track if plot is ready
|
415
|
+
dcc.Download(id=self.download_id),
|
416
|
+
html.Div(
|
417
|
+
[self.get_measure_dropdown(), self.get_xaxis_dropdown(), self.get_plot_button()],
|
418
|
+
style={'display': 'flex', 'align-items': 'center', 'justify-content': 'center'}
|
419
|
+
),
|
420
|
+
html.Div(
|
421
|
+
[self.get_filename_input(), self.get_save_button()],
|
422
|
+
style={'display': 'flex', 'align-items': 'center', 'justify-content': 'center', 'margin-top': '20px'}
|
423
|
+
)
|
424
|
+
])
|
425
|
+
|
426
|
+
def update_callbacks(self, callback_func, save_func):
|
427
|
+
"""
|
428
|
+
Updates the callbacks for the Measure section components.
|
429
|
+
|
430
|
+
Parameters
|
431
|
+
----------
|
432
|
+
callback_func : callable
|
433
|
+
A function to generate the plot based on the selected measure and x-axis.
|
434
|
+
save_func : callable
|
435
|
+
A function to save the data to a file when the "Save Data" button is clicked.
|
436
|
+
"""
|
437
|
+
def button_style(enabled: bool) -> tuple:
|
438
|
+
"""
|
439
|
+
Helper function to generate button styles and states.
|
440
|
+
|
441
|
+
Parameters
|
442
|
+
----------
|
443
|
+
enabled : bool
|
444
|
+
Whether the button should be enabled.
|
445
|
+
|
446
|
+
Returns
|
447
|
+
-------
|
448
|
+
tuple
|
449
|
+
A tuple containing (disabled, style) for the button.
|
450
|
+
"""
|
451
|
+
if enabled:
|
452
|
+
return False, {'height': '36px', 'background-color': '#28a745', 'color': 'white'} # Green
|
453
|
+
return True, {'height': '36px', 'background-color': 'grey', 'color': 'white'} # Grey
|
454
|
+
|
455
|
+
@self.app.callback(
|
456
|
+
[Output("plot-image", "src"), Output(self.plot_ready_store_id, "data")],
|
457
|
+
Input(self.plot_button_id, "n_clicks"),
|
458
|
+
State(self.dropdown_id, "value"),
|
459
|
+
State(self.xaxis_input_id, "value")
|
460
|
+
)
|
461
|
+
def trigger_callback(n_clicks: int, measure: str, xaxis: str) -> tuple:
|
462
|
+
"""
|
463
|
+
Trigger the plot generation callback.
|
464
|
+
|
465
|
+
Parameters
|
466
|
+
----------
|
467
|
+
n_clicks : int
|
468
|
+
Number of button clicks.
|
469
|
+
measure : str
|
470
|
+
Selected measure.
|
471
|
+
xaxis : str
|
472
|
+
Selected x-axis parameter.
|
473
|
+
|
474
|
+
Returns
|
475
|
+
-------
|
476
|
+
tuple
|
477
|
+
The plot source and a flag indicating plot readiness.
|
478
|
+
"""
|
479
|
+
if n_clicks > 0:
|
480
|
+
plot_src = callback_func(measure, xaxis)
|
481
|
+
return plot_src, True
|
482
|
+
return None, False
|
483
|
+
|
484
|
+
@self.app.callback(
|
485
|
+
[Output(self.save_button_id, "disabled"), Output(self.save_button_id, "style")],
|
486
|
+
Input(self.plot_ready_store_id, "data")
|
487
|
+
)
|
488
|
+
def update_save_button_style(plot_ready: bool) -> tuple:
|
489
|
+
"""
|
490
|
+
Update the save button's style based on plot readiness.
|
491
|
+
|
492
|
+
Parameters
|
493
|
+
----------
|
494
|
+
plot_ready : bool
|
495
|
+
Whether the plot is ready.
|
496
|
+
|
497
|
+
Returns
|
498
|
+
-------
|
499
|
+
tuple
|
500
|
+
Disabled state and style for the save button.
|
501
|
+
"""
|
502
|
+
return button_style(plot_ready)
|
503
|
+
|
504
|
+
@self.app.callback(
|
505
|
+
[Output(self.plot_button_id, "disabled"), Output(self.plot_button_id, "style")],
|
506
|
+
Input(self.xaxis_input_id, "value")
|
507
|
+
)
|
508
|
+
def update_plot_button_style(xaxis_value: str) -> tuple:
|
509
|
+
"""
|
510
|
+
Update the plot button's style based on the x-axis selection.
|
511
|
+
|
512
|
+
Parameters
|
513
|
+
----------
|
514
|
+
xaxis_value : str
|
515
|
+
The selected x-axis value.
|
516
|
+
|
517
|
+
Returns
|
518
|
+
-------
|
519
|
+
tuple
|
520
|
+
Disabled state and style for the plot button.
|
521
|
+
"""
|
522
|
+
return button_style(bool(xaxis_value))
|
523
|
+
|
524
|
+
@self.app.callback(
|
525
|
+
Output(self.download_id, "data"),
|
526
|
+
Input(self.save_button_id, "n_clicks"),
|
527
|
+
State(self.plot_ready_store_id, "data"),
|
528
|
+
State(self.filename_input_id, "value"),
|
529
|
+
State(self.dropdown_id, "value")
|
530
|
+
)
|
531
|
+
def save_data(n_clicks: int, plot_ready: bool, filename: str, measure: str) -> dict:
|
532
|
+
"""
|
533
|
+
Trigger file download when the save button is clicked.
|
534
|
+
|
535
|
+
Parameters
|
536
|
+
----------
|
537
|
+
n_clicks : int
|
538
|
+
Number of times the save button has been clicked.
|
539
|
+
plot_ready : bool
|
540
|
+
Whether the plot is ready.
|
541
|
+
filename : str
|
542
|
+
The file name for the downloaded data.
|
543
|
+
measure : str
|
544
|
+
Selected measure.
|
545
|
+
xaxis : str
|
546
|
+
Selected x-axis parameter.
|
547
|
+
|
548
|
+
Returns
|
549
|
+
-------
|
550
|
+
dict or None
|
551
|
+
File content and metadata for download, or None if conditions are not met.
|
552
|
+
"""
|
553
|
+
if n_clicks > 0 and plot_ready:
|
554
|
+
dataframe = save_func(filename=filename, measure=measure)
|
555
|
+
content = dataframe.to_csv(index=False)
|
556
|
+
return {"content": content, "filename": filename, "type": "text/csv"}
|
557
|
+
return None
|
558
|
+
|
559
|
+
@self.app.callback(
|
560
|
+
Output(self.xaxis_input_id, "options"),
|
561
|
+
Input(f"{self.scatterer_section.dropdown_id}-data", "children"),
|
562
|
+
Input(f"{self.source_section.dropdown_id}-data", "children"),
|
563
|
+
Input(f"{self.detector_section.dropdown_id}-data", "children")
|
564
|
+
)
|
565
|
+
def update_xaxis_options(scatterer_data, source_data, detector_data):
|
566
|
+
"""
|
567
|
+
Dynamically update x-axis options based on Scatterer, Source, and Detector sections.
|
568
|
+
|
569
|
+
Parameters
|
570
|
+
----------
|
571
|
+
scatterer_data : str
|
572
|
+
The data from the scatterer section.
|
573
|
+
source_data : str
|
574
|
+
The data from the source section.
|
575
|
+
detector_data : str
|
576
|
+
The data from the detector section.
|
577
|
+
|
578
|
+
Returns
|
579
|
+
-------
|
580
|
+
list
|
581
|
+
A list of dictionaries for the updated dropdown options.
|
582
|
+
"""
|
583
|
+
options = []
|
584
|
+
|
585
|
+
# Recompute _xaxis_options for all sections dynamically
|
586
|
+
for section in [self.scatterer_section, self.source_section, self.detector_section]:
|
587
|
+
section._xaxis_options = [] # Reset options
|
588
|
+
section._xaxis_options_length = [] # Reset lengths
|
589
|
+
|
590
|
+
# Parse inputs and update _xaxis_options
|
591
|
+
for key, value in section.data.items():
|
592
|
+
try:
|
593
|
+
parsed_value = parse_string_to_array_or_float(value)
|
594
|
+
if isinstance(parsed_value, numpy.ndarray) and parsed_value.size > 1:
|
595
|
+
section._xaxis_options.append(f"{section.name}:{key}")
|
596
|
+
section._xaxis_options_length.append(parsed_value.size)
|
597
|
+
except ValueError:
|
598
|
+
pass # Ignore invalid inputs
|
599
|
+
|
600
|
+
# Add the recomputed options to the x-axis dropdown
|
601
|
+
options.extend(
|
602
|
+
[{"label": f"{opt} ({size})", "value": opt} for opt, size in zip(section._xaxis_options, section._xaxis_options_length)]
|
603
|
+
)
|
604
|
+
|
605
|
+
return options
|
606
|
+
|