TwoPy 0.3.6__py3-none-any.whl → 0.3.8__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.
- twopy/napari/plotting/docks/response_plot_widget.py +3 -2
- twopy/napari/plotting/export.py +15 -1
- twopy/napari/session.py +23 -3
- twopy/napari/version_check.py +17 -7
- {twopy-0.3.6.dist-info → twopy-0.3.8.dist-info}/METADATA +1 -1
- {twopy-0.3.6.dist-info → twopy-0.3.8.dist-info}/RECORD +9 -9
- {twopy-0.3.6.dist-info → twopy-0.3.8.dist-info}/WHEEL +0 -0
- {twopy-0.3.6.dist-info → twopy-0.3.8.dist-info}/entry_points.txt +0 -0
- {twopy-0.3.6.dist-info → twopy-0.3.8.dist-info}/top_level.txt +0 -0
|
@@ -583,11 +583,12 @@ class _ResponsePlotWidget(QWidget):
|
|
|
583
583
|
"""Show the copyable update command when a newer release is known."""
|
|
584
584
|
if notice is None:
|
|
585
585
|
return
|
|
586
|
-
self._update_notice_label.setText(notice.
|
|
586
|
+
self._update_notice_label.setText(notice.text)
|
|
587
|
+
print(notice.text, flush=True)
|
|
587
588
|
if self._viewer is not None:
|
|
588
589
|
refresh_empty_viewer_message(
|
|
589
590
|
self._viewer,
|
|
590
|
-
update_command=notice.
|
|
591
|
+
update_command=notice.text,
|
|
591
592
|
)
|
|
592
593
|
|
|
593
594
|
def _ignore_update_notice_error(self, _error: Exception) -> None:
|
twopy/napari/plotting/export.py
CHANGED
|
@@ -388,7 +388,7 @@ def current_recording_image(
|
|
|
388
388
|
Returns:
|
|
389
389
|
Two-dimensional display-coordinate image.
|
|
390
390
|
"""
|
|
391
|
-
movie_layer =
|
|
391
|
+
movie_layer = _visible_layer_by_name(viewer, "aligned movie")
|
|
392
392
|
if movie_layer is not None:
|
|
393
393
|
data = np.asarray(cast(NapariLayerWithData, movie_layer).data)
|
|
394
394
|
if data.ndim == 3:
|
|
@@ -397,6 +397,12 @@ def current_recording_image(
|
|
|
397
397
|
if data.ndim == 2:
|
|
398
398
|
return cast(npt.NDArray[np.float64], data)
|
|
399
399
|
|
|
400
|
+
mean_layer = _visible_layer_by_name(viewer, "mean image")
|
|
401
|
+
if mean_layer is not None:
|
|
402
|
+
data = np.asarray(cast(NapariLayerWithData, mean_layer).data)
|
|
403
|
+
if data.ndim == 2:
|
|
404
|
+
return cast(npt.NDArray[np.float64], data)
|
|
405
|
+
|
|
400
406
|
mean_image = recording.alignment_valid_crop.crop_image(recording.mean_image)
|
|
401
407
|
return cast(npt.NDArray[np.float64], mean_image)
|
|
402
408
|
|
|
@@ -948,3 +954,11 @@ def _layer_by_name(viewer: object | None, name: str) -> object | None:
|
|
|
948
954
|
if getattr(layer, "name", None) == name:
|
|
949
955
|
return layer
|
|
950
956
|
return None
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def _visible_layer_by_name(viewer: object | None, name: str) -> object | None:
|
|
960
|
+
"""Return a named layer only when napari is currently showing it."""
|
|
961
|
+
layer = _layer_by_name(viewer, name)
|
|
962
|
+
if layer is None or not bool(getattr(layer, "visible", True)):
|
|
963
|
+
return None
|
|
964
|
+
return layer
|
twopy/napari/session.py
CHANGED
|
@@ -269,6 +269,27 @@ def remove_loaded_recording_layers(
|
|
|
269
269
|
_remove_layer(viewer, layer)
|
|
270
270
|
|
|
271
271
|
|
|
272
|
+
def _clear_all_viewer_layers(viewer: object) -> None:
|
|
273
|
+
"""Clear every viewer layer because twopy owns the layer stack."""
|
|
274
|
+
layer_collection = getattr(viewer, "layers", None)
|
|
275
|
+
clear = getattr(layer_collection, "clear", None)
|
|
276
|
+
if callable(clear):
|
|
277
|
+
clear()
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
for collection_name in ("images", "labels"):
|
|
281
|
+
collection = getattr(viewer, collection_name, None)
|
|
282
|
+
if isinstance(collection, list):
|
|
283
|
+
collection.clear()
|
|
284
|
+
|
|
285
|
+
selection = getattr(layer_collection, "selection", None)
|
|
286
|
+
if selection is not None and hasattr(selection, "active"):
|
|
287
|
+
try:
|
|
288
|
+
cast(_LayerSelection, selection).active = None
|
|
289
|
+
except (AttributeError, TypeError):
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
|
|
272
293
|
def record_loaded_view(
|
|
273
294
|
state: NapariSessionState,
|
|
274
295
|
*,
|
|
@@ -444,7 +465,7 @@ def unload_loaded_recording(state: NapariSessionState, index: int) -> None:
|
|
|
444
465
|
|
|
445
466
|
|
|
446
467
|
def unload_all_loaded_recordings(state: NapariSessionState) -> None:
|
|
447
|
-
"""Unload every recording and
|
|
468
|
+
"""Unload every recording and clear all twopy-owned layers from napari.
|
|
448
469
|
|
|
449
470
|
Args:
|
|
450
471
|
state: Mutable napari session state.
|
|
@@ -454,8 +475,7 @@ def unload_all_loaded_recordings(state: NapariSessionState) -> None:
|
|
|
454
475
|
"""
|
|
455
476
|
if len(state.loaded_recordings) == 0:
|
|
456
477
|
return
|
|
457
|
-
|
|
458
|
-
remove_loaded_recording_layers(state.viewer, loaded_recording)
|
|
478
|
+
_clear_all_viewer_layers(state.viewer)
|
|
459
479
|
state.loaded_recordings.clear()
|
|
460
480
|
clear_matching_state = getattr(
|
|
461
481
|
state.group_matching_panel,
|
twopy/napari/version_check.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Check whether PyPI has a newer twopy release for the napari app.
|
|
2
2
|
|
|
3
3
|
Inputs: installed twopy version, PyPI JSON metadata, and local UI cache.
|
|
4
|
-
Outputs: an optional copyable update
|
|
4
|
+
Outputs: an optional copyable update notice for the Metadata tab.
|
|
5
5
|
|
|
6
6
|
The app only tells users how to update. It does not choose or run an
|
|
7
7
|
environment manager, because users may launch twopy from pip, conda, or another
|
|
@@ -97,14 +97,24 @@ class UpdateNotice:
|
|
|
97
97
|
latest_version: Newer version available from the package index.
|
|
98
98
|
command: Update command shown to the user.
|
|
99
99
|
|
|
100
|
-
The Metadata tab shows
|
|
101
|
-
user's Python environment is managed.
|
|
100
|
+
The Metadata tab shows text the user can copy. The command stays plain so
|
|
101
|
+
twopy does not assume how the user's Python environment is managed.
|
|
102
102
|
"""
|
|
103
103
|
|
|
104
104
|
current_version: str
|
|
105
105
|
latest_version: str
|
|
106
106
|
command: str = PIP_UPDATE_COMMAND
|
|
107
107
|
|
|
108
|
+
@property
|
|
109
|
+
def text(self) -> str:
|
|
110
|
+
"""Return the copyable user-facing update notice."""
|
|
111
|
+
return (
|
|
112
|
+
"Update available!\n"
|
|
113
|
+
f"Latest version is {self.latest_version}.\n"
|
|
114
|
+
"To update, run:\n"
|
|
115
|
+
f"{self.command}"
|
|
116
|
+
)
|
|
117
|
+
|
|
108
118
|
|
|
109
119
|
def check_for_update(
|
|
110
120
|
*,
|
|
@@ -131,9 +141,9 @@ def check_for_update(
|
|
|
131
141
|
Update notice, or ``None`` when twopy is current or the check cannot
|
|
132
142
|
prove that a newer final release exists.
|
|
133
143
|
|
|
134
|
-
A fresh cache avoids one network request per launch
|
|
135
|
-
|
|
136
|
-
show the update command.
|
|
144
|
+
A fresh cache avoids one network request per launch only after it has
|
|
145
|
+
already seen a newer version. If PyPI is unreachable and an older cache
|
|
146
|
+
proves a newer version exists, twopy can still show the update command.
|
|
137
147
|
"""
|
|
138
148
|
checked_now = _utc(now)
|
|
139
149
|
cache = read_version_check_cache(state_file=state_file)
|
|
@@ -261,7 +271,7 @@ def _cache_is_fresh(
|
|
|
261
271
|
latest = Version(cache.latest_version)
|
|
262
272
|
except InvalidVersion:
|
|
263
273
|
return False
|
|
264
|
-
if latest
|
|
274
|
+
if latest <= current:
|
|
265
275
|
return False
|
|
266
276
|
return now - checked_at <= max_age
|
|
267
277
|
|
|
@@ -114,13 +114,13 @@ twopy/napari/protocols.py,sha256=ZZKBrihoXNzBYdPp7z7dB9aetZsHrcWwYEdJ40w_w08,396
|
|
|
114
114
|
twopy/napari/responses.py,sha256=5pehTKhX0vXt243uH8RorHO0Mnwc_AhTGFujJeL8hoQ,11803
|
|
115
115
|
twopy/napari/roi.py,sha256=C74NY22w1ZwFSikb3IWXOXXv3yQv91KBGBQ4TQqYCVE,14901
|
|
116
116
|
twopy/napari/roi_signatures.py,sha256=f7agEqdEKlRYFZBTV6zTDobOSpKy5A3FpP0_HYdg_7o,618
|
|
117
|
-
twopy/napari/session.py,sha256=
|
|
117
|
+
twopy/napari/session.py,sha256=wcpBmQHoKQ9vZPLs39a_t6GLMCnuj4QcxSiL7bVOZlA,19012
|
|
118
118
|
twopy/napari/sidebar.py,sha256=wBMWvVlSUJdM8uRfq1KI6ebN3Da0nLZjenIyojGQn2o,8520
|
|
119
119
|
twopy/napari/state.py,sha256=P4QD9XZdeHvm_Ebjn7nOqV49gmDUTqBr0kSlOSZruhU,7337
|
|
120
120
|
twopy/napari/text.py,sha256=amsxpoq4zMj5sHnNIiWpcVS5sS2KzIh7d1vQD2C4paE,2127
|
|
121
121
|
twopy/napari/trial_timeline.py,sha256=oZSrYDEWfV60mrI9yekc813cc2W8-8EQZVwb7EUQn74,24349
|
|
122
122
|
twopy/napari/types.py,sha256=yBHw9FSWFdjf8Xp-bK2SKBlH2BW7nIhUgPZceG63amc,1201
|
|
123
|
-
twopy/napari/version_check.py,sha256=
|
|
123
|
+
twopy/napari/version_check.py,sha256=YzU8-fzbcPpCxrdOPciudVnRKi4f2tAS7nWhkanm4R4,9084
|
|
124
124
|
twopy/napari/viewer.py,sha256=FIIRjPmuBkgQshTNFVXzXQFtubgnMI2KOs4eAiXhMq4,22079
|
|
125
125
|
twopy/napari/assets/twopy-app-icon-128.png,sha256=93BO_VF7_CsUCRKpd5OrIid0ax3eV1vUciUtc8UyjP8,10629
|
|
126
126
|
twopy/napari/assets/twopy-app-icon-16.png,sha256=uTRc_JlpzN1jNrWAu4Zi_xUd2nTvNa9TI--yzkGoJZs,1284
|
|
@@ -146,7 +146,7 @@ twopy/napari/group_matching/window.py,sha256=bLxT-qUAjzSkJVhynBeQXKMZIfeIlWl5gq1
|
|
|
146
146
|
twopy/napari/plotting/__init__.py,sha256=JWUrDXYi6x8YYMrzGZyJtIKz6U8-CR9cVxDIkNsnuVY,1572
|
|
147
147
|
twopy/napari/plotting/data.py,sha256=Rpiy_NePnd56-ixunN8inp8ez57Vfn41h-UhBE5pZX0,16343
|
|
148
148
|
twopy/napari/plotting/dff_options.py,sha256=-Jwa5A15qJpKYUFnQi8ZedftPZUTWsdQ-IXy-T3VWu8,15956
|
|
149
|
-
twopy/napari/plotting/export.py,sha256=
|
|
149
|
+
twopy/napari/plotting/export.py,sha256=rEXVzXW1ojgphi53CI0djOIs4f1FtpQsZkMis_4ZUUY,30891
|
|
150
150
|
twopy/napari/plotting/export_controls.py,sha256=oy0k7Zi3Iyv5Fhz1Wx4qWwxU-ZYsmMAzVxhriVI6w4M,11105
|
|
151
151
|
twopy/napari/plotting/form_controls.py,sha256=qqiiOzfIT7oHHlft9qUBf2Z_6rUZP8vfX5XCW8tp_hA,2526
|
|
152
152
|
twopy/napari/plotting/label_visibility.py,sha256=415JAD6dqYYJvGIikgay2RPTHEc2FEsULzzlTjLzeVE,9246
|
|
@@ -167,7 +167,7 @@ twopy/napari/plotting/docks/options_panel.py,sha256=Lp5sieUtzt8iRetyv8_P_4-ST5nK
|
|
|
167
167
|
twopy/napari/plotting/docks/paths.py,sha256=hR2kP5-benxEfxpDedpbbosW1h2c4-no9zN6Da6DD94,4253
|
|
168
168
|
twopy/napari/plotting/docks/plot_area.py,sha256=m7tvd9DmESq-4nhwIXqVVahCmlzTF1oRF44ph3hpdBs,12765
|
|
169
169
|
twopy/napari/plotting/docks/response_map_area.py,sha256=5jFrfH0CZOp8D70-Wt6eNDlS-ny7SuzxdthVdzUWIgk,16033
|
|
170
|
-
twopy/napari/plotting/docks/response_plot_widget.py,sha256=
|
|
170
|
+
twopy/napari/plotting/docks/response_plot_widget.py,sha256=5rXk3JaRUT4xdfBP_l0c4reWNt1N5xv9Yr7TdU9IPbo,88225
|
|
171
171
|
twopy/napari/plotting/docks/save_actions.py,sha256=06xSrv_xzti7Ii1LtERiSQKDf4bt8898BucxxnHNgFk,6063
|
|
172
172
|
twopy/napari/plotting/docks/visibility_state.py,sha256=kmj6fwRV1IrUPFOjAMaHC92P8fNOtWqpzTPJhPUKFKg,5229
|
|
173
173
|
twopy/napari/plotting/roi_generation/__init__.py,sha256=-NGko64QCV08KtQR0L6jTswhAk-sQZzWWBE9hO-ax20,1067
|
|
@@ -180,8 +180,8 @@ twopy/parity/psycho5.py,sha256=Xg7Q8Ipyv6hH9AXYKLxU0cLx9z10uWDLg7PNxT-rDyk,13908
|
|
|
180
180
|
twopy/parity/roi.py,sha256=Q3deH6xRGCmTXASTlPHQgzH7FhBWkdjRUgh9skyS8j0,4975
|
|
181
181
|
twopy/parity/roi_extraction.py,sha256=Z-ABVSB4Z6IUqZMWU1bq6Ko1tGP4uTdzk3As0gHaHTA,10372
|
|
182
182
|
twopy/parity/saved_analysis.py,sha256=Kbpaxh1zapR5XxnHEkin8r4mEZX8kEtonudeZcwI7Dw,5597
|
|
183
|
-
twopy-0.3.
|
|
184
|
-
twopy-0.3.
|
|
185
|
-
twopy-0.3.
|
|
186
|
-
twopy-0.3.
|
|
187
|
-
twopy-0.3.
|
|
183
|
+
twopy-0.3.8.dist-info/METADATA,sha256=jjT2lCro_K1biXVpbzMoRSipJPkbzaJ3nvZU8USEsxo,4143
|
|
184
|
+
twopy-0.3.8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
185
|
+
twopy-0.3.8.dist-info/entry_points.txt,sha256=RnmRKOxEv5ZwLN-dArdSB678gM2iNxbE30VwRpwhp8o,44
|
|
186
|
+
twopy-0.3.8.dist-info/top_level.txt,sha256=8MB6QGpnHMTeTfEE543DOr676HcemeoELujrJkFcvak,6
|
|
187
|
+
twopy-0.3.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|