Skip to content

Commit bc7b353

Browse files
authored
Plugin for SpatialQuery (#354)
* Spatial-Query plugin and example notebook * Version * Widget plugins module * Update * Linting * Lint * Update comments * Update docs * Update
1 parent c88eb37 commit bc7b353

File tree

8 files changed

+512
-30
lines changed

8 files changed

+512
-30
lines changed

.coveragerc_omit

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ omit =
1111
vitessce/data_utils/ome.py
1212
vitessce/data_utils/entities.py
1313
vitessce/data_utils/multivec.py
14-
vitessce/widget_plugins/demo_plugin.py
14+
vitessce/widget_plugins/demo_plugin.py
15+
vitessce/widget_plugins/spatial_query.py
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {
7+
"tags": []
8+
},
9+
"outputs": [],
10+
"source": [
11+
"#!pip install \"vitessce[all]==3.3.0\" esbuild_py anndata\n",
12+
"!pip install \"mlxtend~=0.23.0\"\n",
13+
"#!pip install -i \"https://test.pypi.org/simple/\" SpatialQuery\n",
14+
"!pip install \"SpatialQuery @ git+https://github.com/ShaokunAn/Spatial-Query@main\""
15+
]
16+
},
17+
{
18+
"cell_type": "code",
19+
"execution_count": 1,
20+
"metadata": {},
21+
"outputs": [],
22+
"source": [
23+
"from os.path import join\n",
24+
"from anndata import read_h5ad\n",
25+
"from vitessce import (\n",
26+
" VitessceConfig,\n",
27+
" AnnDataWrapper,\n",
28+
" ViewType as vt,\n",
29+
" CoordinationType as ct,\n",
30+
" CoordinationLevel as CL,\n",
31+
")\n",
32+
"from vitessce.widget_plugins import SpatialQueryPlugin"
33+
]
34+
},
35+
{
36+
"cell_type": "code",
37+
"execution_count": 2,
38+
"metadata": {},
39+
"outputs": [],
40+
"source": [
41+
"adata = read_h5ad(join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad\"))\n",
42+
"zarr_path = join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad.zarr\")\n",
43+
"adata.write_zarr(zarr_path)"
44+
]
45+
},
46+
{
47+
"cell_type": "code",
48+
"execution_count": 3,
49+
"metadata": {},
50+
"outputs": [],
51+
"source": [
52+
"plugin = SpatialQueryPlugin(adata)"
53+
]
54+
},
55+
{
56+
"cell_type": "code",
57+
"execution_count": 12,
58+
"metadata": {},
59+
"outputs": [],
60+
"source": [
61+
"vc = VitessceConfig(schema_version=\"1.0.16\", name=\"Spatial-Query\")\n",
62+
"dataset = vc.add_dataset(\"Query results\").add_object(AnnDataWrapper(\n",
63+
" adata_path=zarr_path,\n",
64+
" obs_feature_matrix_path=\"X\",\n",
65+
" obs_set_paths=[\"obs/predicted.ASCT.celltype\"],\n",
66+
" obs_set_names=[\"Cell Type\"],\n",
67+
" obs_spots_path=\"obsm/X_spatial\",\n",
68+
" feature_labels_path=\"var/hugo_symbol\",\n",
69+
" coordination_values={\n",
70+
" \"featureLabelsType\": \"Gene symbol\",\n",
71+
" }\n",
72+
"))\n",
73+
"\n",
74+
"spatial_view = vc.add_view(\"spatialBeta\", dataset=dataset)\n",
75+
"lc_view = vc.add_view(\"layerControllerBeta\", dataset=dataset)\n",
76+
"sets_view = vc.add_view(\"obsSets\", dataset=dataset)\n",
77+
"features_view = vc.add_view(\"featureList\", dataset=dataset)\n",
78+
"sq_view = vc.add_view(\"spatialQuery\", dataset=dataset)\n",
79+
"\n",
80+
"obs_set_selection_scope, = vc.add_coordination(\"obsSetSelection\",)\n",
81+
"obs_set_selection_scope.set_value(None)\n",
82+
"\n",
83+
"sets_view.use_coordination(obs_set_selection_scope)\n",
84+
"sq_view.use_coordination(obs_set_selection_scope)\n",
85+
"spatial_view.use_coordination(obs_set_selection_scope)\n",
86+
"features_view.use_coordination(obs_set_selection_scope)\n",
87+
"\n",
88+
"vc.link_views([spatial_view, lc_view, sets_view, features_view],\n",
89+
" [\"additionalObsSets\", \"obsSetColor\"],\n",
90+
" [plugin.additional_obs_sets, plugin.obs_set_color]\n",
91+
")\n",
92+
"vc.link_views_by_dict([spatial_view, lc_view], {\n",
93+
" \"spotLayer\": CL([\n",
94+
" {\n",
95+
" \"obsType\": \"cell\",\n",
96+
" \"spatialSpotRadius\": 15,\n",
97+
" },\n",
98+
" ])\n",
99+
"})\n",
100+
"\n",
101+
"vc.layout((spatial_view | (lc_view / features_view)) / (sets_view | sq_view));"
102+
]
103+
},
104+
{
105+
"cell_type": "code",
106+
"execution_count": 13,
107+
"metadata": {},
108+
"outputs": [
109+
{
110+
"name": "stderr",
111+
"output_type": "stream",
112+
"text": [
113+
"/Users/mkeller/software/miniconda3/envs/vitessce-python-notebooks/lib/python3.9/site-packages/traitlets/traitlets.py:869: DeprecationWarning: Deprecated in traitlets 4.1, use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)\n",
114+
" warn(\"Deprecated in traitlets 4.1, \" + msg, DeprecationWarning, stacklevel=2)\n",
115+
"/Users/mkeller/software/miniconda3/envs/vitessce-python-notebooks/lib/python3.9/site-packages/traitlets/traitlets.py:869: DeprecationWarning: Deprecated in traitlets 4.1, use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)\n",
116+
" warn(\"Deprecated in traitlets 4.1, \" + msg, DeprecationWarning, stacklevel=2)\n"
117+
]
118+
},
119+
{
120+
"data": {
121+
"application/vnd.jupyter.widget-view+json": {
122+
"model_id": "6f30fb3eac1644478df256bf79a986e9",
123+
"version_major": 2,
124+
"version_minor": 1
125+
},
126+
"text/plain": [
127+
"VitessceWidget(config={'version': '1.0.16', 'name': 'Spatial-Query', 'description': '', 'datasets': [{'uid': '…"
128+
]
129+
},
130+
"execution_count": 13,
131+
"metadata": {},
132+
"output_type": "execute_result"
133+
}
134+
],
135+
"source": [
136+
"vw = vc.widget(height=900, plugins=[plugin], remount_on_uid_change=False)\n",
137+
"vw"
138+
]
139+
},
140+
{
141+
"cell_type": "code",
142+
"execution_count": null,
143+
"metadata": {},
144+
"outputs": [],
145+
"source": []
146+
}
147+
],
148+
"metadata": {
149+
"kernelspec": {
150+
"display_name": "Python 3 (ipykernel)",
151+
"language": "python",
152+
"name": "python3"
153+
},
154+
"language_info": {
155+
"codemirror_mode": {
156+
"name": "ipython",
157+
"version": 3
158+
},
159+
"file_extension": ".py",
160+
"mimetype": "text/x-python",
161+
"name": "python",
162+
"nbconvert_exporter": "python",
163+
"pygments_lexer": "ipython3",
164+
"version": "3.9.0"
165+
}
166+
},
167+
"nbformat": 4,
168+
"nbformat_minor": 4
169+
}

