-
-
Notifications
You must be signed in to change notification settings - Fork 422
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
Multicanvas viewer #5348
Comments
Thanks for writing this up - I'm excited to see this work being pushed forward and am happy to help out as a reviewer in general on this work. Still need to collect some general thoughts on this, but here are a few points/questions that come to me first.
|
Thanks for checking:). I am currently working on it in between coursework, but from next week onwards I can spend more time on it. The initial step is indeed to implement the grid mode as described. This would also get rid of the current transformation being performed when applying the current grid mode. Given that a large part required for this proposed work is already implemented in Vispy @brisvag and I thought this would be the easiest to get started. Regarding point 2, it seems to me given the different layer dims this would require the implementation of what we now have called true multicanvas. However, I do believe we can make individual grid tiles resizable as these methods are already implemented for viewboxes on the vispy side. |
Actually, before the grid, the zeroth step is to decouple the canvas by splitting it into model and backend (just like we did with layers and recently overlays). I think we can/should do this in its own refactor PR without touching any functionality: it will make it easier to review when we actually work on the grid. As for point 2: I agree that this would (at least at first) fall within the true multicanvas world. |
@brisvag, did you already start on this yourself? To me intuitively seems that we could decouple the canvas functionalities one step at the time, for example #5296 and then next decoupling mouse events. The actual decoupling of the canvas would then be easier. What are your thoughts regarding this? Just checking whether I am thinking correctly about this. |
I didn't, I'm a bit busy until end of this week, feel free to take a stab at it! Sure, smaller non-breaking PRs are the best if you can manage :) The main refactor I refer to when I say decoupling would play out like thisin my mind:
|
Great to see the options and current roadblocks listed so clearly. Here's one comment from jni to add to the list of motivations. It's a use case that feels similar-ish to the orthoslice motivation listed above, so I felt it might be helpful to link here.
|
Another use case: VR can be implemented with two canvases with linked cameras some distance apart. |
uuuuuh nice! |
# Description This is a PR in preparation of multi canvas view as discussed in #5348. It is a refactor of mainly the QtViewer and the Vispy Canvas. A lot of the Vispy functionality was within the Qt viewer, while not granting a way for example for scene parents to be adjusted on the Vispy side later on. For implementation of the multicanvas viewer it is required to have more control on the Vispy side. Furthermore, ensuring that Qt and Vispy work as much as possible in isolation also ensures that the codebase of the 2 is not too entangled and thus allows for switching to other back or frontends more easily. With this PR, all the VispyCanvas calls have been moved to VispyCanvas, except for key events and mouse events. Rerouting key events is already discussed in another PR (#5296). For mouse events a PR will be opened at a later stage to allow napari to handle mouse events happening on different canvases and keeping track of them. This would require significant changes and thus would be better handled in a separate PR. The PR does not affect the user API. Also, backward compatibility has been added to take into account plugin developers who have based their plugins on the current codebase. Regarding the tests, these all have been adjusted to reflect the code changes. However, it could be worth discussing whether certain tests would be better to move to _vispy/_tests instead of _qt/_tests. ## Type of change - [x] Refactor # References This PR is a prerequisite for #5348 # How has this been tested? Locally using pytest. All tests passed. To the best of my knowledge the _process_mouse_event fix discussed in PR #1798 is not covered by tests. I tried recreating the issue to see whether the fix was still required, but could not recreate. To be certain the code fix has been maintained by [dda628d](dda628d), but it could be worth checking by others whether this fix is still required. ## Final checklist: - [x] My PR is the minimum possible work for the desired functionality - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] I have added tests - [x] If I included new strings, I have used `trans.` to make them localizable. For more information see our [translations guide](https://napari.org/developers/translations.html). --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Grzegorz Bokota <[email protected]> Co-authored-by: Lorenzo Gaifas <[email protected]> Co-authored-by: Andy Sweet <[email protected]>
This issue has been mentioned on Image.sc Forum. There might be relevant details there: https://forum.image.sc/t/how-to-maximize-this-custom-mulitple-view-grid/84400/2 |
This issue has been mentioned on Image.sc Forum. There might be relevant details there: https://forum.image.sc/t/how-to-maximize-this-custom-mulitple-view-grid/84400/3 |
This needs to be linked too: #1478 |
What's the status of the Multicanvas viewer? It is a really useful feature, especially for interactive analysis of multi-omics data. |
I have been a bit swamped and there has been discussion on how to actually open the PRs for this. At first the idea was to make it part of an experimental plug-in, but after some messing around this is actually more complex than implementing it in a hidden fashion in napari. This to first be able to properly test everything without messing up the user experience while at the same time keeping PRs small. End of the month I will have time for this. |
This issue has been mentioned on Image.sc Forum. There might be relevant details there: https://forum.image.sc/t/geometric-shapes-in-3d-interactive-mode/95798/2 |
Hi all! I’ve been starting to tackle multi-canvas (what’s called “True multicanvas” above) work from a design perspective. The goal at this point is not to reach some prescriptive visual design solution, but to provide ideas on how different answers to the lingering multi-canvas experience questions might actually appear in the viewer so that we can make decisions informed by the user-facing outcomes. At the time of writing, I’m aware of two similar features/workarounds users (and plugin developers) are using to get around a lack of “true multicanvas” in napari: grid mode and multiple viewers (I’ll call it multi-viewer). Multi-canvas work overlaps with these, but is still distinct to address pain points that continue to exist with grid mode and multi-viewer approaches. Use cases and pain pointsThe first thing I want to do is document some of the needs and motivations I see behind a potential multi-canvas feature. NAP-9 and the rest of this issue already do some of this and are included in what I’m describing. Use cases include
Pain points include
However, there’s also some things that grid mode and multi-viewer seem to be effective at. It’s worth paying attention to these to note what experience we may want to continue in multi-canvas. The strengths of grid mode are
The strengths of multi-viewer are
Spot the difference (feature comparison)To clarify what’s already supported in napari versus what is requested of multi-canvas, I put features side-by-side (like multi-canvas!?) in a table.
Software I’m looking atFor your reference, I’m comparing what has been described for multi-canvas to a few other pieces of software to see what user experiences are like for similar features elsewhere.
As always, feedback is welcome and encouraged. If this sounds right to you or if you think I misunderstood some part of multi-canvas, that would be very helpful to know. If you have anything else you’d like me to reference or a workflow you are hoping to complete with multi-canvas, feel free to share. Thank you! |
A few other examples of multi-canvas viewers.
|
QuPath (https://qupath.github.io) also has this feature. There it's called multiple viewers, but the viewer in QuPath is analogous to the napari canvas. Edit: in case it's not clear, the QuPath "viewers" are all in the same application window, which in napari terms would be in the same viewer. |
Ilastik: https://www.ilastik.org/gallery# |
Thanks @isabela-pf, great summary as always 💪
I was referring to this functionality, which I find very intutive and convenient: https://www.youtube.com/watch?v=DIF4pUHNuwU
This can actually be done by adjusting the It's currently under discussion whether we should allow this level of control on grid-mode (potentially sacrificing part of the current once-click-and-done nature) or if this should be a true-multicanvas-only feature. |
Thanks for these @isabela-pf! I don't have time to go over every element in detail but for now I just wanted to drop in and say that I really like the idea of tabbed layer lists for the canvases. I was honestly quite confused about how we were going to handle these things. 😅 I like that better because (a) one could break up the layer lists into multiple panels if one has massive amounts of monitor space, and (b) I don't think clicking on a canvas to select it (last option) should be the primary interaction mode because clicking on a canvas typically would have a few side effects, and it's one of those things that makes you think, "if I click on this am I going to also add a point/change a view focus/etc? Or do I need an extra click for that?" |
Thanks for the breakdown @isabela-pf! Strong agree with @jni: we should use dock widgets for layerlists, so we get for free the ability to tile, tab and split out to a new window. I think the same is true for the canvases themselves, so I would go for a combination of "Multiple layer lists" and "Canvas Tabs". We should discuss about To get more into details:
I don't think grid-mode should be replaced. Forgive me if I sound like a broken record on this, but I really think grid mode and "true multicanvas" are solving 2 different problems, and using multicanvas as a stand-in to grid mode would be like using a cannon to kill a fly. As to where to put the "add canvas" button, I think I prefer the "between layer controls and layer list", as every other place seems off-topic to me. No strong opinions here though.
Grid mode is just an alternative way for a canvas to display its contents (i.e: some canvases may be in grid mode while others aren't). Multiple viewers should be able to accept canvases from other viewers with a simple drag-n-drop. Ultimately, a viewer should be just a container for canvases which takes care of displaying controls, layerlists and dims for them.
Whichever one is selected on the tabs. In case of un-tabbed modes, we should probably have a colored border to highlight the corresponding widget?
Cameras should be unaffected: each canvas has its own camera which behaves the same as the current. We should offer api (and some simple gui) to link cameras from different canvases, as well other things (like dims).
If we use dock widgets, it should be straightforward! |
My comments + notes in response to @isabela-pf's discussion during the community meeting today: +1 for keeping grid mode. @brisvag has good arguments here. Maybe grid mode can be less of a first class mode (e.g. replace the grid mode button, but still preserve the ability to use grid mode)
What types of controls could we imagine for canvases?
How many canvases do we need to consider supporting eventually? In the limit of tons of canvases napari presumably becomes unusable, so are we thinking like 4, 8, ??? Why is there 1 set of dims controls, shouldn't there be a unique set of dims controls be on each canvas?
Can the layer list reflect the currently selected canvas, and have a button (or smth) that would switch to a "global" layer list?
|
Just a side note: in conversations with @kushalkolar, we were trying to understand our separate vocabularies for napari and fastplotlib, and came up with Viewer <=> Figure and Canvas <=> Subplot. In that mind frame, I think that the number of canvases should be unlimited, or rather limited only by resources (screen size, GPU memory, etc) rather than some arbitrary code limit, and we should think about both automatic and guided layout algorithms for subplots, as well as several "named" layouts such as orthoviews. |
limited - should mean that we do not care that the interface looks bad over a given number of canvases. |
I don't see why we would put a limit. If it looks bad, the solution is easy: reduce the number of canvases or split into separate windows. |
Uses less screen real estate, easier to use the same console, no extra windows to track via window manager. Why browser tabs instead of new windows? :P
There should be a notion of
Not sure about a "global" list, what's the intended purpose? But yeah, I think layer list should match the active canvas (and have tabs to switch to others). |
I wasn't in the meetings so I'm probably missing the arguments for this, but I don't see why we would do this. Grid mode is a quick toggle just like 2D-3D and axes swapping, and people are used to having it there. It makes sense to me that it should stay. Even if we thing that multicanvas should also be there, why not just add the button instead of replacing? Altough I also don't think we should put the "add canvas" button there, to be honest. Can someone write down here a summary of why this was the conclusion at the community meeting? Most of the other proposed options for how to add a canvas make more sense to me. |
When discussing the last mockups at the community meeting, I received feedback that there wasn’t confidence that previous choices were being made based on actual use cases. There was also a discussion about all the possibilities multi-canvas opens up, and an immediate agreement that it’s a powerful concept that’s very easy to go overboard on. Out of this discussion we also agreed it would be helpful to outline the features required to create a fully usable multi-canvas without building for niche features yet. Feedback that would be especially useful at this juncture includes:
Use CasesUse cases are the “why” behind requests for multi-canvas in napari. Earlier in this issue I described use cases found from reading the various issues prior to this one. They are general umbrellas that hold more specific use cases underneath them. I’ll organize this section by similar groupings while also providing examples of more specific use cases. 1. Viewing the same data different ways side by sideThis involves loading identical data into different canvases whether it is loaded repeatedly or linked across canvases. Users may want to
Request for an orthogonal view is one of the use cases I’ve seen most. 2. Comparing the same data processed differentlyThis involves loading the same data into different canvases and performing different actions on it per canvas. Users may want to
3. Viewing related data side by sideData may be gathered as a series, belong to different runs in the same set, or have other relationships. Exploring this data in full can benefit from being viewed side by side as multi-canvas would enable. Users may want to
4. Viewing different data side by sideThis is loading different data into different canvases. Users may want to
Required featuresIn order for multi-canvas to be fully functional, I propose it needs the following:
Based on our discussions, I think the following might also be considered, but I do think we can have some kind of multi-canvas experience without them as well.
What’s nextI’d like to make a decision that can help us move into more specific questions and interactions if possible. |
100% agree on the must-haves. Ont he others:
Can already be done via
IMO not just a must have, but basically an inevitable implication of "add/remove layers to canvas" and my point above.
There was a discussion about how to transition from "assuming there is a single canvas" to "there are now multiple canvases" and how plugins would interact with that. Our (temporary?) solution was to have the concept of an "active canvas" and for now ensure that any API that worked before (
Should come for free :P |
Adding another comment to bring this up on people's notifications. Feedback on if this minimum approach seems like a good first step to aim for when multi-canvas development starts, isn't certain about this option, if information and/or features are missing are all helpful. Other feedback is welcome as well. I'm starting work on NAP-6 and will have some split napari attention as the year heads towards its close. It'd be great to find some agreement on direction on multi-canvas work in order to keep inching these efforts forwards. Thank you! |
Will we be able to pull a canvas out of the main window (like you can do with dock widgets)? If so, will we also be able to pull the per-canvas layer list out? This might be silly, but I'm thinking about how usable this would be in a dual monitor setup... |
@kephale I don't think that's silly! It makes a lot of sense to consider, especially with the frequency I've heard people using napari mention dual monitor setups. As far as I'm aware, napari currently doesn't allow for undocking the single canvas from the main window (presumably because everything else around it can be undocked and it can be alone in the main window as if it were undocked). So this makes me ask Is the intent that only the additional canvas(es) are undockable from the main window? (Indicated here by the overlapping square icons like those used in napari.) Or is the intent that all canvases become undockable? Which would also potentially leave the main window empty if all widgets were to be undocked at once. |
I don't see why would special case a canvas over another... we should also be able to reorder them, or show them in tabs. So I would say we should just use the qt dock widgets. |
At today's community meeting we discussed this last direction of the minimum necessary functionality and interface changes to the viewer to have multi-canvas. So far, there does not seem to be any comments against this direction and multiple in favor of it. I'll be following up in more detail so there is a more comprehensive version of what this approach needs in terms of user interactions. For now, I wanted to summarize the main points of discussion at the meeting today.
Thank you all for following up on multi-canvas! |
I mostly agree, but I think the plan (mainly for API reasons) was to have a concept of a "main" canvas. If this is still the case there may be places it needs to be special-cased. For example will we allow users to close the main canvas? If so, does another canvas become the main? Can users close all of the canvases and have a viewer without a canvas? |
By that I meant that I don't think there should be one canvas that's always the "main" and other secondary ones that will always be secondary. I think this would lead us down wrong paths in the implementation, and won't actually save us work in the long run. Instead, IMO we should aim to achieve this special casing by having an "active" canvas. I would say no, let's prevent at least for now removing all the canvases, that should be easy and save us some pain. I imagine the implementation path like this (ideally in separate PRs): |
🚀 Feature
A napari multicanvas view has been a long-requested feature that could be implemented in different ways.
There are two main approaches that we are interested in:
This issue serves to discuss the various possibilities for implementation in Napari.
Motivation
Over time in several issues, various motivations have been described for the multicanvas view. To name a few:
For previous discussions see the following issues:
- #2338 Multicanvas API Thoughts
- #760 Linked multicanvas support
- #662 Linked 2D views
- #561 multicanvas grid display for layers in Napari
Also:
- NAP-3 Spaces
Pitch
Alternatives
Definition of multicanvas
There are several components that may or may not be part of a Multicanvas implementation. Here are some possible features, starting from the simplest in terms of interface, and slowly adding complexity:
Additionally, there are two main ways to obtain multiple canvases in the GUI:
a.
Subdivide a single GL canvas into multiple viewboxes ("virtual" canvases).b.
Create separate canvasesAny combination of the above is possible, and as much customizability is desirable. However, there are two "main use cases" that we think should be handled differently:
Grid Mode
This is the combination of
1
(but possibly also allowing2
and3.1
. later on). Due to it being easier to implement at the moment, this would be the first to be implemented (see explanation below).It should be a drop-in replacement of the current grid mode, but instead of translating each layer in world space, a grid of viewboxes is used. Cameras a linked (at least by default), stride and shape can be set (like current grid mode) and a single layer list is used and automatically distributed across viewboxes.
True Multicanvas
This is the combination of
1
,2
,3
(preferably3.2
) andb
. This cover every other use case, by putting more power (and more responsibility) in the hands of the user. It should allow (or not disallow) things likeNotably, it could be possible (and maybe desirable) to allow a combination of the two implementations above. One might have two "true canvases", where the second one is in
Grid Mode
and thus split into a grid of mini-canvases.Current situation
++napari.Canvas and vispy.scene.canvas.SceneCanvas++
Currently the napari.Canvas is a Vispy.scene.canvas.SceneCanvas in the backend which automatically draws the contents of a scene and is able to receive a set of events:
One complication is that currently, these events are firstly handled on the Vispy side. The flow of events looks like this:
In order to not surrender control to Vispy we would like to move to the situation of:
For this please take notion of #5296 Reroute key events.
++Grid mode++
Currently, a grid mode has been implemented in Napari which arranges different layers in a grid that is viewed using one camera. To be able to display all image layers in the grid, the stride parameter is used to control the number of layers displayed in a grid square. While already useful, it's impossible for example to zoom in on a feature and see it on every layer.
Under the hood, this works by setting the
World2Grid
transform of each layer, which is a simple translation. This results in the layer being "physically" shifted in world space to the new location. (This is also currently buggy if one attempts to roll dimensions because this is not taken into account by the machinery that sets the transform.)Event coupling issue
One of the first obstacles in this work is caused by the current tight coupling of the Vispy backend and the Napari models when it comes to the canvas. The
QtViewer
code in particular is hard to disentangle from the Vispy code.Proposed multicanvas implementations
In general, decoupling the canvas model from the Vispy backend is the first step for a successful implementation. The
ViewerModel
should hold a new field calledcanvas
which is itself anEventedModel
:Events from this model would be sent to a wrapper around
vispy.SceneCanvas
(similarly to ourVispyBaseLayer
objects) which would take care of translating the model into vispy calls.Grid Mode
Part of the work for Grid Mode is already done on the Vispy side. It adds a grid to the central widget of the canvas which is then populated by adding a viewbox to each grid tile. Each viewbox has its own camera. The Vispy implementation also has for each camera a list of which cameras are linked and which properties have to be synced between the different cameras. Cameras are not linked by default. This backend linking could be used by napari, or we could write our own implementation (depending on what fits best).
It should be relatively easy to implement a drop-in replacement of the current grid mode with something like this:
The vispy backend could then receive events accordingly, and if
Canvas.grid.active = True
, generate a grid and reorganize the scenegraph accordingly.As stated before, each viewbox has its own camera. To me it intuitively seems the most logical to have it as a default that all cameras are linked. There seem to be multiple ways by which this can be achieved.
I think it is not necessary to have each camera linked to each camera. Instead, one 'host' camera would be linked to all cameras, while all the other cameras are linked to the 'host' camera. In this case, QT would receive an event on a grid tile that is propagated to the camera of the viewbox, which then propagates it to the host camera and from there to all the other cameras.
This implementation would be less dependent on Vispy. In this case, QT would receive an event. Napari determines on which grid tile this event was created. Napari keeps track of the cameras that exist and should be synced and emits the event to all cameras accordingly. Potentially, psygnal could be used for this.
For both cases at a minimum it is required to have a wrapper around
vispy.scene.widgets.viewbox.ViewBox
. This wrapper is exposed through the grid class (see above). In case of only linked cameras, only 1 camera would have to be exposed on the Napari side, but since a camera list seems to be easy to implement this would not be the first option as a camera list for which it is tracked which ones should be linked is a more generalized solution.NOTE: The layer._get_value() method might have to be updated to work in viewbox coordinates instead of canvas coordinates.
True Multicanvas
For a True Multicanvas implementation, there are more design and UI/UX considerations to do. For example:
Canvas
objects live? A list inViewerModel.canvases
?Canvas
have its ownDims
andCamera
? How do we expose them? And if not, where do we put them?Additional considerations
The current grid mode in Napari makes use of stride in order to control the number of images displayed per grid tile. While this ensures that all image layers can be viewed in grid mode on one canvas, it does create a composite image which can be less informative than looking at grayscale images. In the case of highly multiplexed imaging, the number of markers can exceed 50. In this case, I believe users would like to be able to switch to different 'tabs'. Each tab displays a grid and is only rendered as the tab is activated. Should this be implemented in the Napari core or as a plugin?
In addition to this, a user might like to save the view settings of what is displayed on each grid and how. This configuration could then be imported again in Napari, activating the grid mode and displaying the content according to the configuration.
Additional context
For work on layer lists, it was mentioned during one of the Napari community meetings that @jwindhager has been working on this.
Co-authored by @brisvag
The text was updated successfully, but these errors were encountered: