pycat-napari 1.6.330__tar.gz → 1.6.332__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 (323) hide show
  1. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/CHANGELOG.md +46 -0
  2. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/PKG-INFO +1 -1
  3. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/pyproject.toml +1 -1
  4. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/central_manager.py +6 -3
  5. pycat_napari-1.6.332/src/pycat/navigator/executor.py +163 -0
  6. pycat_napari-1.6.332/src/pycat/navigator/templates.py +135 -0
  7. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/navigator_dock.py +56 -0
  8. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/.gitignore +0 -0
  9. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/CONTRIBUTING.md +0 -0
  10. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/LICENSE +0 -0
  11. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/README.md +0 -0
  12. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/THIRD_PARTY_LICENSES.txt +0 -0
  13. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/benchmarks/README.md +0 -0
  14. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/config/README.md +0 -0
  15. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/config/requirements-arm-mac.txt +0 -0
  16. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/config/requirements-base.txt +0 -0
  17. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/config/requirements-devbio-napari-arm-mac.txt +0 -0
  18. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/config/requirements-devbio-napari.txt +0 -0
  19. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/__init__.py +0 -0
  20. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/__init__.py +0 -0
  21. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/__init__.py +0 -0
  22. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/_common.py +0 -0
  23. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/analysis_steps.py +0 -0
  24. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/brightfield_steps.py +0 -0
  25. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/invitro_steps.py +0 -0
  26. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/io_steps.py +0 -0
  27. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/preprocessing_steps.py +0 -0
  28. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch/steps/segmentation_steps.py +0 -0
  29. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch_processor.py +0 -0
  30. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/batch_step_registry.py +0 -0
  31. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/data/__init__.py +0 -0
  32. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/data/data_modules.py +0 -0
  33. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/__init__.py +0 -0
  34. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/czi_seam.py +0 -0
  35. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/dialogs.py +0 -0
  36. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/file_io.py +0 -0
  37. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/frap_io.py +0 -0
  38. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/image_reader.py +0 -0
  39. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/image_source.py +0 -0
  40. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/image_structure.py +0 -0
  41. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/lazy_guard.py +0 -0
  42. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/lazy_sources.py +0 -0
  43. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/load_channel_identity.py +0 -0
  44. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/local_cache.py +0 -0
  45. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/metadata_extract.py +0 -0
  46. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/multidim_io.py +0 -0
  47. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/naming.py +0 -0
  48. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/napari_adapter.py +0 -0
  49. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/__init__.py +0 -0
  50. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/czi_bioformats.py +0 -0
  51. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/image_reader_2d.py +0 -0
  52. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/ims_reader.py +0 -0
  53. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/mask_reader.py +0 -0
  54. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/stack_layer_builders.py +0 -0
  55. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/readers/stack_metadata.py +0 -0
  56. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/routing.py +0 -0
  57. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/scenes.py +0 -0
  58. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/session.py +0 -0
  59. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/session_loader.py +0 -0
  60. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/session_manifest.py +0 -0
  61. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/sidecar_discovery.py +0 -0
  62. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/stack_access.py +0 -0
  63. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/stack_load.py +0 -0
  64. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/stack_openers.py +0 -0
  65. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/storage_probe.py +0 -0
  66. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/tagging.py +0 -0
  67. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/tiff_planes.py +0 -0
  68. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/tifffile_zarr_shim.py +0 -0
  69. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/viewer_load.py +0 -0
  70. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/writers.py +0 -0
  71. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/file_io/zarr_compat.py +0 -0
  72. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/fix_tifffile.py +0 -0
  73. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/icons/pycat_logo_512.png +0 -0
  74. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/icons/pycat_mark.png +0 -0
  75. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/__init__.py +0 -0
  76. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/adapters.py +0 -0
  77. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/canonical.py +0 -0
  78. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/capabilities.py +0 -0
  79. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/context.py +0 -0
  80. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/contracts.py +0 -0
  81. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/data/PyCAT_layer_tag_hierarchy_and_module_flow.xlsx +0 -0
  82. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/data/PyCAT_module_information_contracts.xlsx +0 -0
  83. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/data/PyCAT_question_tree_and_method_mapping.xlsx +0 -0
  84. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/data/operation_catalog.json +0 -0
  85. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/execution.py +0 -0
  86. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/gates.py +0 -0
  87. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/loader.py +0 -0
  88. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/modules.py +0 -0
  89. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/op_catalog.py +0 -0
  90. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/operation_spec.py +0 -0
  91. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/planner.py +0 -0
  92. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/quality_gates.py +0 -0
  93. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/question_engine.py +0 -0
  94. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/registry.py +0 -0
  95. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/scientific_tree.py +0 -0
  96. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/session.py +0 -0
  97. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/navigator/tags.py +0 -0
  98. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/run_pycat.py +0 -0
  99. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/__init__.py +0 -0
  100. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/advanced_analysis_ui.py +0 -0
  101. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/analysis_plots.py +0 -0
  102. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/batch_roi_tools.py +0 -0
  103. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/benchmark_tools.py +0 -0
  104. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/biological_qc_tools.py +0 -0
  105. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/brightfield_tools.py +0 -0
  106. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/brightfield_ui.py +0 -0
  107. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/clean_spot_detection_tools.py +0 -0
  108. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_modes.py +0 -0
  109. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/__init__.py +0 -0
  110. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/coarsening.py +0 -0
  111. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/frame_quality.py +0 -0
  112. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/intensity.py +0 -0
  113. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/moduli.py +0 -0
  114. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/msd.py +0 -0
  115. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/photobleaching.py +0 -0
  116. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/relaxation.py +0 -0
  117. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics/survival.py +0 -0
  118. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics_tools.py +0 -0
  119. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/condensate_physics_ui.py +0 -0
  120. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/contrast_cascade_tools.py +0 -0
  121. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/contrast_cascade_ui.py +0 -0
  122. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/control_validation.py +0 -0
  123. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/correlation_func_analysis_tools.py +0 -0
  124. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/data_qc_tools.py +0 -0
  125. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/data_qc_ui.py +0 -0
  126. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/data_viz_tools.py +0 -0
  127. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/drawing_layers.py +0 -0
  128. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/dynamic_spatial_tools.py +0 -0
  129. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fd_curve_tools.py +0 -0
  130. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fd_curve_ui.py +0 -0
  131. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/feature_analysis_tools.py +0 -0
  132. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/feature_redundancy.py +0 -0
  133. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fft_bandpass_tools.py +0 -0
  134. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fibril_tools.py +0 -0
  135. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fibril_ui.py +0 -0
  136. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/frap_tools.py +0 -0
  137. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/frap_ui.py +0 -0
  138. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fusion_tools.py +0 -0
  139. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/fusion_ui.py +0 -0
  140. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/gaussian_localization_tools.py +0 -0
  141. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/general_image_tools.py +0 -0
  142. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/gpu_utils.py +0 -0
  143. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/__init__.py +0 -0
  144. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/_base.py +0 -0
  145. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/background.py +0 -0
  146. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/deblur.py +0 -0
  147. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/filters.py +0 -0
  148. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/preprocessing.py +0 -0
  149. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/size_estimation.py +0 -0
  150. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing/upscaling.py +0 -0
  151. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/image_processing_tools.py +0 -0
  152. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/intensity_profile_tools.py +0 -0
  153. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro/__init__.py +0 -0
  154. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro/analysis.py +0 -0
  155. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro/field_summary.py +0 -0
  156. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro/partition.py +0 -0
  157. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro/size_distribution.py +0 -0
  158. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro_bf_ui.py +0 -0
  159. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro_fluor_ui.py +0 -0
  160. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/invitro_tools.py +0 -0
  161. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/kymograph_tools.py +0 -0
  162. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/label_and_mask_tools.py +0 -0
  163. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/layer_tools.py +0 -0
  164. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/measurement_stability.py +0 -0
  165. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/molecular_counting_tools.py +0 -0
  166. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/morphological_complexity_tools.py +0 -0
  167. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/nb_tools.py +0 -0
  168. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/nb_ui.py +0 -0
  169. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/numba_utils.py +0 -0
  170. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/obj_based_coloc_analysis_tools.py +0 -0
  171. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/organizational_metrics_tools.py +0 -0
  172. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/partial_volume_tools.py +0 -0
  173. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/partition_enrichment_tools.py +0 -0
  174. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/pipeline_diagnostic_tools.py +0 -0
  175. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/pipeline_snr_tools.py +0 -0
  176. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/pixel_wise_corr_analysis_tools.py +0 -0
  177. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/qc_gallery.py +0 -0
  178. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/qc_gallery_ui.py +0 -0
  179. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/ratiometric_tools.py +0 -0
  180. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/scan_qc_tools.py +0 -0
  181. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/__init__.py +0 -0
  182. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/_common.py +0 -0
  183. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/cellpose.py +0 -0
  184. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/fz.py +0 -0
  185. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/intensity.py +0 -0
  186. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/local_thresholding.py +0 -0
  187. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/morphology.py +0 -0
  188. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/puncta_refinement.py +0 -0
  189. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/subcellular.py +0 -0
  190. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation/watershed.py +0 -0
  191. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation_scale_advisor.py +0 -0
  192. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/segmentation_tools.py +0 -0
  193. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/smlm_tools.py +0 -0
  194. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/spatial_acf_tools.py +0 -0
  195. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/spatial_metrology_tools.py +0 -0
  196. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/spatial_metrology_ui.py +0 -0
  197. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/spatial_randomness_tools.py +0 -0
  198. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/spida_tools.py +0 -0
  199. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/spida_ui.py +0 -0
  200. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/temperature_tools.py +0 -0
  201. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/temperature_ui.py +0 -0
  202. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/temporal_enhancement_tools.py +0 -0
  203. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/__init__.py +0 -0
  204. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/analysis.py +0 -0
  205. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/correlation.py +0 -0
  206. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/execution.py +0 -0
  207. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/frame_access.py +0 -0
  208. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/preprocessing.py +0 -0
  209. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries/ui.py +0 -0
  210. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries_condensate_tools.py +0 -0
  211. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries_invitro_fluor_ui.py +0 -0
  212. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/timeseries_invitro_tools.py +0 -0
  213. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/topology_tools.py +0 -0
  214. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/trackmate_bridge.py +0 -0
  215. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/ts_cache_manager.py +0 -0
  216. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/ts_cellpose_tools.py +0 -0
  217. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/two_channel_coloc_tools.py +0 -0
  218. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/unmixing_tools.py +0 -0
  219. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/video_export_tools.py +0 -0
  220. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/__init__.py +0 -0
  221. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/analysis.py +0 -0
  222. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/detection.py +0 -0
  223. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/drift.py +0 -0
  224. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/host.py +0 -0
  225. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/msd_adapter.py +0 -0
  226. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/napari_adapter.py +0 -0
  227. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/panels.py +0 -0
  228. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/populations.py +0 -0
  229. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/results_dock.py +0 -0
  230. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/table_adapter.py +0 -0
  231. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt/viscosity.py +0 -0
  232. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt_tools.py +0 -0
  233. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/vpt_ui.py +0 -0
  234. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/zstack_segmentation_tools.py +0 -0
  235. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/toolbox/zstack_segmentation_ui.py +0 -0
  236. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/__init__.py +0 -0
  237. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/brushable_table.py +0 -0
  238. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/brushable_workspace.py +0 -0
  239. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/channel_identity_dialog.py +0 -0
  240. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/comparative_figures_ui.py +0 -0
  241. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/coordinate_readout.py +0 -0
  242. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/feature_explorer_dock.py +0 -0
  243. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/field_status.py +0 -0
  244. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/home_dock.py +0 -0
  245. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/linked_selection_dock.py +0 -0
  246. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/menu_manager.py +0 -0
  247. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/metadata_contradiction_panel.py +0 -0
  248. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/operation_gating.py +0 -0
  249. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/preferences_dialog.py +0 -0
  250. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/preset_picker.py +0 -0
  251. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/scene_switcher.py +0 -0
  252. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_analysis_mixin.py +0 -0
  253. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_diagnostics_mixin.py +0 -0
  254. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_filtering_mixin.py +0 -0
  255. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_imageops_mixin.py +0 -0
  256. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_labels_mixin.py +0 -0
  257. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_modules.py +0 -0
  258. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_segmentation_mixin.py +0 -0
  259. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/ui_utils.py +0 -0
  260. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/ui/workflow_checklist.py +0 -0
  261. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/__init__.py +0 -0
  262. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/analysis_presets.py +0 -0
  263. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/app_mode.py +0 -0
  264. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/batch_brushing.py +0 -0
  265. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/brushing.py +0 -0
  266. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/calibration.py +0 -0
  267. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/channel_designations.py +0 -0
  268. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/channel_modality.py +0 -0
  269. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/channel_naming.py +0 -0
  270. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/cohort_targets.py +0 -0
  271. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/comparative_figures.py +0 -0
  272. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/comparative_stats.py +0 -0
  273. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/consolidated_table.py +0 -0
  274. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/dataset_identity.py +0 -0
  275. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/dock_space.py +0 -0
  276. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/entity_ref.py +0 -0
  277. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/entity_registry.py +0 -0
  278. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/environment_check.py +0 -0
  279. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/errors.py +0 -0
  280. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/feature_cards.py +0 -0
  281. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/feature_explorer.py +0 -0
  282. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/feature_families.py +0 -0
  283. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/feature_provenance.py +0 -0
  284. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/feature_registry.py +0 -0
  285. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/field_registry_hub.py +0 -0
  286. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/figure_refine.py +0 -0
  287. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/figure_spec.py +0 -0
  288. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/filter_store.py +0 -0
  289. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/fit_quality.py +0 -0
  290. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/frame_interval.py +0 -0
  291. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/general_utils.py +0 -0
  292. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/intensity_semantics.py +0 -0
  293. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/layer_bindings.json +0 -0
  294. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/layer_tag_hook.py +0 -0
  295. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/layer_tags.py +0 -0
  296. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/logging_utils.py +0 -0
  297. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/marker_logic.py +0 -0
  298. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/math_utils.py +0 -0
  299. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/measurement.py +0 -0
  300. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/measurement_ontology.py +0 -0
  301. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/metadata_contradictions.py +0 -0
  302. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/metadata_validity.py +0 -0
  303. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/notify.py +0 -0
  304. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/object_graph.py +0 -0
  305. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/object_ref.py +0 -0
  306. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/operation_runner.py +0 -0
  307. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/pixel_size.py +0 -0
  308. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/plot_backend_pyqtgraph.py +0 -0
  309. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/plot_backend_selection.py +0 -0
  310. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/plot_backends.py +0 -0
  311. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/preferences.py +0 -0
  312. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/qt_worker.py +0 -0
  313. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/quality_gate.py +0 -0
  314. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/reliability.py +0 -0
  315. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/result_models.py +0 -0
  316. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/results_store.py +0 -0
  317. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/sample_metadata.py +0 -0
  318. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/selection_overlay.py +0 -0
  319. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/selection_service.py +0 -0
  320. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/tag_registry.py +0 -0
  321. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/tag_resolver.py +0 -0
  322. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/src/pycat/utils/user_settings.py +0 -0
  323. {pycat_napari-1.6.330 → pycat_napari-1.6.332}/tests/README.md +0 -0
