TwoPy 0.3.6__tar.gz → 0.3.8__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 (261) hide show
  1. {twopy-0.3.6 → twopy-0.3.8}/PKG-INFO +1 -1
  2. {twopy-0.3.6 → twopy-0.3.8}/pyproject.toml +1 -1
  3. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/response_plot_widget.py +3 -2
  4. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/export.py +15 -1
  5. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/session.py +23 -3
  6. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/version_check.py +17 -7
  7. {twopy-0.3.6 → twopy-0.3.8}/src/twopy.egg-info/PKG-INFO +1 -1
  8. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_export.py +26 -0
  9. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_loaded_recordings.py +1 -0
  10. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_plot_controls.py +6 -1
  11. {twopy-0.3.6 → twopy-0.3.8}/tests/test_version_check.py +27 -0
  12. {twopy-0.3.6 → twopy-0.3.8}/README.md +0 -0
  13. {twopy-0.3.6 → twopy-0.3.8}/setup.cfg +0 -0
  14. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/__init__.py +0 -0
  15. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/__init__.pyi +0 -0
  16. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/_segments.py +0 -0
  17. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/_version.py +0 -0
  18. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/__init__.py +0 -0
  19. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/background_subtraction.py +0 -0
  20. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/dff.py +0 -0
  21. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/dff_options.py +0 -0
  22. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/epoch_mapping.py +0 -0
  23. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/group_matching.py +0 -0
  24. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/kernels/__init__.py +0 -0
  25. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/kernels/_fitting.py +0 -0
  26. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/kernels/_stimulus.py +0 -0
  27. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/motion.py +0 -0
  28. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/persistence.py +0 -0
  29. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_context.py +0 -0
  30. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_maps.py +0 -0
  31. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_plotting.py +0 -0
  32. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_processing/__init__.py +0 -0
  33. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_processing/apply.py +0 -0
  34. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_processing/options.py +0 -0
  35. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_processing/persistence.py +0 -0
  36. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_processing/qc.py +0 -0
  37. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_processing/signals.py +0 -0
  38. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/response_window_options.py +0 -0
  39. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/responses.py +0 -0
  40. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/timing.py +0 -0
  41. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/trials.py +0 -0
  42. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis/workflow.py +0 -0
  43. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/analysis_cache.py +0 -0
  44. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/api.py +0 -0
  45. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/cache_inventory.py +0 -0
  46. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
  47. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/calibrations/pixel_size.csv +0 -0
  48. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/config.example.yml +0 -0
  49. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/config.py +0 -0
  50. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/__init__.py +0 -0
  51. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/alignment_crop.py +0 -0
  52. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/api.py +0 -0
  53. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/hdf5_writing.py +0 -0
  54. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/matlab_values.py +0 -0
  55. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/orientation.py +0 -0
  56. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/source_loading.py +0 -0
  57. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/stimulus_code.py +0 -0
  58. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/conversion/types.py +0 -0
  59. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/converted.py +0 -0
  60. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/__init__.py +0 -0
  61. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/context.py +0 -0
  62. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/discovery.py +0 -0
  63. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/metadata.py +0 -0
  64. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/native.py +0 -0
  65. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/native_workflows/__init__.py +0 -0
  66. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/native_workflows/direction_selectivity.py +0 -0
  67. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/native_workflows/response_kernels.py +0 -0
  68. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/parameters.py +0 -0
  69. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/provenance.py +0 -0
  70. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/registry.py +0 -0
  71. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/results.py +0 -0
  72. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/runner.py +0 -0
  73. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/custom/workflows.py +0 -0
  74. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/__init__.py +0 -0
  75. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/cache.py +0 -0
  76. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/catalog.py +0 -0
  77. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/connection.py +0 -0
  78. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/generic.py +0 -0
  79. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/modeled.py +0 -0
  80. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/search.py +0 -0
  81. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/sql.py +0 -0
  82. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/database/types.py +0 -0
  83. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/filenames.py +0 -0
  84. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/frame_ranges.py +0 -0
  85. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/hdf5_utils.py +0 -0
  86. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/inspection.py +0 -0
  87. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/matlab.py +0 -0
  88. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/__init__.py +0 -0
  89. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/__main__.py +0 -0
  90. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon-128.png +0 -0
  91. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon-16.png +0 -0
  92. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon-256.png +0 -0
  93. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon-32.png +0 -0
  94. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon-512.png +0 -0
  95. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon-64.png +0 -0
  96. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon.icns +0 -0
  97. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/assets/twopy-app-icon.png +0 -0
  98. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/controls.py +0 -0
  99. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/custom_tab.py +0 -0
  100. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/database_favorite_editor.py +0 -0
  101. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/database_favorites.py +0 -0
  102. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/database_search.py +0 -0
  103. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/dims.py +0 -0
  104. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/display.py +0 -0
  105. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/display_paths.py +0 -0
  106. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/empty_state.py +0 -0
  107. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/errors.py +0 -0
  108. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/__init__.py +0 -0
  109. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/cards.py +0 -0
  110. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/fov_assignment.py +0 -0
  111. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/fov_cards.py +0 -0
  112. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/images.py +0 -0
  113. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/protocols.py +0 -0
  114. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/response_preview.py +0 -0
  115. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/responses.py +0 -0
  116. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/roi_assignment.py +0 -0
  117. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/roi_cards.py +0 -0
  118. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/style.py +0 -0
  119. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/tables.py +0 -0
  120. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/group_matching/window.py +0 -0
  121. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/interactive.py +0 -0
  122. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/latest_worker.py +0 -0
  123. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/launcher.py +0 -0
  124. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/live_analysis.py +0 -0
  125. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/load_controller.py +0 -0
  126. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/load_progress.py +0 -0
  127. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/load_workflow.py +0 -0
  128. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/loaded_recordings_csv.py +0 -0
  129. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/loading.py +0 -0
  130. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/movie.py +0 -0
  131. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/output_routing.py +0 -0
  132. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/paths.py +0 -0
  133. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/__init__.py +0 -0
  134. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/data.py +0 -0
  135. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/dff_options.py +0 -0
  136. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/__init__.py +0 -0
  137. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/api.py +0 -0
  138. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/dynamic_options.py +0 -0
  139. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/options_panel.py +0 -0
  140. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/paths.py +0 -0
  141. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/plot_area.py +0 -0
  142. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/response_map_area.py +0 -0
  143. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/save_actions.py +0 -0
  144. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
  145. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/export_controls.py +0 -0
  146. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/form_controls.py +0 -0
  147. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/label_visibility.py +0 -0
  148. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/normalization_options.py +0 -0
  149. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/options.py +0 -0
  150. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/panels.py +0 -0
  151. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/preview_strip.py +0 -0
  152. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/processing_options.py +0 -0
  153. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/response_map_colors.py +0 -0
  154. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/response_map_display.py +0 -0
  155. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/response_map_options.py +0 -0
  156. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/response_window_options.py +0 -0
  157. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/roi_generation/__init__.py +0 -0
  158. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/roi_generation/actions.py +0 -0
  159. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/roi_generation/controls.py +0 -0
  160. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/roi_generation/options.py +0 -0
  161. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/plotting/widgets.py +0 -0
  162. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/protocols.py +0 -0
  163. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/responses.py +0 -0
  164. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/roi.py +0 -0
  165. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/roi_signatures.py +0 -0
  166. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/sidebar.py +0 -0
  167. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/state.py +0 -0
  168. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/text.py +0 -0
  169. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/trial_timeline.py +0 -0
  170. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/types.py +0 -0
  171. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/napari/viewer.py +0 -0
  172. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/parity/__init__.py +0 -0
  173. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/parity/dff.py +0 -0
  174. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/parity/psycho5.py +0 -0
  175. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/parity/roi.py +0 -0
  176. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/parity/roi_extraction.py +0 -0
  177. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/parity/saved_analysis.py +0 -0
  178. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/photodiode.py +0 -0
  179. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/photodiode_classification.py +0 -0
  180. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/pixel_calibration.py +0 -0
  181. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/pixel_calibration_profiles.py +0 -0
  182. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/response_roi_extraction.py +0 -0
  183. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/roi.py +0 -0
  184. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/roi_extraction.py +0 -0
  185. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/roi_mask_cleanup.py +0 -0
  186. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/session.py +0 -0
  187. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/spatial.py +0 -0
  188. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/spatial_orientation.py +0 -0
  189. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/stimulus.py +0 -0
  190. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/synchronization.py +0 -0
  191. {twopy-0.3.6 → twopy-0.3.8}/src/twopy/typing_guards.py +0 -0
  192. {twopy-0.3.6 → twopy-0.3.8}/src/twopy.egg-info/SOURCES.txt +0 -0
  193. {twopy-0.3.6 → twopy-0.3.8}/src/twopy.egg-info/dependency_links.txt +0 -0
  194. {twopy-0.3.6 → twopy-0.3.8}/src/twopy.egg-info/entry_points.txt +0 -0
  195. {twopy-0.3.6 → twopy-0.3.8}/src/twopy.egg-info/requires.txt +0 -0
  196. {twopy-0.3.6 → twopy-0.3.8}/src/twopy.egg-info/top_level.txt +0 -0
  197. {twopy-0.3.6 → twopy-0.3.8}/tests/test_api.py +0 -0
  198. {twopy-0.3.6 → twopy-0.3.8}/tests/test_background_subtraction.py +0 -0
  199. {twopy-0.3.6 → twopy-0.3.8}/tests/test_cache_inventory.py +0 -0
  200. {twopy-0.3.6 → twopy-0.3.8}/tests/test_config.py +0 -0
  201. {twopy-0.3.6 → twopy-0.3.8}/tests/test_conversion.py +0 -0
  202. {twopy-0.3.6 → twopy-0.3.8}/tests/test_converted.py +0 -0
  203. {twopy-0.3.6 → twopy-0.3.8}/tests/test_custom_workflows.py +0 -0
  204. {twopy-0.3.6 → twopy-0.3.8}/tests/test_database.py +0 -0
  205. {twopy-0.3.6 → twopy-0.3.8}/tests/test_dff.py +0 -0
  206. {twopy-0.3.6 → twopy-0.3.8}/tests/test_epoch_mapping.py +0 -0
  207. {twopy-0.3.6 → twopy-0.3.8}/tests/test_filenames.py +0 -0
  208. {twopy-0.3.6 → twopy-0.3.8}/tests/test_frame_ranges.py +0 -0
  209. {twopy-0.3.6 → twopy-0.3.8}/tests/test_function_inventory.py +0 -0
  210. {twopy-0.3.6 → twopy-0.3.8}/tests/test_group_matching.py +0 -0
  211. {twopy-0.3.6 → twopy-0.3.8}/tests/test_hdf5_utils.py +0 -0
  212. {twopy-0.3.6 → twopy-0.3.8}/tests/test_import.py +0 -0
  213. {twopy-0.3.6 → twopy-0.3.8}/tests/test_inspection.py +0 -0
  214. {twopy-0.3.6 → twopy-0.3.8}/tests/test_kernels.py +0 -0
  215. {twopy-0.3.6 → twopy-0.3.8}/tests/test_load_workflow.py +0 -0
  216. {twopy-0.3.6 → twopy-0.3.8}/tests/test_matlab.py +0 -0
  217. {twopy-0.3.6 → twopy-0.3.8}/tests/test_motion.py +0 -0
  218. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari.py +0 -0
  219. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_database_search.py +0 -0
  220. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_display.py +0 -0
  221. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_group_matching_responses.py +0 -0
  222. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_group_matching_roi_cards.py +0 -0
  223. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_live_controller.py +0 -0
  224. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_load_controller.py +0 -0
  225. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_load_progress.py +0 -0
  226. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_load_tab.py +0 -0
  227. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_path_resolution.py +0 -0
  228. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_plot_data.py +0 -0
  229. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_plot_widgets.py +0 -0
  230. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_processing_controls.py +0 -0
  231. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_response_map_display.py +0 -0
  232. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_response_workflow.py +0 -0
  233. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_roi_generation.py +0 -0
  234. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_roi_visibility.py +0 -0
  235. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_text.py +0 -0
  236. {twopy-0.3.6 → twopy-0.3.8}/tests/test_napari_timeline.py +0 -0
  237. {twopy-0.3.6 → twopy-0.3.8}/tests/test_packaging.py +0 -0
  238. {twopy-0.3.6 → twopy-0.3.8}/tests/test_parity.py +0 -0
  239. {twopy-0.3.6 → twopy-0.3.8}/tests/test_parity_psycho5.py +0 -0
  240. {twopy-0.3.6 → twopy-0.3.8}/tests/test_parity_roi_extraction.py +0 -0
  241. {twopy-0.3.6 → twopy-0.3.8}/tests/test_persistence.py +0 -0
  242. {twopy-0.3.6 → twopy-0.3.8}/tests/test_photodiode_classification.py +0 -0
  243. {twopy-0.3.6 → twopy-0.3.8}/tests/test_pixel_calibration.py +0 -0
  244. {twopy-0.3.6 → twopy-0.3.8}/tests/test_pixel_calibration_profiles.py +0 -0
  245. {twopy-0.3.6 → twopy-0.3.8}/tests/test_real_fixture.py +0 -0
  246. {twopy-0.3.6 → twopy-0.3.8}/tests/test_recording_timing.py +0 -0
  247. {twopy-0.3.6 → twopy-0.3.8}/tests/test_response_map_area.py +0 -0
  248. {twopy-0.3.6 → twopy-0.3.8}/tests/test_response_maps.py +0 -0
  249. {twopy-0.3.6 → twopy-0.3.8}/tests/test_response_processing.py +0 -0
  250. {twopy-0.3.6 → twopy-0.3.8}/tests/test_response_roi_extraction.py +0 -0
  251. {twopy-0.3.6 → twopy-0.3.8}/tests/test_response_window_options.py +0 -0
  252. {twopy-0.3.6 → twopy-0.3.8}/tests/test_responses.py +0 -0
  253. {twopy-0.3.6 → twopy-0.3.8}/tests/test_roi.py +0 -0
  254. {twopy-0.3.6 → twopy-0.3.8}/tests/test_roi_extraction.py +0 -0
  255. {twopy-0.3.6 → twopy-0.3.8}/tests/test_roi_mask_cleanup.py +0 -0
  256. {twopy-0.3.6 → twopy-0.3.8}/tests/test_saved_analysis.py +0 -0
  257. {twopy-0.3.6 → twopy-0.3.8}/tests/test_session.py +0 -0
  258. {twopy-0.3.6 → twopy-0.3.8}/tests/test_stimulus.py +0 -0
  259. {twopy-0.3.6 → twopy-0.3.8}/tests/test_synchronization.py +0 -0
  260. {twopy-0.3.6 → twopy-0.3.8}/tests/test_trials.py +0 -0
  261. {twopy-0.3.6 → twopy-0.3.8}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: twopy
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Summary: Two-photon imaging analysis tool with a napari interface.
5
5
  Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "twopy"
