pykrait 0.3.2__tar.gz → 0.4.0__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 (44) hide show
  1. pykrait-0.4.0/.gitignore +63 -0
  2. {pykrait-0.3.2 → pykrait-0.4.0}/LICENSE +1 -1
  3. pykrait-0.4.0/PKG-INFO +178 -0
  4. pykrait-0.4.0/README.md +123 -0
  5. {pykrait-0.3.2 → pykrait-0.4.0}/pyproject.toml +29 -29
  6. pykrait-0.4.0/src/pykrait/__init__.py +27 -0
  7. pykrait-0.4.0/src/pykrait/__main__.py +20 -0
  8. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/app.py +19 -19
  9. pykrait-0.4.0/src/pykrait/gui/async_workers.py +397 -0
  10. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/custom_widgets.py +35 -10
  11. pykrait-0.4.0/src/pykrait/gui/fileselection.py +753 -0
  12. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/mainwindow.py +364 -309
  13. pykrait-0.4.0/src/pykrait/io/files.py +292 -0
  14. pykrait-0.4.0/src/pykrait/io/frame_cache.py +292 -0
  15. pykrait-0.4.0/src/pykrait/io/images.py +646 -0
  16. pykrait-0.4.0/src/pykrait/pipeline/pipeline.py +2043 -0
  17. pykrait-0.4.0/src/pykrait/preprocessing/motion_correction.py +752 -0
  18. pykrait-0.4.0/src/pykrait/preprocessing/segmentation.py +328 -0
  19. pykrait-0.4.0/src/pykrait/preprocessing/timeseries_extraction.py +315 -0
  20. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/filtering.py +55 -0
  21. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/oscillations.py +62 -27
  22. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/peak_analysis.py +122 -38
  23. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/synchronicity.py +110 -37
  24. pykrait-0.4.0/src/pykrait/visualization/__init__.py +1 -0
  25. pykrait-0.4.0/src/pykrait/visualization/image_overlays.py +300 -0
  26. pykrait-0.3.2/.gitignore +0 -185
  27. pykrait-0.3.2/PKG-INFO +0 -81
  28. pykrait-0.3.2/README.md +0 -30
  29. pykrait-0.3.2/src/.python-version +0 -1
  30. pykrait-0.3.2/src/pykrait/__main__.py +0 -10
  31. pykrait-0.3.2/src/pykrait/gui/async_workers.py +0 -323
  32. pykrait-0.3.2/src/pykrait/gui/fileselection.py +0 -713
  33. pykrait-0.3.2/src/pykrait/io/files.py +0 -191
  34. pykrait-0.3.2/src/pykrait/io/images.py +0 -263
  35. pykrait-0.3.2/src/pykrait/pipeline/pipeline.py +0 -804
  36. pykrait-0.3.2/src/pykrait/preprocessing/segmentation.py +0 -201
  37. pykrait-0.3.2/src/pykrait/preprocessing/timeseries_extraction.py +0 -441
  38. pykrait-0.3.2/src/pykrait/visualization/image_overlays.py +0 -236
  39. {pykrait-0.3.2/src/pykrait → pykrait-0.4.0/src/pykrait/gui}/__init__.py +0 -0
  40. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/resources/logo.png +0 -0
  41. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/io/__init__.py +0 -0
  42. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/pipeline/__init__.py +0 -0
  43. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/preprocessing/__init__.py +0 -0
  44. {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/__init__.py +0 -0
@@ -0,0 +1,63 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+
8
+ # Distribution / packaging
9
+ dist/
10
+ build/
11
+ *.egg-info/
12
+ *.egg
13
+ MANIFEST
14
+
15
+ # Virtual environments
16
+ .venv/
17
+ venv/
18
+ env/
19
+
20
+ # uv
21
+ .uv/
22
+ uv.lock
23
+
24
+ # Testing
25
+ .pytest_cache/
26
+ .coverage
27
+ coverage.xml
28
+ htmlcov/
29
+
30
+ # Linting
31
+ .ruff_cache/
32
+ .mypy_cache/
33
+
34
+ # Jupyter
35
+ .ipynb_checkpoints/
36
+
37
+ # Docs
38
+ docs/_build/
39
+ docs/autoapi/
40
+ docs/source/_autosummary/
41
+ docs/make.bat
42
+ docs/Makefile
43
+
44
+ # IDEs
45
+ .vscode/
46
+ .idea/
47
+ .claude/
48
+ *.swp
49
+ *.swo
50
+
51
+ # macOS
52
+ .DS_Store
53
+
54
+ # Secrets
55
+ .env
56
+ *.pem
57
+
58
+ # cellpose models
59
+ cellpose_models/
60
+ notebooks/batch_notebook.ipynb
61
+
62
+ # local notebooks, may contain real data paths and executed outputs
63
+ notebooks/local_*.ipynb
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Phaleanopsis
3
+ Copyright (c) 2026 Phaleanopsis
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
pykrait-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.5
2
+ Name: pykrait
3
+ Version: 0.4.0
4
+ Summary: Python Calcium Recording Analysis and Interpretation Toolbox
5
+ Author-email: phaleanopsis <22663502+phaleanopsis@users.noreply.github.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: aicspylibczi>=3.3.1
14
+ Requires-Dist: bioio-czi<5,>=2.4.0
15
+ Requires-Dist: bioio-imageio<2
16
+ Requires-Dist: bioio-ome-tiff<2,>=1.1.0
17
+ Requires-Dist: bioio-tifffile<2,>=1.3.0
18
+ Requires-Dist: bioio<4,>=3.2.0
19
+ Requires-Dist: cellpose<5,>=4.0.8
20
+ Requires-Dist: dask>=2025.5.1
21
+ Requires-Dist: fsspec>=2022.8.0
22
+ Requires-Dist: imagecodecs>=2025.3.30
23
+ Requires-Dist: matplotlib>=3.10.3
24
+ Requires-Dist: numcodecs<0.16,>=0.15
25
+ Requires-Dist: numpy>2
26
+ Requires-Dist: opencv-python-headless>=4.10.0.84
27
+ Requires-Dist: pandas>=2.3.1
28
+ Requires-Dist: psutil>=6.0
29
+ Requires-Dist: pylibczirw>=6.0; sys_platform != 'darwin' or platform_machine != 'x86_64'
30
+ Requires-Dist: pyyaml>=6.0.2
31
+ Requires-Dist: scikit-image>=0.25.2
32
+ Requires-Dist: scipy>=1.15.3
33
+ Requires-Dist: tifffile>=2025.5.10
34
+ Requires-Dist: tqdm>=4.66
35
+ Requires-Dist: xarray>=2025.12
36
+ Requires-Dist: zarr>=2.15.0
37
+ Requires-Dist: zstandard>=0.23.0
38
+ Provides-Extra: dev
39
+ Requires-Dist: build>=1.2.2.post1; extra == 'dev'
40
+ Requires-Dist: hatch>=1.14.1; extra == 'dev'
41
+ Requires-Dist: ipywidgets>=8.1.7; extra == 'dev'
42
+ Requires-Dist: myst-parser>=5.0.0; extra == 'dev'
43
+ Requires-Dist: pydata-sphinx-theme>=0.16.1; extra == 'dev'
44
+ Requires-Dist: pytest-cov>=6.0; extra == 'dev'
45
+ Requires-Dist: pytest>=8.4.1; extra == 'dev'
46
+ Requires-Dist: ruff>=0.11.13; extra == 'dev'
47
+ Requires-Dist: sphinx-autoapi>=3.6.0; extra == 'dev'
48
+ Requires-Dist: sphinx-favicon>=1.0.1; extra == 'dev'
49
+ Requires-Dist: sphinx>=8.1.3; extra == 'dev'
50
+ Requires-Dist: tomli>=2.2.1; extra == 'dev'
51
+ Requires-Dist: twine>=6.1.0; extra == 'dev'
52
+ Provides-Extra: gui
53
+ Requires-Dist: pyside6>=6.9.1; extra == 'gui'
54
+ Description-Content-Type: text/markdown
55
+
56
+ <p align="center">
57
+ <img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/logo.png" alt="pyKrait" width="200">
58
+ </p>
59
+
60
+ <h1 style="text-align: center;">pyKrait</h1>
61
+
62
+ <p align="center">
63
+ <a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/v/pykrait" alt="PyPI"></a>
64
+ <a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/pyversions/pykrait" alt="Python versions"></a>
65
+ <a href="https://github.com/Winklerlab-HD/pykrait/actions"><img src="https://img.shields.io/github/actions/workflow/status/Winklerlab-HD/pykrait/CI.yaml" alt="CI"></a>
66
+ <a href="https://pykrait.readthedocs.io"><img src="https://img.shields.io/readthedocs/pykrait" alt="Docs"></a>
67
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
68
+ </p>
69
+ <p align="center">
70
+ End-to-end pipeline for segmenting cells, detecting calcium peaks,<br>
71
+ and quantifying oscillation periodicity and spatial synchronicity<br>
72
+ from time-lapse microscopy.
73
+ </p>
74
+
75
+
76
+ <p align="center">
77
+ <img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/pyKrait_steps.png" alt="Analysis Steps of the pykrait package" width="600">
78
+ </p>
79
+
80
+ ## Overview
81
+ pyKrait (**py**thon **C**alcium **r**ecording **a**nalysis and **i**nterpretation **t**oolbox) automatically processes calcium-imaging videos (.czi, .tif, .tiff) and computes calcium activity, peak statistics, periodicity scores, and neighbour-aware synchronicity z-scores.
82
+
83
+ ## Installation
84
+
85
+ It is recommended to install pyKrait in a virtual environment using either venv or uv. The graphical user interface is optional, install it with `pip install "pykrait[gui]"`.
86
+
87
+ ### Using `uv`
88
+
89
+ ```bash
90
+ uv venv --python 3.12
91
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
92
+ uv pip install 'pykrait[gui]'
93
+ ```
94
+
95
+ ### Standard `venv`
96
+
97
+ ```bash
98
+ python -m venv .venv
99
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
100
+ pip install --upgrade pip
101
+ pip install "pykrait[gui]"
102
+ ```
103
+
104
+
105
+ ## Quickstart
106
+
107
+ ### Launch the GUI
108
+
109
+ From inside the activated virtual environment:
110
+ ```bash
111
+ python -m pykrait
112
+ ```
113
+ or
114
+ ```bash
115
+ uv run python -m pykrait
116
+ ```
117
+ The GUI then walks you through video selection, segmentation, peak detection, periodicity, and synchronicity, and lets you save results to disk.
118
+
119
+ ### Batch process an entire folder
120
+
121
+ ```python
122
+ from pykrait.pipeline.pipeline import BatchExperiment, AnalysisParameters
123
+ from pykrait.io.files import concat_analysis_files
124
+
125
+ experiment = BatchExperiment(
126
+ folder="/path/to/videos",
127
+ params=AnalysisParameters(),
128
+ extension=".czi",
129
+ )
130
+ experiment.run()
131
+
132
+ concat_analysis_files("/path/to/videos", filetype="output")
133
+ ```
134
+
135
+ This produces one `Analysis_<video>/` directory per video and an `analysis_output_overview.csv` at the root of the folder.
136
+
137
+ To analyze videos in subfolders, pass `recursive=True`; to analyze only some of the acquisitions, pass `filter_string` / `exclude_string` (e.g. `filter_string="*_timeseries.czi"`, where a pattern with `*`, `?` or `[]` matches the whole file name and anything else matches as a substring).
138
+
139
+ ## Example Notebooks
140
+
141
+ | Scenario | Notebook |
142
+ |----------|----------|
143
+ | Run a folder with custom parameters | [notebooks/example_batch_with_custom_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_with_custom_parameters.ipynb) |
144
+ | Re-run a folder reusing saved parameters and cached masks | [notebooks/example_batch_reusing_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_reusing_parameters.ipynb) |
145
+ | Inspect or refine a single video interactively | Launch the GUI: `python -m pykrait` |
146
+
147
+ ## How it works
148
+
149
+ For each video, pyKrait runs the following stages:
150
+
151
+ 1. **Load** — lazy `Dask` array via `bioio` (`.czi`, `.tif`, `.tiff`).
152
+ 2. **Correct motion** *(optional, off by default)* — align every frame to a template, either by a rigid translation or by a mesh-based / polynomial optical-flow warp.
153
+ 3. **Project** — STD or SUM projection across time, with optional CLAHE.
154
+ 4. **Segment** — Cellpose (`cpsam` by default, or a custom model path).
155
+ 5. **Extract** — per-cell mean intensity per frame, computed lazily on Dask.
156
+ 6. **Detrend** — Blackman-windowed sinc filter (cf. pyBOAT).
157
+ 7. **Detect peaks** — `scipy.signal.find_peaks` with width / height / prominence thresholds.
158
+ 8. **Periodicity** — STD and CoV of inter-peak intervals, scored against a shuffled-peaks null.
159
+ 9. **Adjacency** — neighbour graph from segmented masks (kernel-based proximity).
160
+ 10. **Synchronicity** — co-firing peaks within a time window and topological distance, z-scored against label-shuffled controls.
161
+
162
+ ## Documentation
163
+
164
+ Full API reference and tutorials: **<https://pykrait.readthedocs.io/en/latest/>**.
165
+
166
+ ## Citation
167
+
168
+ If you use pyKrait, please cite the [preprint](https://www.biorxiv.org/content/10.64898/2026.05.10.723715v1): Hebach, N. R., Olshausen, N., Schlag, J., Mayer, C. D., Henkenjohann, J., Kraft, T., Bang, S., Thommek, C., Nürnberg, C., Horsak, N. E., Hoffmann, D. C., Kourtesakis, A., Porzberg, N., Flores Valle, A., Linke, C. Z., Yang, Y., Azorín, D. D., Hausmann, D., Venkataramani, V., … Karreman, M. A. (2026). Collective cancer cell calcium activity drives brain metastasis. bioRxiv, 2026.05.10.723715. https://doi.org/10.64898/2026.05.10.723715
169
+
170
+ ## Acknowledgements
171
+
172
+ pyKrait builds on and heavily uses prior open-source work:
173
+
174
+ - [Cellpose](https://github.com/MouseLand/cellpose) for cell segmentation
175
+ - [pyBOAT](https://github.com/tensionhead/pyBOAT) for the sinc-filter detrending approach
176
+ - [bioio](https://github.com/bioio-devs/bioio) for image I/O
177
+ - [Dask](https://www.dask.org/) for lazy array computation
178
+
@@ -0,0 +1,123 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/logo.png" alt="pyKrait" width="200">
3
+ </p>
4
+
5
+ <h1 style="text-align: center;">pyKrait</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/v/pykrait" alt="PyPI"></a>
9
+ <a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/pyversions/pykrait" alt="Python versions"></a>
10
+ <a href="https://github.com/Winklerlab-HD/pykrait/actions"><img src="https://img.shields.io/github/actions/workflow/status/Winklerlab-HD/pykrait/CI.yaml" alt="CI"></a>
11
+ <a href="https://pykrait.readthedocs.io"><img src="https://img.shields.io/readthedocs/pykrait" alt="Docs"></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
13
+ </p>
14
+ <p align="center">
15
+ End-to-end pipeline for segmenting cells, detecting calcium peaks,<br>
16
+ and quantifying oscillation periodicity and spatial synchronicity<br>
17
+ from time-lapse microscopy.
18
+ </p>
19
+
20
+
21
+ <p align="center">
22
+ <img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/pyKrait_steps.png" alt="Analysis Steps of the pykrait package" width="600">
23
+ </p>
24
+
25
+ ## Overview
26
+ pyKrait (**py**thon **C**alcium **r**ecording **a**nalysis and **i**nterpretation **t**oolbox) automatically processes calcium-imaging videos (.czi, .tif, .tiff) and computes calcium activity, peak statistics, periodicity scores, and neighbour-aware synchronicity z-scores.
27
+
28
+ ## Installation
29
+
30
+ It is recommended to install pyKrait in a virtual environment using either venv or uv. The graphical user interface is optional, install it with `pip install "pykrait[gui]"`.
31
+
32
+ ### Using `uv`
33
+
34
+ ```bash
35
+ uv venv --python 3.12
36
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
37
+ uv pip install 'pykrait[gui]'
38
+ ```
39
+
40
+ ### Standard `venv`
41
+
42
+ ```bash
43
+ python -m venv .venv
44
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
45
+ pip install --upgrade pip
46
+ pip install "pykrait[gui]"
47
+ ```
48
+
49
+
50
+ ## Quickstart
51
+
52
+ ### Launch the GUI
53
+
54
+ From inside the activated virtual environment:
55
+ ```bash
56
+ python -m pykrait
57
+ ```
58
+ or
59
+ ```bash
60
+ uv run python -m pykrait
61
+ ```
62
+ The GUI then walks you through video selection, segmentation, peak detection, periodicity, and synchronicity, and lets you save results to disk.
63
+
64
+ ### Batch process an entire folder
65
+
66
+ ```python
67
+ from pykrait.pipeline.pipeline import BatchExperiment, AnalysisParameters
68
+ from pykrait.io.files import concat_analysis_files
69
+
70
+ experiment = BatchExperiment(
71
+ folder="/path/to/videos",
72
+ params=AnalysisParameters(),
73
+ extension=".czi",
74
+ )
75
+ experiment.run()
76
+
77
+ concat_analysis_files("/path/to/videos", filetype="output")
78
+ ```
79
+
80
+ This produces one `Analysis_<video>/` directory per video and an `analysis_output_overview.csv` at the root of the folder.
81
+
82
+ To analyze videos in subfolders, pass `recursive=True`; to analyze only some of the acquisitions, pass `filter_string` / `exclude_string` (e.g. `filter_string="*_timeseries.czi"`, where a pattern with `*`, `?` or `[]` matches the whole file name and anything else matches as a substring).
83
+
84
+ ## Example Notebooks
85
+
86
+ | Scenario | Notebook |
87
+ |----------|----------|
88
+ | Run a folder with custom parameters | [notebooks/example_batch_with_custom_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_with_custom_parameters.ipynb) |
89
+ | Re-run a folder reusing saved parameters and cached masks | [notebooks/example_batch_reusing_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_reusing_parameters.ipynb) |
90
+ | Inspect or refine a single video interactively | Launch the GUI: `python -m pykrait` |
91
+
92
+ ## How it works
93
+
94
+ For each video, pyKrait runs the following stages:
95
+
96
+ 1. **Load** — lazy `Dask` array via `bioio` (`.czi`, `.tif`, `.tiff`).
97
+ 2. **Correct motion** *(optional, off by default)* — align every frame to a template, either by a rigid translation or by a mesh-based / polynomial optical-flow warp.
98
+ 3. **Project** — STD or SUM projection across time, with optional CLAHE.
99
+ 4. **Segment** — Cellpose (`cpsam` by default, or a custom model path).
100
+ 5. **Extract** — per-cell mean intensity per frame, computed lazily on Dask.
101
+ 6. **Detrend** — Blackman-windowed sinc filter (cf. pyBOAT).
102
+ 7. **Detect peaks** — `scipy.signal.find_peaks` with width / height / prominence thresholds.
103
+ 8. **Periodicity** — STD and CoV of inter-peak intervals, scored against a shuffled-peaks null.
104
+ 9. **Adjacency** — neighbour graph from segmented masks (kernel-based proximity).
105
+ 10. **Synchronicity** — co-firing peaks within a time window and topological distance, z-scored against label-shuffled controls.
106
+
107
+ ## Documentation
108
+
109
+ Full API reference and tutorials: **<https://pykrait.readthedocs.io/en/latest/>**.
110
+
111
+ ## Citation
112
+
113
+ If you use pyKrait, please cite the [preprint](https://www.biorxiv.org/content/10.64898/2026.05.10.723715v1): Hebach, N. R., Olshausen, N., Schlag, J., Mayer, C. D., Henkenjohann, J., Kraft, T., Bang, S., Thommek, C., Nürnberg, C., Horsak, N. E., Hoffmann, D. C., Kourtesakis, A., Porzberg, N., Flores Valle, A., Linke, C. Z., Yang, Y., Azorín, D. D., Hausmann, D., Venkataramani, V., … Karreman, M. A. (2026). Collective cancer cell calcium activity drives brain metastasis. bioRxiv, 2026.05.10.723715. https://doi.org/10.64898/2026.05.10.723715
114
+
115
+ ## Acknowledgements
116
+
117
+ pyKrait builds on and heavily uses prior open-source work:
118
+
119
+ - [Cellpose](https://github.com/MouseLand/cellpose) for cell segmentation
120
+ - [pyBOAT](https://github.com/tensionhead/pyBOAT) for the sinc-filter detrending approach
121
+ - [bioio](https://github.com/bioio-devs/bioio) for image I/O
122
+ - [Dask](https://www.dask.org/) for lazy array computation
123
+
@@ -1,42 +1,51 @@
1
1
  [project]
2
2
  name = "pykrait"
3
- version = "0.3.2"
4
- description = "Python Calcium Recording Analysis and Inference Toolbox"
3
+ version = "0.4.0"
4
+ description = "Python Calcium Recording Analysis and Interpretation Toolbox"
5
5
  readme = { file = "README.md", content-type = "text/markdown" }
6
6
  license = { text = "MIT" }
7
7
  authors = [
8
8
  { name = "phaleanopsis", email = "22663502+phaleanopsis@users.noreply.github.com" }
9
9
  ]
10
10
  requires-python = ">=3.11"
11
+ # bioio plugins and cellpose change their APIs between major versions (bioio-czi 4 removed
12
+ # use_aicspylibczi), so they are capped at the next untested major version
11
13
  dependencies = [
12
14
  "aicspylibczi>=3.3.1",
13
- "bioio>=3.0.0",
14
- "bioio-czi>=2.4.0",
15
- "bioio-ome-tiff>=1.1.0",
16
- "bioio-tifffile>=1.3.0",
17
- "bioio-imageio",
18
- "cellpose>=4.0.5",
15
+ "bioio>=3.2.0,<4",
16
+ "bioio-czi>=2.4.0,<5",
17
+ "bioio-ome-tiff>=1.1.0,<2",
18
+ "bioio-tifffile>=1.3.0,<2",
19
+ "bioio-imageio<2",
20
+ "cellpose>=4.0.8,<5",
19
21
  "dask>=2025.5.1",
20
22
  "fsspec>=2022.8.0",
21
23
  "imagecodecs>=2025.3.30",
22
- "lxml>=5.4.0",
23
24
  "matplotlib>=3.10.3",
24
- "numcodecs<0.16",
25
+ # zarr 2 imports numcodecs.blosc.cbuffer_sizes, which numcodecs 0.16 removed
26
+ "numcodecs>=0.15,<0.16",
25
27
  "numpy>2",
26
- "ome-types>=0.5.3",
27
- "ome-zarr>=0.10.2",
28
- "pip>=25.1.1",
29
- "pydantic<2",
28
+ "opencv-python-headless>=4.10.0.84",
30
29
  "pyyaml>=6.0.2",
31
- "resource-backed-dask-array>=0.1.0",
32
30
  "scikit-image>=0.25.2",
33
31
  "scipy>=1.15.3",
34
- "xarray>2025",
32
+ "tifffile>=2025.5.10",
33
+ "tqdm>=4.66",
34
+ "xarray>=2025.12",
35
35
  "pandas>=2.3.1",
36
+ "psutil>=6.0",
37
+ # reads CZI files much faster than bioio; there is no wheel for Intel Macs, which
38
+ # fall back to bioio
39
+ "pylibCZIrw>=6.0; sys_platform != 'darwin' or platform_machine != 'x86_64'",
36
40
  "zstandard>=0.23.0",
37
- "scikit-learn>=1.7.2",
41
+ "zarr>=2.15.0",
42
+ ]
43
+ classifiers = [
44
+ "Programming Language :: Python :: 3",
45
+ "Programming Language :: Python :: 3.11",
46
+ "Programming Language :: Python :: 3.12",
47
+ "Programming Language :: Python :: 3.13",
38
48
  ]
39
-
40
49
  [project.optional-dependencies]
41
50
  gui = [
42
51
  "pyside6>=6.9.1",
@@ -53,7 +62,8 @@ dev = [
53
62
  "tomli>=2.2.1",
54
63
  "twine>=6.1.0",
55
64
  "pytest>=8.4.1",
56
- "pytest-cov"
65
+ "pytest-cov>=6.0",
66
+ "myst-parser>=5.0.0",
57
67
  ]
58
68
 
59
69
  [build-system]
@@ -92,17 +102,7 @@ source = ["pykrait"]
92
102
  show_missing = true
93
103
  skip_covered = true
94
104
 
95
- [tool.setuptools.package-data]
96
- "pykrait.gui.resources" = ["logo.png"]
97
-
98
105
  [tool.ruff]
99
106
  src = ["src"]
100
107
  line-length = 88
101
108
  target-version = "py311"
102
-
103
- [dependency-groups]
104
- dev = [
105
- "build>=1.2.2.post1",
106
- "hatch>=1.14.1",
107
- "twine>=6.1.0",
108
- ]
@@ -0,0 +1,27 @@
1
+ import logging
2
+ import sys
3
+
4
+
5
+ class _StdoutHandler(logging.StreamHandler):
6
+ """Writes to the current sys.stdout, which notebooks and tests replace after import."""
7
+
8
+ @property
9
+ def stream(self):
10
+ return sys.stdout
11
+
12
+ @stream.setter
13
+ def stream(self, value):
14
+ pass
15
+
16
+
17
+ # pykrait reports its progress through the "pykrait" logger. By default the messages are
18
+ # printed to stdout as before; silence them with
19
+ # logging.getLogger("pykrait").setLevel(logging.WARNING), or remove this handler to
20
+ # route them through your own logging configuration.
21
+ _logger = logging.getLogger(__name__)
22
+ _handler = _StdoutHandler()
23
+ _handler.setFormatter(logging.Formatter("%(message)s"))
24
+ _logger.addHandler(_handler)
25
+ if _logger.level == logging.NOTSET: # keep a level that was set before the import
26
+ _logger.setLevel(logging.INFO)
27
+ _logger.propagate = False
@@ -0,0 +1,20 @@
1
+ """
2
+ Copyright © 2026 Phaleanopsis
3
+ """
4
+
5
+ # src/pykrait/__main__.py
6
+ import sys
7
+
8
+ try:
9
+ from pykrait.gui.app import AppController
10
+ except ImportError as e:
11
+ if getattr(e, "name", None) and e.name.startswith("PySide6"):
12
+ sys.exit(
13
+ "The pyKrait GUI needs the optional GUI dependencies. "
14
+ 'Install them with: pip install "pykrait[gui]"'
15
+ )
16
+ raise
17
+
18
+ if __name__ == "__main__":
19
+ app_controller = AppController()
20
+ app_controller.run()
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import sys
2
3
  import os
3
4
  import warnings
@@ -8,15 +9,18 @@ from pykrait.gui.fileselection import FileSelectionWindow
8
9
  from pykrait.gui.mainwindow import MainWindow
9
10
  from pykrait.io.files import get_pykrait_version
10
11
 
12
+ logger = logging.getLogger(__name__)
13
+
14
+
11
15
  def warning_no_source_line(message, category, filename, lineno, file=None, line=None):
12
16
  return f"{filename}:{lineno}: {category.__name__}: {message}\n"
13
17
 
18
+
14
19
  class AppController:
15
20
  def __init__(self):
16
-
17
21
  # format warnings to not print the source line every time
18
22
  warnings.formatwarning = warning_no_source_line
19
-
23
+
20
24
  self.app = QApplication(sys.argv)
21
25
 
22
26
  # Loading icon from resources
@@ -25,7 +29,6 @@ class AppController:
25
29
  from importlib.resources import files
26
30
 
27
31
  icon_path = files("pykrait.gui.resources").joinpath("logo.png")
28
- os.path.exists(icon_path)
29
32
  except Exception:
30
33
  icon_path = os.path.abspath(
31
34
  os.path.join(
@@ -36,28 +39,21 @@ class AppController:
36
39
  "logo.png",
37
40
  )
38
41
  )
39
- os.path.exists(icon_path)
40
42
  if os.path.exists(icon_path):
41
- appIcon.addFile(str(icon_path), QSize(16, 16))
42
- appIcon.addFile(str(icon_path), QSize(24, 24))
43
- appIcon.addFile(str(icon_path), QSize(32, 32))
44
- appIcon.addFile(str(icon_path), QSize(48, 48))
45
- appIcon.addFile(str(icon_path), QSize(64, 64))
46
- appIcon.addFile(str(icon_path), QSize(256, 256))
43
+ for size in (16, 24, 32, 48, 64, 256):
44
+ appIcon.addFile(str(icon_path), QSize(size, size))
47
45
  self.app.setWindowIcon(appIcon)
48
46
  else:
49
- print(f"Warning: Icon file not found at {icon_path}")
47
+ logger.warning(f"Warning: Icon file not found at {icon_path}")
50
48
 
51
49
  # Set application name and version
52
50
  self.app.setApplicationName("pyKrait")
53
51
  version = get_pykrait_version()
54
52
  if version is not None:
55
53
  self.app.setApplicationVersion(version)
56
- self.file_selection = FileSelectionWindow()
57
- self.file_selection.analysis_complete.connect(self.on_analysis_complete)
58
- self.file_selection.show()
59
-
60
- #updating the style
54
+ self._open_file_selection()
55
+
56
+ # updating the style
61
57
  self.app.setStyle("Fusion")
62
58
  light_palette = self.app.style().standardPalette()
63
59
  self.app.setPalette(light_palette)
@@ -72,6 +68,7 @@ class AppController:
72
68
  # Launch main window, keep reference
73
69
  self.main_window = MainWindow(
74
70
  frames=results["frames"],
71
+ display_channel=results.get("intensity_channel", 0),
75
72
  mask=results["masks"],
76
73
  rois=results["rois"],
77
74
  mean_intensities=results["mean_intensities"],
@@ -81,6 +78,11 @@ class AppController:
81
78
  )
82
79
  self.main_window.show()
83
80
 
81
+ def _open_file_selection(self):
82
+ self.file_selection = FileSelectionWindow()
83
+ self.file_selection.analysis_complete.connect(self.on_analysis_complete)
84
+ self.file_selection.show()
85
+
84
86
  def run(self):
85
87
  sys.exit(self.app.exec())
86
88
 
@@ -89,6 +91,4 @@ class AppController:
89
91
  self.main_window.close()
90
92
  self.main_window = None
91
93
 
92
- self.file_selection = FileSelectionWindow()
93
- self.file_selection.analysis_complete.connect(self.on_analysis_complete)
94
- self.file_selection.show()
94
+ self._open_file_selection()