arrayview 0.51.0__tar.gz → 0.51.1__tar.gz

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.
Files changed (91) hide show
  1. {arrayview-0.51.0 → arrayview-0.51.1}/PKG-INFO +3 -1
  2. {arrayview-0.51.0 → arrayview-0.51.1}/README.md +2 -0
  3. {arrayview-0.51.0 → arrayview-0.51.1}/pyproject.toml +1 -1
  4. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_launcher.py +14 -0
  5. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_loading.py +2 -0
  6. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_query.py +15 -1
  7. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_viewer.html +2795 -814
  8. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_api.py +46 -0
  9. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_browser.py +521 -29
  10. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_cli.py +29 -0
  11. arrayview-0.51.1/tests/test_glides.py +166 -0
  12. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_mode_roundtrip.py +5 -1
  13. arrayview-0.51.1/tests/test_pane_names.py +512 -0
  14. {arrayview-0.51.0 → arrayview-0.51.1}/.gitignore +0 -0
  15. {arrayview-0.51.0 → arrayview-0.51.1}/LICENSE +0 -0
  16. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/ARCHITECTURE.md +0 -0
  17. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/__init__.py +0 -0
  18. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/__main__.py +0 -0
  19. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_analysis.py +0 -0
  20. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_app.py +0 -0
  21. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_bootstrap_app.py +0 -0
  22. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_config.py +0 -0
  23. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_diagnostics.py +0 -0
  24. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_dicom.py +0 -0
  25. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_diff.py +0 -0
  26. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_extra_ports.py +0 -0
  27. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_icon.png +0 -0
  28. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_imaging.py +0 -0
  29. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_instance_registry.py +0 -0
  30. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_io.py +0 -0
  31. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_launch_plan.py +0 -0
  32. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_launch_trace.py +0 -0
  33. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_lifecycle.py +0 -0
  34. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_overlays.py +0 -0
  35. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_platform.py +0 -0
  36. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_render.py +0 -0
  37. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_analysis.py +0 -0
  38. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_drop.py +0 -0
  39. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_export.py +0 -0
  40. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_persistence.py +0 -0
  41. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_preferences.py +0 -0
  42. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_preload.py +0 -0
  43. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_rendering.py +0 -0
  44. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_segmentation.py +0 -0
  45. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_state.py +0 -0
  46. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_vectorfield.py +0 -0
  47. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_routes_websocket.py +0 -0
  48. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_segmentation.py +0 -0
  49. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_server.py +0 -0
  50. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_session.py +0 -0
  51. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_session_spec.py +0 -0
  52. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_shell.html +0 -0
  53. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_source_safety.py +0 -0
  54. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_synthetic_mri.py +0 -0
  55. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_torch.py +0 -0
  56. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_tutorial.py +0 -0
  57. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_vectorfield.py +0 -0
  58. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_view_memory.py +0 -0
  59. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_vscode.py +0 -0
  60. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_vscode_browser.py +0 -0
  61. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_vscode_extension.py +0 -0
  62. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_vscode_signal.py +0 -0
  63. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/_watch.py +0 -0
  64. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/arrayview-opener.vsix +0 -0
  65. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/gsap.min.js +0 -0
  66. {arrayview-0.51.0 → arrayview-0.51.1}/src/arrayview/todo.md +0 -0
  67. {arrayview-0.51.0 → arrayview-0.51.1}/tests/capture_patient_loading.py +0 -0
  68. {arrayview-0.51.0 → arrayview-0.51.1}/tests/capture_v_animation.py +0 -0
  69. {arrayview-0.51.0 → arrayview-0.51.1}/tests/conftest.py +0 -0
  70. {arrayview-0.51.0 → arrayview-0.51.1}/tests/make_vectorfield_test_arrays.py +0 -0
  71. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_1d_arrays.py +0 -0
  72. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_backend_shared.py +0 -0
  73. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_config.py +0 -0
  74. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_dicom.py +0 -0
  75. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_drop_import.py +0 -0
  76. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_instance_registry.py +0 -0
  77. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_io.py +0 -0
  78. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_large_arrays.py +0 -0
  79. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_launch_blackbox.py +0 -0
  80. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_nifti_meta.py +0 -0
  81. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_nifti_series.py +0 -0
  82. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_rgb_pixel_art.py +0 -0
  83. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_safe_server_stop.py +0 -0
  84. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_source_safety.py +0 -0
  85. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_torch.py +0 -0
  86. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_tutorial.py +0 -0
  87. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_view_memory.py +0 -0
  88. {arrayview-0.51.0 → arrayview-0.51.1}/tests/test_vsix_bundle.py +0 -0
  89. {arrayview-0.51.0 → arrayview-0.51.1}/tests/ui_audit.py +0 -0
  90. {arrayview-0.51.0 → arrayview-0.51.1}/tests/v_anim_frames/.gitkeep +0 -0
  91. {arrayview-0.51.0 → arrayview-0.51.1}/tests/visual_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: arrayview
