Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API for programmatically loading single elements into Napari #189

Open
aeisenbarth opened this issue Dec 12, 2023 · 3 comments
Open

API for programmatically loading single elements into Napari #189

aeisenbarth opened this issue Dec 12, 2023 · 3 comments
Labels
good first issue Good for newcomers

Comments

@aeisenbarth
Copy link
Contributor

Use case

  • As a plugin developer, I want to programmatically display a single SpatialData image so that my plugin can support SpatialData as storage format and provide interaction specific to some element(s).

Workarounds

Currently, I have these options:

  • ✓ Instruct the user to open the GUI of napari-spatialdata, double-click on (the only) coordinate system, double-click on the image name.
  • Request Napari to use the plugin for opening the SpatialData path:
    viewer.open(sdata_path, plugin="napari-spatialdata")
    Here, I cannot pass the element name. **kwargs are passed to Napari's add_layer method, so there is no way to extend this. Also, napari-spatialdata returns layer data [(None,)] which is a sentinal that tells Napari that the plugin successfully read the path, but loaded no layer.
  • Request Napari to use the plugin for opening the element path:
    viewer.open(sdata_path / "images" / image_name, plugin="napari-spatialdata")
    This is an invalid command, the plugin only accepts valid SpatialData paths, not subpaths to contained elements.
  • Reimplement everything on my own.
    Use spatialdata.read_zarr(sdata_path).images[image_name] and pass the array and transformation to Napari. However, I have to handle both SpatialImage/MultiscaleSpatialImage, extract the scale levels to a plain list, order the axes, convert the transformation to a plain matrix… basically rebuilding the private function _adjust_channels_order (which I cannot savely import) and add_sdata_image.

It remains for discussion whether such an API should:

  • add the element to the viewer
  • or just read and convert the SpatialData element to arguments that can be passed to viewer.add_image(…) so that the user/developer has further control over passing them to the viewer.

Requirements:

  • Given a SpatialData path, element names (and optional coordinate system), read and convert the element to be compatible with Napari, for example as layer data tuple Tuple[DataType, Metadata, LayerName].
  • If adding the element to the viewer, return a reference to the layer.
  • Allow loading without GUI because it takes a lot of screen space and can hinder using other Napari plugins at the same time.
@LucaMarconato
Copy link
Member

Thank you @aeisenbarth for reaching out and for the feature request. I agree that it's an important use case and it would facilitate the interaction between our and other napari plugins.

We also needed similar functions, to be able to instantiate specific elements to the viewer programmatically (to be used during testing) https://github.com/scverse/napari-spatialdata/pull/170/files and in a headless mode https://github.com/scverse/napari-spatialdata/pull/140/files or to fire an instance with already a coordinate system selected https://github.com/scverse/napari-spatialdata/pull/151/files.

Nevertheless, the mentioned PRs don't cover your use case, so it would need to be addressed in a separate PR.

I am currently focusing on merging old PRs in spatialdata, but after that I am happy to work more on this repository.

@melonora
Copy link
Collaborator

Hey @aeisenbarth! Does what @LucaMarconato stated in his comment suit your needs? If so we can close this issue.

@aeisenbarth
Copy link
Contributor Author

I should have a look again into the Interactive class. I am actually not using that, but SdataWidget directly.

I have the impression, Interactive is considered as the public API and should be used, but it did not fit my needs, because:

  • In the old times, it fired up a Napari instance, instead of living nicely together with plugins in an existing Napari viewer. (Solve with Improved ergonomics of Interactive #140)
  • It is not itself registered as a dock widget in Napari, but its SdataWidget attribute. That means Interactive is not an alive object while using Napari, but it gets garbage-collected immediately after all contained dock widgets are instantiated and registered in Napari. Currently, I programmatically access the plugin through the registered dock widget.
  • I only need the element selection, the other docks (range slider, colorbar, AnnData obs/X selector) are not useful but take all space so that other plugins cannot be used anymore.

The original API proposed in this was to convert SpatialData elements directly to Napari layers without GUI, so that me as user would add the layers to Napari.

In contrast to that, I would also be fine with a different API for GUI automation:

  • get_spatialdata_interactive(): Static function to get a reference to the existing controler (Interactive), or instantiate a new one. This could be achieved by making Interactive a singleton and having a static (module-level) reference to it. Or add a back-reference from SdataWidget→Interactive to allow accessing the API when you have a dock reference.
  • set_spatialdata(sdata) or add_spatialdata(sdata): Add a given SpatialData instance to the SdataWidget dock so that its coordinate systems and elements are listed in the widgets. Currently, it tries to support multiple SpatialData elements in an evented list SdataWidget._sdata. If this is too complex to maintain, I would much rather have a single SpatialData. Also, the coordinate systems widget only shows coordinates systems of the first loaded SpatialData and does not update when another one is added to the evented list afterward.
  • add_element(element, element_coordinate_system): Add an element to the viewer and show it selected in the elements widget. Return the corresponding Napari layers so I don't have to guess them with heuristics (like subtracting layers after - before).
  • switch_coordinate_system(coordinate_system): Set a coordinate systems to elements in the viewer and show it selected in the widget.
  • Add a function or parameters to the constructor to control which docks to show or hide (SdataWidget, QtAdataViewWidget)

@LucaMarconato LucaMarconato added the good first issue Good for newcomers label Oct 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

3 participants