ueler-viewer 0.5.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 (69) hide show
  1. ueler_viewer-0.5.0/LICENSE.txt +30 -0
  2. ueler_viewer-0.5.0/MANIFEST.in +38 -0
  3. ueler_viewer-0.5.0/PKG-INFO +260 -0
  4. ueler_viewer-0.5.0/README.md +201 -0
  5. ueler_viewer-0.5.0/pyproject.toml +106 -0
  6. ueler_viewer-0.5.0/setup.cfg +4 -0
  7. ueler_viewer-0.5.0/ueler/__init__.py +56 -0
  8. ueler_viewer-0.5.0/ueler/bia_loader.py +753 -0
  9. ueler_viewer-0.5.0/ueler/cell_table.py +557 -0
  10. ueler_viewer-0.5.0/ueler/constants.py +26 -0
  11. ueler_viewer-0.5.0/ueler/data_loader.py +763 -0
  12. ueler_viewer-0.5.0/ueler/export/__init__.py +11 -0
  13. ueler_viewer-0.5.0/ueler/export/job.py +282 -0
  14. ueler_viewer-0.5.0/ueler/image_utils.py +285 -0
  15. ueler_viewer-0.5.0/ueler/rendering/__init__.py +37 -0
  16. ueler_viewer-0.5.0/ueler/rendering/engine.py +768 -0
  17. ueler_viewer-0.5.0/ueler/runner.py +414 -0
  18. ueler_viewer-0.5.0/ueler/viewer/__init__.py +45 -0
  19. ueler_viewer-0.5.0/ueler/viewer/annotation_display.py +110 -0
  20. ueler_viewer-0.5.0/ueler/viewer/annotation_palette_editor.py +113 -0
  21. ueler_viewer-0.5.0/ueler/viewer/channel_grid_view.py +507 -0
  22. ueler_viewer-0.5.0/ueler/viewer/checkpoint_store.py +193 -0
  23. ueler_viewer-0.5.0/ueler/viewer/color_palettes.py +81 -0
  24. ueler_viewer-0.5.0/ueler/viewer/decorators.py +43 -0
  25. ueler_viewer-0.5.0/ueler/viewer/image_display.py +1314 -0
  26. ueler_viewer-0.5.0/ueler/viewer/images/__init__.py +48 -0
  27. ueler_viewer-0.5.0/ueler/viewer/images/loading.gif +0 -0
  28. ueler_viewer-0.5.0/ueler/viewer/images/ready.png +0 -0
  29. ueler_viewer-0.5.0/ueler/viewer/interfaces.py +32 -0
  30. ueler_viewer-0.5.0/ueler/viewer/layout_utils.py +114 -0
  31. ueler_viewer-0.5.0/ueler/viewer/log_console.py +122 -0
  32. ueler_viewer-0.5.0/ueler/viewer/main_viewer.py +5697 -0
  33. ueler_viewer-0.5.0/ueler/viewer/map_descriptor_loader.py +229 -0
  34. ueler_viewer-0.5.0/ueler/viewer/mask_color_overlay.py +447 -0
  35. ueler_viewer-0.5.0/ueler/viewer/observable.py +34 -0
  36. ueler_viewer-0.5.0/ueler/viewer/palette_store.py +88 -0
  37. ueler_viewer-0.5.0/ueler/viewer/plugin/__init__.py +409 -0
  38. ueler_viewer-0.5.0/ueler/viewer/plugin/_chart_common.py +428 -0
  39. ueler_viewer-0.5.0/ueler/viewer/plugin/cell_annotation.py +545 -0
  40. ueler_viewer-0.5.0/ueler/viewer/plugin/cell_gallery.py +961 -0
  41. ueler_viewer-0.5.0/ueler/viewer/plugin/cell_table_editor.py +206 -0
  42. ueler_viewer-0.5.0/ueler/viewer/plugin/channel_picker_widget.py +860 -0
  43. ueler_viewer-0.5.0/ueler/viewer/plugin/chart.py +880 -0
  44. ueler_viewer-0.5.0/ueler/viewer/plugin/export_fovs.py +2950 -0
  45. ueler_viewer-0.5.0/ueler/viewer/plugin/go_to.py +143 -0
  46. ueler_viewer-0.5.0/ueler/viewer/plugin/heatmap.py +359 -0
  47. ueler_viewer-0.5.0/ueler/viewer/plugin/heatmap_adapter.py +101 -0
  48. ueler_viewer-0.5.0/ueler/viewer/plugin/heatmap_layers.py +2152 -0
  49. ueler_viewer-0.5.0/ueler/viewer/plugin/histogram.py +1115 -0
  50. ueler_viewer-0.5.0/ueler/viewer/plugin/mask_class_list_widget.py +509 -0
  51. ueler_viewer-0.5.0/ueler/viewer/plugin/mask_painter.py +2815 -0
  52. ueler_viewer-0.5.0/ueler/viewer/plugin/plugin_base.py +191 -0
  53. ueler_viewer-0.5.0/ueler/viewer/plugin/roi_expression_editor.py +247 -0
  54. ueler_viewer-0.5.0/ueler/viewer/plugin/roi_manager_plugin.py +2752 -0
  55. ueler_viewer-0.5.0/ueler/viewer/plugin/run_flowsom.py +329 -0
  56. ueler_viewer-0.5.0/ueler/viewer/plugin/scatter_widget.py +335 -0
  57. ueler_viewer-0.5.0/ueler/viewer/plugin/tile_gallery_widget.py +276 -0
  58. ueler_viewer-0.5.0/ueler/viewer/rendering.py +35 -0
  59. ueler_viewer-0.5.0/ueler/viewer/roi_manager.py +489 -0
  60. ueler_viewer-0.5.0/ueler/viewer/scale_bar.py +172 -0
  61. ueler_viewer-0.5.0/ueler/viewer/tag_expression.py +251 -0
  62. ueler_viewer-0.5.0/ueler/viewer/tooltip_utils.py +106 -0
  63. ueler_viewer-0.5.0/ueler/viewer/ui_components.py +957 -0
  64. ueler_viewer-0.5.0/ueler/viewer/virtual_map_layer.py +492 -0
  65. ueler_viewer-0.5.0/ueler_viewer.egg-info/PKG-INFO +260 -0
  66. ueler_viewer-0.5.0/ueler_viewer.egg-info/SOURCES.txt +67 -0
  67. ueler_viewer-0.5.0/ueler_viewer.egg-info/dependency_links.txt +1 -0
  68. ueler_viewer-0.5.0/ueler_viewer.egg-info/requires.txt +35 -0
  69. ueler_viewer-0.5.0/ueler_viewer.egg-info/top_level.txt +1 -0
