napari-spatialdata 0.3.1__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.
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/.pre-commit-config.yaml +4 -4
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/.readthedocs.yaml +1 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/CHANGELOG.md +15 -1
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/PKG-INFO +9 -3
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/README.md +4 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/conf.py +1 -1
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/pyproject.toml +6 -5
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/setup.cfg +4 -2
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/__main__.py +2 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_constants/_constants.py +1 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_constants/_pkg_constants.py +1 -0
- napari-spatialdata-0.4.0/src/napari_spatialdata/_constants/config.py +2 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_interactive.py +16 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_model.py +90 -102
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_scatterwidgets.py +8 -8
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_sdata_widgets.py +6 -4
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_version.py +2 -2
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_view.py +166 -38
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_viewer.py +223 -47
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_widgets.py +88 -70
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/utils/_categoricals_utils.py +18 -4
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/utils/_test_utils.py +24 -10
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/utils/_utils.py +99 -13
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/utils/_viewer_utils.py +2 -2
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata.egg-info/PKG-INFO +9 -3
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata.egg-info/SOURCES.txt +1 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata.egg-info/requires.txt +5 -2
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/conftest.py +7 -2
- napari-spatialdata-0.4.0/tests/plots/groundtruth/Images_can_add_element_image.png +0 -0
- napari-spatialdata-0.4.0/tests/plots/groundtruth/Images_can_add_element_multiple.png +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/test_scatterwidgets.py +10 -8
- napari-spatialdata-0.4.0/tests/test_spatialdata.py +346 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/test_utils.py +6 -6
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/test_widgets.py +53 -86
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tox.ini +1 -0
- napari-spatialdata-0.3.1/tests/plots/groundtruth/Images_can_add_element_image.png +0 -0
- napari-spatialdata-0.3.1/tests/plots/groundtruth/Images_can_add_element_multiple.png +0 -0
- napari-spatialdata-0.3.1/tests/test_spatialdata.py +0 -229
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/.github/workflows/test_and_deploy.yml +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/.gitignore +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/.mypy.ini +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/.napari/DESCRIPTION.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/LICENSE +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/MANIFEST.in +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/Makefile +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/_static/.gitkeep +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/_templates/.gitkeep +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/_templates/autosummary/class.rst +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/api.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/cli.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/contributing.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/extensions/typed_returns.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/index.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/make.bat +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/notebooks/mibitof_analysis.ipynb +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/notebooks/nanostring_analysis.ipynb +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/notebooks/scatterwidget.ipynb +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/notebooks/spatialdata.ipynb +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/references.bib +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/references.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/docs/template_usage.md +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/examples/spatialdata_empty.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/examples/spatialdata_visium.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/__init__.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_constants/__init__.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_constants/_utils.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_reader.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/napari.yaml +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/utils/__init__.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata.egg-info/dependency_links.txt +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata.egg-info/entry_points.txt +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata.egg-info/top_level.txt +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/__init__.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/plots/groundtruth/Images_can_add_element_label.png +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/test_cli.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/test_interactive.py +0 -0
- {napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/tests/test_viewer.py +0 -0
|
@@ -7,18 +7,18 @@ default_stages:
|
|
|
7
7
|
minimum_pre_commit_version: 2.9.3
|
|
8
8
|
repos:
|
|
9
9
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
10
|
-
rev: v1.
|
|
10
|
+
rev: v1.9.0
|
|
11
11
|
hooks:
|
|
12
12
|
- id: mypy
|
|
13
13
|
additional_dependencies: [numpy>=1.23]
|
|
14
14
|
exclude: docs
|
|
15
15
|
- repo: https://github.com/psf/black
|
|
16
|
-
rev:
|
|
16
|
+
rev: 24.3.0
|
|
17
17
|
hooks:
|
|
18
18
|
- id: black
|
|
19
19
|
additional_dependencies: [toml]
|
|
20
20
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
|
21
|
-
rev:
|
|
21
|
+
rev: v4.0.0-alpha.8
|
|
22
22
|
hooks:
|
|
23
23
|
- id: prettier
|
|
24
24
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
@@ -49,7 +49,7 @@ repos:
|
|
|
49
49
|
hooks:
|
|
50
50
|
- id: blacken-docs
|
|
51
51
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
52
|
-
rev: v0.
|
|
52
|
+
rev: v0.3.3
|
|
53
53
|
hooks:
|
|
54
54
|
- id: ruff
|
|
55
55
|
args: [--fix, --exit-non-zero-on-fix]
|
|
@@ -8,7 +8,21 @@ and this project adheres to [Semantic Versioning][].
|
|
|
8
8
|
[keep a changelog]: https://keepachangelog.com/en/1.0.0/
|
|
9
9
|
[semantic versioning]: https://semver.org/spec/v2.0.0.html
|
|
10
10
|
|
|
11
|
-
## [0.
|
|
11
|
+
## [0.4.0] - 2023-03-24
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Multi table support #199 @melonora
|
|
16
|
+
- Color shapes and points by values in dataframe in addition to tables #216
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Avoid exception when registering shortcuts #201 @aeisenbarth
|
|
21
|
+
- Fix wrong point size when affine specified #193
|
|
22
|
+
- Wrong element visiblity when changing coordinate system #207
|
|
23
|
+
- Cleaned up data model #180
|
|
24
|
+
- Fixes correct matching of geometies and annoations via join #208
|
|
25
|
+
- Better docstrings #219
|
|
12
26
|
|
|
13
27
|
## [0.3.1] - 2023-11-11
|
|
14
28
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: napari-spatialdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Interactive visualization of spatial omics data with napari
|
|
5
5
|
Home-page: https://github.com/scverse/napari-spatialdata.git
|
|
6
6
|
Author: giovanni palla
|
|
@@ -30,7 +30,7 @@ Requires-Dist: dask
|
|
|
30
30
|
Requires-Dist: geopandas
|
|
31
31
|
Requires-Dist: loguru
|
|
32
32
|
Requires-Dist: matplotlib
|
|
33
|
-
Requires-Dist: napari
|
|
33
|
+
Requires-Dist: napari>=0.4.19
|
|
34
34
|
Requires-Dist: napari-matplotlib
|
|
35
35
|
Requires-Dist: numba
|
|
36
36
|
Requires-Dist: numpy
|
|
@@ -38,6 +38,7 @@ Requires-Dist: packaging
|
|
|
38
38
|
Requires-Dist: pandas
|
|
39
39
|
Requires-Dist: pillow
|
|
40
40
|
Requires-Dist: qtpy
|
|
41
|
+
Requires-Dist: scanpy
|
|
41
42
|
Requires-Dist: scipy
|
|
42
43
|
Requires-Dist: shapely
|
|
43
44
|
Requires-Dist: scikit-learn
|
|
@@ -66,9 +67,10 @@ Requires-Dist: sphinx-copybutton; extra == "doc"
|
|
|
66
67
|
Requires-Dist: sphinx-qt-documentation; extra == "doc"
|
|
67
68
|
Requires-Dist: myst-nb; extra == "doc"
|
|
68
69
|
Requires-Dist: squidpy; extra == "doc"
|
|
69
|
-
Requires-Dist: scanpy; extra == "doc"
|
|
70
70
|
Provides-Extra: readthedocs
|
|
71
71
|
Requires-Dist: pydantic<2; extra == "readthedocs"
|
|
72
|
+
Provides-Extra: pre
|
|
73
|
+
Requires-Dist: spatialdata>=0.1.0-pre0; extra == "pre"
|
|
72
74
|
Provides-Extra: all
|
|
73
75
|
Requires-Dist: PyQt5; extra == "all"
|
|
74
76
|
|
|
@@ -149,6 +151,10 @@ Distributed under the terms of the [BSD-3] license,
|
|
|
149
151
|
|
|
150
152
|
If you encounter any problems, please file an [issue] along with a detailed description.
|
|
151
153
|
|
|
154
|
+
## Citation
|
|
155
|
+
|
|
156
|
+
Marconato, L., Palla, G., Yamauchi, K.A. et al. SpatialData: an open and universal data framework for spatial omics. Nat Methods (2024). https://doi.org/10.1038/s41592-024-02212-x
|
|
157
|
+
|
|
152
158
|
[napari]: https://github.com/napari/napari
|
|
153
159
|
[cookiecutter]: https://github.com/audreyr/cookiecutter
|
|
154
160
|
[@napari]: https://github.com/napari
|
|
@@ -75,6 +75,10 @@ Distributed under the terms of the [BSD-3] license,
|
|
|
75
75
|
|
|
76
76
|
If you encounter any problems, please file an [issue] along with a detailed description.
|
|
77
77
|
|
|
78
|
+
## Citation
|
|
79
|
+
|
|
80
|
+
Marconato, L., Palla, G., Yamauchi, K.A. et al. SpatialData: an open and universal data framework for spatial omics. Nat Methods (2024). https://doi.org/10.1038/s41592-024-02212-x
|
|
81
|
+
|
|
78
82
|
[napari]: https://github.com/napari/napari
|
|
79
83
|
[cookiecutter]: https://github.com/audreyr/cookiecutter
|
|
80
84
|
[@napari]: https://github.com/napari
|
|
@@ -135,7 +135,7 @@ nitpick_ignore = [
|
|
|
135
135
|
# ("py:obj", "napari_spatialdata.QtAdataViewWidget.insertAction"),
|
|
136
136
|
# ("py:obj", "napari_spatialdata.QtAdataViewWidget.scroll"),
|
|
137
137
|
# ("py:obj", "napari_spatialdata.QtAdataViewWidget.setTabOrder"),
|
|
138
|
-
# ("py:class", "napari_spatialdata._model.
|
|
138
|
+
# ("py:class", "napari_spatialdata._model.DataModel"),
|
|
139
139
|
# ("py:obj", "napari_spatialdata.QtAdataScatterWidget.scroll"),
|
|
140
140
|
# ("py:obj", "napari_spatialdata.QtAdataScatterWidget.insertAction"),
|
|
141
141
|
# If building the documentation fails because of a missing link that is outside your control,
|
|
@@ -37,6 +37,9 @@ exclude = [
|
|
|
37
37
|
"dist",
|
|
38
38
|
"setup.py",
|
|
39
39
|
]
|
|
40
|
+
line-length = 120
|
|
41
|
+
target-version = "py38"
|
|
42
|
+
[tool.ruff.lint]
|
|
40
43
|
ignore = [
|
|
41
44
|
# Do not assign a lambda expression, use a def -> lambda expression assignments are convenient
|
|
42
45
|
"E731",
|
|
@@ -56,7 +59,6 @@ ignore = [
|
|
|
56
59
|
# Missing docstring in magic method
|
|
57
60
|
"D105",
|
|
58
61
|
]
|
|
59
|
-
line-length = 120
|
|
60
62
|
select = [
|
|
61
63
|
"D", # flake8-docstrings
|
|
62
64
|
"I", # isort
|
|
@@ -76,8 +78,9 @@ select = [
|
|
|
76
78
|
"RET", # flake8-raise
|
|
77
79
|
]
|
|
78
80
|
unfixable = ["B", "UP", "C4", "BLE", "T20", "RET"]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
[tool.ruff.lint.pydocstyle]
|
|
82
|
+
convention = "numpy"
|
|
83
|
+
[tool.ruff.lint.per-file-ignores]
|
|
81
84
|
"tests/*" = ["D", "PT", "B024"]
|
|
82
85
|
"*/__init__.py" = ["F401", "D104", "D107"]
|
|
83
86
|
"docs/*" = ["D","B","E","A"]
|
|
@@ -86,5 +89,3 @@ target-version = "py38"
|
|
|
86
89
|
"src/napari_spatialdata/_constants/_utils.py" = ["D101","D102", "B024"]
|
|
87
90
|
"src/napari_spatialdata/_widgets.py" = ["D"]
|
|
88
91
|
"src/napari_spatialdata/_scatterwidgets.py" = ["D"]
|
|
89
|
-
[tool.ruff.pydocstyle]
|
|
90
|
-
convention = "numpy"
|
|
@@ -37,7 +37,7 @@ install_requires =
|
|
|
37
37
|
geopandas
|
|
38
38
|
loguru
|
|
39
39
|
matplotlib
|
|
40
|
-
napari
|
|
40
|
+
napari>=0.4.19
|
|
41
41
|
napari-matplotlib
|
|
42
42
|
numba
|
|
43
43
|
numpy
|
|
@@ -45,6 +45,7 @@ install_requires =
|
|
|
45
45
|
pandas
|
|
46
46
|
pillow
|
|
47
47
|
qtpy
|
|
48
|
+
scanpy
|
|
48
49
|
scipy
|
|
49
50
|
shapely
|
|
50
51
|
scikit-learn
|
|
@@ -77,9 +78,10 @@ doc =
|
|
|
77
78
|
sphinx-qt-documentation
|
|
78
79
|
myst-nb
|
|
79
80
|
squidpy
|
|
80
|
-
scanpy
|
|
81
81
|
readthedocs =
|
|
82
82
|
pydantic<2
|
|
83
|
+
pre =
|
|
84
|
+
spatialdata>=0.1.0-pre0
|
|
83
85
|
all =
|
|
84
86
|
PyQt5
|
|
85
87
|
|
{napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_interactive.py
RENAMED
|
@@ -34,6 +34,19 @@ class Interactive:
|
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
def add_element(self, element: str, element_coordinate_system: str, view_element_system: bool = False) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Add an element of a spatial data object being visualized with interactive to the viewer.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
element
|
|
43
|
+
The name of the element in the spatial data object to add as napari layer.
|
|
44
|
+
element_coordinate_system
|
|
45
|
+
The coordinate system in which the layer should be visualized.
|
|
46
|
+
view_element_system
|
|
47
|
+
Whether to switch to element_coordinate_system or to switch back to current active coordinate system
|
|
48
|
+
after adding an element as layer.
|
|
49
|
+
"""
|
|
37
50
|
duplicate_element_names, _ = get_duplicate_element_names(self._sdata)
|
|
38
51
|
elements, name_to_add = get_elements_meta_mapping(
|
|
39
52
|
self._sdata, element_coordinate_system, duplicate_element_names, element
|
|
@@ -58,6 +71,7 @@ class Interactive:
|
|
|
58
71
|
raise ValueError(f"Element {element} not found in coordinate system {element_coordinate_system}.")
|
|
59
72
|
|
|
60
73
|
def switch_coordinate_system(self, coordinate_system: str) -> None:
|
|
74
|
+
"""Switch to a coordinate system present in the spatialdata object(s)."""
|
|
61
75
|
widget_item = get_itemindex_by_text(self._sdata_widget.coordinate_system_widget, coordinate_system)
|
|
62
76
|
if widget_item:
|
|
63
77
|
self._sdata_widget.coordinate_system_widget.setCurrentItem(widget_item)
|
|
@@ -80,7 +94,9 @@ class Interactive:
|
|
|
80
94
|
self.run()
|
|
81
95
|
|
|
82
96
|
def run(self) -> None:
|
|
97
|
+
"""Run the napari application."""
|
|
83
98
|
napari.run()
|
|
84
99
|
|
|
85
100
|
def screenshot(self) -> NDArrayA | Any:
|
|
101
|
+
"""Take a screenshot of the viewer in its current state."""
|
|
86
102
|
return self._viewer.screenshot(canvas_only=False)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any, Optional, Sequence, Tuple, Union
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas as pd
|
|
@@ -8,65 +8,63 @@ from napari.layers import Layer
|
|
|
8
8
|
from napari.utils.events import EmitterGroup, Event
|
|
9
9
|
|
|
10
10
|
from napari_spatialdata._constants._constants import Symbol
|
|
11
|
-
from napari_spatialdata._constants._pkg_constants import Key
|
|
12
11
|
from napari_spatialdata.utils._utils import NDArrayA, _ensure_dense_vector
|
|
13
12
|
|
|
14
|
-
__all__ = ["
|
|
13
|
+
__all__ = ["DataModel"]
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
@dataclass
|
|
18
|
-
class
|
|
17
|
+
class DataModel:
|
|
19
18
|
"""Model which holds the data for interactive visualization."""
|
|
20
19
|
|
|
21
20
|
events: EmitterGroup = field(init=False, default=None, repr=True)
|
|
21
|
+
_table_names: Sequence[Optional[str]] = field(default_factory=list, init=False)
|
|
22
|
+
_active_table_name: Optional[str] = field(default=None, init=False, repr=True)
|
|
22
23
|
_layer: Layer = field(init=False, default=None, repr=True)
|
|
23
24
|
_adata: Optional[AnnData] = field(init=False, default=None, repr=True)
|
|
24
|
-
_spatial_key: str = field(default=Key.obsm.spatial, repr=False)
|
|
25
25
|
_adata_layer: Optional[str] = field(init=False, default=None, repr=False)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_points_var: Optional[pd.Series] = field(init=False, default=None, repr=True)
|
|
30
|
-
_scale: Optional[float] = field(init=False, default=None)
|
|
26
|
+
_region_key: Optional[str] = field(default=None, repr=True)
|
|
27
|
+
_instance_key: Optional[str] = field(default=None, repr=True)
|
|
28
|
+
_color_by: str = field(default="", repr=True, init=False)
|
|
31
29
|
_system_name: Optional[str] = field(default=None, repr=True)
|
|
32
30
|
|
|
33
|
-
_spot_diameter: Union[NDArrayA, float] = field(init=False, default=1)
|
|
34
|
-
_point_diameter: Union[NDArrayA, float] = field(init=False, default=1)
|
|
35
31
|
_scale_key: Optional[str] = field(init=False, default="tissue_hires_scalef") # TODO(giovp): use constants for these
|
|
36
32
|
|
|
37
33
|
_palette: Optional[str] = field(init=False, default=None, repr=False)
|
|
38
34
|
_cmap: str = field(init=False, default="viridis", repr=False)
|
|
39
35
|
_symbol: str = field(init=False, default=Symbol.DISC, repr=False)
|
|
40
36
|
|
|
41
|
-
VALID_ATTRIBUTES = ("obs", "var", "obsm", "
|
|
37
|
+
VALID_ATTRIBUTES = ("obs", "var", "obsm", "columns_df")
|
|
42
38
|
|
|
43
39
|
def __post_init__(self) -> None:
|
|
44
40
|
self.events = EmitterGroup(
|
|
45
41
|
source=self,
|
|
46
42
|
layer=Event,
|
|
47
43
|
adata=Event,
|
|
44
|
+
color_by=Event,
|
|
48
45
|
)
|
|
49
46
|
|
|
50
|
-
def get_items(self, attr: str) -> Tuple[str, ...]:
|
|
47
|
+
def get_items(self, attr: str) -> Optional[Tuple[str, ...]]:
|
|
51
48
|
"""
|
|
52
49
|
Return valid keys for an attribute.
|
|
53
50
|
|
|
54
51
|
Parameters
|
|
55
52
|
----------
|
|
56
53
|
attr
|
|
57
|
-
Attribute of :mod:`anndata.AnnData` to access
|
|
54
|
+
Attribute of :mod:`anndata.AnnData` to access or if columns_df indicates
|
|
55
|
+
the column is part of the SpatialElement dataframe and this will be retrieved.
|
|
58
56
|
|
|
59
57
|
Returns
|
|
60
58
|
-------
|
|
61
|
-
The available items.
|
|
59
|
+
The available items coerced to a tuple of strings.
|
|
62
60
|
"""
|
|
63
61
|
if attr in ("obs", "obsm"):
|
|
64
62
|
return tuple(map(str, getattr(self.adata, attr).keys()))
|
|
65
|
-
if attr == "
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return tuple(
|
|
69
|
-
return
|
|
63
|
+
if attr == "columns_df" and self.layer is not None and (df_cols := self.layer.metadata.get("_columns_df")):
|
|
64
|
+
return tuple(map(str, df_cols.columns))
|
|
65
|
+
if attr == "var":
|
|
66
|
+
return tuple(map(str, getattr(self.adata, attr).index))
|
|
67
|
+
return None
|
|
70
68
|
|
|
71
69
|
@_ensure_dense_vector
|
|
72
70
|
def get_obs(
|
|
@@ -86,12 +84,35 @@ class ImageModel:
|
|
|
86
84
|
"""
|
|
87
85
|
if name not in self.adata.obs.columns:
|
|
88
86
|
raise KeyError(f"Key `{name}` not found in `adata.obs`.")
|
|
89
|
-
|
|
87
|
+
if name != self.instance_key:
|
|
88
|
+
adata_obs = self.adata.obs[[self.instance_key, name]]
|
|
89
|
+
adata_obs.set_index(self.instance_key, inplace=True)
|
|
90
|
+
else:
|
|
91
|
+
adata_obs = self.adata.obs
|
|
92
|
+
return adata_obs[name], self._format_key(name)
|
|
93
|
+
|
|
94
|
+
@_ensure_dense_vector
|
|
95
|
+
def get_columns_df(self, name: Union[str, int], **_: Any) -> Tuple[Optional[NDArrayA], str]:
|
|
96
|
+
"""
|
|
97
|
+
Return a column of the dataframe of the SpatialElement.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
name
|
|
102
|
+
Name of the column in the dataframe to retrieve.
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
The dataframe column of interest and the formatted name of the column.
|
|
107
|
+
"""
|
|
108
|
+
if self.layer is None:
|
|
109
|
+
raise ValueError("Layer must be present")
|
|
110
|
+
return self.layer.metadata["_columns_df"][name], self._format_key(name)
|
|
90
111
|
|
|
91
112
|
@_ensure_dense_vector
|
|
92
113
|
def get_var(self, name: Union[str, int], **_: Any) -> Tuple[Optional[NDArrayA], str]: # TODO(giovp): fix docstring
|
|
93
114
|
"""
|
|
94
|
-
Return a
|
|
115
|
+
Return a column in anndata.var_names.
|
|
95
116
|
|
|
96
117
|
Parameters
|
|
97
118
|
----------
|
|
@@ -151,25 +172,6 @@ class ImageModel:
|
|
|
151
172
|
|
|
152
173
|
return (res if res.ndim == 1 else res[:, index]), pretty_name
|
|
153
174
|
|
|
154
|
-
def get_points(self, name: str, **_: Any) -> Tuple[Optional[NDArrayA], str]: # TODO(giovp): fix docstring
|
|
155
|
-
"""
|
|
156
|
-
Return a gene in spatial coordinates.
|
|
157
|
-
|
|
158
|
-
Parameters
|
|
159
|
-
----------
|
|
160
|
-
name
|
|
161
|
-
Gene name.
|
|
162
|
-
|
|
163
|
-
Returns
|
|
164
|
-
-------
|
|
165
|
-
The values and the formatted ``name``.
|
|
166
|
-
"""
|
|
167
|
-
if name not in self.points_var.unique():
|
|
168
|
-
raise KeyError(f"Key `{name}` not found in `adata.uns['points']['gene']`.")
|
|
169
|
-
coords = self.points_coordinates
|
|
170
|
-
coords = coords[self.points_var == name]
|
|
171
|
-
return np.insert(coords[:, ::-1][:, :2] * self.scale, 0, values=0, axis=1), self._format_key(name)
|
|
172
|
-
|
|
173
175
|
def _format_key(
|
|
174
176
|
self, key: Union[str, int], index: Optional[Union[int, str]] = None, adata_layer: bool = False
|
|
175
177
|
) -> str:
|
|
@@ -180,8 +182,28 @@ class ImageModel:
|
|
|
180
182
|
|
|
181
183
|
return str(key) + (f":{self.layer}" if self.layer is not None else ":X")
|
|
182
184
|
|
|
185
|
+
@property
|
|
186
|
+
def color_by(self) -> str:
|
|
187
|
+
"""The name by which the layer is currently colored."""
|
|
188
|
+
return self._color_by
|
|
189
|
+
|
|
190
|
+
@color_by.setter
|
|
191
|
+
def color_by(self, color_column: str) -> None:
|
|
192
|
+
self._color_by = color_column
|
|
193
|
+
self.events.color_by()
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def table_names(self) -> Sequence[Optional[str]]:
|
|
197
|
+
"""The table names annotating the current active napari layer, if any."""
|
|
198
|
+
return self._table_names
|
|
199
|
+
|
|
200
|
+
@table_names.setter
|
|
201
|
+
def table_names(self, table_names: Sequence[Optional[str]]) -> None:
|
|
202
|
+
self._table_names = table_names
|
|
203
|
+
|
|
183
204
|
@property
|
|
184
205
|
def layer(self) -> Optional[Layer]: # noqa: D102
|
|
206
|
+
"""The current active napari layer."""
|
|
185
207
|
return self._layer
|
|
186
208
|
|
|
187
209
|
@layer.setter
|
|
@@ -189,8 +211,18 @@ class ImageModel:
|
|
|
189
211
|
self._layer = layer
|
|
190
212
|
self.events.layer()
|
|
191
213
|
|
|
214
|
+
@property
|
|
215
|
+
def active_table_name(self) -> Optional[str]:
|
|
216
|
+
"""The table name currently active in the widget."""
|
|
217
|
+
return self._active_table_name
|
|
218
|
+
|
|
219
|
+
@active_table_name.setter
|
|
220
|
+
def active_table_name(self, active_table_name: Optional[str]) -> None:
|
|
221
|
+
self._active_table_name = active_table_name
|
|
222
|
+
|
|
192
223
|
@property
|
|
193
224
|
def adata(self) -> AnnData: # noqa: D102
|
|
225
|
+
"""The anndata object corresponding to the current active table name."""
|
|
194
226
|
return self._adata
|
|
195
227
|
|
|
196
228
|
@adata.setter
|
|
@@ -198,16 +230,9 @@ class ImageModel:
|
|
|
198
230
|
self._adata = adata
|
|
199
231
|
self.events.adata()
|
|
200
232
|
|
|
201
|
-
@property
|
|
202
|
-
def spatial_key(self) -> str: # noqa: D102
|
|
203
|
-
return self._spatial_key
|
|
204
|
-
|
|
205
|
-
@spatial_key.setter
|
|
206
|
-
def spatial_key(self, key: str) -> None:
|
|
207
|
-
self._spatial_key = key
|
|
208
|
-
|
|
209
233
|
@property
|
|
210
234
|
def adata_layer(self) -> Optional[str]: # noqa: D102
|
|
235
|
+
"""The current anndata layer."""
|
|
211
236
|
return self._adata_layer
|
|
212
237
|
|
|
213
238
|
@adata_layer.setter
|
|
@@ -215,65 +240,26 @@ class ImageModel:
|
|
|
215
240
|
self._adata_layer = adata_layer
|
|
216
241
|
|
|
217
242
|
@property
|
|
218
|
-
def
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
@coordinates.setter
|
|
222
|
-
def coordinates(self, coordinates: NDArrayA) -> None:
|
|
223
|
-
self._coordinates = coordinates
|
|
224
|
-
|
|
225
|
-
@property
|
|
226
|
-
def points_coordinates(self) -> NDArrayA: # noqa: D102
|
|
227
|
-
if TYPE_CHECKING:
|
|
228
|
-
assert self._points_coordinates is not None
|
|
229
|
-
return self._points_coordinates
|
|
230
|
-
|
|
231
|
-
@points_coordinates.setter
|
|
232
|
-
def points_coordinates(self, points_coordinates: NDArrayA) -> None:
|
|
233
|
-
self._points_coordinates = points_coordinates
|
|
234
|
-
|
|
235
|
-
@property
|
|
236
|
-
def points_var(self) -> pd.Series: # noqa: D102
|
|
237
|
-
return self._points_var
|
|
238
|
-
|
|
239
|
-
@points_var.setter
|
|
240
|
-
def points_var(self, points_var: pd.Series) -> None:
|
|
241
|
-
self._points_var = points_var
|
|
242
|
-
|
|
243
|
-
@property
|
|
244
|
-
def scale(self) -> Optional[float]: # noqa: D102
|
|
245
|
-
return self._scale
|
|
246
|
-
|
|
247
|
-
@scale.setter
|
|
248
|
-
def scale(self, scale: float) -> None:
|
|
249
|
-
self._scale = scale
|
|
250
|
-
|
|
251
|
-
@property
|
|
252
|
-
def spot_diameter(self) -> Union[NDArrayA, float]: # noqa: D102
|
|
253
|
-
return self._spot_diameter
|
|
254
|
-
|
|
255
|
-
@spot_diameter.setter
|
|
256
|
-
def spot_diameter(self, spot_diameter: Union[NDArrayA, float]) -> None:
|
|
257
|
-
self._spot_diameter = spot_diameter
|
|
258
|
-
|
|
259
|
-
@property
|
|
260
|
-
def point_diameter(self) -> Union[NDArrayA, float]: # noqa: D102
|
|
261
|
-
return self._point_diameter
|
|
243
|
+
def region_key(self) -> Optional[str]: # noqa: D102
|
|
244
|
+
"""The region key of the currently active table in the widget."""
|
|
245
|
+
return self._region_key
|
|
262
246
|
|
|
263
|
-
@
|
|
264
|
-
def
|
|
265
|
-
self.
|
|
247
|
+
@region_key.setter
|
|
248
|
+
def region_key(self, region_key: str) -> None:
|
|
249
|
+
self._region_key = region_key
|
|
266
250
|
|
|
267
251
|
@property
|
|
268
|
-
def
|
|
269
|
-
|
|
252
|
+
def instance_key(self) -> Optional[str]: # noqa: D102
|
|
253
|
+
"""The instance key of the currently active table in the widget."""
|
|
254
|
+
return self._instance_key
|
|
270
255
|
|
|
271
|
-
@
|
|
272
|
-
def
|
|
273
|
-
self.
|
|
256
|
+
@instance_key.setter
|
|
257
|
+
def instance_key(self, instance_key: str) -> None:
|
|
258
|
+
self._instance_key = instance_key
|
|
274
259
|
|
|
275
260
|
@property
|
|
276
261
|
def palette(self) -> Optional[str]: # noqa: D102
|
|
262
|
+
"""The palette from which to draw the colors."""
|
|
277
263
|
return self._palette
|
|
278
264
|
|
|
279
265
|
@palette.setter
|
|
@@ -282,6 +268,7 @@ class ImageModel:
|
|
|
282
268
|
|
|
283
269
|
@property
|
|
284
270
|
def cmap(self) -> str: # noqa: D102
|
|
271
|
+
"""The continuous color map to draw colors from."""
|
|
285
272
|
return self._cmap
|
|
286
273
|
|
|
287
274
|
@cmap.setter
|
|
@@ -306,6 +293,7 @@ class ImageModel:
|
|
|
306
293
|
|
|
307
294
|
@property
|
|
308
295
|
def system_name(self) -> Optional[str]: # noqa: D102
|
|
296
|
+
"""The layer name."""
|
|
309
297
|
return self._system_name
|
|
310
298
|
|
|
311
299
|
@system_name.setter
|
{napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_scatterwidgets.py
RENAMED
|
@@ -14,11 +14,11 @@ from matplotlib.widgets import LassoSelector
|
|
|
14
14
|
from napari.layers import Layer
|
|
15
15
|
from napari.viewer import Viewer
|
|
16
16
|
from napari_matplotlib.base import NapariMPLWidget
|
|
17
|
-
from pandas.api.types import
|
|
17
|
+
from pandas.api.types import CategoricalDtype
|
|
18
18
|
from qtpy import QtWidgets
|
|
19
19
|
from qtpy.QtCore import Signal
|
|
20
20
|
|
|
21
|
-
from napari_spatialdata._model import
|
|
21
|
+
from napari_spatialdata._model import DataModel
|
|
22
22
|
from napari_spatialdata._widgets import AListWidget, ComponentWidget
|
|
23
23
|
from napari_spatialdata.utils._categoricals_utils import _add_categorical_legend
|
|
24
24
|
from napari_spatialdata.utils._utils import NDArrayA, _get_categorical, _set_palette
|
|
@@ -57,7 +57,7 @@ class SelectFromCollection:
|
|
|
57
57
|
|
|
58
58
|
def __init__(
|
|
59
59
|
self,
|
|
60
|
-
model:
|
|
60
|
+
model: DataModel,
|
|
61
61
|
ax: Axes,
|
|
62
62
|
collection: Collection,
|
|
63
63
|
data: list[NDArrayA],
|
|
@@ -113,7 +113,7 @@ class ScatterListWidget(AListWidget):
|
|
|
113
113
|
_text = None
|
|
114
114
|
_chosen = None
|
|
115
115
|
|
|
116
|
-
def __init__(self, model:
|
|
116
|
+
def __init__(self, model: DataModel, attr: str, color: bool, **kwargs: Any):
|
|
117
117
|
AListWidget.__init__(self, None, model, attr, **kwargs)
|
|
118
118
|
self.attrChanged.connect(self._onChange)
|
|
119
119
|
self._color = color
|
|
@@ -136,7 +136,7 @@ class ScatterListWidget(AListWidget):
|
|
|
136
136
|
self.chosen = item
|
|
137
137
|
if isinstance(vec, np.ndarray):
|
|
138
138
|
self.data = vec
|
|
139
|
-
elif
|
|
139
|
+
elif vec is not None and isinstance(vec.dtype, CategoricalDtype):
|
|
140
140
|
self.data = vec
|
|
141
141
|
colortypes = _set_palette(self.model.adata, key=item, palette=self.model.palette, vec=vec)
|
|
142
142
|
if self._color:
|
|
@@ -209,7 +209,7 @@ class ScatterListWidget(AListWidget):
|
|
|
209
209
|
|
|
210
210
|
|
|
211
211
|
class MatplotlibWidget(NapariMPLWidget):
|
|
212
|
-
def __init__(self, viewer: Viewer | None, model:
|
|
212
|
+
def __init__(self, viewer: Viewer | None, model: DataModel):
|
|
213
213
|
self.is_widget = False
|
|
214
214
|
if viewer is None:
|
|
215
215
|
viewer = Viewer()
|
|
@@ -291,7 +291,7 @@ class MatplotlibWidget(NapariMPLWidget):
|
|
|
291
291
|
|
|
292
292
|
|
|
293
293
|
class AxisWidgets(QtWidgets.QWidget):
|
|
294
|
-
def __init__(self, model:
|
|
294
|
+
def __init__(self, model: DataModel, name: str, color: bool = False):
|
|
295
295
|
super().__init__()
|
|
296
296
|
|
|
297
297
|
self._model = model
|
|
@@ -346,6 +346,6 @@ class AxisWidgets(QtWidgets.QWidget):
|
|
|
346
346
|
self.component_widget.clear()
|
|
347
347
|
|
|
348
348
|
@property
|
|
349
|
-
def model(self) ->
|
|
349
|
+
def model(self) -> DataModel:
|
|
350
350
|
""":mod:`napari` viewer."""
|
|
351
351
|
return self._model
|
{napari-spatialdata-0.3.1 → napari-spatialdata-0.4.0}/src/napari_spatialdata/_sdata_widgets.py
RENAMED
|
@@ -113,7 +113,9 @@ class SdataWidget(QWidget):
|
|
|
113
113
|
for layer in self.viewer_model.viewer.layers:
|
|
114
114
|
element_name = layer.metadata.get("name")
|
|
115
115
|
if element_name:
|
|
116
|
-
if elements and
|
|
116
|
+
if elements and (
|
|
117
|
+
layer.name not in elements or element_name != elements[layer.name]["original_name"]
|
|
118
|
+
):
|
|
117
119
|
layer.visible = False
|
|
118
120
|
elif layer.metadata["_active_in_cs"]:
|
|
119
121
|
layer.visible = True
|
|
@@ -124,10 +126,10 @@ class SdataWidget(QWidget):
|
|
|
124
126
|
def _add_shapes(self, sdata: SpatialData, key: str, selected_cs: str, multi: bool) -> None:
|
|
125
127
|
original_name = key[: key.rfind("_")] if multi else key
|
|
126
128
|
|
|
127
|
-
if type(sdata.shapes[original_name].iloc[0]
|
|
129
|
+
if type(sdata.shapes[original_name].iloc[0].geometry) == shapely.geometry.point.Point:
|
|
128
130
|
self.viewer_model.add_sdata_circles(sdata, key, selected_cs, multi)
|
|
129
|
-
elif (type(sdata.shapes[original_name].iloc[0]
|
|
130
|
-
type(sdata.shapes[original_name].iloc[0]
|
|
131
|
+
elif (type(sdata.shapes[original_name].iloc[0].geometry) == shapely.geometry.polygon.Polygon) or (
|
|
132
|
+
type(sdata.shapes[original_name].iloc[0].geometry) == shapely.geometry.multipolygon.MultiPolygon
|
|
131
133
|
):
|
|
132
134
|
self.viewer_model.add_sdata_shapes(sdata, key, selected_cs, multi)
|
|
133
135
|
else:
|