@@ -1,3 +1,49 @@
1
+ ## [1.6.332] - 2026-07-24
2
+ ### Added — **Navigator execution-adapter layer, Phase 1: "Run analysis" computes a plan through the batch handlers, gate-respecting (guided == batch == manual).**
3
+ `selection_scale` Part 2 established there is no uniform "run this op" — but the batch `_STEP_MAP` handlers ARE
4
+ the proven "same computation" route (uniform `(state, image_path, params, output_dir)`; `test_route_equivalence`
5
+ asserts batch == manual). This ships the executor that drives them:
6
+ - New Qt-free **`navigator/executor.py`**: `run_plan(plan, state, …)` executes a compiled plan by driving the
7
+ batch handlers in the gate-respecting order (`execution.execution_order`), threading each step's output
8
+ through a shared `state` as a batch replay does. An `ExecAdapter` maps a plan step to a batch handler and
9
+ derives its params; **a step with no adapter is reported ('needs_panel'), never invoked with guessed
10
+ arguments** — the whole point of an adapter layer over a generic `fn(image)` (which would produce wrong
11
+ science silently). Gate semantics are READ from `execution_order`: a blocker halts the run (state untouched),
12
+ a caveat runs with its reason, probes first.
13
+ - **One proven adapter (`background_removal`)** — the shortest chain `test_route_equivalence` covers — so the
14
+ acceptance gate is pinned: `tests/navigator/test_navigator_executor.py` (`base`, 5) asserts **guided == batch
15
+ == manual, bit for bit**, plus a blocker halts + leaves state untouched, a caveat runs, and an uncovered step
16
+ is reported not invoked.
17
+ - **The Run button is wired** (`run_plan_via_central_manager`, passed as `on_run` from `central_manager`):
18
+ adapter-covered steps run through the batch handlers; uncovered steps are reported "run these from their
19
+ method panels, in order". Safe on an uncovered plan (nothing runs); best-effort.
20
+ - **Later phases** (per the `navigator_execution_adapters` spec): a parameter-review panel, more adapters one
21
+ workflow at a time (each behind its own route-equivalence test), and dock progress/cancel.
22
+
23
+ ## [1.6.331] - 2026-07-24
24
+ ### Added — **Save an answered guided plan as a reusable method template (selection_scale Part 3).**
25
+ The navigator's payoff: a plan the user likes should be reusable on other data without re-answering. A guided
26
+ template IS a preset with provenance about how it was authored — so it follows the `analysis_presets`
27
+ discipline (it records the ANSWERS that produced it) but, being a user-level artefact that survives sessions,
28
+ lives in the general `user_settings` service rather than the hardcoded, sensitivity-validated preset registry.
29
+ - New Qt-free **`navigator/templates.py`**: `GuidedTemplate` (name, observables, target, the question trail,
30
+ step names, parameters) + `template_from_plan` / `save_template` / `list_templates` / `load_template` /
31
+ `delete_template` / `rename_template` (never overwrites), persisted under `user_settings` key
32
+ `navigator.templates`.
33
+ - **The verdicts are deliberately NOT stored.** A quality-gate verdict is a property of the DATA, not the
34
+ plan — a template runnable on one dataset may be blocked on another — so applying a template
35
+ (`intent_from_template` → recompile against the new context) **re-evaluates every gate**. Carrying a verdict
36
+ over would assert quality never checked on this data. A corrupt template entry degrades to "not available"
37
+ (skipped), never a crash.
38
+ - **Reachable**: a '💾 Save as template…' button in the navigator plan view persists the current answers under
39
+ a user-supplied name.
40
+ - Tests: `tests/navigator/test_navigator_templates.py` (`base`, 7 — save+reload carries steps and answers,
41
+ applying to a different dataset re-evaluates the gates, verdicts absent, persistence across sessions,
42
+ delete/rename-never-overwrites, corrupt-degrades) + a dock integration test for the Save button. Full
43
+ core-or-base green.
44
+ - **Follow-on:** the template-picker UI (start a new analysis FROM a saved template — the apply half's UI; the
45
+ mechanism is done and tested).
46
+
1
47
  ## [1.6.330] - 2026-07-24