@@ -0,0 +1,30 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025-2026, Yu-Le Wu, Hartmann Lab,
4
+ German Cancer Research Center (DKFZ), Heidelberg, Germany.
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation
15
+ and/or other materials provided with the distribution.
16
+
17
+ 3. Neither the name of the copyright holder nor the names of its
18
+ contributors may be used to endorse or promote products derived from
19
+ this software without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,38 @@
1
+ # Controls the sdist only; the wheel is driven by [tool.setuptools] in
2
+ # pyproject.toml. An sdist is a build input, so it must carry everything needed
3
+ # to rebuild the wheel — and nothing else.
4
+
5
+ include LICENSE.txt
6
+ include README.md
7
+ include pyproject.toml
8
+
9
+ # Packaged runtime assets (also declared as package-data for the wheel).
10
+ recursive-include ueler/viewer/images *
11
+
12
+ # Development scaffolding that is not needed to build or run the package.
13
+ prune tests
14
+ prune tests_tmp
15
+ prune dev_note
16
+ prune doc
17
+ prune docs
18
+ prune script
19
+ prune tools
20
+ prune site
21
+ prune env
22
+ prune .binder
23
+ prune .github
24
+ prune .claude
25
+ prune .specify
26
+ prune specs
27
+ prune graphify-out
28
+ prune ueler/graphify-out
29
+
30
+ exclude Makefile
31
+ exclude mkdocs.yml
32
+ exclude CLAUDE.md
33
+ exclude sitecustomize.py
34
+ exclude usercustomize.py
35
+
36
+ global-exclude *.py[cod]
37
+ global-exclude __pycache__/*
38
+ global-exclude .DS_Store
@@ -0,0 +1,260 @@
1
+ Metadata-Version: 2.4
2
+ Name: ueler-viewer
3
+ Version: 0.5.0
4
+ Summary: Unified Exploratory Linked Viewer for spatial proteomics and multiplexed imaging
5
+ Author: Yu-Le Wu (Hartmann Lab, DKFZ)
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/HartmannLab/UELer
8
+ Project-URL: Documentation, https://hartmannlab.github.io/UELer/
9
+ Project-URL: Repository, https://github.com/HartmannLab/UELer
10
+ Project-URL: Issues, https://github.com/HartmannLab/UELer/issues
11
+ Project-URL: Changelog, https://github.com/HartmannLab/UELer/blob/main/doc/log.md
12
+ Keywords: spatial proteomics,multiplexed imaging,image viewer,jupyter,MIBI,IMC,bioimage analysis
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Framework :: Jupyter
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
22
+ Classifier: Topic :: Scientific/Engineering :: Visualization
23
+ Requires-Python: <3.13,>=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE.txt
26
+ Requires-Dist: scikit-image>=0.19
27
+ Requires-Dist: dask
28
+ Requires-Dist: dask-image
29
+ Requires-Dist: ipydatagrid
30
+ Requires-Dist: bqplot
31
+ Requires-Dist: seaborn
32
+ Requires-Dist: seaborn-image
33
+ Requires-Dist: opencv-python-headless
34
+ Requires-Dist: watchfiles
35
+ Requires-Dist: jupyter-scatter
36
+ Requires-Dist: ipywidgets>=8.1
37
+ Requires-Dist: anywidget>=0.9
38
+ Requires-Dist: numpy
39
+ Requires-Dist: pandas>=2.0
40
+ Requires-Dist: ipykernel
41
+ Requires-Dist: ipympl
42
+ Requires-Dist: anndata>=0.10
43
+ Requires-Dist: tifffile
44
+ Requires-Dist: fsspec[http]
45
+ Requires-Dist: requests
46
+ Requires-Dist: bokeh>=3.0
47
+ Requires-Dist: jupyter_bokeh>=4.0
48
+ Provides-Extra: ark
49
+ Requires-Dist: ark-analysis==0.7.0; extra == "ark"
50
+ Provides-Extra: dev
51
+ Requires-Dist: pytest>=7.0; extra == "dev"
52
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
53
+ Requires-Dist: build>=1.0; extra == "dev"
54
+ Requires-Dist: twine>=5.0; extra == "dev"
55
+ Provides-Extra: docs
56
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
57
+ Requires-Dist: mike>=2.1; extra == "docs"
58
+ Dynamic: license-file
59
+
60
+ # UELer
61
+ Unified Exploratory Linked Viewer: a Jupyter-based framework for interactive exploration of multiplexed imaging datasets.
62
+
63
+ ## Try it on Binder
64
+ You can try UELer without installation by launching it on [Binder](https://mybinder.org/v2/gh/HartmannLab/UELer/main?urlpath=%2Fdoc%2Ftree%2Fscript%2Frun_ueler_binder.ipynb):
65
+ [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/HartmannLab/UELer/main?urlpath=%2Fdoc%2Ftree%2Fscript%2Frun_ueler_binder.ipynb)
66
+
67
+ ## Installation
68
+
69
+ ### Option A — install with pip (recommended)
70
+
71
+ **The install name is `ueler-viewer`; the import name is `ueler`.**
72
+
73
+ The **stable** release is on PyPI:
74
+
75
+ ```shell
76
+ pip install ueler-viewer
77
+ ```
78
+
79
+ This pulls in every runtime dependency. Two optional extras are available:
80
+
81
+ ```shell
82
+ pip install "ueler-viewer[ark]" # adds ark-analysis (pinned) for ark-based workflows
83
+ pip install "ueler-viewer[docs]" # adds the mkdocs toolchain for building the docs
84
+ ```
85
+
86
+ Requires Python 3.10, 3.11, or 3.12. Then, in Python:
87
+
88
+ ```python
89
+ import ueler
90
+ ```
91
+
92
+ #### Pre-releases (TestPyPI)
93
+
94
+ Every release also goes to **TestPyPI**, and pre-releases (`alpha`, `beta`, `rc`) go there *only* — so use this if you want a preview of a version that is not out yet. Keep the command on one line: `--extra-index-url` is required, because TestPyPI does not mirror UELer's runtime dependencies, and `--pre` is what lets pip pick a pre-release at all.
95
+
96
+ ```shell
97
+ pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ueler-viewer
98
+ ```
99
+
100
+ Extras work the same way — `"ueler-viewer[ark]"`, `"ueler-viewer[docs]"` — and appending `==0.6.0rc1` pins one specific preview. Full details, including how to get back to the stable channel: [the installation page](https://hartmannlab.github.io/UELer/installation/).
101
+
102
+ #### If the install fails
103
+
104
+ - **`No matching distribution found for scikit-image>=0.19`** (or for any other dependency) — specific to the TestPyPI command: the resolver is only seeing TestPyPI, which hosts an empty `scikit-image` project. Keep the command on **one line**: the `--extra-index-url https://pypi.org/simple/` part is what lets the dependencies come from real PyPI, and it is easy to lose when a multi-line command is pasted.
105
+ - **Installing with `uv`** — uv's default `--index-strategy first-index` stops at the first index that lists a package at all, so it never falls back to PyPI for the dependencies. It needs an extra flag:
106
+
107
+ ```shell
108
+ uv pip install --prerelease=allow --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ --index-strategy unsafe-best-match ueler-viewer
109
+ ```
110
+ - **You installed an earlier release under the old `ueler` distribution name** — run `pip uninstall ueler` first. Both distributions install the same `ueler/` package, and pip does not know they are the same project, so having both leaves two installs fighting over the same files.
111
+
112
+ ### Option B — install from source (for development)
113
+
114
+ Use this if you want to modify UELer or track the `develop` branch.
115
+
116
+ 1. Create a compatible environment from the `env/environment.yml` file in this repository:
117
+
118
+ ```shell
119
+ micromamba env create --name ark-analysis-ueler --file environment.yml
120
+ ```
121
+ 2. Clone the repository and activate the environment:
122
+
123
+ ```shell
124
+ git clone https://github.com/HartmannLab/UELer.git
125
+ micromamba activate ark-analysis-ueler
126
+ ```
127
+ 3. Install in editable mode from the cloned directory:
128
+
129
+ ```shell
130
+ cd UELer
131
+ pip install -e .
132
+ ```
133
+
134
+ ### Upgrade UELer
135
+ If you installed the stable release from PyPI:
136
+ ```shell
137
+ pip install --upgrade ueler-viewer
138
+ ```
139
+ If you installed a pre-release from TestPyPI:
140
+ ```shell
141
+ pip install --upgrade --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ueler-viewer
142
+ ```
143
+ Coming from a release installed as `ueler` rather than `ueler-viewer`? Run `pip uninstall ueler` first — see [If the install fails](#if-the-install-fails).
144
+ If you installed from source, pull the latest commits in your UELer directory. Re-run the install only when the dependencies changed — an editable install picks up code changes on its own:
145
+ ```shell
146
+ git pull
147
+ pip install -e . # only needed if env/environment.yml or pyproject.toml changed
148
+ ```
149
+
150
+ ## Getting started
151
+ 1. Open your favorite editor that supports Jupyter notebook.
152
+ 2. Open the starter notebook `script/run_ueler.ipynb`. If you installed with pip rather than cloning, download it from [the repository](https://github.com/HartmannLab/UELer/blob/main/script/run_ueler.ipynb).
153
+ 3. Select the kernel for an ark-analysis compatible conda/micromamba env.
154
+ 4. Change the lines according to the instructions in the notebook: when configuring the `/script/run_ueler.ipynb`, ensure that you specify the following directory paths:
155
+ - **`base_folder`**: The directory containing the FOV (Field of View) folders with image data (e.g., `.../image_data`).
156
+ - **`masks_folder`** (optional): The directory containing the segmentation `.tif` files for cell segmentation (e.g., `.../segmentation/cellpose_output`).
157
+ - **`annotations_folder`** (optional): The directory containing annotation files for marking regions of interest (e.g., `.../annotations`).
158
+ - **`cell_table_path`** (optional): The path to the file containing the cell table data (e.g., `.../segmentation/cell_table/cell_table_size_normalized.csv`).
159
+ Make sure these paths are correctly set in the notebook for the viewer to access the data correctly.
160
+
161
+ 5. Run the code and you will see the viewer displayed.
162
+
163
+ ### Streaming from the BioImage Archive (BIA)
164
+ You can explore a public BioImage Archive study (an `S-BIAD*` accession) without downloading the
165
+ whole dataset first:
166
+ ```python
167
+ from ueler.runner import run_viewer_bia
168
+
169
+ viewer = run_viewer_bia(
170
+ "S-BIAD2557", # accession id (or a direct HTTPS base URL)
171
+ descriptor={ # optional; auto-detection is attempted if omitted
172
+ "mode": "folder",
173
+ "base": "Files/spatial_murine_iCCAvsHCC/image_data",
174
+ "mask_dir": "Files/spatial_murine_iCCAvsHCC/segmentation/cleaned_mask",
175
+ "mask_glob": "{fov}_*.tiff",
176
+ },
177
+ )
178
+ ```
179
+ Because BIA studies have no standard folder layout, a small JSON **descriptor** (a dict or a path
180
+ to a `.json` file) maps the study files onto FOVs / channels / masks; when omitted, UELer attempts
181
+ to auto-detect the folder-per-FOV, OME-TIFF-per-FOV, or zip-container layouts. The descriptor is
182
+ flexible enough for the variation seen across real studies:
183
+ - **Masks** accept either a single `mask_dir`/`mask_glob`, or a `masks` list of sources — each with
184
+ an optional `name` (renames masks named `<fov>.tiff` to a clean label) or `per_fov: true` (masks
185
+ stored in a per-FOV subfolder `<dir>/<fov>/*.tiff`). `annotations` uses the same shape.
186
+ - **Zipped FOVs**: set `"fov_container": "zip"` when each FOV is a `<FOV>.zip` of channel TIFFs —
187
+ UELer reads a single channel straight out of the remote zip via an HTTP byte-range request rather
188
+ than downloading the whole archive.
189
+
190
+ Pyramidal OME-TIFFs and single zip members are streamed via HTTP byte-range requests; other files
191
+ (e.g. single-resolution MIBI TIFFs) are downloaded once into a local cache. A per-study
192
+ **workspace** at `~/.ueler/bia/<accession>/` (override with `local_dir=`) holds your persistent
193
+ `.UELer` work (ROIs, checkpoints, palettes) plus a disposable `cache/` of downloaded images.
194
+
195
+ Examples for three real studies — `S-BIAD2557` (single-dir masks), `S-BIAD2864` (two named mask
196
+ folders), and `S-BIAD2708` (zipped FOVs + per-FOV masks) — are in `script/run_ueler_BIA.ipynb`.
197
+
198
+ ## User interface
199
+ ![GUI_preview](https://raw.githubusercontent.com/HartmannLab/UELer/main/doc/GUI_preview.png)
200
+ The GUI can be split into four main regions (wide plugins toggle the optional footer automatically):
201
+ - left: overall settings (channel, annotation, and mask accordions)
202
+ - middle: main viewer with overlay controls and image navigation
203
+ - right: plugin tools (Mask Painter, ROI Manager, palette editors, statistics panels)
204
+ - bottom (optional): wide plugin tabs (e.g., horizontal heatmap or gallery extensions)
205
+
206
+ For more details, see the [user guide](https://hartmannlab.github.io/UELer/latest/tutorials/user-interface).
207
+
208
+ ## New Update
209
+ ### **UELer v0.5.0 Summary**
210
+
211
+ The `0.5.0` line turns UELer from a repository you clone into a package you install. This is the first release published to PyPI.
212
+
213
+ **Installation and licensing**
214
+
215
+ - **`pip install ueler-viewer` — you still `import ueler`.** PyPI prohibits the name `ueler`, so the distribution is `ueler-viewer`, like `scikit-image`/`skimage` or `opencv-python`/`cv2`. Nothing in your code changes. **Stable releases come from PyPI; alphas, betas and release candidates are only on TestPyPI** — see the [installation guide](https://hartmannlab.github.io/UELer/latest/installation). If you hold an editable install of the old `ueler` distribution, `pip uninstall ueler` first: pip treats the two as unrelated projects even though both own the `ueler/` package.
216
+ - **Relicensed from GPL-3.0-only to BSD 3-Clause.** UELer is imported into other people's pipelines, which is where copyleft bites hardest — under the GPL, distributing a pipeline that imported `ueler` pulled that pipeline into the GPL too. BSD-3 matches the surrounding stack (scikit-image, dask, bokeh, anndata, napari). Existing users only gain permissions.
217
+ - **Supported Python: 3.10, 3.11 and 3.12**, and `pandas>=2.0` is now a declared dependency rather than arriving through seaborn and anndata.
218
+ - **Breaking: `import ueler` no longer claims the top-level names `viewer`, `constants`, `data_loader` and `image_utils`.** The pre-0.2 compatibility hook is removed, along with the `ensure_aliases=` argument of `run_viewer()` / `run_viewer_bia()`. The canonical paths (`ueler.constants`, `ueler.viewer.*`, …) are unchanged.
219
+
220
+ **New in the viewer**
221
+
222
+ - **Line and polygon ROIs, in the ROI manager.** Click a chain of points onto the canvas — left-click adds a vertex, left-drag moves one, right-click deletes the nearest, `ctrl+z`/`ctrl+y` undo and redo, `enter` finishes — with a live length or perimeter readout in px and µm. Shapes are ordinary ROI rows, so tags, thumbnails, filtering, CSV import/export and batch export all apply; exporting a shape exports its bounding box. Older ROI CSVs load unchanged.
223
+ - **Cells selected in the image now reach the plots.** A **Follow main viewer** checkbox (*Linked plugins* tab, off by default) in the Scatter plot, Histogram and Heatmap mirrors the image's live selection into the plot, across several FOVs in map mode. It is the counterpart of the **Main viewer** checkbox, which pushes the other way.
224
+ - **A handful of selected cells is now visible in the histogram.** Five cells out of 80 000 drew a bar 0.006 % of the plot height. When the selection's peak falls below 5 % of the tallest bar, the bins holding selected cells are tinted over their full height, with the proportional overlay still drawn on top. **Mark faint selections** turns it off.
225
+ - **The interactive scatter is the default everywhere, VS Code included.** The static-Matplotlib fallback for VS Code worked around a webview bug that no longer happens, and cost every VS Code user their linked brushing. `UELER_SCATTER_BACKEND=static` remains as an opt-out.
226
+
227
+ **Fixed**
228
+
229
+ - **Locating a single cell works in the channel grid view.** Go-To, a gallery tile, a scatter or heatmap point, and centring on a saved ROI all did nothing at all in grid mode — no error, no movement.
230
+ - **Painting a cell table no longer crashes on pandas extension dtypes.** The Mask Painter raised `TypeError: Cannot interpret '<StringDtype…>' as a data type` whenever the identifier column was categorical or nullable — which AnnData `obs` columns routinely are.
231
+ - **The scatter matrix follows the standard SPLOM convention:** a row shares its y-axis, a column shares its x-axis. The previous layout was the transpose.
232
+ - **A finished shape stays on screen**, instead of vanishing until the ROI was saved and loaded back, and **`Save shape` now finishes the drawing for you**.
233
+
234
+ **Removed**
235
+
236
+ - **The `Chart (heatmap)` plugin.** A near-duplicate of the Scatter plot reading its axes from the cluster × marker matrix, offering nothing Scatter plot or Heatmap does not. The real **Heatmap**, **Scatter plot** and **Histogram** are untouched — the plugin once labelled *Chart* is today's **Scatter plot**, not the removed one.
237
+
238
+ **Worth knowing**
239
+
240
+ - **`ENABLE_MAP_MODE` must be set before `import ueler`.** The flag is read at module import time, so setting it later has no effect — even before `run_viewer()`.
241
+ - **The pixel size defaults to 390 nm — the MIBI pixel pitch — for every dataset**, and it drives the scale bar in the viewer and in every batch-exported image. Set it from your acquisition metadata (1000 nm for IMC), or to `0` to omit the scale bar entirely. The new [display settings](https://hartmannlab.github.io/UELer/latest/tutorials/display-settings) page covers this and the 99.9th-percentile contrast default.
242
+ - **The [documentation site](https://hartmannlab.github.io/UELer/latest/) was audited against the code**, and a checker now runs on every docs deploy so install commands, the Python range, extras, repo paths, environment variables and UI labels cannot drift from the software again. Two new developer pages cover [plugin development](https://hartmannlab.github.io/UELer/latest/develop-notes/plugin-development) and [mask rendering & coloring](https://hartmannlab.github.io/UELer/latest/develop-notes/mask-rendering).
243
+ - **Behind the scenes:** CI runs the suite on Python 3.10–3.12 and installs the built wheel outside the repository, a skipped test counts as a failure, and a pushed tag routes itself — pre-releases to TestPyPI, stable only after a matching release candidate is confirmed to ship identical code. Publishing uses PyPI Trusted Publishing; no API token exists in the repository.
244
+
245
+ _Earlier changes (v0.4.4 and before) are in the [update log](https://github.com/HartmannLab/UELer/blob/main/doc/log.md)._
246
+
247
+ ## License
248
+ UELer is released under the **BSD 3-Clause License** — see
249
+ [LICENSE.txt](https://github.com/HartmannLab/UELer/blob/main/LICENSE.txt).
250
+
251
+ You are free to use, modify and redistribute UELer, including in commercial and closed-source
252
+ work, provided you keep the copyright notice and do not use the authors' names to endorse a
253
+ derived product. This is the same license as `scikit-image`, `dask`, `bokeh`, `anndata` and
254
+ `napari`, so UELer imposes no constraints your existing scientific Python stack does not.
255
+
256
+ If you use UELer in published work, a citation is appreciated but not required.
257
+
258
+ ## Issues and contact
259
+ Bug reports and feature requests: [GitHub Issues](https://github.com/HartmannLab/UELer/issues).
260
+ Maintained by Yu-Le Wu, Hartmann Lab, DKFZ Heidelberg.
@@ -0,0 +1,201 @@
1
+ # UELer
2
+ Unified Exploratory Linked Viewer: a Jupyter-based framework for interactive exploration of multiplexed imaging datasets.
3
+
4
+ ## Try it on Binder
5
+ You can try UELer without installation by launching it on [Binder](https://mybinder.org/v2/gh/HartmannLab/UELer/main?urlpath=%2Fdoc%2Ftree%2Fscript%2Frun_ueler_binder.ipynb):
6
+ [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/HartmannLab/UELer/main?urlpath=%2Fdoc%2Ftree%2Fscript%2Frun_ueler_binder.ipynb)
7
+
8
+ ## Installation
9
+
10
+ ### Option A — install with pip (recommended)
11
+
12
+ **The install name is `ueler-viewer`; the import name is `ueler`.**
13
+
14
+ The **stable** release is on PyPI:
15
+
16
+ ```shell
17
+ pip install ueler-viewer
18
+ ```
19
+
20
+ This pulls in every runtime dependency. Two optional extras are available:
21
+
22
+ ```shell
23
+ pip install "ueler-viewer[ark]" # adds ark-analysis (pinned) for ark-based workflows
24
+ pip install "ueler-viewer[docs]" # adds the mkdocs toolchain for building the docs
25
+ ```
26
+
27
+ Requires Python 3.10, 3.11, or 3.12. Then, in Python:
28
+
29
+ ```python
30
+ import ueler
31
+ ```
32
+
33
+ #### Pre-releases (TestPyPI)
34
+
35
+ Every release also goes to **TestPyPI**, and pre-releases (`alpha`, `beta`, `rc`) go there *only* — so use this if you want a preview of a version that is not out yet. Keep the command on one line: `--extra-index-url` is required, because TestPyPI does not mirror UELer's runtime dependencies, and `--pre` is what lets pip pick a pre-release at all.
36
+
37
+ ```shell
38
+ pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ueler-viewer
39
+ ```
40
+
41
+ Extras work the same way — `"ueler-viewer[ark]"`, `"ueler-viewer[docs]"` — and appending `==0.6.0rc1` pins one specific preview. Full details, including how to get back to the stable channel: [the installation page](https://hartmannlab.github.io/UELer/installation/).
42
+
43
+ #### If the install fails
44
+
45
+ - **`No matching distribution found for scikit-image>=0.19`** (or for any other dependency) — specific to the TestPyPI command: the resolver is only seeing TestPyPI, which hosts an empty `scikit-image` project. Keep the command on **one line**: the `--extra-index-url https://pypi.org/simple/` part is what lets the dependencies come from real PyPI, and it is easy to lose when a multi-line command is pasted.
46
+ - **Installing with `uv`** — uv's default `--index-strategy first-index` stops at the first index that lists a package at all, so it never falls back to PyPI for the dependencies. It needs an extra flag:
47
+
48
+ ```shell
49
+ uv pip install --prerelease=allow --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ --index-strategy unsafe-best-match ueler-viewer
50
+ ```
51
+ - **You installed an earlier release under the old `ueler` distribution name** — run `pip uninstall ueler` first. Both distributions install the same `ueler/` package, and pip does not know they are the same project, so having both leaves two installs fighting over the same files.
52
+
53
+ ### Option B — install from source (for development)
54
+
55
+ Use this if you want to modify UELer or track the `develop` branch.
56
+
57
+ 1. Create a compatible environment from the `env/environment.yml` file in this repository:
58
+
59
+ ```shell
60
+ micromamba env create --name ark-analysis-ueler --file environment.yml
61
+ ```
62
+ 2. Clone the repository and activate the environment:
63
+
64
+ ```shell
65
+ git clone https://github.com/HartmannLab/UELer.git
66
+ micromamba activate ark-analysis-ueler
67
+ ```
68
+ 3. Install in editable mode from the cloned directory:
69
+
70
+ ```shell
71
+ cd UELer
72
+ pip install -e .
73
+ ```
74
+
75
+ ### Upgrade UELer
76
+ If you installed the stable release from PyPI:
77
+ ```shell
78
+ pip install --upgrade ueler-viewer
79
+ ```
80
+ If you installed a pre-release from TestPyPI:
81
+ ```shell
82
+ pip install --upgrade --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ueler-viewer
83
+ ```
84
+ Coming from a release installed as `ueler` rather than `ueler-viewer`? Run `pip uninstall ueler` first — see [If the install fails](#if-the-install-fails).
85
+ If you installed from source, pull the latest commits in your UELer directory. Re-run the install only when the dependencies changed — an editable install picks up code changes on its own:
86
+ ```shell
87
+ git pull
88
+ pip install -e . # only needed if env/environment.yml or pyproject.toml changed
89
+ ```
90
+
91
+ ## Getting started
92
+ 1. Open your favorite editor that supports Jupyter notebook.
93
+ 2. Open the starter notebook `script/run_ueler.ipynb`. If you installed with pip rather than cloning, download it from [the repository](https://github.com/HartmannLab/UELer/blob/main/script/run_ueler.ipynb).
94
+ 3. Select the kernel for an ark-analysis compatible conda/micromamba env.
95
+ 4. Change the lines according to the instructions in the notebook: when configuring the `/script/run_ueler.ipynb`, ensure that you specify the following directory paths:
96
+ - **`base_folder`**: The directory containing the FOV (Field of View) folders with image data (e.g., `.../image_data`).
97
+ - **`masks_folder`** (optional): The directory containing the segmentation `.tif` files for cell segmentation (e.g., `.../segmentation/cellpose_output`).
98
+ - **`annotations_folder`** (optional): The directory containing annotation files for marking regions of interest (e.g., `.../annotations`).
99
+ - **`cell_table_path`** (optional): The path to the file containing the cell table data (e.g., `.../segmentation/cell_table/cell_table_size_normalized.csv`).
100
+ Make sure these paths are correctly set in the notebook for the viewer to access the data correctly.
101
+
102
+ 5. Run the code and you will see the viewer displayed.
103
+
104
+ ### Streaming from the BioImage Archive (BIA)
105
+ You can explore a public BioImage Archive study (an `S-BIAD*` accession) without downloading the
106
+ whole dataset first:
107
+ ```python
108
+ from ueler.runner import run_viewer_bia
109
+
110
+ viewer = run_viewer_bia(
111
+ "S-BIAD2557", # accession id (or a direct HTTPS base URL)
112
+ descriptor={ # optional; auto-detection is attempted if omitted
113
+ "mode": "folder",
114
+ "base": "Files/spatial_murine_iCCAvsHCC/image_data",
115
+ "mask_dir": "Files/spatial_murine_iCCAvsHCC/segmentation/cleaned_mask",
116
+ "mask_glob": "{fov}_*.tiff",
117
+ },
118
+ )
119
+ ```
120
+ Because BIA studies have no standard folder layout, a small JSON **descriptor** (a dict or a path
121
+ to a `.json` file) maps the study files onto FOVs / channels / masks; when omitted, UELer attempts
122
+ to auto-detect the folder-per-FOV, OME-TIFF-per-FOV, or zip-container layouts. The descriptor is
123
+ flexible enough for the variation seen across real studies:
124
+ - **Masks** accept either a single `mask_dir`/`mask_glob`, or a `masks` list of sources — each with
125
+ an optional `name` (renames masks named `<fov>.tiff` to a clean label) or `per_fov: true` (masks
126
+ stored in a per-FOV subfolder `<dir>/<fov>/*.tiff`). `annotations` uses the same shape.
127
+ - **Zipped FOVs**: set `"fov_container": "zip"` when each FOV is a `<FOV>.zip` of channel TIFFs —
128
+ UELer reads a single channel straight out of the remote zip via an HTTP byte-range request rather
129
+ than downloading the whole archive.
130
+
131
+ Pyramidal OME-TIFFs and single zip members are streamed via HTTP byte-range requests; other files
132
+ (e.g. single-resolution MIBI TIFFs) are downloaded once into a local cache. A per-study
133
+ **workspace** at `~/.ueler/bia/<accession>/` (override with `local_dir=`) holds your persistent
134
+ `.UELer` work (ROIs, checkpoints, palettes) plus a disposable `cache/` of downloaded images.
135
+
136
+ Examples for three real studies — `S-BIAD2557` (single-dir masks), `S-BIAD2864` (two named mask
137
+ folders), and `S-BIAD2708` (zipped FOVs + per-FOV masks) — are in `script/run_ueler_BIA.ipynb`.
138
+
139
+ ## User interface
140
+ ![GUI_preview](https://raw.githubusercontent.com/HartmannLab/UELer/main/doc/GUI_preview.png)
141
+ The GUI can be split into four main regions (wide plugins toggle the optional footer automatically):
142
+ - left: overall settings (channel, annotation, and mask accordions)
143
+ - middle: main viewer with overlay controls and image navigation
144
+ - right: plugin tools (Mask Painter, ROI Manager, palette editors, statistics panels)
145
+ - bottom (optional): wide plugin tabs (e.g., horizontal heatmap or gallery extensions)
146
+
147
+ For more details, see the [user guide](https://hartmannlab.github.io/UELer/latest/tutorials/user-interface).
148
+
149
+ ## New Update
150
+ ### **UELer v0.5.0 Summary**
151
+
152
+ The `0.5.0` line turns UELer from a repository you clone into a package you install. This is the first release published to PyPI.
153
+
154
+ **Installation and licensing**
155
+
156
+ - **`pip install ueler-viewer` — you still `import ueler`.** PyPI prohibits the name `ueler`, so the distribution is `ueler-viewer`, like `scikit-image`/`skimage` or `opencv-python`/`cv2`. Nothing in your code changes. **Stable releases come from PyPI; alphas, betas and release candidates are only on TestPyPI** — see the [installation guide](https://hartmannlab.github.io/UELer/latest/installation). If you hold an editable install of the old `ueler` distribution, `pip uninstall ueler` first: pip treats the two as unrelated projects even though both own the `ueler/` package.
157
+ - **Relicensed from GPL-3.0-only to BSD 3-Clause.** UELer is imported into other people's pipelines, which is where copyleft bites hardest — under the GPL, distributing a pipeline that imported `ueler` pulled that pipeline into the GPL too. BSD-3 matches the surrounding stack (scikit-image, dask, bokeh, anndata, napari). Existing users only gain permissions.
158
+ - **Supported Python: 3.10, 3.11 and 3.12**, and `pandas>=2.0` is now a declared dependency rather than arriving through seaborn and anndata.
159
+ - **Breaking: `import ueler` no longer claims the top-level names `viewer`, `constants`, `data_loader` and `image_utils`.** The pre-0.2 compatibility hook is removed, along with the `ensure_aliases=` argument of `run_viewer()` / `run_viewer_bia()`. The canonical paths (`ueler.constants`, `ueler.viewer.*`, …) are unchanged.
160
+
161
+ **New in the viewer**
162
+
163
+ - **Line and polygon ROIs, in the ROI manager.** Click a chain of points onto the canvas — left-click adds a vertex, left-drag moves one, right-click deletes the nearest, `ctrl+z`/`ctrl+y` undo and redo, `enter` finishes — with a live length or perimeter readout in px and µm. Shapes are ordinary ROI rows, so tags, thumbnails, filtering, CSV import/export and batch export all apply; exporting a shape exports its bounding box. Older ROI CSVs load unchanged.
164
+ - **Cells selected in the image now reach the plots.** A **Follow main viewer** checkbox (*Linked plugins* tab, off by default) in the Scatter plot, Histogram and Heatmap mirrors the image's live selection into the plot, across several FOVs in map mode. It is the counterpart of the **Main viewer** checkbox, which pushes the other way.
165
+ - **A handful of selected cells is now visible in the histogram.** Five cells out of 80 000 drew a bar 0.006 % of the plot height. When the selection's peak falls below 5 % of the tallest bar, the bins holding selected cells are tinted over their full height, with the proportional overlay still drawn on top. **Mark faint selections** turns it off.
166
+ - **The interactive scatter is the default everywhere, VS Code included.** The static-Matplotlib fallback for VS Code worked around a webview bug that no longer happens, and cost every VS Code user their linked brushing. `UELER_SCATTER_BACKEND=static` remains as an opt-out.
167
+
168
+ **Fixed**
169
+
170
+ - **Locating a single cell works in the channel grid view.** Go-To, a gallery tile, a scatter or heatmap point, and centring on a saved ROI all did nothing at all in grid mode — no error, no movement.
171
+ - **Painting a cell table no longer crashes on pandas extension dtypes.** The Mask Painter raised `TypeError: Cannot interpret '<StringDtype…>' as a data type` whenever the identifier column was categorical or nullable — which AnnData `obs` columns routinely are.
172
+ - **The scatter matrix follows the standard SPLOM convention:** a row shares its y-axis, a column shares its x-axis. The previous layout was the transpose.
173
+ - **A finished shape stays on screen**, instead of vanishing until the ROI was saved and loaded back, and **`Save shape` now finishes the drawing for you**.
174
+
175
+ **Removed**
176
+
177
+ - **The `Chart (heatmap)` plugin.** A near-duplicate of the Scatter plot reading its axes from the cluster × marker matrix, offering nothing Scatter plot or Heatmap does not. The real **Heatmap**, **Scatter plot** and **Histogram** are untouched — the plugin once labelled *Chart* is today's **Scatter plot**, not the removed one.
178
+
179
+ **Worth knowing**
180
+
181
+ - **`ENABLE_MAP_MODE` must be set before `import ueler`.** The flag is read at module import time, so setting it later has no effect — even before `run_viewer()`.
182
+ - **The pixel size defaults to 390 nm — the MIBI pixel pitch — for every dataset**, and it drives the scale bar in the viewer and in every batch-exported image. Set it from your acquisition metadata (1000 nm for IMC), or to `0` to omit the scale bar entirely. The new [display settings](https://hartmannlab.github.io/UELer/latest/tutorials/display-settings) page covers this and the 99.9th-percentile contrast default.
183
+ - **The [documentation site](https://hartmannlab.github.io/UELer/latest/) was audited against the code**, and a checker now runs on every docs deploy so install commands, the Python range, extras, repo paths, environment variables and UI labels cannot drift from the software again. Two new developer pages cover [plugin development](https://hartmannlab.github.io/UELer/latest/develop-notes/plugin-development) and [mask rendering & coloring](https://hartmannlab.github.io/UELer/latest/develop-notes/mask-rendering).
184
+ - **Behind the scenes:** CI runs the suite on Python 3.10–3.12 and installs the built wheel outside the repository, a skipped test counts as a failure, and a pushed tag routes itself — pre-releases to TestPyPI, stable only after a matching release candidate is confirmed to ship identical code. Publishing uses PyPI Trusted Publishing; no API token exists in the repository.
185
+
186
+ _Earlier changes (v0.4.4 and before) are in the [update log](https://github.com/HartmannLab/UELer/blob/main/doc/log.md)._
187
+
188
+ ## License
189
+ UELer is released under the **BSD 3-Clause License** — see
190
+ [LICENSE.txt](https://github.com/HartmannLab/UELer/blob/main/LICENSE.txt).
191
+
192
+ You are free to use, modify and redistribute UELer, including in commercial and closed-source
193
+ work, provided you keep the copyright notice and do not use the authors' names to endorse a
194
+ derived product. This is the same license as `scikit-image`, `dask`, `bokeh`, `anndata` and
195
+ `napari`, so UELer imposes no constraints your existing scientific Python stack does not.
196
+
197
+ If you use UELer in published work, a citation is appreciated but not required.
198
+
199
+ ## Issues and contact
200
+ Bug reports and feature requests: [GitHub Issues](https://github.com/HartmannLab/UELer/issues).
201
+ Maintained by Yu-Le Wu, Hartmann Lab, DKFZ Heidelberg.
@@ -0,0 +1,106 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ # The *distribution* name only — what `pip install` asks for. The import name is
7
+ # `ueler` and is unaffected: it comes from the package directory, not from here.
8
+ # `ueler` is administratively prohibited on PyPI (most likely as typo-squat
9
+ # protection for the live `euler` project), so the distribution ships under
10
+ # `ueler-viewer` on both indexes. See dev_note/issue_tracking/issue79_dist_name_rename.md.
11
+ name = "ueler-viewer"
12
+ version = "0.5.0"
13
+ description = "Unified Exploratory Linked Viewer for spatial proteomics and multiplexed imaging"
14
+ readme = "README.md"
15
+ requires-python = ">=3.10,<3.13"
16
+ license = "BSD-3-Clause"
17
+ license-files = ["LICENSE.txt"]
18
+ authors = [
19
+ { name = "Yu-Le Wu (Hartmann Lab, DKFZ)" }
20
+ ]
21
+ keywords = [
22
+ "spatial proteomics",
23
+ "multiplexed imaging",
24
+ "image viewer",
25
+ "jupyter",
26
+ "MIBI",
27
+ "IMC",
28
+ "bioimage analysis",
29
+ ]
30
+ # No "License ::" classifier here on purpose: PEP 639 forbids combining one with
31
+ # the `license` SPDX expression above, and setuptools warns if both are present.
32
+ # No "Topic :: Scientific/Engineering :: Image Processing" either: UELer displays
33
+ # and links images, it does not offer filtering, segmentation or registration, so
34
+ # Visualization + Bio-Informatics is the honest pair.
35
+ # The Python classifiers must stay in step with `requires-python`.
36
+ classifiers = [
37
+ "Development Status :: 4 - Beta",
38
+ "Intended Audience :: Science/Research",
39
+ "Framework :: Jupyter",
40
+ "Operating System :: OS Independent",
41
+ "Programming Language :: Python :: 3",
42
+ "Programming Language :: Python :: 3.10",
43
+ "Programming Language :: Python :: 3.11",
44
+ "Programming Language :: Python :: 3.12",
45
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
46
+ "Topic :: Scientific/Engineering :: Visualization",
47
+ ]
48
+ dependencies = [
49
+ "scikit-image>=0.19",
50
+ "dask",
51
+ "dask-image",
52
+ "ipydatagrid",
53
+ "bqplot",
54
+ "seaborn",
55
+ "seaborn-image",
56
+ "opencv-python-headless",
57
+ "watchfiles",
58
+ "jupyter-scatter",
59
+ "ipywidgets>=8.1",
60
+ "anywidget>=0.9",
61
+ "numpy",
62
+ # Declared explicitly although seaborn/anndata would drag it in anyway: the
63
+ # viewer imports pandas directly in ~14 modules, and leaving it transitive is
64
+ # what let pip resolve a different major version per Python minor (pandas 3
65
+ # needs >=3.11, so the CI 3.12 leg got the new string dtype and 3.10/3.11 did
66
+ # not). The floor is conservative rather than measured -- the suite is only
67
+ # exercised against 2.3 and, via `future.infer_string`, pandas-3 semantics.
68
+ "pandas>=2.0",
69
+ "ipykernel",
70
+ "ipympl",
71
+ "anndata>=0.10",
72
+ "tifffile",
73
+ "fsspec[http]",
74
+ "requests",
75
+ "bokeh>=3.0",
76
+ "jupyter_bokeh>=4.0",
77
+ ]
78
+
79
+ [project.optional-dependencies]
80
+ ark = ["ark-analysis==0.7.0"]
81
+ dev = [
82
+ "pytest>=7.0",
83
+ "pytest-cov>=4.0",
84
+ "build>=1.0",
85
+ "twine>=5.0",
86
+ ]
87
+ docs = [
88
+ "mkdocs-material>=9.5",
89
+ "mike>=2.1",
90
+ ]
91
+
92
+ # These become the "Project links" sidebar on the PyPI page. Absolute URLs only —
93
+ # a release is browsed outside the repository, so nothing repo-relative works here.
94
+ [project.urls]
95
+ Homepage = "https://github.com/HartmannLab/UELer"
96
+ Documentation = "https://hartmannlab.github.io/UELer/"
97
+ Repository = "https://github.com/HartmannLab/UELer"
98
+ Issues = "https://github.com/HartmannLab/UELer/issues"
99
+ Changelog = "https://github.com/HartmannLab/UELer/blob/main/doc/log.md"
100
+
101
+ [tool.setuptools.packages.find]
102
+ include = ["ueler", "ueler.*"]
103
+ exclude = ["tests", "tests.*", "dev_note", "script"]
104
+
105
+ [tool.setuptools.package-data]
106
+ "ueler.viewer" = ["images/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+