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.
- waveorder/_version.py +2 -2
- waveorder/assets/waveorder_plugin_logo.png +0 -0
- waveorder/cli/apply_inverse_models.py +14 -10
- waveorder/cli/apply_inverse_transfer_function.py +3 -3
- waveorder/cli/compute_transfer_function.py +9 -7
- waveorder/cli/printing.py +6 -2
- waveorder/cli/settings.py +51 -51
- waveorder/cli/utils.py +1 -1
- waveorder/focus.py +73 -9
- waveorder/io/utils.py +5 -3
- waveorder/models/phase_thick_3d.py +103 -4
- waveorder/plugin/gui.py +198 -799
- waveorder/plugin/gui.ui +0 -795
- waveorder/plugin/main_widget.py +6 -572
- waveorder/plugin/tab_recon.py +196 -96
- {waveorder-3.0.0a2.dist-info → waveorder-3.0.0a3.dist-info}/METADATA +18 -3
- {waveorder-3.0.0a2.dist-info → waveorder-3.0.0a3.dist-info}/RECORD +21 -22
- {waveorder-3.0.0a2.dist-info → waveorder-3.0.0a3.dist-info}/WHEEL +1 -1
- {waveorder-3.0.0a2.dist-info → waveorder-3.0.0a3.dist-info}/licenses/LICENSE +12 -0
- waveorder/acq/acquisition_workers.py +0 -650
- {waveorder-3.0.0a2.dist-info → waveorder-3.0.0a3.dist-info}/entry_points.txt +0 -0
- {waveorder-3.0.0a2.dist-info → waveorder-3.0.0a3.dist-info}/top_level.txt +0 -0
waveorder/plugin/tab_recon.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
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 =
|
|
2413
|
-
|
|
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.
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
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
|
-
|
|
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 =
|
|
2554
|
-
|
|
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
|
-
|
|
2561
|
-
|
|
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=
|
|
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
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
)
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
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.
|
|
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/
|
|
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
|
[](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=
|
|
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=
|
|
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=
|
|
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=
|
|
26
|
-
waveorder/cli/apply_inverse_transfer_function.py,sha256=
|
|
27
|
-
waveorder/cli/compute_transfer_function.py,sha256=
|
|
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=
|
|
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=
|
|
36
|
-
waveorder/cli/utils.py,sha256
|
|
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=
|
|
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=
|
|
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=
|
|
51
|
-
waveorder/plugin/gui.ui,sha256=
|
|
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=
|
|
54
|
-
waveorder/plugin/tab_recon.py,sha256=
|
|
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.
|
|
66
|
-
waveorder-3.0.
|
|
67
|
-
waveorder-3.0.
|
|
68
|
-
waveorder-3.0.
|
|
69
|
-
waveorder-3.0.
|
|
70
|
-
waveorder-3.0.
|
|
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,,
|
|
@@ -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.
|