2
48
  ### Added — **Gate-respecting execution model for a guided plan, and an actionable run order in the dock (selection_scale Part 2).**
3
49
  Part 2 asked to wire the navigator's Run button to execute the compiled plan "through the existing execution
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycat-napari
3
- Version: 1.6.330
3
+ Version: 1.6.332
4
4
  Summary: Python Condensate Analysis Toolbox - A napari-based tool for biomolecular condensate analysis
5
5
  Project-URL: Homepage, https://github.com/BanerjeeLab-repertoire/pycat-napari
6
6
  Project-URL: Repository, https://github.com/BanerjeeLab-repertoire/pycat-napari
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pycat-napari"
7
- version = "1.6.330"
7
+ version = "1.6.332"
8
8
  description = "Python Condensate Analysis Toolbox - A napari-based tool for biomolecular condensate analysis"
9
9
  readme = "README.md"
10
10
  # 3.13 is BLOCKED UPSTREAM, not by PyCAT's own code (which compiles and passes core on 3.13).
@@ -119,9 +119,12 @@ class CentralManager:
119
119
  getattr(self.menu_manager, 'file_menu', None),
120
120
  getattr(getattr(self.viewer, 'window', None), '_qt_window', None))
121
121
  # Navigator: guided-analysis dock (question flow -> quality-gated, editable plan). Entry point here,
