waveorder 3.0.0a2__py3-none-any.whl → 3.0.0a3__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,12 +1,24 @@
1
1
  import datetime
2
2
  import json
3
+ import logging
3
4
  import os
4
5
  import threading
5
6
  import time
7
+ import types
6
8
  import uuid
7
9
  import warnings
8
10
  from pathlib import Path
9
- from typing import TYPE_CHECKING, Annotated, Final, List, Literal, Union
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ Annotated,
14
+ Any,
15
+ Final,
16
+ List,
17
+ Literal,
18
+ Union,
19
+ get_args,
20
+ get_origin,
21
+ )
10
22
 
11
23
  from iohub.ngff import open_ome_zarr
12
24
  from magicgui import widgets
@@ -28,7 +40,8 @@ if TYPE_CHECKING:
28
40
 
29
41
  from concurrent.futures import ThreadPoolExecutor
30
42
 
31
- from pydantic.v1 import BaseModel, NonNegativeInt, ValidationError
43
+ from pydantic import BaseModel, NonNegativeInt, ValidationError
44
+ from pydantic_core import PydanticUndefinedType
32
45
 
33
46
  from waveorder.cli.settings import (
34
47
  BirefringenceApplyInverseSettings,
@@ -57,11 +70,11 @@ from waveorder.io import utils
57
70
 
58
71
  MSG_SUCCESS = {"msg": "success"}
59
72
 
60
- _validate_alert = ""
61
- _validate_ok = "✔️"
73
+ _validate_alert = "⚠️"
74
+ _validate_ok = ""
62
75
  _green_dot = "🟢"
63
76
  _red_dot = "🔴"
64
- _info_icon = ""
77
+ _info_icon = "ℹ️"
65
78
 
66
79
  # For now replicate CLI processing modes - these could reside in the CLI settings file as well
67
80
  # for consistency
@@ -84,6 +97,48 @@ MULTI_JOBS_REFS = {}
84
97
  ROW_POP_QUEUE = []
85
98
 
86
99
 
100
+ def unwrap_optional(ftype: Any) -> Any:
101
+ """Unwrap Optional[X] to get X. Handles both Optional[X] and X | None syntax.
102
+
103
+ Args:
104
+ ftype: A type annotation
105
+
106
+ Returns:
107
+ The unwrapped type, or ftype unchanged if not Optional
108
+ """
109
+ origin = get_origin(ftype)
110
+ if origin is Union or isinstance(ftype, types.UnionType):
111
+ args = [a for a in get_args(ftype) if a is not type(None)]
112
+ if len(args) == 1:
113
+ return args[0]
114
+ return ftype
115
+
116
+
117
+ def is_subclass_of(
118
+ ftype: Any,
119
+ base: type | tuple[type, ...],
120
+ *,
121
+ require_optional: bool = False,
122
+ ) -> bool:
123
+ """Check if ftype (possibly Optional-wrapped) is a subclass of base.
124
+
125
+ Args:
126
+ ftype: A type annotation
127
+ base: The base type(s) to check against (can be tuple for multiple)
128
+ require_optional: If True, only return True if ftype was Optional-wrapped
129
+
130
+ Returns:
131
+ True if the (unwrapped) ftype is a subclass of base
132
+ """
133
+ inner = unwrap_optional(ftype)
134
+ if require_optional and inner is ftype:
135
+ return False
136
+ # Handle Annotated types by unwrapping them
137
+ if get_origin(inner) is Annotated:
138
+ inner = get_args(inner)[0]
139
+ return isinstance(inner, type) and issubclass(inner, base)
140
+
141
+
87
142
  # Main class for the Reconstruction tab
88
143
  # Not efficient since instantiated from GUI
89
144
  # Does not have access to common functions in main_widget
@@ -405,7 +460,9 @@ class Ui_ReconTab_Form(QWidget):
405
460
 
406
461
  def hideEvent(self, event):
407
462
  if event.type() == QEvent.Type.Hide and (
408
- self._ui is not None and self._ui.isVisible()
463
+ hasattr(self, "_ui")
464
+ and self._ui is not None
465
+ and self._ui.isVisible()
409
466
  ):
410
467
  pass
411
468
 
@@ -1027,7 +1084,8 @@ class Ui_ReconTab_Form(QWidget):
1027
1084
  json_txt = str(json_txt)
1028
1085
  # ToDo: format it better
1029
1086
  # formatted txt does not show up properly in msg-box ??
1030
- except:
1087
+ except (TypeError, KeyError, AttributeError):
1088
+ # msg is not in expected validation error format
1031
1089
  json_txt = str(msg)
1032
1090
 
1033
1091
  # show is a message box
@@ -1305,26 +1363,15 @@ class Ui_ReconTab_Form(QWidget):
1305
1363
  except Exception as exc:
1306
1364
  return None, exc.args
1307
1365
 
1308
- # ToDo: Temporary fix to over ride the 'input_channel_names' default value
1309
- # Needs revisitation
1310
1366
  def fix_model(self, model, exclude_modes, attr_key, attr_val):
1367
+ """Update model attribute while excluding specified modes."""
1311
1368
  try:
1312
- for mode in exclude_modes:
1313
- model = settings.ReconstructionSettings.copy(
1314
- model,
1315
- exclude={mode},
1316
- deep=True,
1317
- update={attr_key: attr_val},
1318
- )
1319
- settings.ReconstructionSettings.__setattr__(
1320
- model, attr_key, attr_val
1321
- )
1322
- if hasattr(model, attr_key):
1323
- model.__fields__[attr_key].default = attr_val
1324
- model.__fields__[attr_key].field_info.default = attr_val
1325
- except Exception as exc:
1326
- return print(exc.args)
1327
- return model
1369
+ data = model.model_dump(exclude=set(exclude_modes))
1370
+ data[attr_key] = attr_val
1371
+ return settings.ReconstructionSettings.model_validate(data)
1372
+ except ValidationError as exc:
1373
+ logging.error(f"fix_model failed: {exc}")
1374
+ return None
1328
1375
 
1329
1376
  # Creates UI controls from model based on selections
1330
1377
  def build_acq_contols(self):
@@ -2409,8 +2456,10 @@ class Ui_ReconTab_Form(QWidget):
2409
2456
  # instantiate the pydantic model form the kwargs we just pulled
2410
2457
  try:
2411
2458
  try:
2412
- pydantic_model = settings.ReconstructionSettings.parse_obj(
2413
- pydantic_kwargs
2459
+ pydantic_model = (
2460
+ settings.ReconstructionSettings.model_validate(
2461
+ pydantic_kwargs
2462
+ )
2414
2463
  )
2415
2464
  return pydantic_model, MSG_SUCCESS
2416
2465
  except ValidationError as exc:
@@ -2421,7 +2470,7 @@ class Ui_ReconTab_Form(QWidget):
2421
2470
  # test to make sure model converts to json which should ensure compatibility with yaml export
2422
2471
  def validate_and_return_json(self, pydantic_model):
2423
2472
  try:
2424
- json_format = pydantic_model.json(indent=4)
2473
+ json_format = pydantic_model.model_dump_json(indent=4)
2425
2474
  return json_format, MSG_SUCCESS
2426
2475
  except Exception as exc:
2427
2476
  return None, exc.args
@@ -2465,47 +2514,62 @@ class Ui_ReconTab_Form(QWidget):
2465
2514
  # Ignoring NoneType since those should be Optional but maybe needs displaying ??
2466
2515
  # ToDo: Needs revisitation, Union check
2467
2516
  # Displaying Union field "time_indices" as LineEdit component
2468
- # excludes handles fields that are not supposed to show up from __fields__
2517
+ # excludes handles fields that are not supposed to show up from model_fields
2469
2518
  # json_dict adds ability to provide new set of default values at time of container creation
2470
2519
 
2471
2520
  def add_pydantic_to_container(
2472
2521
  self,
2473
- py_model: Union[BaseModel, BaseModel],
2522
+ py_model: type[BaseModel] | BaseModel,
2474
2523
  container: widgets.Container,
2475
- excludes=[],
2524
+ excludes=None,
2476
2525
  json_dict=None,
2477
2526
  ):
2478
2527
  # recursively traverse a pydantic model adding widgets to a container. When a nested
2479
2528
  # pydantic model is encountered, add a new nested container
2529
+ if excludes is None:
2530
+ excludes = []
2480
2531
 
2481
- for field, field_def in py_model.__fields__.items():
2532
+ # Access model_fields from the class, not the instance
2533
+ model_class = (
2534
+ py_model if isinstance(py_model, type) else type(py_model)
2535
+ )
2536
+ is_instance = not isinstance(py_model, type)
2537
+ for field, field_def in model_class.model_fields.items():
2482
2538
  if field_def is not None and field not in excludes:
2483
- def_val = field_def.default
2484
- ftype = field_def.type_
2485
- toolTip = ""
2486
- try:
2487
- for f_val in field_def.class_validators.keys():
2488
- toolTip = f"{toolTip}{f_val} "
2489
- except Exception as e:
2490
- pass
2539
+ # Get actual instance value if py_model is an instance, otherwise use class default
2540
+ if is_instance:
2541
+ def_val = getattr(py_model, field)
2542
+ else:
2543
+ def_val = field_def.default
2544
+ if isinstance(def_val, PydanticUndefinedType):
2545
+ def_val = None
2546
+ ftype = field_def.annotation
2547
+
2548
+ # Build tooltip from field metadata
2549
+ tooltip_parts = []
2550
+ if field_def.description:
2551
+ tooltip_parts.append(field_def.description)
2552
+ if field_def.metadata:
2553
+ constraints = [str(m) for m in field_def.metadata]
2554
+ tooltip_parts.extend(constraints)
2555
+ toolTip = " | ".join(tooltip_parts)
2556
+ # Handle nested Pydantic models, including Optional[Model]
2491
2557
  if (
2492
- isinstance(ftype, BaseModel)
2558
+ is_subclass_of(ftype, BaseModel)
2493
2559
  or ftype in PYDANTIC_CLASSES_DEF
2494
2560
  ):
2495
2561
  json_val = None
2496
- if json_dict is not None:
2562
+ if json_dict is not None and field in json_dict:
2497
2563
  json_val = json_dict[field]
2498
2564
  # the field is a pydantic class, add a container for it and fill it
2499
2565
  new_widget_cls = widgets.Container
2500
- new_widget = new_widget_cls(name=field_def.name)
2566
+ new_widget = new_widget_cls(name=field)
2501
2567
  new_widget.tooltip = toolTip
2568
+ # Unwrap Optional[Model] to get Model before recursing
2569
+ unwrapped_ftype = unwrap_optional(ftype)
2502
2570
  self.add_pydantic_to_container(
2503
- ftype, new_widget, excludes, json_val
2571
+ unwrapped_ftype, new_widget, excludes, json_val
2504
2572
  )
2505
- # ToDo: Implement Union check, tried:
2506
- # pydantic.typing.is_union(ftype)
2507
- # isinstance(ftype, types.UnionType)
2508
- # https://stackoverflow.com/questions/45957615/how-to-check-a-variable-against-union-type-during-runtime
2509
2573
  elif isinstance(ftype, type(Union[NonNegativeInt, List, str])):
2510
2574
  if (
2511
2575
  field == "background_path"
@@ -2534,31 +2598,53 @@ class Ui_ReconTab_Form(QWidget):
2534
2598
  warnings.warn(
2535
2599
  message=f"magicgui could not identify a widget for {py_model}.{field}, which has type {ftype}"
2536
2600
  )
2537
- elif isinstance(def_val, float):
2538
- # parse the field, add appropriate widget
2539
- def_step_size = 0.001
2540
- if field_def.name == "regularization_strength":
2541
- def_step_size = 0.00001
2542
- if def_val > -1 and def_val < 1:
2601
+ elif isinstance(def_val, float) or (
2602
+ def_val is None
2603
+ and is_subclass_of(
2604
+ ftype, (int, float), require_optional=True
2605
+ )
2606
+ ):
2607
+ # Handle float fields, including Optional[float] with None value
2608
+
2609
+ # For Optional numeric types with None, use LineEdit instead of FloatSpinBox
2610
+ # This allows empty string to represent None
2611
+ # Note: if we entered via the is_subclass_of check, def_val is guaranteed None
2612
+ if def_val is None:
2543
2613
  new_widget_cls, ops = get_widget_class(
2544
2614
  None,
2545
- ftype,
2546
- dict(
2547
- name=field_def.name,
2548
- value=def_val,
2549
- step=float(def_step_size),
2550
- ),
2615
+ str,
2616
+ dict(name=field, value=""),
2551
2617
  )
2552
2618
  new_widget = new_widget_cls(**ops)
2553
- new_widget.tooltip = toolTip
2554
- else:
2555
- new_widget_cls, ops = get_widget_class(
2556
- None,
2557
- ftype,
2558
- dict(name=field_def.name, value=def_val),
2619
+ new_widget.tooltip = (
2620
+ toolTip + " (Optional - leave empty for None)"
2559
2621
  )
2560
- new_widget = new_widget_cls(**ops)
2561
- new_widget.tooltip = toolTip
2622
+ else:
2623
+ # Regular float field
2624
+ def_step_size = 0.001
2625
+ if field == "regularization_strength":
2626
+ def_step_size = 0.00001
2627
+
2628
+ if def_val > -1 and def_val < 1:
2629
+ new_widget_cls, ops = get_widget_class(
2630
+ None,
2631
+ ftype,
2632
+ dict(
2633
+ name=field,
2634
+ value=def_val,
2635
+ step=float(def_step_size),
2636
+ ),
2637
+ )
2638
+ new_widget = new_widget_cls(**ops)
2639
+ new_widget.tooltip = toolTip
2640
+ else:
2641
+ new_widget_cls, ops = get_widget_class(
2642
+ None,
2643
+ ftype,
2644
+ dict(name=field, value=def_val),
2645
+ )
2646
+ new_widget = new_widget_cls(**ops)
2647
+ new_widget.tooltip = toolTip
2562
2648
  if isinstance(new_widget, widgets.EmptyWidget):
2563
2649
  warnings.warn(
2564
2650
  message=f"magicgui could not identify a widget for {py_model}.{field}, which has type {ftype}"
@@ -2566,7 +2652,7 @@ class Ui_ReconTab_Form(QWidget):
2566
2652
  else:
2567
2653
  # parse the field, add appropriate widget
2568
2654
  new_widget_cls, ops = get_widget_class(
2569
- None, ftype, dict(name=field_def.name, value=def_val)
2655
+ None, ftype, dict(name=field, value=def_val)
2570
2656
  )
2571
2657
  new_widget = new_widget_cls(**ops)
2572
2658
  if isinstance(new_widget, widgets.EmptyWidget):
@@ -2605,38 +2691,52 @@ class Ui_ReconTab_Form(QWidget):
2605
2691
  container: widgets.Container,
2606
2692
  pydantic_model,
2607
2693
  pydantic_kwargs: dict,
2608
- excludes=[],
2694
+ excludes=None,
2609
2695
  json_dict=None,
2610
2696
  ):
2611
2697
  # given a container that was instantiated from a pydantic model, get the arguments
2612
2698
  # needed to instantiate that pydantic model from the container.
2613
2699
  # traverse model fields, pull out values from container
2614
- for field, field_def in pydantic_model.__fields__.items():
2615
- if field_def is not None and field not in excludes:
2616
- ftype = field_def.type_
2617
- if (
2618
- isinstance(ftype, BaseModel)
2619
- or ftype in PYDANTIC_CLASSES_DEF
2620
- ):
2621
- # go deeper
2622
- pydantic_kwargs[field] = (
2623
- {}
2624
- ) # new dictionary for the new nest level
2625
- # any pydantic class will be a container, so pull that out to pass
2626
- # to the recursive call
2627
- sub_container = getattr(container, field_def.name)
2628
- self.get_pydantic_kwargs(
2629
- sub_container,
2630
- ftype,
2631
- pydantic_kwargs[field],
2632
- excludes,
2633
- json_dict,
2634
- )
2635
- else:
2636
- # not a pydantic class, just pull the field value from the container
2637
- if hasattr(container, field_def.name):
2638
- value = getattr(container, field_def.name).value
2639
- pydantic_kwargs[field] = value
2700
+ if excludes is None:
2701
+ excludes = []
2702
+
2703
+ # Access model_fields from the class, not the instance
2704
+ model_class = (
2705
+ pydantic_model
2706
+ if isinstance(pydantic_model, type)
2707
+ else type(pydantic_model)
2708
+ )
2709
+
2710
+ for field, field_def in model_class.model_fields.items():
2711
+ if field in excludes:
2712
+ continue
2713
+
2714
+ ftype = field_def.annotation
2715
+ if is_subclass_of(ftype, BaseModel):
2716
+ # Nested Pydantic model - recurse
2717
+ pydantic_kwargs[field] = {}
2718
+ self.get_pydantic_kwargs(
2719
+ getattr(container, field),
2720
+ unwrap_optional(ftype),
2721
+ pydantic_kwargs[field],
2722
+ excludes,
2723
+ json_dict,
2724
+ )
2725
+ else:
2726
+ # Leaf field - extract value from container widget
2727
+ value = getattr(container, field).value
2728
+ # Handle Optional numeric types: convert empty string to None, parse numeric strings
2729
+ if is_subclass_of(
2730
+ ftype, (int, float), require_optional=True
2731
+ ) and isinstance(value, str):
2732
+ if value == "" or value.lower() in ("none", "null"):
2733
+ value = None
2734
+ else:
2735
+ try:
2736
+ value = float(value)
2737
+ except (ValueError, TypeError):
2738
+ pass
2739
+ pydantic_kwargs[field] = value
2640
2740
 
2641
2741
  # copied from main_widget
2642
2742
  # file open/select dialog
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waveorder
3
- Version: 3.0.0a2
3
+ Version: 3.0.0a3
4
4
  Summary: Wave-optical simulations and deconvolution of optical properties
5
5
  Author-email: CZ Biohub SF <compmicro@czbiohub.org>
6
6
  Maintainer-email: Talon Chandler <talon.chandler@czbiohub.org>, Shalin Mehta <shalin.mehta@czbiohub.org>
@@ -33,6 +33,18 @@ License: BSD 3-Clause License
33
33
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
34
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
35
 
36
+ ## License Notice for Dependencies
37
+
38
+ This repository is licensed under the BSD License; however, it relies on certain
39
+ third-party dependencies that are licensed under the GNU General Public License
40
+ (GPL). Specifically:
41
+
42
+ - PyQt5 is licensed under the GNU General Public License v3 or later (GPLv3+).
43
+ This library is not included in this repository but must be installed separately
44
+ by users. Please be aware that the GPL license terms apply to these
45
+ dependencies, and certain uses of GPL-licensed code may have licensing
46
+ implications for your own software.
47
+
36
48
  Project-URL: Homepage, https://github.com/mehta-lab/waveorder
37
49
  Project-URL: Repository, https://github.com/mehta-lab/waveorder
38
50
  Project-URL: Issues, https://github.com/mehta-lab/waveorder/issues
@@ -71,14 +83,17 @@ Requires-Dist: qtpy
71
83
  Requires-Dist: wget>=3.2
72
84
  Provides-Extra: visual
73
85
  Requires-Dist: napari[pyqt6]; extra == "visual"
86
+ Requires-Dist: PyQt6<6.8,>=6.5; extra == "visual"
74
87
  Requires-Dist: napari-ome-zarr>=0.3.2; extra == "visual"
75
88
  Requires-Dist: pycromanager==0.27.2; extra == "visual"
76
89
  Requires-Dist: jupyter; extra == "visual"
90
+ Requires-Dist: jupytext; extra == "visual"
77
91
  Provides-Extra: dev
78
92
  Requires-Dist: black==25.1.0; extra == "dev"
79
93
  Requires-Dist: click>=8.2.0; extra == "dev"
80
94
  Requires-Dist: hypothesis; extra == "dev"
81
95
  Requires-Dist: napari[pyqt6]; extra == "dev"
96
+ Requires-Dist: PyQt6<6.8,>=6.5; extra == "dev"
82
97
  Requires-Dist: pre-commit; extra == "dev"
83
98
  Requires-Dist: pytest-cov; extra == "dev"
84
99
  Requires-Dist: pytest-qt; extra == "dev"
@@ -102,7 +117,7 @@ Requires-Dist: waveorder[docs]; extra == "all"
102
117
  Dynamic: license-file
103
118
 
104
119
  <p align="center">
105
- <img src="https://github.com/user-attachments/assets/1b4fabd4-2ead-40fa-ae88-99642a1589f1" alt="Image" width="50%">
120
+ <img src="https://github.com/user-attachments/assets/00367e51-0e0a-445e-8d12-2a273f9014aa" alt="Image" width="50%">
106
121
  </p>
107
122
 
108
123
  [![Python package index](https://img.shields.io/pypi/v/waveorder.svg)](https://pypi.org/project/waveorder)
@@ -313,7 +328,7 @@ We also maintain three dependency sets for different interfaces:
313
328
 
314
329
  ```sh
315
330
  pip install waveorder # API, CLI
316
- pip install waveorder[visual] # API, CLI, GUI
331
+ pip install waveorder[visual] # API, CLI, GUI
317
332
  pip install waveorder[all] # API, CLI, GUI, docs, dev dependencies
318
333
  ```
319
334
 
@@ -1,9 +1,9 @@
1
1
  waveorder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- waveorder/_version.py,sha256=4T3YnpoBezB_PnD8TGrripaPhzvljf62vnxX5xRPzJY,712
2
+ waveorder/_version.py,sha256=1X31aM0jCGJPnD5p_rYNKOtonoGoP_xWzwERdd1UV5s,712
3
3
  waveorder/background_estimator.py,sha256=GFy3N0qqp5M6JVZBbIUvTYhSin8Njg7zA2WN69pKLAE,12398
4
4
  waveorder/correction.py,sha256=uAWDKXq-FYwi1obxxWq0A-suNVf1cvqUnPsDC-LIlsM,3460
5
5
  waveorder/filter.py,sha256=UatcxE3xqwwB1p0IVoEjH8Qw0CB6hvcXvnRR6IRNXsQ,7325
6
- waveorder/focus.py,sha256=hreENdCZCKj2pXoom3FgcevJX63q7-kKhCI9atRarcI,7956
6
+ waveorder/focus.py,sha256=4IenjsMfwDkZlSImNMB3mnyuDz4XF7VJWiaCmzDLseI,10928
7
7
  waveorder/napari.yaml,sha256=iqmzGwSopu-L38yxuAnhMTuBvDqKIm2mTWfxjn6Duks,1463
8
8
  waveorder/optics.py,sha256=RikR3kkPFoVUS86jiGQZey7Jtz5Ga-xKWnXsexo3Okc,44242
9
9
  waveorder/reconstruct.py,sha256=nF00-uxYF67QVcNClp1VIB31ElUfbjO3RNxbxc0ELH4,832
@@ -14,44 +14,43 @@ waveorder/waveorder_reconstructor.py,sha256=-MluWmnZnCZm7Xu-8V8QWX9ma0_5oAZO2Xly
14
14
  waveorder/waveorder_simulator.py,sha256=uRRX_wcWzJzlVcfToLpIlh4e8Xt9NjTvdonyGEf2Z1c,45805
15
15
  waveorder/acq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  waveorder/acq/acq_functions.py,sha256=4GJCkxncFunsRbLesf1LErY7CaA26dMh2goVVUr72y0,5128
17
- waveorder/acq/acquisition_workers.py,sha256=J30-ZdOej-R2jHnL5Nrwom3EV2xpnBYPGnJKsIyy-YA,22719
18
17
  waveorder/assets/HSV_legend.png,sha256=7k9l23V-is7TIVnKchIat9JE_UGSqjbRzgVROeNrNJU,12132
19
18
  waveorder/assets/JCh_legend.png,sha256=waQ980-7-APUqzuvBjKh5PKE91jaCbKire0NJf-y6AQ,14900
20
- waveorder/assets/waveorder_plugin_logo.png,sha256=euszlEwFVBBwLNpPm0WBwZob2YfJ0_9L4tznOxnbKuw,13817
19
+ waveorder/assets/waveorder_plugin_logo.png,sha256=3advwUtIqhXJmHrqXF3NmWB8OMjQP2N-Nsbi-IaIQw8,14686
21
20
  waveorder/calib/Calibration.py,sha256=Fl6zQOJg4V6nKtbAlNL7s9kslmrxk6q-LAueh59F1bA,52007
22
21
  waveorder/calib/Optimization.py,sha256=uBQRvZ3tsG52pOSwnRj3Lduf_wVTTYfcjegAZsN58UE,13909
23
22
  waveorder/calib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
23
  waveorder/calib/calibration_workers.py,sha256=xI1Zg_D6m1wuls4af0Nlzl_3fADeAeHzSO2gWKGaORo,15325
25
- waveorder/cli/apply_inverse_models.py,sha256=L620cFSocqi9JlIT-EWSikbS9PcDOismH5zG6tgEmRA,10166
26
- waveorder/cli/apply_inverse_transfer_function.py,sha256=bFFa_zKUFd425jEF9LgRkDDb_2q9ZiljWFuGAQ45Zy0,13336
27
- waveorder/cli/compute_transfer_function.py,sha256=37ixvtrFsl7c9CIrjkDFg-XbWFENnHzB8J7UVy2EJpM,14253
24
+ waveorder/cli/apply_inverse_models.py,sha256=8nrwj4bgHnha59uOvvV0PqdRyjyldWfUs_ZbfdtJ-ZE,10344
25
+ waveorder/cli/apply_inverse_transfer_function.py,sha256=EgmzIoDX4o7GqYIQfk7wgJZ8DxqfXeTAJjDXbMOqWPY,13369
26
+ waveorder/cli/compute_transfer_function.py,sha256=IfOtxVSYqcF9urDBxDftm8Txw3h6jNzAswIOgnXtRf0,14319
28
27
  waveorder/cli/gui_widget.py,sha256=bPbeAAQEFzD2WnlO_vKZ5RSdaFi6J3Psouzfuj7u9f4,1343
29
28
  waveorder/cli/main.py,sha256=uW18kwZOpYbgUKfGXexIF3LITxkUJeRMHnTmAwAPWjc,942
30
29
  waveorder/cli/monitor.py,sha256=XBhqyd7dxnXrfca23rm0oAILEnx8lEVPsS2oSfmt8kw,4757
31
30
  waveorder/cli/option_eat_all.py,sha256=GJgI4owf59IhZTtQiLsUy19WQJLJDXB-Gg7WIf5aREw,1823
32
31
  waveorder/cli/parsing.py,sha256=IWSjRCp2KQiEzw4T_aCvMSoPrKoRRbIJP0Tc8Pcb-R4,3476
33
- waveorder/cli/printing.py,sha256=rfGeONEsQT9AJnFY2VDCmi-1E62xdqwrwpJYBT_hFWg,237
32
+ waveorder/cli/printing.py,sha256=tCWRE7YnE4ByROAhaYlCjJLnCwZ3IEcm3Bo6ZoO1uxI,326
34
33
  waveorder/cli/reconstruct.py,sha256=qbFCoMZC9utDofx8oU0BUljMC-MoN_ykrp1sXGe39zo,1792
35
- waveorder/cli/settings.py,sha256=J0CD_ZEv64ehe1WXgdsg9PQCrpim_tF1Uj0VsgE22kY,6418
36
- waveorder/cli/utils.py,sha256=r--7VDF_YiyjHx2ApI0tCf8CX076kpBzXR7OdzFbXwc,5646
34
+ waveorder/cli/settings.py,sha256=d5EmqnzNjwDHVnP4xONAamBe6I4rdNHWS1pUXKyu1bw,6604
35
+ waveorder/cli/utils.py,sha256=-7RYPUEzhTPhmgQUSptNxE1GmnAjzuucixEBWcRL0KI,5667
37
36
  waveorder/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
37
  waveorder/io/_reader.py,sha256=l17s_-1aARCFn76VIpDljoOIL088uPx3A1mTl3T5l9o,1630
39
38
  waveorder/io/core_functions.py,sha256=g4HrkfcQinEhCPdt0FtRSXRXUjRL1ZQe5g0sClnjVZA,6553
40
39
  waveorder/io/metadata_reader.py,sha256=DiVqdJiwSduHa6eOrfJfcky1ZoFI3ySPEXJrbuFM8fg,5483
41
- waveorder/io/utils.py,sha256=0k4Co1CsCszJGG-t5xKmY2ZOZ5qDHI0XUA97Fpj9zNo,4732
40
+ waveorder/io/utils.py,sha256=ArmKCq5_Wj3tyiD9Zl6jaUkjgIslJffJj5xRUGtMcMM,4804
42
41
  waveorder/io/visualization.py,sha256=u_EGSc2s1pkB-xvGVxmmMr2zQObihjkV0PCB_7yizak,4753
43
42
  waveorder/models/inplane_oriented_thick_pol3d.py,sha256=bx5yViNz7wY5BBXUea1Tw0RhYsEzErENRnAbgpa34C0,5992
44
43
  waveorder/models/inplane_oriented_thick_pol3d_vector.py,sha256=Qh1E0fWHh6KThQnW3TYBGmDtNrO98n9W42t6G_nQO5w,9759
45
44
  waveorder/models/isotropic_fluorescent_thick_3d.py,sha256=0bu9OdYj2aP1phzRxod8tExURaYdgwddAD61BbGF-lg,10387
46
45
  waveorder/models/isotropic_fluorescent_thin_3d.py,sha256=uOxRTOKu5Viv_KTKlW7c91M7hREx3eER-owKFyrhRCQ,10239
47
46
  waveorder/models/isotropic_thin_3d.py,sha256=BPp0DR3Pu8b7NpnGXUJ1ZqdGbs_P7-H5iVGagVClPtg,11328
48
- waveorder/models/phase_thick_3d.py,sha256=sOHphv6_SfarLxrhj4bcZwM-5bdjXS4-j6RCreOBTmA,8654
47
+ waveorder/models/phase_thick_3d.py,sha256=eUmf_AbSDMwFNDfE8WPGr5sJShvL4YAjsZff_KO559k,12323
49
48
  waveorder/plugin/__init__.py,sha256=4Idw8HLl6LZ-E0cgW9mSsCAQk-D4Hnmoi0W9iaV6yrw,333
50
- waveorder/plugin/gui.py,sha256=b_5CdovGO7LF8qQNY9YJV4y1Gc_jEpYpVQ6mV0gJ1IM,80655
51
- waveorder/plugin/gui.ui,sha256=NjbgLSgYnLVs-OHeEmm224p0MNdT5kat3BrioA4jnCY,79154
49
+ waveorder/plugin/gui.py,sha256=NazopP-wb3Fup4FWfgH4zWdkOKvilpxWrOXgsI0xquI,50593
50
+ waveorder/plugin/gui.ui,sha256=7_CUDI2Wqbh2EBal9-ZmNJcb7cCCxiZcQe-DO6NBnM4,49920
52
51
  waveorder/plugin/job_manager.py,sha256=FNQ9T-djbJM2SzRx99khybCjEF5vG-8pS6jEBHqo7og,1157
53
- waveorder/plugin/main_widget.py,sha256=jrI7c-jZ-rhxO3ACwG_Rl7Eb6Ttw0aMNbrspCH0s120,78011
54
- waveorder/plugin/tab_recon.py,sha256=SGI_xUCDauChyq9PfHXbGPvBtMqfgIeqfmMIkXCPK4M,128329
52
+ waveorder/plugin/main_widget.py,sha256=Vf5Eve9ik_86mdsSbezacME96eFnDf7A9myDaCnHHt4,58098
53
+ waveorder/plugin/tab_recon.py,sha256=kiMhTa6sxqmQAYw9gGpcu66y0vEB-_e5mMAP7V136qU,131698
55
54
  waveorder/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
55
  waveorder/scripts/launch_napari.py,sha256=0Sx_2XCJ38pBfe7QvthUn0Vu8ZcqD6qHjwBvzR6V2JY,221
57
56
  waveorder/scripts/repeat-cal-acq-rec.py,sha256=XVdQSoTacNczd58MBty4Qa4umprdUkgiwCkWQQr7p4g,3908
@@ -62,9 +61,9 @@ waveorder/visuals/jupyter_visuals.py,sha256=6kxICjEtP1qc1EuETc_NJ6Y4A7nVaC-bP3wl
62
61
  waveorder/visuals/matplotlib_visuals.py,sha256=v1zi0ZlXEV5CcpNzTWL0vDJ2Md2-RSHnc3rAB61wimg,10915
63
62
  waveorder/visuals/napari_visuals.py,sha256=bDEgHvoexdsGW4pIbnLfBCU4e4be41PMyKx9Mq9so1c,2361
64
63
  waveorder/visuals/utils.py,sha256=QC5WSc2yzPMjk66IjA39iNFgO_0It6evma201hH8Lg4,1001
65
- waveorder-3.0.0a2.dist-info/licenses/LICENSE,sha256=v_wJE3YLyTOjHj0kgPB9hP9YDieI0WMw7XjOgTmIr2c,1509
66
- waveorder-3.0.0a2.dist-info/METADATA,sha256=BlCdzh-GSlirBjMdYRegfgRRa8iIZQ_Da81IxxYHgME,14783
67
- waveorder-3.0.0a2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
68
- waveorder-3.0.0a2.dist-info/entry_points.txt,sha256=cOCI4v8cdinVJiyAMlYZtxK6GCS4GF6Qpk1hTsgW18Y,106
69
- waveorder-3.0.0a2.dist-info/top_level.txt,sha256=i3zReXiiMTnyPk93W7aEz_oEfsLnfR_Kzl7PW7kUslA,10
70
- waveorder-3.0.0a2.dist-info/RECORD,,
64
+ waveorder-3.0.0a3.dist-info/licenses/LICENSE,sha256=MATRcU1PWMarfKd4-d4q6eMdy4JWzC6fpavJI_zXHeE,2068
65
+ waveorder-3.0.0a3.dist-info/METADATA,sha256=TLwY7IBS3b8wdV9CBwA0lYARs3KOyHlxmVAA7z6m6fQ,15576
66
+ waveorder-3.0.0a3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
67
+ waveorder-3.0.0a3.dist-info/entry_points.txt,sha256=cOCI4v8cdinVJiyAMlYZtxK6GCS4GF6Qpk1hTsgW18Y,106
68
+ waveorder-3.0.0a3.dist-info/top_level.txt,sha256=i3zReXiiMTnyPk93W7aEz_oEfsLnfR_Kzl7PW7kUslA,10
69
+ waveorder-3.0.0a3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -26,3 +26,15 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
26
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
27
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ ## License Notice for Dependencies
31
+
32
+ This repository is licensed under the BSD License; however, it relies on certain
33
+ third-party dependencies that are licensed under the GNU General Public License
34
+ (GPL). Specifically:
35
+
36
+ - PyQt5 is licensed under the GNU General Public License v3 or later (GPLv3+).
37
+ This library is not included in this repository but must be installed separately
38
+ by users. Please be aware that the GPL license terms apply to these
39
+ dependencies, and certain uses of GPL-licensed code may have licensing
40
+ implications for your own software.