ethograph 0.2.3__tar.gz → 0.2.4__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.
Potentially problematic release.
This version of ethograph might be problematic. Click here for more details.
- {ethograph-0.2.3 → ethograph-0.2.4}/PKG-INFO +2 -1
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/cli.py +5 -6
- ethograph-0.2.4/ethograph/gui/grid_section_container.py +227 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_changepoints.py +1 -1
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_ephys.py +7 -5
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_meta.py +5 -4
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_navigation.py +4 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_plot_settings.py +8 -2
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/intervals.py +19 -13
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph.egg-info/PKG-INFO +2 -1
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph.egg-info/SOURCES.txt +1 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph.egg-info/requires.txt +1 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/pyproject.toml +1 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/.claude/settings.local.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/.github/copilot-instructions.md +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/.github/workflows/docs_build_and_deploy.yml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/.github/workflows/test_and_deploy.yml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/.gitignore +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/.readthedocs.yaml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/CLAUDE.md +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/LICENSE +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/MANIFEST.in +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/README.md +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/ReadMe.md +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/changepoint_settings_old.yaml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/mapping.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/mapping_Ivy.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/mapping_setup_audio.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Crow1_inference_20260330_161224.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Crow1_inference_20260330_161259.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Freddy_inference_20260330_160621.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Freddy_train_20251021_164220.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Freddy_train_20251021_164220_epoch-100.model +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_CV_20260130_173800.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_inference_20260203_092442.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_inference_20260204_141527.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_inference_20260206_122331.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_train_20260202_191138_epoch-100.model +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_train_20260203_153754.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260312_100707.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260327_102414.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260330_140808.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260402_104151.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_train_20260326_190925.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_train_20260331_175332.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/ReadMe - model Freddy_train_20251021_164220_epoch-100.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/space.yaml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/__init__.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/__main__.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/assets/icon.ico +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/assets/icon.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/assets/menu.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/crowlab/io_matlab.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/crowlab/legacy.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/datasets.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/__init__.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/audio_changepoints.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/changepoints.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/energy.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/movement.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/neural.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/oscillatory.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/features/preprocessing.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/__init__.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/app_constants.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/app_state.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/birdpark0.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/birdpark1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/canary.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/cricket0.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/cricket1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/cricket2.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/lockbox1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/lockbox2.gif +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/moll1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/assets/templates/moll2.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/audio_player.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/dialog_busy_progress.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/dialog_function_params.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/dialog_pose_video_matcher.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/dialog_screen_recorder.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/dialog_select_template.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/dialog_video_downsample.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/label_drawing_mixin.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/make_pretty.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/napari.yaml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/notify.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_audiotrace.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_base.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_container.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_ephystrace.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_heatmap.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_lineplot.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_overlay.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_psth.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_raster.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_space.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/plots_spectrogram.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/pose_render.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/shortcuts.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/templates/wizard_nwb_codegen.j2 +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/video_manager.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/video_sync.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widget_trials.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_data.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_help.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_io.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_labels.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_psth.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/widgets_transform.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_boris.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_media_files.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_multi_builder.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_multi_codegen.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_multi_tabs.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_multi_timeline.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_multi_trials.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_nwb.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_overview.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/gui/wizard_single.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/catalog.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/data_loader.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/dataset.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/ephys_loader.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/metadata_table.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/nwb_alignment.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/nwb_import.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/plot_sources.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/pynapple.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/time_model.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/time_sources.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/trialtree.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/io/validation.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/__init__.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/boris.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/converters.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/crowsetta_format.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/export.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/ml.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/plots.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/predictions.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/labels/tsv_store.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/model/batch_gen.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/model/cetnet_encoder.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/model/dataset.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/model/eval_metrics.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/model/eval_plotting.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/shortcuts.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/__init__.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/arraytools.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/audio.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/download.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/nwb.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/paths.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/qt.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/sequences.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/stream_durations.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/utils/xr_utils.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/base_extractor.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/checkpoint/S3D_kinetics400_torchified.pt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/extract_s3d.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/s3d.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/s3d.yml +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/transforms.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph/video_features/utils.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph.egg-info/dependency_links.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph.egg-info/entry_points.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/ethograph.egg-info/top_level.txt +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/birdpark0.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/birdpark1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/canary.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/cricket0.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/cricket1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/cricket2.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/lockbox1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/lockbox2.gif +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/moll1.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/assets/moll2.png +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/convert_dataset_birdpark.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/convert_dataset_pair24.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/convert_datset_lockbox.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/create_dataset_Moll25.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/create_dataset_cricket.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/examples/segment_audio_whisperseg.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/DOCS_commands.md +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/PUBLISHING_commands.md +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/dandi_ibl-brainwide.ipynb.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/debug_gui.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/debug_launch_copy_paste.json +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/ephys/ephys_drift_diagnosis.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/ephys/ephys_loader.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/ethograph_alignment_setup_20260402_220508.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/model/eval.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/model/find_best_s3d.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/model/model_config_Ivy.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/model/model_config_Poppy.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/model/model_run.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/model/s3d_features.py +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/scripts/nwb_video_testing.ipynb +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/setup.cfg +0 -0
- {ethograph-0.2.3 → ethograph-0.2.4}/todo.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ethograph
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A multimodal timeseries GUI for neuroethology
|
|
5
5
|
Author-email: Akseli Ilmanen <akseli.ilmanen@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -26,6 +26,7 @@ Requires-Dist: pandas
|
|
|
26
26
|
Requires-Dist: xarray
|
|
27
27
|
Requires-Dist: netcdf4
|
|
28
28
|
Requires-Dist: scipy
|
|
29
|
+
Requires-Dist: scikit-learn
|
|
29
30
|
Requires-Dist: matplotlib
|
|
30
31
|
Requires-Dist: pynapple
|
|
31
32
|
Requires-Dist: pyyaml
|
|
@@ -32,13 +32,9 @@ def _ensure_qt_plugins():
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def _fix_wayland_opengl():
|
|
35
|
-
"""Force XCB/GLX on
|
|
35
|
+
"""Force XCB/GLX on GNOME+Wayland to avoid OpenGL crashes with napari.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
AMD hardware across PyQt6 and PySide6. XCB (X11 via XWayland) is the
|
|
39
|
-
recommended workaround per napari's own troubleshooting guide. We only
|
|
40
|
-
apply this when Wayland is actually active and the user has not already
|
|
41
|
-
set a preference via the environment.
|
|
37
|
+
See https://github.com/Akseli-Ilmanen/ethograph/issues/1
|
|
42
38
|
"""
|
|
43
39
|
if sys.platform != "linux":
|
|
44
40
|
return
|
|
@@ -47,6 +43,9 @@ def _fix_wayland_opengl():
|
|
|
47
43
|
)
|
|
48
44
|
if not wayland_active:
|
|
49
45
|
return
|
|
46
|
+
desktop = os.environ.get("XDG_CURRENT_DESKTOP", "").upper()
|
|
47
|
+
if "GNOME" not in desktop:
|
|
48
|
+
return
|
|
50
49
|
os.environ.setdefault("QT_QPA_PLATFORM", "xcb")
|
|
51
50
|
os.environ.setdefault("PYOPENGL_PLATFORM", "glx")
|
|
52
51
|
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""3x3 grid tab bar replacing the collapsible accordion headers.
|
|
2
|
+
|
|
3
|
+
GridSectionContainer is a drop-in replacement for CollapsibleWidgetContainer.
|
|
4
|
+
Instead of 9 stacked clickable header rows it shows a compact 3-column grid of
|
|
5
|
+
buttons at the top; clicking one expands that section's content below (one at a
|
|
6
|
+
time, same accordion semantics as before).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from qtpy.QtCore import QObject, QSize, Qt, Signal
|
|
12
|
+
from qtpy.QtWidgets import (
|
|
13
|
+
QGridLayout,
|
|
14
|
+
QPushButton,
|
|
15
|
+
QSizePolicy,
|
|
16
|
+
QStackedWidget,
|
|
17
|
+
QVBoxLayout,
|
|
18
|
+
QWidget,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_GRID_COLS = 3
|
|
22
|
+
|
|
23
|
+
# Short labels shown on the buttons; order must match add_widget call order in
|
|
24
|
+
# widgets_meta.py.
|
|
25
|
+
_SHORT_LABELS = ["Help", "I/O", "Data", "Neural", "Label", "CP", "Plots", "Trials", "Nav"]
|
|
26
|
+
|
|
27
|
+
_STATUS_CHARS = set("✅❌🎯⭕")
|
|
28
|
+
|
|
29
|
+
_BTN_STYLE = """
|
|
30
|
+
QPushButton {{
|
|
31
|
+
padding: 3px 2px;
|
|
32
|
+
font-size: 7pt;
|
|
33
|
+
border: 1px solid rgba(255,255,255,35);
|
|
34
|
+
border-radius: 3px;
|
|
35
|
+
background: rgba(255,255,255,12);
|
|
36
|
+
color: #cccccc;
|
|
37
|
+
min-height: 22px;
|
|
38
|
+
}}
|
|
39
|
+
QPushButton:hover {{
|
|
40
|
+
background: rgba(255,255,255,25);
|
|
41
|
+
color: #ffffff;
|
|
42
|
+
border: 1px solid rgba(255,255,255,60);
|
|
43
|
+
}}
|
|
44
|
+
QPushButton:checked {{
|
|
45
|
+
background: rgba(65,125,195,160);
|
|
46
|
+
border: 1px solid rgba(90,150,220,200);
|
|
47
|
+
color: #ffffff;
|
|
48
|
+
font-weight: bold;
|
|
49
|
+
}}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class _AdaptiveStack(QStackedWidget):
|
|
54
|
+
"""QStackedWidget that reports only the *current* page's size hint.
|
|
55
|
+
|
|
56
|
+
The default QStackedWidget always sizes itself to the largest of all its
|
|
57
|
+
pages. That causes the content area to be over-tall when a short section
|
|
58
|
+
is active, which makes QVBoxLayout distribute the spare pixels as blank
|
|
59
|
+
gaps between the section's own widgets. Overriding sizeHint / minimumSizeHint
|
|
60
|
+
so they reflect the current page fixes this entirely.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def sizeHint(self) -> QSize:
|
|
64
|
+
w = self.currentWidget()
|
|
65
|
+
return w.sizeHint() if w is not None else super().sizeHint()
|
|
66
|
+
|
|
67
|
+
def minimumSizeHint(self) -> QSize:
|
|
68
|
+
w = self.currentWidget()
|
|
69
|
+
return w.minimumSizeHint() if w is not None else super().minimumSizeHint()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SectionProxy(QObject):
|
|
73
|
+
"""Duck-type stand-in for qt_niu.CollapsibleWidget.
|
|
74
|
+
|
|
75
|
+
Exposes the subset of the QCollapsible API used by MetaWidget so that
|
|
76
|
+
widgets_meta.py needs no structural changes.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
toggled = Signal(bool)
|
|
80
|
+
|
|
81
|
+
def __init__(self, container: GridSectionContainer, index: int) -> None:
|
|
82
|
+
super().__init__()
|
|
83
|
+
self._c = container
|
|
84
|
+
self._i = index
|
|
85
|
+
|
|
86
|
+
# --- QCollapsible-compatible API ---
|
|
87
|
+
|
|
88
|
+
def expand(self, animate: bool = True) -> None:
|
|
89
|
+
self._c._expand(self._i)
|
|
90
|
+
|
|
91
|
+
def collapse(self, animate: bool = False) -> None:
|
|
92
|
+
self._c._collapse(self._i)
|
|
93
|
+
|
|
94
|
+
def isExpanded(self) -> bool:
|
|
95
|
+
return self._c._active == self._i
|
|
96
|
+
|
|
97
|
+
def content(self) -> QWidget | None:
|
|
98
|
+
widgets = self._c._content_widgets
|
|
99
|
+
return widgets[self._i] if self._i < len(widgets) else None
|
|
100
|
+
|
|
101
|
+
def setText(self, full_title: str) -> None:
|
|
102
|
+
self._c._update_button_label(self._i, full_title)
|
|
103
|
+
|
|
104
|
+
def setTitle(self, full_title: str) -> None:
|
|
105
|
+
self.setText(full_title)
|
|
106
|
+
|
|
107
|
+
def _expand_collapse(self, *_args, **_kwargs) -> None:
|
|
108
|
+
"""No-op: _AdaptiveStack + updateGeometry handles sizing."""
|
|
109
|
+
|
|
110
|
+
def _emit_toggled(self, state: bool) -> None:
|
|
111
|
+
self.toggled.emit(state)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class GridSectionContainer(QWidget):
|
|
115
|
+
"""Container with a 3-column grid of tab buttons + a single content area.
|
|
116
|
+
|
|
117
|
+
API mirrors CollapsibleWidgetContainer so MetaWidget.add_widget() and
|
|
118
|
+
MetaWidget.collapsible_widgets work unchanged.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
|
122
|
+
super().__init__(parent)
|
|
123
|
+
|
|
124
|
+
outer = QVBoxLayout(self)
|
|
125
|
+
outer.setContentsMargins(0, 0, 0, 0)
|
|
126
|
+
outer.setSpacing(2)
|
|
127
|
+
outer.setAlignment(Qt.AlignTop)
|
|
128
|
+
|
|
129
|
+
# ── 3×3 grid of buttons ──────────────────────────────────────────────
|
|
130
|
+
self._grid_widget = QWidget()
|
|
131
|
+
self._grid_layout = QGridLayout(self._grid_widget)
|
|
132
|
+
self._grid_layout.setContentsMargins(2, 2, 2, 2)
|
|
133
|
+
self._grid_layout.setSpacing(2)
|
|
134
|
+
for col in range(_GRID_COLS):
|
|
135
|
+
self._grid_layout.setColumnStretch(col, 1)
|
|
136
|
+
outer.addWidget(self._grid_widget)
|
|
137
|
+
|
|
138
|
+
# ── Content area: shows the active section ───────────────────────────
|
|
139
|
+
# _AdaptiveStack reports only the current page's sizeHint so the outer
|
|
140
|
+
# layout never allocates more vertical space than the content needs.
|
|
141
|
+
self._stack = _AdaptiveStack()
|
|
142
|
+
self._stack.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
|
143
|
+
self._stack.hide()
|
|
144
|
+
outer.addWidget(self._stack, 0, Qt.AlignTop)
|
|
145
|
+
|
|
146
|
+
self._buttons: list[QPushButton] = []
|
|
147
|
+
self._content_widgets: list[QWidget] = []
|
|
148
|
+
self._active: int | None = None
|
|
149
|
+
|
|
150
|
+
# Public list of proxy objects (replaces CollapsibleWidgetContainer.collapsible_widgets)
|
|
151
|
+
self.collapsible_widgets: list[SectionProxy] = []
|
|
152
|
+
|
|
153
|
+
# ── Public API (mirrors CollapsibleWidgetContainer) ──────────────────────
|
|
154
|
+
|
|
155
|
+
def add_widget(
|
|
156
|
+
self,
|
|
157
|
+
widget: QWidget,
|
|
158
|
+
collapsible: bool = True,
|
|
159
|
+
widget_title: str = "",
|
|
160
|
+
) -> None:
|
|
161
|
+
index = len(self._content_widgets)
|
|
162
|
+
label = _SHORT_LABELS[index] if index < len(_SHORT_LABELS) else widget_title
|
|
163
|
+
|
|
164
|
+
btn = QPushButton(label)
|
|
165
|
+
btn.setCheckable(True)
|
|
166
|
+
btn.setToolTip(widget_title)
|
|
167
|
+
btn.setStyleSheet(_BTN_STYLE)
|
|
168
|
+
btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
169
|
+
btn.clicked.connect(lambda _, i=index: self._on_btn_clicked(i))
|
|
170
|
+
|
|
171
|
+
row, col = divmod(index, _GRID_COLS)
|
|
172
|
+
self._grid_layout.addWidget(btn, row, col)
|
|
173
|
+
self._buttons.append(btn)
|
|
174
|
+
|
|
175
|
+
self._stack.addWidget(widget)
|
|
176
|
+
self._content_widgets.append(widget)
|
|
177
|
+
|
|
178
|
+
proxy = SectionProxy(self, index)
|
|
179
|
+
self.collapsible_widgets.append(proxy)
|
|
180
|
+
|
|
181
|
+
def notify_content_resized(self) -> None:
|
|
182
|
+
"""Call this when the active section's content changes height.
|
|
183
|
+
|
|
184
|
+
EphysWidget and PlotSettingsWidget call meta_widget.refresh_widget_layout()
|
|
185
|
+
after toggling their sub-panels; that call routes here so the adaptive
|
|
186
|
+
stack recalculates its size hint.
|
|
187
|
+
"""
|
|
188
|
+
self._stack.updateGeometry()
|
|
189
|
+
self.updateGeometry()
|
|
190
|
+
|
|
191
|
+
# ── Internal expand / collapse ───────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
def _on_btn_clicked(self, index: int) -> None:
|
|
194
|
+
if self._active == index:
|
|
195
|
+
self._collapse(index)
|
|
196
|
+
else:
|
|
197
|
+
self._expand(index)
|
|
198
|
+
|
|
199
|
+
def _expand(self, index: int) -> None:
|
|
200
|
+
if self._active is not None and self._active != index:
|
|
201
|
+
prev = self._active
|
|
202
|
+
self._buttons[prev].setChecked(False)
|
|
203
|
+
self.collapsible_widgets[prev]._emit_toggled(False)
|
|
204
|
+
self._active = index
|
|
205
|
+
self._buttons[index].setChecked(True)
|
|
206
|
+
self._stack.setCurrentIndex(index)
|
|
207
|
+
self._stack.show()
|
|
208
|
+
self._stack.updateGeometry()
|
|
209
|
+
self.updateGeometry()
|
|
210
|
+
self.collapsible_widgets[index]._emit_toggled(True)
|
|
211
|
+
|
|
212
|
+
def _collapse(self, index: int) -> None:
|
|
213
|
+
self._active = None
|
|
214
|
+
self._buttons[index].setChecked(False)
|
|
215
|
+
self._stack.hide()
|
|
216
|
+
self._stack.updateGeometry()
|
|
217
|
+
self.updateGeometry()
|
|
218
|
+
self.collapsible_widgets[index]._emit_toggled(False)
|
|
219
|
+
|
|
220
|
+
def _update_button_label(self, index: int, full_title: str) -> None:
|
|
221
|
+
"""Keep the short base label but append a trailing status character."""
|
|
222
|
+
if not (0 <= index < len(self._buttons)):
|
|
223
|
+
return
|
|
224
|
+
base = _SHORT_LABELS[index] if index < len(_SHORT_LABELS) else ""
|
|
225
|
+
label = f"{base} {full_title[-1]}" if full_title and full_title[-1] in _STATUS_CHARS else base
|
|
226
|
+
self._buttons[index].setText(label)
|
|
227
|
+
self._buttons[index].setToolTip(full_title)
|
|
@@ -205,7 +205,7 @@ class ChangepointsWidget(QWidget):
|
|
|
205
205
|
row1_layout = QHBoxLayout()
|
|
206
206
|
row1_layout.setContentsMargins(0, 0, 0, 0)
|
|
207
207
|
|
|
208
|
-
self.show_cp_checkbox = QCheckBox("Show changepoints")
|
|
208
|
+
self.show_cp_checkbox = QCheckBox("Show changepoints (CPs)")
|
|
209
209
|
self.show_cp_checkbox.setToolTip("Display changepoints on plot")
|
|
210
210
|
self.show_cp_checkbox.setChecked(True)
|
|
211
211
|
self.show_cp_checkbox.stateChanged.connect(self._on_show_changepoints_changed)
|
|
@@ -780,7 +780,7 @@ class EphysWidget(QWidget):
|
|
|
780
780
|
toggle_widget.setLayout(toggle_layout)
|
|
781
781
|
|
|
782
782
|
toggle_defs = [
|
|
783
|
-
("traceview_toggle", "TraceView", self._toggle_traceview),
|
|
783
|
+
("traceview_toggle", "Phy TraceView", self._toggle_traceview),
|
|
784
784
|
("firing_rate_toggle", "Firing rates", self._toggle_firing_rate),
|
|
785
785
|
]
|
|
786
786
|
for attr, label, callback in toggle_defs:
|
|
@@ -848,6 +848,8 @@ class EphysWidget(QWidget):
|
|
|
848
848
|
|
|
849
849
|
group = QGroupBox("Ephys trace controls")
|
|
850
850
|
group_layout = QVBoxLayout()
|
|
851
|
+
group_layout.setSpacing(2)
|
|
852
|
+
group_layout.setContentsMargins(2, 2, 2, 2)
|
|
851
853
|
group.setLayout(group_layout)
|
|
852
854
|
layout.addWidget(group)
|
|
853
855
|
|
|
@@ -2244,8 +2246,8 @@ class EphysWidget(QWidget):
|
|
|
2244
2246
|
|
|
2245
2247
|
group = QGroupBox("Firing rates")
|
|
2246
2248
|
group_layout = QVBoxLayout()
|
|
2247
|
-
group_layout.setSpacing(
|
|
2248
|
-
group_layout.setContentsMargins(
|
|
2249
|
+
group_layout.setSpacing(2)
|
|
2250
|
+
group_layout.setContentsMargins(2, 2, 2, 2)
|
|
2249
2251
|
group.setLayout(group_layout)
|
|
2250
2252
|
layout.addWidget(group)
|
|
2251
2253
|
|
|
@@ -2291,8 +2293,8 @@ class EphysWidget(QWidget):
|
|
|
2291
2293
|
|
|
2292
2294
|
pca_group = QGroupBox("PCA")
|
|
2293
2295
|
pca_layout = QVBoxLayout()
|
|
2294
|
-
pca_layout.setSpacing(
|
|
2295
|
-
pca_layout.setContentsMargins(
|
|
2296
|
+
pca_layout.setSpacing(2)
|
|
2297
|
+
pca_layout.setContentsMargins(2, 2, 2, 2)
|
|
2296
2298
|
pca_group.setLayout(pca_layout)
|
|
2297
2299
|
layout.addWidget(pca_group)
|
|
2298
2300
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
from napari.viewer import Viewer
|
|
6
|
-
from qt_niu.collapsible_widget import CollapsibleWidgetContainer
|
|
7
6
|
from qtpy.QtCore import Qt, QTimer
|
|
8
7
|
from qtpy.QtWidgets import (
|
|
9
8
|
QMessageBox,
|
|
@@ -26,6 +25,7 @@ from .app_constants import (
|
|
|
26
25
|
SIDEBAR_MIN_WIDTH_PX,
|
|
27
26
|
)
|
|
28
27
|
from .app_state import ObservableAppState
|
|
28
|
+
from .grid_section_container import GridSectionContainer
|
|
29
29
|
from .make_pretty import LayoutManager
|
|
30
30
|
from .plots_container import UnifiedPanelContainer
|
|
31
31
|
from .shortcuts import bind_global_shortcuts
|
|
@@ -42,7 +42,7 @@ from .widgets_plot_settings import PlotSettingsWidget
|
|
|
42
42
|
logger = logging.getLogger(__name__)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
class MetaWidget(
|
|
45
|
+
class MetaWidget(GridSectionContainer):
|
|
46
46
|
def __init__(self, napari_viewer: Viewer):
|
|
47
47
|
"""Initialize the meta-widget."""
|
|
48
48
|
super().__init__()
|
|
@@ -226,11 +226,11 @@ class MetaWidget(CollapsibleWidgetContainer):
|
|
|
226
226
|
widget_title="Data",
|
|
227
227
|
)
|
|
228
228
|
|
|
229
|
-
# Index 3: Phy
|
|
229
|
+
# Index 3: Phy TraceView
|
|
230
230
|
self.add_widget(
|
|
231
231
|
self.ephys_widget,
|
|
232
232
|
collapsible=True,
|
|
233
|
-
widget_title="Phy
|
|
233
|
+
widget_title="Phy TraceView",
|
|
234
234
|
)
|
|
235
235
|
|
|
236
236
|
# Index 4: Labelling
|
|
@@ -323,6 +323,7 @@ class MetaWidget(CollapsibleWidgetContainer):
|
|
|
323
323
|
|
|
324
324
|
def refresh_widget_layout(self, widget: QWidget):
|
|
325
325
|
self._schedule_layout_refresh()
|
|
326
|
+
self.notify_content_resized()
|
|
326
327
|
|
|
327
328
|
def _cycle_channel(self, direction: int):
|
|
328
329
|
if not self.app_state.ready:
|
|
@@ -100,6 +100,8 @@ class NavigationWidget(QWidget):
|
|
|
100
100
|
# ── Navigate (unified prev/next + mode) ──────────────────────
|
|
101
101
|
navigate_group = QGroupBox("Navigate")
|
|
102
102
|
navigate_layout = QVBoxLayout()
|
|
103
|
+
navigate_layout.setSpacing(2)
|
|
104
|
+
navigate_layout.setContentsMargins(2, 2, 2, 2)
|
|
103
105
|
navigate_group.setLayout(navigate_layout)
|
|
104
106
|
|
|
105
107
|
filter_hint = QLabel("Only navigates trials visible in the Trials tab")
|
|
@@ -234,6 +236,8 @@ class NavigationWidget(QWidget):
|
|
|
234
236
|
# ── Playback ─────────────────────────────────────────────────
|
|
235
237
|
playback_group = QGroupBox("Playback")
|
|
236
238
|
playback_layout = QGridLayout()
|
|
239
|
+
playback_layout.setSpacing(2)
|
|
240
|
+
playback_layout.setContentsMargins(2, 2, 2, 2)
|
|
237
241
|
playback_group.setLayout(playback_layout)
|
|
238
242
|
|
|
239
243
|
self.fps_label = QLabel("Playback FPS:")
|
|
@@ -145,6 +145,8 @@ class PlotSettingsWidget(QWidget):
|
|
|
145
145
|
|
|
146
146
|
group_box = QGroupBox("Axes Controls")
|
|
147
147
|
group_layout = QGridLayout()
|
|
148
|
+
group_layout.setSpacing(2)
|
|
149
|
+
group_layout.setContentsMargins(2, 2, 2, 2)
|
|
148
150
|
group_box.setLayout(group_layout)
|
|
149
151
|
layout.addWidget(group_box)
|
|
150
152
|
|
|
@@ -297,6 +299,8 @@ class PlotSettingsWidget(QWidget):
|
|
|
297
299
|
|
|
298
300
|
group_box = QGroupBox("Space Plot Controls")
|
|
299
301
|
group_layout = QGridLayout()
|
|
302
|
+
group_layout.setSpacing(2)
|
|
303
|
+
group_layout.setContentsMargins(2, 2, 2, 2)
|
|
300
304
|
group_box.setLayout(group_layout)
|
|
301
305
|
layout.addWidget(group_box)
|
|
302
306
|
|
|
@@ -410,8 +414,8 @@ class PlotSettingsWidget(QWidget):
|
|
|
410
414
|
|
|
411
415
|
group_box = QGroupBox("Spectrogram Controls")
|
|
412
416
|
group_layout = QGridLayout()
|
|
413
|
-
group_layout.
|
|
414
|
-
group_layout.setContentsMargins(
|
|
417
|
+
group_layout.setSpacing(2)
|
|
418
|
+
group_layout.setContentsMargins(2, 2, 2, 2)
|
|
415
419
|
group_box.setLayout(group_layout)
|
|
416
420
|
layout.addWidget(group_box)
|
|
417
421
|
|
|
@@ -740,6 +744,8 @@ class PlotSettingsWidget(QWidget):
|
|
|
740
744
|
|
|
741
745
|
hm_group = QGroupBox("Heatmap Display")
|
|
742
746
|
hm_layout = QGridLayout()
|
|
747
|
+
hm_layout.setSpacing(2)
|
|
748
|
+
hm_layout.setContentsMargins(2, 2, 2, 2)
|
|
743
749
|
hm_group.setLayout(hm_layout)
|
|
744
750
|
layout.addWidget(hm_group)
|
|
745
751
|
|
|
@@ -201,17 +201,24 @@ _LABEL_COLORS = [
|
|
|
201
201
|
_GAP_COLOR = [128 / 255.0, 128 / 255.0, 128 / 255.0]
|
|
202
202
|
|
|
203
203
|
|
|
204
|
-
def load_label_mapping(
|
|
204
|
+
def load_label_mapping(
|
|
205
|
+
mapping_file: str | Path = "mapping.txt",
|
|
206
|
+
order: list[int] | None = None,
|
|
207
|
+
) -> Dict[int, Dict]:
|
|
205
208
|
"""Load a label mapping with colors for visualization.
|
|
206
209
|
|
|
207
210
|
Parameters
|
|
208
211
|
----------
|
|
209
212
|
mapping_file : str or Path
|
|
210
|
-
Path to the mapping file.
|
|
213
|
+
Path to the mapping file. Each line is
|
|
211
214
|
``<id> <name> [<branch>] [<event_type>]`` where *branch* is an
|
|
212
215
|
optional integer (default 0) grouping labels into branches for
|
|
213
216
|
independent labeling, and *event_type* is ``"state"`` (default) or
|
|
214
|
-
``"point"``.
|
|
217
|
+
``"point"``.
|
|
218
|
+
order : list[int] or None
|
|
219
|
+
Label IDs in the desired display sequence. If provided, overrides
|
|
220
|
+
the default order (which follows label ID). Any ID not listed
|
|
221
|
+
retains its default position.
|
|
215
222
|
|
|
216
223
|
Returns
|
|
217
224
|
-------
|
|
@@ -229,10 +236,12 @@ def load_label_mapping(mapping_file: str | Path = "mapping.txt") -> Dict[int, Di
|
|
|
229
236
|
>>> mappings = load_label_mapping("mapping.txt")
|
|
230
237
|
>>> mappings[1]["name"]
|
|
231
238
|
'walk'
|
|
232
|
-
>>> mappings[1]["color"].shape
|
|
233
|
-
(3,)
|
|
234
239
|
|
|
235
|
-
|
|
240
|
+
Reorder labels for display without changing the file::
|
|
241
|
+
|
|
242
|
+
mappings = load_label_mapping("mapping.txt", order=[0, 3, 1, 2])
|
|
243
|
+
|
|
244
|
+
Draw labelled intervals on a plot::
|
|
236
245
|
|
|
237
246
|
import matplotlib.pyplot as plt
|
|
238
247
|
import matplotlib.patches as mpatches
|
|
@@ -240,12 +249,9 @@ def load_label_mapping(mapping_file: str | Path = "mapping.txt") -> Dict[int, Di
|
|
|
240
249
|
mappings = load_label_mapping("mapping.txt")
|
|
241
250
|
fig, ax = plt.subplots()
|
|
242
251
|
ax.plot(time, signal)
|
|
243
|
-
|
|
244
252
|
for _, row in intervals_df.iterrows():
|
|
245
|
-
color = mappings[int(row["labels"])]["color"]
|
|
253
|
+
color = mappings[int(row["labels"])]["color"]
|
|
246
254
|
ax.axvspan(row["onset_s"], row["offset_s"], alpha=0.5, color=color)
|
|
247
|
-
|
|
248
|
-
# Build a legend from the mapping
|
|
249
255
|
handles = [mpatches.Patch(color=m["color"], label=m["name"]) for m in mappings.values()]
|
|
250
256
|
ax.legend(handles=handles)
|
|
251
257
|
plt.show()
|
|
@@ -263,11 +269,9 @@ def load_label_mapping(mapping_file: str | Path = "mapping.txt") -> Dict[int, Di
|
|
|
263
269
|
if parts[0].startswith("("):
|
|
264
270
|
nums = parts[0].strip("()").split(",")
|
|
265
271
|
label_id = (int(nums[0]), int(nums[1]))
|
|
266
|
-
order = int(parts[-1])
|
|
267
272
|
label_mappings[label_id] = {
|
|
268
273
|
"name": parts[1],
|
|
269
274
|
"color": _GAP_COLOR,
|
|
270
|
-
"order": order,
|
|
271
275
|
"branch": 0,
|
|
272
276
|
"event_type": EVENT_TYPE_STATE,
|
|
273
277
|
}
|
|
@@ -280,11 +284,13 @@ def load_label_mapping(mapping_file: str | Path = "mapping.txt") -> Dict[int, Di
|
|
|
280
284
|
label_mappings[label_id] = {
|
|
281
285
|
"name": parts[1],
|
|
282
286
|
"color": np.array(_LABEL_COLORS[label_id % len(_LABEL_COLORS)]) / 255.0,
|
|
283
|
-
"order": label_id,
|
|
284
287
|
"branch": branch,
|
|
285
288
|
"event_type": event_type,
|
|
286
289
|
}
|
|
287
290
|
|
|
291
|
+
if order is not None:
|
|
292
|
+
label_mappings = {k: label_mappings[k] for k in order if k in label_mappings}
|
|
293
|
+
|
|
288
294
|
return label_mappings
|
|
289
295
|
|
|
290
296
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ethograph
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A multimodal timeseries GUI for neuroethology
|
|
5
5
|
Author-email: Akseli Ilmanen <akseli.ilmanen@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -26,6 +26,7 @@ Requires-Dist: pandas
|
|
|
26
26
|
Requires-Dist: xarray
|
|
27
27
|
Requires-Dist: netcdf4
|
|
28
28
|
Requires-Dist: scipy
|
|
29
|
+
Requires-Dist: scikit-learn
|
|
29
30
|
Requires-Dist: matplotlib
|
|
30
31
|
Requires-Dist: pynapple
|
|
31
32
|
Requires-Dist: pyyaml
|
|
@@ -68,6 +68,7 @@ ethograph/gui/dialog_pose_video_matcher.py
|
|
|
68
68
|
ethograph/gui/dialog_screen_recorder.py
|
|
69
69
|
ethograph/gui/dialog_select_template.py
|
|
70
70
|
ethograph/gui/dialog_video_downsample.py
|
|
71
|
+
ethograph/gui/grid_section_container.py
|
|
71
72
|
ethograph/gui/label_drawing_mixin.py
|
|
72
73
|
ethograph/gui/make_pretty.py
|
|
73
74
|
ethograph/gui/napari.yaml
|
|
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
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Crow1_inference_20260330_161224.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Crow1_inference_20260330_161259.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Freddy_inference_20260330_160621.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_inference_20260203_092442.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_inference_20260204_141527.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_inference_20260206_122331.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Ivy_train_20260202_191138_epoch-100.model
RENAMED
|
File without changes
|
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260312_100707.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260327_102414.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260330_140808.json
RENAMED
|
File without changes
|
{ethograph-0.2.3 → ethograph-0.2.4}/configs/crowlab/model/Poppy_inference_20260402_104151.json
RENAMED
|
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
|