122
- # not the line-capped menu god-file. on_run is left None until the plan-execution bridge lands.
123
- from pycat.ui.navigator_dock import install_navigator_action
124
- self._navigator_action = install_navigator_action(self.viewer, central_manager=self)
122
+ # not the line-capped menu god-file. Run analysis is wired through the execution-adapter layer (Phase
123
+ # 1): adapter-covered steps run via the batch handlers, the rest are reported "run from your panel".
124
+ from pycat.ui.navigator_dock import install_navigator_action, run_plan_via_central_manager
125
+ self._navigator_action = install_navigator_action(
126
+ self.viewer, central_manager=self,
127
+ on_run=lambda plan: run_plan_via_central_manager(self, plan))
125
128
  # Populate the feature registry so any consumer (the beginner home dock, a capability query) sees
126
129
  # every surfaced-but-otherwise-hidden feature (navigator inc 4). Guarded: a registration hiccup must
127
130
  # never break startup, and the cards' openers import Qt/toolbox lazily so this stays cheap.
@@ -0,0 +1,163 @@
1
+ """**Execute a compiled navigator plan by driving the BATCH step handlers** (execution-adapter layer, Phase 1).
2
+
3
+ `selection_scale` Part 2 established there is no uniform "run this op" — PyCAT operations have bespoke,
4
+ panel-collected signatures. But the **batch `_STEP_MAP` handlers** are the proven "same computation" route:
5
+ they share one signature — ``(state, image_path, params, output_dir)`` — and `test_route_equivalence` already
6
+ asserts they compute byte-identically to the manual GUI route. So the executor drives *those* handlers, in the
7
+ gate-respecting order (`execution.execution_order`), threading each step's output through a shared ``state``
8
+ dict exactly as a batch replay does.
9
+
10
+ The narrow bridge is per-step: an :class:`ExecAdapter` maps a plan step to a batch handler and derives that
11
+ handler's ``params`` from the answers. A plan step with **no adapter is reported** ("run it from its method
12
+ panel"), never invoked with guessed arguments — a `fn(image)` fallback would pass wrong args and produce wrong
13
+ science silently, which is the whole reason this is an adapter layer and not a loop over ``resolve_operation``.
14
+
15
+ Phase 1 ships the executor + registry + ONE proven adapter (``background_removal`` — the shortest chain
16
+ `test_route_equivalence` already covers), so `guided == batch == manual` is pinned; later phases add adapters
17
+ one workflow at a time, each earning its place with a route-equivalence test. Qt-free; a runner is optional
18
+ (off-thread when given, synchronous otherwise)."""
19
+ from __future__ import annotations
20
+
21
+ import dataclasses
22
+ import pathlib
23
+ import tempfile
24
+ from typing import Callable, Optional
25
+
26
+ from .execution import execution_order
27
+
28
+
29
+ @dataclasses.dataclass(frozen=True)
30
+ class ExecAdapter:
31
+ """Bridges ONE plan step to the batch handler that computes it. ``params_from(intent, ctx, state)`` derives
32
+ the handler's ``params`` from the answers (Phase 1: grounded defaults; a preset seed lands in Phase 2)."""
33
+ plan_step: str
34
+ batch_step: str
35
+ params_from: Callable
36
+
37
+
38
+ def _background_removal_params(intent, ctx, state):
39
+ radius = None
40
+ try:
41
+ radius = ctx.get("ball_radius") if ctx is not None else None
42
+ except Exception: # broad-ok: optional_probe — a context miss falls back to the grounded default
43
+ radius = None
44
+ return {"ball_radius": int(radius) if radius else 50, "active_layer": "segmentation image"}
45
+
46
+
47
+ #: The declared adapters. The ONLY place a plan step is tied to a computation — a step absent here is reported
48
+ #: as "run from its panel", never guessed at. Grows one workflow per phase (each behind a route-equivalence test).
49
+ _ADAPTERS: dict = {
50
+ "background_removal": ExecAdapter("background_removal", "background_removal", _background_removal_params),
51
+ }
52
+
53
+
54
+ def has_adapter(step_name: str) -> bool:
55
+ return step_name in _ADAPTERS
56
+
57
+
58
+ @dataclasses.dataclass(frozen=True)
59
+ class StepOutcome:
60
+ """What happened to one step. ``outcome`` is ``'ran'`` / ``'ran_with_caveat'`` / ``'blocked'`` (the run
61
+ stops here) / ``'skipped'`` (after a blocker) / ``'needs_panel'`` (no adapter yet) / ``'error'``."""
62
+ name: str
63
+ outcome: str
64
+ detail: str = ""
65
+
66
+
67
+ @dataclasses.dataclass
68
+ class ExecReport:
69
+ steps: list = dataclasses.field(default_factory=list)
70
+
71
+ @property
72
+ def ran(self) -> list:
73
+ return [s for s in self.steps if s.outcome.startswith("ran")]
74
+
75
+ @property
76
+ def stopped(self) -> bool:
77
+ return any(s.outcome in ("blocked", "error") for s in self.steps)
78
+
79
+ @property
80
+ def needs_panel(self) -> list:
81
+ return [s for s in self.steps if s.outcome == "needs_panel"]
82
+
83
+
84
+ def _build_step_registry() -> dict:
85
+ """The production batch step registry (name → replay handler), built the way `BatchProcessor` does. Only
86
+ the import-clean `batch_step_registry` is touched (no Qt)."""
87
+ registry: dict = {}
88
+
89
+ class _Recorder:
90
+ def register_step(self, name, fn):
91
+ registry[name] = fn
92
+
93
+ from pycat.batch_step_registry import register_all_steps
94
+ register_all_steps(_Recorder())
95
+ return registry
96
+
97
+
98
+ def run_plan(plan, state, *, intent=None, ctx=None, image_path=None, output_dir=None, runner=None,
99
+ on_step: Optional[Callable] = None) -> ExecReport:
100
+ """Execute ``plan``'s steps in gate order by driving the batch handlers, threading ``state`` (a dict the
101
+ handlers read/write, exactly as a batch replay). Returns an :class:`ExecReport`.
102
+
103
+ Gate semantics are READ from :func:`execution.execution_order`, never re-decided: a **blocker** stops the
104
+ run at that step (nothing after it runs); a **caveat** runs with the caveat recorded; **probes** run first.
105
+ A step with no :class:`ExecAdapter` is reported ``'needs_panel'`` — never invoked with guessed arguments.
106
+ A step that raises halts the run (downstream depends on its output). ``runner`` (an ``OperationRunner``)
107
+ runs each handler off the Qt thread when given; otherwise handlers run synchronously (headless / tests)."""
108
+ intent = intent if intent is not None else getattr(plan, "intent", None)
109
+ registry = _build_step_registry()
110
+ report = ExecReport()
111
+
112
+ tmp = None
113
+ if output_dir is None:
114
+ tmp = tempfile.TemporaryDirectory()
115
+ output_dir = pathlib.Path(tmp.name)
116
+ if image_path is None:
117
+ image_path = pathlib.Path(output_dir) / "sample.tif"
118
+
119
+ try:
120
+ halted = False
121
+ for es in execution_order(plan):
122
+ if halted or es.status == "skipped":
123
+ report.steps.append(StepOutcome(es.name, "skipped", es.reason))
124
+ continue
125
+ if es.status == "blocked":
126
+ report.steps.append(StepOutcome(es.name, "blocked", es.reason))
127
+ halted = True
128
+ continue
129
+
130
+ adapter = _ADAPTERS.get(es.name)
131
+ if adapter is None:
132
+ report.steps.append(StepOutcome(
133
+ es.name, "needs_panel", "no execution adapter yet — run this step from its method panel"))
134
+ if on_step:
135
+ on_step(report.steps[-1])
136
+ continue
137
+
138
+ fn = registry.get(adapter.batch_step)
139
+ if fn is None:
140
+ report.steps.append(StepOutcome(es.name, "error",
141
+ f"batch step {adapter.batch_step!r} is not registered"))
142
+ halted = True
143
+ continue
144
+
145
+ params = adapter.params_from(intent, ctx, state)
146
+ try:
147
+ if runner is not None:
148
+ runner.execute(fn, state, image_path, params, output_dir)
149
+ else:
150
+ fn(state, image_path, params, output_dir)
151
+ except Exception as exc: # broad-ok: scientific_result — a failed step halts; report it, never silently continue on stale state
152
+ report.steps.append(StepOutcome(es.name, "error", f"{type(exc).__name__}: {exc}"))
153
+ halted = True
154
+ continue
155
+
156
+ report.steps.append(StepOutcome(
157
+ es.name, "ran_with_caveat" if es.status == "caveat" else "ran", es.reason))
158
+ if on_step:
159
+ on_step(report.steps[-1])
160
+ finally:
161
+ if tmp is not None:
162
+ tmp.cleanup()
163
+ return report
@@ -0,0 +1,135 @@
1
+ """**Save an answered guided plan as a reusable method template** (selection_scale Part 3).
2
+
3
+ The navigator's payoff: the questionnaire produces a plan, and a plan the user likes should be reusable on
4
+ other data without re-answering. A guided template *is* a preset with provenance about how it was authored —
5
+ so it follows the ``analysis_presets`` discipline (it records WHERE it came from: the answers that produced
6
+ it) but is a **user-level, per-session-surviving** artefact, so it lives in the general ``user_settings``
7
+ service rather than the hardcoded, sensitivity-validated ``ANALYSIS_PRESETS`` registry.
8
+
9
+ **What is stored, and what is deliberately NOT.** A template stores the ANSWERS (the intent: observables,
10
+ target, and the question trail) plus the step names the plan produced and any parameters the user set. It does
11
+ **not** store the quality-gate verdicts. Those are a property of the DATA, not the plan — a template runnable
12
+ on one dataset may be blocked on another — so applying a template re-compiles the intent against the new
13
+ data's context, which re-evaluates every gate. Carrying a verdict over would assert quality that was never
14
+ checked here. A corrupt template entry degrades to "not available" (skipped), never a crash.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import dataclasses
19
+
20
+ from .contracts import AnalysisIntent
21
+
22
+ #: user_settings key holding ``{name: serialized_template}``.
23
+ _STORE_KEY = "navigator.templates"
24
+
25
+
26
+ @dataclasses.dataclass(frozen=True)
27
+ class GuidedTemplate:
28
+ """A saved guided plan. ``question`` is the answer trail that produced it — its provenance, so a user
29
+ revisiting the template can see WHY the steps are there and edit the answers to regenerate rather than
30
+ hand-edit steps. ``steps`` is the ordered step names the plan produced (for display / provenance);
31
+ ``parameters`` are any values the user adjusted. Gate verdicts are intentionally absent — they are
32
+ re-evaluated against the data a template is applied to."""
33
+ name: str
34
+ observables: tuple = ()
35
+ target: str = None
36
+ question: str = ""
37
+ steps: tuple = ()
38
+ parameters: dict = dataclasses.field(default_factory=dict)
39
+
40
+
41
+ def template_from_plan(name, intent, plan, *, parameters=None) -> GuidedTemplate:
42
+ """Build a template from an answered ``intent`` and the ``plan`` it compiled to (the answers + the ordered
43
+ step names). No verdicts are captured — see the module docstring."""
44
+ return GuidedTemplate(
45
+ name=str(name),
46
+ observables=tuple(getattr(intent, "observables", ()) or ()),
47
+ target=getattr(intent, "target", None),
48
+ question=getattr(intent, "question", "") or "",
49
+ steps=tuple(s.name for s in getattr(plan, "steps", ())),
50
+ parameters=dict(parameters or {}))
51
+
52
+
53
+ def intent_from_template(template) -> AnalysisIntent:
54
+ """Reconstruct the answers as a fresh :class:`AnalysisIntent`. Compile this against the CURRENT data's
55
+ context to re-run every quality gate — a template blocked on the new data reports it, because the verdicts
56
+ were never stored."""
57
+ return AnalysisIntent(target=template.target, observables=list(template.observables),
58
+ question=template.question)
59
+
60
+
61
+ # ── persistence via user_settings (survives sessions, applies across datasets) ───────────────────────
62
+
63
+ def _store(store):
64
+ if store is not None:
65
+ return store
66
+ from pycat.utils.user_settings import settings
67
+ return settings()
68
+
69
+
70
+ def _serialize(t: GuidedTemplate) -> dict:
71
+ return {"name": t.name, "observables": list(t.observables), "target": t.target,
72
+ "question": t.question, "steps": list(t.steps), "parameters": dict(t.parameters)}
73
+
74
+
75
+ def _deserialize(d) -> "GuidedTemplate | None":
76
+ try:
77
+ return GuidedTemplate(
78
+ name=str(d["name"]),
79
+ observables=tuple(d.get("observables") or ()),
80
+ target=d.get("target"),
81
+ question=str(d.get("question") or ""),
82
+ steps=tuple(d.get("steps") or ()),
83
+ parameters=dict(d.get("parameters") or {}))
84
+ except Exception: # broad-ok: optional_probe — a corrupt entry degrades to "not available", not a crash
85
+ return None
86
+
87
+
88
+ def _read_all(store) -> dict:
89
+ raw = _store(store).get(_STORE_KEY, {}) or {}
90
+ return raw if isinstance(raw, dict) else {}
91
+
92
+
93
+ def save_template(template: GuidedTemplate, *, store=None) -> GuidedTemplate:
94
+ """Persist ``template`` under its name (overwriting a same-named one). Returns it."""
95
+ s = _store(store)
96
+ all_ = _read_all(s)
97
+ all_[template.name] = _serialize(template)
98
+ s.set(_STORE_KEY, all_)
99
+ return template
100
+
101
+
102
+ def list_templates(*, store=None) -> list:
103
+ """Every saved template, name-sorted; corrupt entries are skipped (degrade to 'not available')."""
104
+ out = [_deserialize(v) for v in _read_all(store).values()]
105
+ return sorted((t for t in out if t is not None), key=lambda t: t.name.lower())
106
+
107
+
108
+ def load_template(name, *, store=None):
109
+ """The named template, or ``None`` if absent or corrupt."""
110
+ return _deserialize(_read_all(store).get(name))
111
+
112
+
113
+ def delete_template(name, *, store=None) -> bool:
114
+ """Remove the named template. Returns True if it existed."""
115
+ s = _store(store)
116
+ all_ = _read_all(s)
117
+ if name not in all_:
118
+ return False
119
+ del all_[name]
120
+ s.set(_STORE_KEY, all_)
121
+ return True
122
+
123
+
124
+ def rename_template(old_name, new_name, *, store=None) -> bool:
125
+ """Rename ``old_name`` to ``new_name``. No-op (False) if the source is absent or the target already
126
+ exists — renaming must never silently overwrite another template."""
127
+ s = _store(store)
128
+ all_ = _read_all(s)
129
+ if old_name not in all_ or new_name in all_ or not str(new_name).strip():
130
+ return False
131
+ entry = dict(all_.pop(old_name))
132
+ entry["name"] = str(new_name)
133
+ all_[str(new_name)] = entry
134
+ s.set(_STORE_KEY, all_)
135
+ return True
@@ -128,6 +128,34 @@ def _render_observations(widget, body_layout):
128
128
  "color: gray; font-size: 10px; margin-bottom: 4px;"))
129
129
 
130
130
 
131
+ def run_plan_via_central_manager(central_manager, plan):
132
+ """Execute a compiled plan against the session's data through the execution-adapter layer, and report
133
+ per-step outcomes. Adapter-covered steps run through the batch handlers (the proven 'same computation'
134
+ route); a step with no adapter yet is reported as 'run from its panel' — never invoked with guessed args.
135
+ Safe on an uncovered plan (nothing runs); best-effort, never crashes the UI. This is the wired ``on_run``."""
136
+ try:
137
+ from pycat.navigator.executor import run_plan
138
+ dr = getattr(getattr(central_manager, "active_data_class", None), "data_repository", None)
139
+ if dr is None:
140
+ return
141
+ report = run_plan(plan, dr, ctx=dr)
142
+ ran = [s.name for s in report.ran]
143
+ panel = [s.name for s in report.needs_panel]
144
+ blocked = [s for s in report.steps if s.outcome == "blocked"]
145
+ parts = []
146
+ if ran:
147
+ parts.append("Ran: " + ", ".join(ran) + ".")
148
+ if blocked:
149
+ parts.append(f"Stopped at {blocked[0].name} — {blocked[0].detail}")
150
+ if panel:
151
+ parts.append("Run these from their method panels, in order: " + ", ".join(panel) + ".")
152
+ from napari.utils.notifications import show_info
153
+ show_info(" ".join(parts) or "Nothing to run.")
154
+ except Exception as exc: # broad-ok: ui_cleanup — a run attempt must never crash the guided panel
155
+ from pycat.utils.general_utils import debug_log
156
+ debug_log("navigator: run_plan_via_central_manager failed", exc)
157
+
158
+
131
159
  def _guided_run_note(plan):
132
160
  """The honest guided-run message, made ACTIONABLE with the gate-respecting run ORDER. Auto-running the
