napari / napari

napari: a fast, interactive, multi-dimensional image viewer for python
https://napari.org
BSD 3-Clause "New" or "Revised" License
2.2k stars 421 forks source link

Public access to canvas size #4148

Open aeisenbarth opened 2 years ago

aeisenbarth commented 2 years ago

πŸš€ Feature

There does not seem to be a public way to access the canvas size (alternatively the shape of the displayed/rendered image). viewer._canvas_size is private and also viewer.window.qt_viewer.canvas.size now gives a FutureWarning and will break all such use cases as soon as 0.5.0 is released.

Motivation

The property viewer.camera.zoom is (still) public and connects the size of canvas pixels with the size of world pixels. Since we usually work a lot in the world and data spaces with world pixels, we would also need some information about the canvas space for being able to set a reasonable zoom factor.

Pitch

Provide a public API for reading the current rendered view's size (width, height).

Additional context

Example use case (simplified):

from napari import Viewer
from napari.layers import Layer
import numpy as np

def zoom_layer_extents(viewer: Viewer, layer: Layer):
    dim_indices = [d - viewer.dims.ndim for d in viewer.dims.displayed]
    if np.max(np.abs(dim_indices)) <= layer.ndim:
        extent = layer.extent.world[:, dim_indices]
        canvas_size = np.asarray(viewer.window.qt_viewer.canvas.size)
        layers_size = np.flip(np.diff(extent, axis=0))
        size_ratios = canvas_size / layers_size
        zoom = np.min(size_ratios)
        viewer.camera.zoom = zoom
        viewer.camera.center = np.mean(extent, axis=0)

viewer = Viewer(show=True)
layer1 = viewer.add_image(np.random.random((200, 100)))
layer2 = viewer.add_image(np.random.random((100, 200)))
zoom_layer_extents(viewer, layer2)
FutureWarning: Public access to Window.qt_viewer is deprecated and will be removed in
v0.5.0. It is considered an "implementation detail" of the napari
application, not part of the napari viewer model. If your use case
requires access to qt_viewer, please open an issue to discuss.
  viewer.window.qt_viewer.canvas.size
sofroniewn commented 2 years ago

Agreed this is important to have. We've been hoping to add support for multiple canvases one day, and so I think there was an idea we might have some API where there is a list of canvases. We could though just start with viewer.canvas and then if we ever have multiple add viewer.canvases. It will be a while before we get there. What do @jni and @tlambert03 think?

jni commented 2 years ago

I think let's just make viewer.canvases a list from the beginning, containing one canvas initially. But yes, I need canvas size also in my plugin :joy: So we are 100% on board @aeisenbarth!

sofroniewn commented 2 years ago

Good by me. Could initially just start with adding a Canvas eveneted model with a single attribute size, then a list on Viewer where viewer.canvases[0] was the singleton canvas. We could use a normal list or just start out with an EventedList if we want to have a Canvas.name. I could go either way.

@aeisenbarth do you have any interest in making a PR with this contribution - we can help you through the process if you like!!

alisterburt commented 2 years ago

If we add this it would be really cool to play with using whether or not a canvas is selected/active as a context for mouse callbacks in the viewer - this isn't necessary for public access to the canvas size but would make interesting followup!

edit: +1 on making the canvas list API mirror the layer list API, this can be implemented by subclassing EventedList with a custom lookup https://github.com/napari/napari/blob/842008f6df909bcbc391789badd7f2414eed6ec4/napari/components/layerlist.py#L62-L66

If we do this I would be keen to start using the evented containers in psygnal

Czaki commented 2 years ago

you may also would like to trace #3781, which, I have hope,should be implemented before 0.5.0 and which should allow to set viewing area instead of zoom calculation.

imagesc-bot commented 2 months ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/accessing-private-attributes/100256/2