docs/widget_plugins.rst

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,21 @@ If defined, these plugin arrays are passed to the Vitessce component as `props <
4747
Passing plugin ESM to the widget
4848
--------------------------------
4949

50-
The plugin string can be passed to the widget using the ``plugin_esm`` parameter:
50+
The plugin string can be passed to the widget using the ``plugins`` parameter and passing a subclass of ``VitesscePlugin``:
5151

5252

5353
.. code-block:: python
5454
55-
from vitessce import VitessceConfig
55+
from vitessce import VitessceConfig, VitesscePlugin
5656
57-
vc = VitessceConfig(
58-
description="A Vitessce widget with a custom plugin",
59-
widget=[
60-
{
61-
"plugin": PLUGIN_ESM,
62-
},
63-
],
64-
)
57+
class MyPlugin(VitesscePlugin):
58+
plugin_esm = PLUGIN_ESM
59+
60+
vc = VitessceConfig(description="A Vitessce widget with a custom plugin")
6561
# Some more configuration here...
6662
67-
vc.widget(plugin_esm=PLUGIN_ESM)
63+
plugin = MyPlugin()
64+
vc.widget(plugins=[plugin])
6865
6966
7067
-------------------------------
@@ -149,4 +146,7 @@ vitessce.widget_plugins
149146
***********************
150147

151148
.. automodule:: vitessce.widget_plugins.demo_plugin
149+
:members:
150+
151+
.. automodule:: vitessce.widget_plugins.spatial_query
152152
:members:

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "vitessce"
7-
version = "3.3.0"
7+
version = "3.3.1"
88
authors = [
99
{ name="Mark Keller", email="[email protected]" },
1010
]
@@ -96,4 +96,4 @@ notebook = []
9696
repository = "https://github.com/vitessce/vitessce-python"
9797

9898
[tool.setuptools]
99-
packages = ["vitessce", "vitessce.data_utils"]
99+
packages = ["vitessce", "vitessce.data_utils", "vitessce.widget_plugins"]

vitessce/widget.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,14 +393,23 @@ def get_uid_str(uid):
393393
export default { createPlugins };
394394
"""
395395

396-
# Abstract class for widget plugins to subclass
397-
398396

399397
class VitesscePlugin:
398+
"""
399+
A class that represents a Vitessce widget plugin. Custom plugins can be created by subclassing this class.
400+
"""
400401
plugin_esm = DEFAULT_PLUGIN_ESM
401402
commands = {}
402403

403404
def on_config_change(self, new_config):
405+
"""
406+
Config change handler.
407+
408+
:param dict new_config: The new config object.
409+
410+
:returns: config (likely with new "uid" property) or None
411+
:rtype: dict or None
412+
"""
404413
raise NotImplementedError("on_config_change may optionally be implemented by subclasses.")
405414

406415

@@ -444,7 +453,7 @@ def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=
444453
:param str js_package_version: The version of the NPM package ('vitessce' if not js_dev_mode else '@vitessce/dev').
445454
:param bool js_dev_mode: Should @vitessce/dev be used (typically for debugging purposes)? By default, False.
446455
:param str custom_js_url: A URL to a JavaScript file to use (instead of 'vitessce' or '@vitessce/dev' NPM package).
447-
:param list[WidgetPlugin] plugins: A list of subclasses of WidgetPlugin, defining plugin_esm (string) and/or on_config_change (function). Optional.
456+
:param list[WidgetPlugin] plugins: A list of subclasses of VitesscePlugin. Optional.
448457
:param bool remount_on_uid_change: Passed to the remountOnUidChange prop of the <Vitessce/> React component. By default, True.
449458
450459
.. code-block:: python
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .demo_plugin import DemoPlugin
2+
from .spatial_query import SpatialQueryPlugin

vitessce/widget_plugins/demo_plugin.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,18 @@ def handle_demo_command(message, buffers):
4848
return message.upper(), []
4949

5050

51-
"""
52-
Example of a minimal plugin view that gets the obsType coordination value from the coordination space and renders a button.
53-
This plugin view is not meant to be useful for end-users, but rather to demonstrate how to develop a plugin view that uses coordination (and uses eslint_py for JSX transformation).
54-
55-
:meta hide-value:
56-
57-
.. code-block:: python
58-
59-
from vitessce.widget_plugins import demo_plugin_esm
51+
class DemoPlugin(VitesscePlugin):
52+
"""
53+
Example of a minimal plugin view that gets the obsType coordination value from the coordination space and renders a button.
54+
This plugin view is not meant to be useful for end-users, but rather to demonstrate how to develop a plugin view that uses coordination (and uses eslint_py for JSX transformation).
6055
61-
# ...
62-
vc.widget(plugin_esm=demo_plugin_esm)
63-
"""
56+
.. code-block:: python
6457
58+
from vitessce.widget_plugins import DemoPlugin
6559
66-
class DemoPlugin(VitesscePlugin):
60+
# ...
61+
vc.widget(plugins=[DemoPlugin()])
62+
"""
6763
plugin_esm = PLUGIN_ESM
6864
commands = {
6965
"demo_command": handle_demo_command,

0 commit comments

Comments
 (0)