133
161
  whole plan needs a per-operation execution adapter (PyCAT ops have bespoke, panel-collected signatures —
@@ -144,6 +172,33 @@ def _guided_run_note(plan):
144
172
  return msg + (f", in this order:\n{order}." if order else ".")
145
173
 
146
174
 
175
+ def _add_template_save(widget, body_layout):
176
+ """Add a 'Save as template…' button that persists the answered plan under a user-supplied name (the answers
177
+ + step names, NOT the gate verdicts — see navigator.templates). Reusable on other data without
178
+ re-answering. Best-effort: a save failure never breaks the plan view."""
179
+ from PyQt5.QtWidgets import QPushButton, QInputDialog
180
+
181
+ def _save():
182
+ try:
183
+ name, ok = QInputDialog.getText(widget, "Save guided template", "Template name:")
184
+ if not ok or not str(name).strip():
185
+ return
186
+ from pycat.navigator.templates import template_from_plan, save_template
187
+ plan = widget._session.compile_plan()
188
+ save_template(template_from_plan(str(name).strip(), widget._session.intent, plan))
189
+ from napari.utils.notifications import show_info
190
+ show_info(f"Saved guided template '{str(name).strip()}' — reuse it on other data from Home.")
191
+ except Exception as exc: # broad-ok: ui_cleanup — a save failure must not break the plan view
192
+ from pycat.utils.general_utils import debug_log
193
+ debug_log("navigator: could not save the template", exc)
194
+
195
+ btn = QPushButton("\U0001f4be Save as template…") # 💾
196
+ btn.setToolTip("Save these answers as a reusable method template — apply it to other data without "
197
+ "re-answering (the quality gates re-evaluate on the new data).")
198
+ btn.clicked.connect(_save)
199
+ body_layout.addWidget(btn)
200
+
201
+
147
202
  def build_navigator_widget(session, *, on_run=None, central_manager=None, parent=None):
148
203
  """Build (do not mount) the Navigator widget over ``session``, or return ``None`` if Qt is unavailable
149
204
  (headless). ``on_run(plan)`` is called when the user runs the compiled plan. When ``central_manager`` is
@@ -226,6 +281,7 @@ def build_navigator_widget(session, *, on_run=None, central_manager=None, parent
226
281
  over = QPushButton("↺ Start over")
227
282
  over.clicked.connect(_restart)
228
283
  body_layout.addWidget(over)
284
+ _add_template_save(widget, body_layout)
229
285
 
230
286
  def _answer(spec, value):
231
287
  widget._session.answer(spec, value)
File without changes
File without changes