7
- version = "0.3.6"
7
+ version = "0.3.8"
8
8
  description = "Two-photon imaging analysis tool with a napari interface."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -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.command)
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.command,
591
+ update_command=notice.text,
591
592
  )
592
593
 
593
594
  def _ignore_update_notice_error(self, _error: Exception) -> None:
@@ -388,7 +388,7 @@ def current_recording_image(
388
388
  Returns:
389
389
  Two-dimensional display-coordinate image.
390
390
  """
391
- movie_layer = _layer_by_name(viewer, "aligned movie")
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
@@ -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 remove all owned layers from napari.
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
- for loaded_recording in state.loaded_recordings:
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,
@@ -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 command for the Metadata tab.
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 only ``command`` so twopy does not assume how the
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. If PyPI is unreachable
135
- and an older cache already proves a newer version exists, twopy can still
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 < current:
274
+ if latest <= current:
265
275
  return False
266
276
  return now - checked_at <= max_age
267
277
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: twopy
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Summary: Two-photon imaging analysis tool with a napari interface.
5
5
  Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
6
6
  License-Expression: MIT
@@ -4,6 +4,8 @@ Inputs: shared fake napari state and tiny converted recordings.
4
4
  Outputs: assertions for one napari workflow area.
5
5
  """
6
6
 
7
+ from types import SimpleNamespace
8
+
7
9
  from qtpy.QtWidgets import QSizePolicy
8
10
 
9
11
  from tests.napari_support import (
@@ -41,7 +43,9 @@ from tests.napari_support import (
41
43
  temporary_directory,
42
44
  unittest,
43
45
  )
46
+ from twopy.converted import RecordingData
44
47
  from twopy.napari.output_routing import NapariOutputRoute
48
+ from twopy.napari.plotting.export import current_recording_image
45
49
  from twopy.napari.plotting.panels import SidebarTextLabel
46
50
 
47
51
 
@@ -202,6 +206,28 @@ class NapariExportTest(NapariAdapterTestCase):
202
206
  self.assertEqual(len(fig.axes), 2)
203
207
  np.testing.assert_allclose(fig.axes[1].get_yticks(), (-0.2, 0.0, 0.2))
204
208
 
209
+ def test_recording_export_uses_visible_mean_image_when_movie_hidden(self) -> None:
210
+ """Confirm recording exports match the visible image layer.
211
+
212
+ Inputs: one hidden aligned-movie layer and one visible mean-image layer.
213
+ Outputs: exported image data comes from the visible mean image.
214
+ """
215
+ mean_image = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float64)
216
+ movie_frame = np.array([[9.0, 9.0], [9.0, 9.0]], dtype=np.float64)
217
+ viewer = SimpleNamespace(
218
+ layers={
219
+ "mean image": SimpleNamespace(data=mean_image, visible=True),
220
+ "aligned movie": SimpleNamespace(data=movie_frame, visible=False),
221
+ }
222
+ )
223
+
224
+ image = current_recording_image(
225
+ viewer=viewer,
226
+ recording=cast(RecordingData, object()),
227
+ )
228
+
229
+ np.testing.assert_array_equal(image, mean_image)
230
+
205
231
  def test_response_plot_export_draws_epoch_marker(self) -> None:
206
232
  """Confirm exported response plots show the coarse epoch top rail.
207
233
 
@@ -850,6 +850,7 @@ class NapariLoadedRecordingsTest(NapariAdapterTestCase):
850
850
 
851
851
  unload_buttons["Unload all"].click()
852
852
 
853
+ self.assertEqual(viewer.layer_clear_calls, 1)
853
854
  self.assertEqual(loaded_list.count(), 0)
854
855
  self.assertFalse(sidebar_buttons["Open Group Matching"].isEnabled())
855
856
  self.assertFalse(sidebar_buttons["Reconvert selected"].isEnabled())
@@ -57,7 +57,12 @@ class NapariPlotControlsTest(NapariAdapterTestCase):
57
57
  SidebarTextLabel(analysis_text),
58
58
  SidebarTextLabel("ROI output: /very/long/path/to/rois.h5"),
59
59
  SidebarTextLabel("Saved analysis."),
60
- SidebarTextLabel("python -m pip install -U twopy"),
60
+ SidebarTextLabel(
61
+ "Update available!\n"
62
+ "Latest version is 0.3.6.\n"
63
+ "To update, run:\n"
64
+ "python -m pip install -U twopy",
65
+ ),
61
66
  )
62
67
 
63
68
  tab = response_metadata_tab(
@@ -32,6 +32,15 @@ class VersionCheckTest(unittest.TestCase):
32
32
  self.assertIsNotNone(notice)
33
33
  assert notice is not None
34
34
  self.assertEqual(notice.command, PIP_UPDATE_COMMAND)
35
+ self.assertEqual(
36
+ notice.text,
37
+ (
38
+ "Update available!\n"
39
+ "Latest version is 0.3.3.\n"
40
+ "To update, run:\n"
41
+ "python -m pip install -U twopy"
42
+ ),
43
+ )
35
44
 
36
45
  def test_same_or_older_release_returns_no_notice(self) -> None:
37
46
  """Confirm current installs do not show an update command."""
@@ -84,6 +93,24 @@ class VersionCheckTest(unittest.TestCase):
84
93
  assert notice is not None
85
94
  self.assertEqual(notice.command, PIP_UPDATE_COMMAND)
86
95
 
96
+ def test_fresh_current_cache_still_checks_for_new_release(self) -> None:
97
+ """Confirm a same-version cache does not hide a same-day release."""
98
+ with temporary_directory() as temp_dir:
99
+ state_file = Path(temp_dir) / "state.json"
100
+ now = datetime(2026, 6, 18, 12, tzinfo=UTC)
101
+ _write_cache(state_file, checked_at=now, latest_version="0.3.5")
102
+
103
+ notice = check_for_update(
104
+ current_version="0.3.5",
105
+ now=now + timedelta(hours=1),
106
+ state_file=state_file,
107
+ fetch_latest=lambda: PublishedVersion(version="0.3.6"),
108
+ )
109
+
110
+ self.assertIsNotNone(notice)
111
+ assert notice is not None
112
+ self.assertEqual(notice.latest_version, "0.3.6")
113
+
87
114
  def test_stale_cache_is_used_when_network_fails(self) -> None:
88
115
  """Confirm old cache still helps when PyPI is unreachable."""
89
116
  with temporary_directory() as temp_dir:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes