TwoPy 0.3.2__tar.gz → 0.3.3__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.
- {twopy-0.3.2 → twopy-0.3.3}/PKG-INFO +2 -1
- {twopy-0.3.2 → twopy-0.3.3}/pyproject.toml +3 -2
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/group_matching.py +23 -10
- twopy-0.3.3/src/twopy/napari/assets/twopy-app-icon.png +0 -0
- twopy-0.3.3/src/twopy/napari/empty_state.py +159 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/images.py +17 -11
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/response_preview.py +17 -9
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/roi_assignment.py +106 -51
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/roi_cards.py +122 -38
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/window.py +1 -1
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/options_panel.py +9 -4
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/response_plot_widget.py +31 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/panels.py +5 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/session.py +9 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/state.py +65 -0
- twopy-0.3.3/src/twopy/napari/version_check.py +280 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/viewer.py +26 -1
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy.egg-info/PKG-INFO +2 -1
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy.egg-info/SOURCES.txt +4 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy.egg-info/requires.txt +1 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_group_matching.py +65 -28
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari.py +93 -2
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_group_matching_roi_cards.py +94 -12
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_loaded_recordings.py +14 -10
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_plot_controls.py +4 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_plot_widgets.py +123 -12
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_packaging.py +17 -0
- twopy-0.3.3/tests/test_version_check.py +207 -0
- {twopy-0.3.2 → twopy-0.3.3}/README.md +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/setup.cfg +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/_segments.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/_version.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/background_subtraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/dff.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/dff_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/epoch_mapping.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/kernels/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/kernels/_fitting.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/kernels/_stimulus.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/persistence.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_context.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_maps.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_plotting.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_processing/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_processing/apply.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_processing/options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_processing/persistence.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_processing/qc.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_processing/signals.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/response_window_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/responses.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/timing.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/trials.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis/workflow.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/analysis_cache.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/api.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/cache_inventory.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/calibrations/pixel_size.csv +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/config.example.yml +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/config.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/api.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/hdf5_writing.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/orientation.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/stimulus_code.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/conversion/types.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/converted.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/context.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/discovery.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/metadata.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/native.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/native_workflows/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/native_workflows/direction_selectivity.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/native_workflows/response_kernels.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/parameters.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/provenance.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/registry.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/results.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/runner.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/custom/workflows.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/cache.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/catalog.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/connection.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/generic.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/modeled.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/search.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/sql.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/database/types.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/filenames.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/frame_ranges.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/hdf5_utils.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/inspection.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/matlab.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/controls.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/custom_tab.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/database_favorite_editor.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/database_favorites.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/database_search.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/dims.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/display.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/display_paths.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/errors.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/cards.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/fov_assignment.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/fov_cards.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/protocols.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/responses.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/style.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/group_matching/tables.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/interactive.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/latest_worker.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/launcher.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/live_analysis.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/load_controller.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/load_progress.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/load_workflow.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/loaded_recordings_csv.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/loading.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/movie.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/output_routing.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/paths.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/data.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/dff_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/api.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/dynamic_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/paths.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/plot_area.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/response_map_area.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/save_actions.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/export.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/export_controls.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/form_controls.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/label_visibility.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/normalization_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/preview_strip.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/processing_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_colors.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_display.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/response_window_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/actions.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/controls.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/plotting/widgets.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/responses.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/roi.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/roi_signatures.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/sidebar.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/text.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/trial_timeline.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/napari/types.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/parity/__init__.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/parity/dff.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/parity/psycho5.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/parity/roi.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/parity/roi_extraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/photodiode.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/pixel_calibration.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/pixel_calibration_profiles.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/response_roi_extraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/roi.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/roi_extraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/roi_mask_cleanup.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/session.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/spatial.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/spatial_orientation.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/stimulus.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/synchronization.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy/typing_guards.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_api.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_background_subtraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_cache_inventory.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_config.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_conversion.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_converted.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_custom_workflows.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_database.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_dff.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_filenames.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_frame_ranges.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_function_inventory.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_hdf5_utils.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_import.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_inspection.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_kernels.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_load_workflow.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_matlab.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_motion.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_database_search.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_display.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_export.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_group_matching_responses.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_live_controller.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_load_controller.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_load_progress.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_load_tab.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_path_resolution.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_plot_data.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_processing_controls.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_response_map_display.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_response_workflow.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_roi_generation.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_roi_visibility.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_text.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_napari_timeline.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_parity.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_parity_psycho5.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_parity_roi_extraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_persistence.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_pixel_calibration.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_pixel_calibration_profiles.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_real_fixture.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_recording_timing.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_response_map_area.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_response_maps.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_response_processing.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_response_roi_extraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_response_window_options.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_responses.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_roi.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_roi_extraction.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_roi_mask_cleanup.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_saved_analysis.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_session.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_stimulus.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_synchronization.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_trials.py +0 -0
- {twopy-0.3.2 → twopy-0.3.3}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twopy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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
|
|
@@ -12,6 +12,7 @@ Requires-Dist: h5py<4,>=3.16
|
|
|
12
12
|
Requires-Dist: matplotlib<4,>=3.10
|
|
13
13
|
Requires-Dist: napari[all]<0.8,>=0.7
|
|
14
14
|
Requires-Dist: numpy<3,>=2.3
|
|
15
|
+
Requires-Dist: packaging<27,>=26
|
|
15
16
|
Requires-Dist: pre-commit<5,>=4.6
|
|
16
17
|
Requires-Dist: qtpy<3,>=2.4
|
|
17
18
|
Requires-Dist: pyyaml<7,>=6.0.3
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "twopy"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
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"
|
|
@@ -17,6 +17,7 @@ dependencies = [
|
|
|
17
17
|
"matplotlib>=3.10,<4",
|
|
18
18
|
"napari[all]>=0.7,<0.8",
|
|
19
19
|
"numpy>=2.3,<3",
|
|
20
|
+
"packaging>=26,<27",
|
|
20
21
|
"pre-commit>=4.6,<5",
|
|
21
22
|
"qtpy>=2.4,<3",
|
|
22
23
|
"pyyaml>=6.0.3,<7",
|
|
@@ -37,7 +38,7 @@ Repository = "https://github.com/gumadeiras/twopy"
|
|
|
37
38
|
where = ["src"]
|
|
38
39
|
|
|
39
40
|
[tool.setuptools.package-data]
|
|
40
|
-
twopy = ["calibrations/*.csv", "config.example.yml"]
|
|
41
|
+
twopy = ["calibrations/*.csv", "config.example.yml", "napari/assets/*.png"]
|
|
41
42
|
|
|
42
43
|
[tool.ruff]
|
|
43
44
|
line-length = 88
|
|
@@ -143,7 +143,7 @@ def make_manual_fov_group_rows(
|
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
def make_manual_roi_match_rows(
|
|
146
|
-
recording_rois:
|
|
146
|
+
recording_rois: Sequence[tuple[Path, str]],
|
|
147
147
|
*,
|
|
148
148
|
group_cell_id: int,
|
|
149
149
|
fov_group_id: str = "",
|
|
@@ -153,7 +153,9 @@ def make_manual_roi_match_rows(
|
|
|
153
153
|
"""Create validated match-table rows for one user decision.
|
|
154
154
|
|
|
155
155
|
Args:
|
|
156
|
-
recording_rois:
|
|
156
|
+
recording_rois: Recording path and ROI label pairs selected for one
|
|
157
|
+
group. A recording can appear more than once when multiple ROIs
|
|
158
|
+
from that recording belong in the group.
|
|
157
159
|
group_cell_id: Positive cell-group identifier for this decision.
|
|
158
160
|
fov_group_id: Optional user-facing FOV group identifier.
|
|
159
161
|
status: Decision status for every emitted row.
|
|
@@ -172,16 +174,27 @@ def make_manual_roi_match_rows(
|
|
|
172
174
|
raise ValueError(msg)
|
|
173
175
|
|
|
174
176
|
rows: list[ManualRoiMatchRow] = []
|
|
177
|
+
seen: set[tuple[Path, str]] = set()
|
|
175
178
|
for recording_path, roi_label in sorted(
|
|
176
|
-
recording_rois
|
|
177
|
-
key=lambda item: str(item[0]),
|
|
179
|
+
recording_rois,
|
|
180
|
+
key=lambda item: (str(item[0]), item[1]),
|
|
178
181
|
):
|
|
182
|
+
validated_path = _validate_recording_path(recording_path)
|
|
183
|
+
validated_roi = _validate_roi_label(roi_label)
|
|
184
|
+
key = (validated_path, validated_roi)
|
|
185
|
+
if key in seen:
|
|
186
|
+
msg = (
|
|
187
|
+
"Manual ROI match rows cannot repeat the same recording and "
|
|
188
|
+
f"ROI label: {validated_path} {validated_roi}."
|
|
189
|
+
)
|
|
190
|
+
raise ValueError(msg)
|
|
191
|
+
seen.add(key)
|
|
179
192
|
rows.append(
|
|
180
193
|
ManualRoiMatchRow(
|
|
181
194
|
fov_group_id=fov_group_id,
|
|
182
195
|
group_cell_id=group_cell_id,
|
|
183
|
-
recording_path=
|
|
184
|
-
roi_label=
|
|
196
|
+
recording_path=validated_path,
|
|
197
|
+
roi_label=validated_roi,
|
|
185
198
|
status=status,
|
|
186
199
|
note=note,
|
|
187
200
|
),
|
|
@@ -225,7 +238,7 @@ def matched_manual_roi_groups(
|
|
|
225
238
|
|
|
226
239
|
def add_manual_roi_match_group(
|
|
227
240
|
rows: Sequence[ManualRoiMatchRow],
|
|
228
|
-
recording_rois:
|
|
241
|
+
recording_rois: Sequence[tuple[Path, str]],
|
|
229
242
|
*,
|
|
230
243
|
fov_group_id: str,
|
|
231
244
|
note: str = "",
|
|
@@ -234,7 +247,7 @@ def add_manual_roi_match_group(
|
|
|
234
247
|
|
|
235
248
|
Args:
|
|
236
249
|
rows: Existing manual ROI match rows.
|
|
237
|
-
recording_rois:
|
|
250
|
+
recording_rois: Selected recording path and ROI label pairs.
|
|
238
251
|
fov_group_id: FOV id for the new matched group.
|
|
239
252
|
note: Optional note copied to every new row.
|
|
240
253
|
|
|
@@ -255,7 +268,7 @@ def add_manual_roi_match_group(
|
|
|
255
268
|
|
|
256
269
|
def replace_manual_roi_match_group(
|
|
257
270
|
rows: Sequence[ManualRoiMatchRow],
|
|
258
|
-
recording_rois:
|
|
271
|
+
recording_rois: Sequence[tuple[Path, str]],
|
|
259
272
|
*,
|
|
260
273
|
fov_group_id: str,
|
|
261
274
|
group_cell_id: int,
|
|
@@ -265,7 +278,7 @@ def replace_manual_roi_match_group(
|
|
|
265
278
|
|
|
266
279
|
Args:
|
|
267
280
|
rows: Existing manual ROI match rows.
|
|
268
|
-
recording_rois: Replacement recording
|
|
281
|
+
recording_rois: Replacement recording path and ROI label pairs.
|
|
269
282
|
fov_group_id: FOV id containing the group.
|
|
270
283
|
group_cell_id: Existing group id to replace.
|
|
271
284
|
note: Optional note copied to every replacement row.
|
|
Binary file
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Empty-viewer message helpers for the twopy napari adapter.
|
|
2
|
+
|
|
3
|
+
Inputs: napari viewer objects that may expose welcome and text overlays.
|
|
4
|
+
Outputs: a twopy-owned message while no recording is loaded.
|
|
5
|
+
|
|
6
|
+
The center viewer belongs to napari, but twopy owns the empty recording
|
|
7
|
+
workflow. These helpers hide napari's generic welcome overlay and use napari's
|
|
8
|
+
public text overlay for the small twopy-specific launch message.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Protocol, cast
|
|
12
|
+
|
|
13
|
+
from twopy._version import __version__
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"EMPTY_VIEWER_MESSAGE",
|
|
17
|
+
"hide_empty_viewer_message",
|
|
18
|
+
"refresh_empty_viewer_message",
|
|
19
|
+
"show_empty_viewer_message",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
_BASE_EMPTY_VIEWER_MESSAGE = (
|
|
23
|
+
f"twopy {__version__}\n\n"
|
|
24
|
+
"Getting Started\n\n"
|
|
25
|
+
"Search or load recordings manually\n"
|
|
26
|
+
"using the Load tab on the right."
|
|
27
|
+
)
|
|
28
|
+
EMPTY_VIEWER_MESSAGE = _BASE_EMPTY_VIEWER_MESSAGE
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _WelcomeScreen(Protocol):
|
|
32
|
+
"""Small shape of napari's welcome overlay."""
|
|
33
|
+
|
|
34
|
+
visible: bool
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _ViewerWithWelcomeScreen(Protocol):
|
|
38
|
+
"""Small viewer shape for napari welcome-screen updates."""
|
|
39
|
+
|
|
40
|
+
welcome_screen: object
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class _ViewerWithTextOverlay(Protocol):
|
|
44
|
+
"""Small viewer shape for napari text overlay updates."""
|
|
45
|
+
|
|
46
|
+
text_overlay: object
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class _TextOverlay(Protocol):
|
|
50
|
+
"""Small shape of napari's canvas text overlay."""
|
|
51
|
+
|
|
52
|
+
text: str
|
|
53
|
+
visible: bool
|
|
54
|
+
position: str
|
|
55
|
+
font_size: int
|
|
56
|
+
color: object
|
|
57
|
+
box: bool
|
|
58
|
+
box_color: object
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def show_empty_viewer_message(
|
|
62
|
+
viewer: object, *, update_command: str | None = None
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Show twopy's empty-recording message on a napari viewer.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
viewer: Napari viewer or test double.
|
|
68
|
+
update_command: Optional pip command shown when a newer twopy release is
|
|
69
|
+
known.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
None.
|
|
73
|
+
|
|
74
|
+
This replaces napari's generic welcome screen only while twopy has no
|
|
75
|
+
loaded recording. The message tells the user what the empty canvas means in
|
|
76
|
+
the twopy workflow.
|
|
77
|
+
"""
|
|
78
|
+
welcome_screen = _welcome_screen(viewer)
|
|
79
|
+
if welcome_screen is not None:
|
|
80
|
+
welcome_screen.visible = False
|
|
81
|
+
|
|
82
|
+
overlay = _text_overlay(viewer)
|
|
83
|
+
if overlay is None:
|
|
84
|
+
return
|
|
85
|
+
overlay.text = empty_viewer_message(update_command=update_command)
|
|
86
|
+
overlay.visible = True
|
|
87
|
+
overlay.position = "top_center"
|
|
88
|
+
overlay.font_size = 13
|
|
89
|
+
overlay.color = "white"
|
|
90
|
+
overlay.box = True
|
|
91
|
+
overlay.box_color = (0.0, 0.0, 0.0, 0.65)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def hide_empty_viewer_message(viewer: object) -> None:
|
|
95
|
+
"""Hide twopy's empty-recording message when real content owns the viewer.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
viewer: Napari viewer or test double.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
None.
|
|
102
|
+
|
|
103
|
+
The helper clears only the exact twopy empty message. Trial HUD text and any
|
|
104
|
+
other overlay owner are left untouched.
|
|
105
|
+
"""
|
|
106
|
+
overlay = _text_overlay(viewer)
|
|
107
|
+
if overlay is None or not _is_empty_viewer_message(overlay.text):
|
|
108
|
+
return
|
|
109
|
+
overlay.text = ""
|
|
110
|
+
overlay.visible = False
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def refresh_empty_viewer_message(
|
|
114
|
+
viewer: object,
|
|
115
|
+
*,
|
|
116
|
+
update_command: str | None,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Update twopy's empty-recording message when it already owns the overlay.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
viewer: Napari viewer or test double.
|
|
122
|
+
update_command: Optional pip command shown when a newer twopy release is
|
|
123
|
+
known.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
None.
|
|
127
|
+
"""
|
|
128
|
+
overlay = _text_overlay(viewer)
|
|
129
|
+
if overlay is None or not _is_empty_viewer_message(overlay.text):
|
|
130
|
+
return
|
|
131
|
+
overlay.text = empty_viewer_message(update_command=update_command)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def empty_viewer_message(*, update_command: str | None = None) -> str:
|
|
135
|
+
"""Return the empty-viewer text for the current update state."""
|
|
136
|
+
if not update_command:
|
|
137
|
+
return _BASE_EMPTY_VIEWER_MESSAGE
|
|
138
|
+
return f"{_BASE_EMPTY_VIEWER_MESSAGE}\n\nUpdate\n{update_command}"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _is_empty_viewer_message(text: str) -> bool:
|
|
142
|
+
"""Return whether text is owned by twopy's empty-recording overlay."""
|
|
143
|
+
return text == EMPTY_VIEWER_MESSAGE or text.startswith(
|
|
144
|
+
f"{EMPTY_VIEWER_MESSAGE}\n\nUpdate\n"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _welcome_screen(viewer: object) -> _WelcomeScreen | None:
|
|
149
|
+
"""Return napari's welcome screen overlay when the viewer exposes one."""
|
|
150
|
+
if not hasattr(viewer, "welcome_screen"):
|
|
151
|
+
return None
|
|
152
|
+
return cast(_WelcomeScreen, cast(_ViewerWithWelcomeScreen, viewer).welcome_screen)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _text_overlay(viewer: object) -> _TextOverlay | None:
|
|
156
|
+
"""Return napari's text overlay when the viewer exposes one."""
|
|
157
|
+
if not hasattr(viewer, "text_overlay"):
|
|
158
|
+
return None
|
|
159
|
+
return cast(_TextOverlay, cast(_ViewerWithTextOverlay, viewer).text_overlay)
|
|
@@ -50,16 +50,16 @@ def mean_image_roi_overlay_pixmap(
|
|
|
50
50
|
*,
|
|
51
51
|
mean_image_layer: object,
|
|
52
52
|
roi_labels_layer: object | None,
|
|
53
|
-
|
|
53
|
+
roi_labels: tuple[str, ...],
|
|
54
54
|
selected_color: QColor,
|
|
55
55
|
contrast_percentile: float,
|
|
56
56
|
) -> QPixmap | None:
|
|
57
|
-
"""Return a mean-image preview with
|
|
57
|
+
"""Return a mean-image preview with selected ROIs highlighted.
|
|
58
58
|
|
|
59
59
|
Args:
|
|
60
60
|
mean_image_layer: Napari mean-image layer.
|
|
61
61
|
roi_labels_layer: Napari Labels layer for the same displayed crop.
|
|
62
|
-
|
|
62
|
+
roi_labels: ``roi_####`` labels to highlight.
|
|
63
63
|
selected_color: Color used for the selected ROI mask and matching trace.
|
|
64
64
|
contrast_percentile: Upper percentile used as the white point.
|
|
65
65
|
|
|
@@ -72,7 +72,11 @@ def mean_image_roi_overlay_pixmap(
|
|
|
72
72
|
if image is None:
|
|
73
73
|
return None
|
|
74
74
|
rgba = _grayscale_rgba(image, contrast_percentile=contrast_percentile)
|
|
75
|
-
|
|
75
|
+
label_values = tuple(
|
|
76
|
+
label_value
|
|
77
|
+
for label_value in (_roi_label_value(roi_label) for roi_label in roi_labels)
|
|
78
|
+
if label_value is not None
|
|
79
|
+
)
|
|
76
80
|
labels = _layer_image(roi_labels_layer)
|
|
77
81
|
roi_centers: tuple[tuple[int, float, float], ...] = ()
|
|
78
82
|
if labels is not None and labels.shape == image.shape:
|
|
@@ -81,7 +85,7 @@ def mean_image_roi_overlay_pixmap(
|
|
|
81
85
|
_blend_roi_masks(
|
|
82
86
|
rgba,
|
|
83
87
|
integer_labels,
|
|
84
|
-
|
|
88
|
+
selected_labels=label_values,
|
|
85
89
|
selected_color=selected_color,
|
|
86
90
|
)
|
|
87
91
|
pixmap = _rgba_pixmap(rgba, size=ROI_PREVIEW_SIZE)
|
|
@@ -89,7 +93,7 @@ def mean_image_roi_overlay_pixmap(
|
|
|
89
93
|
pixmap,
|
|
90
94
|
roi_centers=roi_centers,
|
|
91
95
|
image_shape=image.shape,
|
|
92
|
-
|
|
96
|
+
selected_labels=label_values,
|
|
93
97
|
)
|
|
94
98
|
return pixmap
|
|
95
99
|
|
|
@@ -166,14 +170,15 @@ def _blend_roi_masks(
|
|
|
166
170
|
rgba: np.ndarray,
|
|
167
171
|
labels: np.ndarray,
|
|
168
172
|
*,
|
|
169
|
-
|
|
173
|
+
selected_labels: tuple[int, ...],
|
|
170
174
|
selected_color: QColor,
|
|
171
175
|
) -> None:
|
|
172
176
|
"""Blend all ROI masks into the preview, emphasizing the selected mask."""
|
|
177
|
+
selected = set(selected_labels)
|
|
173
178
|
roi_values = tuple(
|
|
174
179
|
int(value)
|
|
175
180
|
for value in np.unique(labels)
|
|
176
|
-
if int(value) > 0 and int(value)
|
|
181
|
+
if int(value) > 0 and int(value) not in selected
|
|
177
182
|
)
|
|
178
183
|
for roi_value in roi_values:
|
|
179
184
|
_blend_mask_color(
|
|
@@ -182,7 +187,7 @@ def _blend_roi_masks(
|
|
|
182
187
|
color=(31, 180, 190),
|
|
183
188
|
alpha=0.28,
|
|
184
189
|
)
|
|
185
|
-
|
|
190
|
+
for selected_label in selected_labels:
|
|
186
191
|
selected_mask = labels == selected_label
|
|
187
192
|
rgba[selected_mask, 0] = selected_color.red()
|
|
188
193
|
rgba[selected_mask, 1] = selected_color.green()
|
|
@@ -228,7 +233,7 @@ def _draw_roi_numbers(
|
|
|
228
233
|
*,
|
|
229
234
|
roi_centers: tuple[tuple[int, float, float], ...],
|
|
230
235
|
image_shape: tuple[int, ...],
|
|
231
|
-
|
|
236
|
+
selected_labels: tuple[int, ...],
|
|
232
237
|
) -> None:
|
|
233
238
|
"""Draw ROI numbers at scaled ROI centroids on top of a preview pixmap."""
|
|
234
239
|
if len(roi_centers) == 0 or len(image_shape) < 2:
|
|
@@ -244,13 +249,14 @@ def _draw_roi_numbers(
|
|
|
244
249
|
painter.setFont(font)
|
|
245
250
|
x_scale = pixmap.width() / width
|
|
246
251
|
y_scale = pixmap.height() / height
|
|
252
|
+
selected = set(selected_labels)
|
|
247
253
|
for label_value, x_center, y_center in roi_centers:
|
|
248
254
|
text = str(label_value)
|
|
249
255
|
x = int(round(x_center * x_scale))
|
|
250
256
|
y = int(round(y_center * y_scale))
|
|
251
257
|
foreground = (
|
|
252
258
|
QColor(255, 246, 230)
|
|
253
|
-
if label_value
|
|
259
|
+
if label_value in selected
|
|
254
260
|
else QColor(215, 255, 255)
|
|
255
261
|
)
|
|
256
262
|
painter.setPen(QColor(0, 0, 0))
|
|
@@ -36,7 +36,10 @@ from twopy.napari.roi import roi_label_image_from_layer_for_recording
|
|
|
36
36
|
from twopy.napari.session import LoadedNapariRecording
|
|
37
37
|
from twopy.napari.text import counted_noun
|
|
38
38
|
|
|
39
|
+
SelectedRoiKey = tuple[Path, str]
|
|
40
|
+
|
|
39
41
|
__all__ = [
|
|
42
|
+
"SelectedRoiKey",
|
|
40
43
|
"SelectedRoiResponse",
|
|
41
44
|
"add_response_legend",
|
|
42
45
|
"combined_response_plot_data",
|
|
@@ -58,6 +61,11 @@ class SelectedRoiResponse:
|
|
|
58
61
|
plot_data: ResponsePlotData
|
|
59
62
|
color: QColor
|
|
60
63
|
|
|
64
|
+
@property
|
|
65
|
+
def key(self) -> SelectedRoiKey:
|
|
66
|
+
"""Return the visibility key for this selected ROI trace."""
|
|
67
|
+
return (self.recording_path, self.roi_label)
|
|
68
|
+
|
|
61
69
|
|
|
62
70
|
def selected_roi_response_plot_data(
|
|
63
71
|
recording: LoadedNapariRecording,
|
|
@@ -184,11 +192,11 @@ def selected_response_visibility_status(
|
|
|
184
192
|
"""Return the Selected ROIs summary for the current trace visibility."""
|
|
185
193
|
if visible_count == total_count:
|
|
186
194
|
if total_count == 1:
|
|
187
|
-
return "Showing the selected
|
|
188
|
-
selected_traces = counted_noun(total_count, "selected
|
|
195
|
+
return "Showing the selected ROI trace."
|
|
196
|
+
selected_traces = counted_noun(total_count, "selected ROI trace")
|
|
189
197
|
return f"Showing all {selected_traces}."
|
|
190
|
-
total_traces = counted_noun(total_count, "selected
|
|
191
|
-
hidden_traces = counted_noun(total_count - visible_count, "
|
|
198
|
+
total_traces = counted_noun(total_count, "selected ROI trace")
|
|
199
|
+
hidden_traces = counted_noun(total_count - visible_count, "ROI trace")
|
|
192
200
|
return f"Showing {visible_count} of {total_traces}; {hidden_traces} hidden."
|
|
193
201
|
|
|
194
202
|
|
|
@@ -261,18 +269,18 @@ def add_response_legend(
|
|
|
261
269
|
layout: QGridLayout,
|
|
262
270
|
selected_responses: tuple[SelectedRoiResponse, ...],
|
|
263
271
|
*,
|
|
264
|
-
|
|
265
|
-
set_visible: Callable[[
|
|
272
|
+
hidden_responses: set[SelectedRoiKey],
|
|
273
|
+
set_visible: Callable[[SelectedRoiKey, bool], None],
|
|
266
274
|
max_width: int,
|
|
267
275
|
) -> None:
|
|
268
|
-
"""Add clickable color toggles for overlaid
|
|
276
|
+
"""Add clickable color toggles for overlaid selected-ROI traces."""
|
|
269
277
|
row_index = 0
|
|
270
278
|
row_width = 0
|
|
271
279
|
spacing = max(0, layout.spacing())
|
|
272
280
|
available_width = max(120, max_width)
|
|
273
281
|
row_layout = _new_response_legend_row(layout, row_index, spacing)
|
|
274
282
|
for response in selected_responses:
|
|
275
|
-
visible = response.
|
|
283
|
+
visible = response.key not in hidden_responses
|
|
276
284
|
recording_label = format_recording_minute_label(response.recording_path)
|
|
277
285
|
button = QPushButton(
|
|
278
286
|
f"{recording_label} - ROI {roi_label_display_id(response.roi_label)}",
|
|
@@ -281,7 +289,7 @@ def add_response_legend(
|
|
|
281
289
|
button.setChecked(visible)
|
|
282
290
|
button.setStyleSheet(trace_button_style(response.color, visible=visible))
|
|
283
291
|
button.clicked.connect(
|
|
284
|
-
lambda checked,
|
|
292
|
+
lambda checked, key=response.key: set_visible(key, checked),
|
|
285
293
|
)
|
|
286
294
|
button_width = button.sizeHint().width()
|
|
287
295
|
button.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|