3
- Version: 0.51.0
3
+ Version: 0.51.1
4
4
  Summary: Fast multi-dimensional array viewer
5
5
  Project-URL: Home, https://github.com/oscarvanderheide/arrayview
6
6
  Project-URL: Source, https://github.com/oscarvanderheide/arrayview
@@ -39,6 +39,8 @@ Description-Content-Type: text/markdown
39
39
 
40
40
  _This is my array viewer. There are many like it, but this one is mine._
41
41
 
42
+ <img src="docs/teaser.gif" alt="ArrayView: scrolling a volume, switching colormaps, three planes, and a difference view" width="800">
43
+
42
44
  Arrayview lets you scroll through multi-dimensional arrays.
43
45
 
44
46
  Open it from the shell, from Python, Julia, or Matlab, or inside a Jupyter notebook. Use it locally or over SSH.
@@ -2,6 +2,8 @@
2
2
 
3
3
  _This is my array viewer. There are many like it, but this one is mine._
4
4
 
5
+ <img src="docs/teaser.gif" alt="ArrayView: scrolling a volume, switching colormaps, three planes, and a difference view" width="800">
6
+
5
7
  Arrayview lets you scroll through multi-dimensional arrays.
6
8
 
7
9
  Open it from the shell, from Python, Julia, or Matlab, or inside a Jupyter notebook. Use it locally or over SSH.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arrayview"
7
- version = "0.51.0"
7
+ version = "0.51.1"
8
8
  description = "Fast multi-dimensional array viewer"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12"
@@ -1323,6 +1323,7 @@ def _load_session_from_filepath(
1323
1323
  dir_exclude_cases: list[str] | None = None,
1324
1324
  collection_load: str = "lazy",
1325
1325
  collection_stack: str = "auto",
1326
+ collection_keep_order: bool = False,
1326
1327
  background: bool = False,
1327
1328
  expected_server_id: str | None = None,
1328
1329
  native_request_id: str | None = None,
@@ -1365,6 +1366,7 @@ def _load_session_from_filepath(
1365
1366
  payload["dir_exclude_cases"] = dir_exclude_cases or []
1366
1367
  payload["load"] = collection_load
1367
1368
  payload["stack"] = collection_stack
1369
+ payload["keep_order"] = collection_keep_order
1368
1370
  if notify and compare_sids:
1369
1371
  payload["compare_sid"] = compare_sids[0]
1370
1372
  payload["compare_sids"] = _join_query_values(compare_sids)
@@ -1686,6 +1688,7 @@ def _register_cli_session_with_existing_server_impl(
1686
1688
  dir_exclude_cases: list[str] | None = None,
1687
1689
  collection_load: str = "lazy",
1688
1690
  collection_stack: str = "auto",
1691
+ collection_keep_order: bool = False,
1689
1692
  overlay_names: list[str] | None = None,
1690
1693
  background_base: bool = False,
1691
1694
  expected_server_id: str | None = None,
@@ -1741,6 +1744,7 @@ def _register_cli_session_with_existing_server_impl(
1741
1744
  dir_exclude_cases=dir_exclude_cases,
1742
1745
  collection_load=collection_load,
1743
1746
  collection_stack=collection_stack,
1747
+ collection_keep_order=collection_keep_order,
1744
1748
  background=background_base,
1745
1749
  expected_server_id=expected_server_id,
1746
1750
  native_request_id=native_request_id,
@@ -1838,6 +1842,7 @@ def _handle_cli_existing_server(
1838
1842
  dir_exclude_cases: list[str] | None = None,
1839
1843
  collection_load: str = "lazy",
1840
1844
  collection_stack: str = "auto",
1845
+ collection_keep_order: bool = False,
1841
1846
  overlay_names: list[str] | None = None,
1842
1847
  cleanup_dir: str | None = None,
1843
1848
  source_staging_dirs: dict[str, str] | None = None,
@@ -1866,6 +1871,7 @@ def _handle_cli_existing_server(
1866
1871
  dir_exclude_cases=dir_exclude_cases,
1867
1872
  collection_load=collection_load,
1868
1873
  collection_stack=collection_stack,
1874
+ collection_keep_order=collection_keep_order,
1869
1875
  overlay_names=overlay_names,
1870
1876
  background_base=(
1871
1877
  is_remote
@@ -1954,6 +1960,7 @@ def _handle_cli_existing_server(
1954
1960
  dir_exclude_cases=dir_exclude_cases,
1955
1961
  collection_load=collection_load,
1956
1962
  collection_stack=collection_stack,
1963
+ collection_keep_order=collection_keep_order,
1957
1964
  overlay_names=overlay_names,
1958
1965
  source_staging_dirs=source_staging_dirs,
1959
1966
  )
@@ -2084,6 +2091,7 @@ def _handle_cli_spawned_daemon(
2084
2091
  dir_exclude_cases: list[str] | None = None,
2085
2092
  collection_load: str = "lazy",
2086
2093
  collection_stack: str = "auto",
2094
+ collection_keep_order: bool = False,
2087
2095
  overlay_names: list[str] | None = None,
2088
2096
  cleanup_dir: str | None = None,
2089
2097
  source_staging_dirs: dict[str, str] | None = None,
@@ -2147,6 +2155,7 @@ def _handle_cli_spawned_daemon(
2147
2155
  dir_exclude_cases=dir_exclude_cases,
2148
2156
  collection_load=collection_load,
2149
2157
  collection_stack=collection_stack,
2158
+ collection_keep_order=collection_keep_order,
2150
2159
  overlay_names=resolved_overlay_names,
2151
2160
  cleanup_dir=cleanup_dir,
2152
2161
  source_staging_dirs=source_staging_dirs,
@@ -2202,6 +2211,7 @@ def _handle_cli_spawned_daemon(
2202
2211
  f" dir_exclude_cases={repr(dir_exclude_cases)},"
2203
2212
  f" collection_load={repr(collection_load)},"
2204
2213
  f" collection_stack={repr(collection_stack)},"
2214
+ f" collection_keep_order={repr(collection_keep_order)},"
2205
2215
  f" cleanup_dir={repr(cleanup_dir)},"
2206
2216
  f" source_staging_dirs={repr(source_staging_dirs or {})},"
2207
2217
  f" watch={watch},"
@@ -4729,6 +4739,7 @@ def _serve_daemon(
4729
4739
  dir_exclude_cases: list[str] | None = None,
4730
4740
  collection_load: str = "lazy",
4731
4741
  collection_stack: str = "auto",
4742
+ collection_keep_order: bool = False,
4732
4743
  cleanup_dir: str | None = None,
4733
4744
  source_staging_dirs: dict[str, str] | None = None,
4734
4745
  watch: bool = False,
@@ -4868,6 +4879,7 @@ def _serve_daemon(
4868
4879
  exclude_cases=dir_exclude_cases,
4869
4880
  load=collection_load,
4870
4881
  stack=collection_stack,
4882
+ keep_order=collection_keep_order,
4871
4883
  )
4872
4884
  collection_spatial_ndim = len(_summary["spatial_shape"])
4873
4885
  elif (filepath.endswith(".npz") or filepath.endswith(".mat")) and not cleanup:
@@ -6706,6 +6718,7 @@ def arrayview():
6706
6718
  dir_exclude_cases=sorted(excluded_cases),
6707
6719
  collection_load=args.load,
6708
6720
  collection_stack=args.stack_policy or "auto",
6721
+ collection_keep_order=getattr(args, "keep_file_order", False),
6709
6722
  cleanup_dir=getattr(args, "_tutorial_cleanup_dir", None),
6710
6723
  source_staging_dirs=(
6711
6724
  source_staging_dirs if not args.stack_mode else None
@@ -6746,6 +6759,7 @@ def arrayview():
6746
6759
  dir_exclude_cases=sorted(excluded_cases),
6747
6760
  collection_load=args.load,
6748
6761
  collection_stack=args.stack_policy or "auto",
6762
+ collection_keep_order=getattr(args, "keep_file_order", False),
6749
6763
  source_staging_dirs=(
6750
6764
  source_staging_dirs if not args.stack_mode else None
6751
6765
  ),
@@ -227,6 +227,7 @@ def register_loading_routes(app, *, notify_shells, setup_rgb) -> None:
227
227
  tuple(str(case) for case in body.get("dir_exclude_cases", [])),
228
228
  body.get("load", "lazy"),
229
229
  body.get("stack", "auto"),
230
+ bool(body.get("keep_order", False)),
230
231
  )
231
232
  for existing in [] if requested_sid else SESSIONS.values():
232
233
  if getattr(existing, "collection_identity", None) == collection_identity:
@@ -440,6 +441,7 @@ def register_loading_routes(app, *, notify_shells, setup_rgb) -> None:
440
441
  exclude_cases=body.get("dir_exclude_cases", []),
441
442
  load=body.get("load", "lazy"),
442
443
  stack=body.get("stack", "auto"),
444
+ keep_order=bool(body.get("keep_order", False)),
443
445
  )
444
446
  elif filepath.endswith(".npz") or filepath.endswith(".mat"):
445
447
  _array_keys = await asyncio.to_thread(list_array_keys, filepath)
@@ -429,7 +429,7 @@ def register_query_routes(app, *, get_session_or_404, pil_image, pil_imageops) -
429
429
  info["overlay_labels"] = []
430
430
  try:
431
431
  if np.issubdtype(session.data.dtype, np.integer):
432
- labels = np.unique(session.data)
432
+ labels = np.unique(_overlay_label_sample(session))
433
433
  labels = labels[labels > 0]
434
434
  if labels.size <= 16:
435
435
  info["overlay_labels"] = [int(value) for value in labels]
@@ -531,6 +531,18 @@ def register_query_routes(app, *, get_session_or_404, pil_image, pil_imageops) -
531
531
  )
532
532
 
533
533
 
534
+ def _overlay_label_sample(session):
535
+ """Overlay values to read label ids from: the whole array, or only the
536
+ first case of a collection (reading every case would stall opening; the
537
+ viewer adds labels found in other cases as they are visited)."""
538
+ spatial = getattr(session, "collection_spatial_ndim", None)
539
+ shape = tuple(session.shape)
540
+ if spatial is None or not (0 < spatial < len(shape)):
541
+ return np.asarray(session.data)
542
+ index = (slice(None),) * spatial + (0,) * (len(shape) - spatial)
543
+ return np.asarray(session.data[index])
544
+
545
+
534
546
  def _overlay_slice_counts(ov_session, base_shape, dim_x, dim_y, axis, idx_list):
535
547
  """Count overlay pixels per label on every slice along ``axis``."""
536
548
  n = int(base_shape[axis])
@@ -555,6 +567,8 @@ def _overlay_slice_counts(ov_session, base_shape, dim_x, dim_y, axis, idx_list):
555
567
  return empty
556
568
  kept = [a for a in base_axes if a in (dim_x, dim_y, axis)]
557
569
  if axis in kept:
570
+ # Ragged collections return this case's own size, not the max-shape facade.
571
+ n = int(vol.shape[kept.index(axis)])
558
572
  vol = np.moveaxis(vol, kept.index(axis), 0).reshape(n, -1)
559
573
  else:
560
574
  # The overlay has no such axis: it looks the same on every slice.