Git Product home page Git Product logo

napari's Introduction

napari

multi-dimensional image viewer for python

napari on Binder image.sc forum License Build Status Code coverage Supported Python versions Python package index Python package index download statistics Development Status Code style: black DOI NEP29

napari is a fast, interactive, multi-dimensional image viewer for Python. It's designed for browsing, annotating, and analyzing large multi-dimensional images. It's built on top of Qt (for the GUI), vispy (for performant GPU-based rendering), and the scientific Python stack (numpy, scipy).

We're developing napari in the open! But the project is in an alpha stage, and there will still likely be breaking changes with each release. You can follow progress on this repository, test out new versions as we release them, and contribute ideas and code.

If you want to refer to our documentation, please go to napari.org. If you want to contribute to it, please refer to the contributing section below.

We're working on tutorials, but you can also quickly get started by looking below.

installation

It is recommended to install napari into a virtual environment, like this:

conda create -y -n napari-env -c conda-forge python=3.9
conda activate napari-env
python -m pip install "napari[all]"

If you prefer conda over pip, you can replace the last line with: conda install -c conda-forge napari pyqt

See here for the full installation guide.

simple example

(The examples below require the scikit-image package to run. We just use data samples from this package for demonstration purposes. If you change the examples to use your own dataset, you may not need to install this package.)

From inside an IPython shell, you can open up an interactive viewer by calling

from skimage import data
import napari

viewer = napari.view_image(data.cells3d(), channel_axis=1, ndisplay=3)

napari viewer with a multichannel image of cells displayed as two image layers: nuclei and membrane.

To use napari from inside a script, use napari.run():

from skimage import data
import napari

viewer = napari.view_image(data.cells3d(), channel_axis=1, ndisplay=3)
napari.run()  # start the "event loop" and show the viewer

features

Check out the scripts in our examples folder to see some of the functionality we're developing!

napari supports six main different layer types, Image, Labels, Points, Vectors, Shapes, and Surface, each corresponding to a different data type, visualization, and interactivity. You can add multiple layers of different types into the viewer and then start working with them, adjusting their properties.

All our layer types support n-dimensional data and the viewer provides the ability to quickly browse and visualize either 2D or 3D slices of the data.

napari also supports bidirectional communication between the viewer and the Python kernel, which is especially useful when launching from jupyter notebooks or when using our built-in console. Using the console allows you to interactively load and save data from the viewer and control all the features of the viewer programmatically.

You can extend napari using custom shortcuts, key bindings, and mouse functions.

tutorials

For more details on how to use napari checkout our tutorials. These are still a work in progress, but we'll be updating them regularly.

mission, values, and roadmap

For more information about our plans for napari you can read our mission and values statement, which includes more details on our vision for supporting a plugin ecosystem around napari. You can see details of the project roadmap here.

contributing

Contributions are encouraged! Please read our contributing guide to get started. Given that we're in an early stage, you may want to reach out on our Github Issues before jumping in.

If you want to contribute or edit to our documentation, please go to napari/docs.

code of conduct

napari has a Code of Conduct that should be honored by everyone who participates in the napari community.

governance

You can learn more about how the napari project is organized and managed from our governance model, which includes information about, and ways to contact the @napari/steering-council and @napari/core-devs.

citing napari

If you find napari useful please cite this repository using its DOI as follows:

napari contributors (2019). napari: a multi-dimensional image viewer for python. doi:10.5281/zenodo.3555620

Note this DOI will resolve to all versions of napari. To cite a specific version please find the DOI of that version on our zenodo page. The DOI of the latest version is in the badge at the top of this page.

help

We're a community partner on the image.sc forum and all help and support requests should be posted on the forum with the tag napari. We look forward to interacting with you there.

Bug reports should be made on our github issues using the bug report template. If you think something isn't working, don't hesitate to reach out - it is probably us and not you!

institutional and funding partners

CZI logo

napari's People

Contributors

aganders3 avatar ahmetcansolak avatar alisterburt avatar andy-sweet avatar brisvag avatar carreau avatar czaki avatar dalthviz avatar dependabot[bot] avatar dragadoncila avatar dstansby avatar genevievebuckley avatar goanpeca avatar grlee77 avatar jaimergp avatar jni avatar jookuma avatar kcpevey avatar kevinyamauchi avatar kne42 avatar liu-ziyang avatar lucyleeow avatar melissawm avatar napari-bot avatar ppwadhwa avatar pre-commit-ci[bot] avatar psobolewskiphd avatar pwinston avatar sofroniewn avatar tlambert03 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

napari's Issues

Making Viewer a self-contained Structure

I would prefer to see this code inside of the viewer,
Any reason it is not there?
I would like to keep the window to a minimum and
only concerned with 'window'-specific things like menus, etc...

The viewer widget should be self contained and should be able work independently of the window,
(as part of something else for example...).

Originally posted by @royerloic in https://github.com/Napari/napari-gui/pull/63/files

@royerloic, currently viewer is not the thing you are talking about (that's why it is not there but here), however i think a self-contained viewer would be better and we can easily switch to that. Just requires some easy refactoring. If we are all okay with that i can address this right away after clim PR.

Creating examples folder

Currently we have a demo.py in the root. This should change and we should have examples folder in the root and examples in it.

Shape layers API

❓ Questions and Help

I've done some exploring of how to make polygons etc in vispy and have now found some highly performant approaches (drawing 10,000's very quickly and zooming / panning with no lag). The question is - how do we want to expose shapes to our users both those working from the notebook and those working in the gui?

Right now we have the add_image and add_markers function which adds layers of those types. During my initial exploration of this stuff I created add_rectangles and add_ellipses functions to add those layers too. While that was nice from working in the notebook, it became frustrating in the gui to be forced to have the different shapes in different layers and switch layers when wanting to interact with them. I raised the idea of instead having a generic shape layer and add_shapes instead at the meeting on thursday and people seemed to prefer this approach. Curious though if anyone disagrees.

If we have an add_shapes function on the viewer then what are it's inputs? If we want to support markers, rectangles, ellipses, line-segments, polygons, lines etc. how do we get this information in?
One option is to pass a list of dictionaries like the following

add_shapes([{'type': 'markers', 'data':[xxxx]}, {'type': 'polygons', 'data':[zzzz]}, {'type': 'markers', 'data':[yyyy]}])

or a single dictionary

add_shapes({'markers':[xxxx, yyyy], 'polygons':[zzzz]})

or just as keyword arguments

add_shapes(makers=[xxxx, yyyy], polygons=[zzzz], lines=None, ellipses=None)

As we added new shape types we would add support for new elements of the dictionary / keyword args.

I'd lean towards the third option - but one important question is how granular can the additional shape properties get - i.e. face_color, edge_color, edge_width, line_color, line_width, marker_edge_color, marker_face_color, marker_size, edge_width, marker_edge_width, marker_symbol.
How many of these properties are forced to be the same across classes - i.e. all rectangles vs all ellipses vs all lines?
And how many of these properties are allowed to vary within instances of the same class?
Some of them I know how to make different for each instance within the same class in a fast way, others I don't know how to do that yet (though it is ultimately possible somehow).
Given these considerations how does it look to set these properties from the notebook and how does it look to change them from dropdown menus in the gui. Right now most of these properties apply to all instances of all classes - that's the simplest way to have the dropdown menus work. But it is possible to be more like illustrator where you can select specific instances and then recolor say specific ones. It would probably suggest a different type of UI though where those color tools were now not tied to specific layers. In our system if you want to have red polygons and blue polygons you would create two different layers. I'm not really sure right now how to give people "rainbow colored" / "randomly colored" shapes either.

Finally after you've created one of these shape layers and added some shapes to it, if you now want to add more data from the notebook what additional methods should be available to you?

layer.append_data(......)
layer.set_data(.....)

These could be two nice options that allow you to either add shapes to the existing ones using append_data or to replace all the data using set_data.

Color blending across layers

❓ Questions and Help

A use case that I think will come up frequently is having multiple color channels of data that we want to visualize simultaneously (probably most commonly between 2-6 at a time). I think we want to create an independent image layer for each color channel, assign each it's own colormap with independently adjustable clims (which we can do now), but what we then lack is the ability to do any form of color blending (beyond a global opacity). FIJI does this quite nicely - see bellow two channels of information being blended together (one green / one magenta). Does anyone have any thoughts on how to implement this behaviour using vispy? @royerloic @freeman-lab - I tried digging around a bit to see if blending modes were present and I couldn't find any. I can imaging how we would do these computations in the cpu using numpy (but I'm guessing that's not a popular option!!!).

Also as side note vispy lacks a lot of the colormaps that we'll want for cell biology that go from black to a particular color (like red, green, blue, cyan, yellow, magenta), and I think we'll need to add them.

image

Stylings for editing text of layer names

From @jni

  • frame around it with background matching the surrounding
  • when you click on it, the background becomes white and you're editing
  • when you press enter or click out, the rename is executed and the background goes back to matching the surrounds.

Layer selection rules

How should we determine how layers can be selected from the GUI list?

Since it is a list, imo it's most intuitive/standard to interact with it in the following way ("this layer" always refers to the last clicked layer):

  • click
    • if no layers are selected, select this layer
    • if another layer is selected, (deselect them and) select only this layer
    • if this layer is selected, deselect this layer
  • ctrl/cmd click
    • toggle inclusion of this layer in the current selection
  • shift click
    • first click: select this layer
    • second click: select this layer and all other layers in between the this and the first clicked layer

1D, 2D, 3D viewing

The viewer should supports 3 modes:
1D: Using vispy plotting integrated in the scene graph
2D: current code
3D: Vispy volume renderer

The number of sliders is automatically adapted,
other types of objects (lines, points, etc..) should also be displayed
by simple projection in a straightforward manner.

Unify layers.ipynb and demo.py example

❓ Questions and Help

Right now our notebook and scripting examples use slightly different versions of the api imshow and add_image. These should be unified.

In general we should think about what examples we want and what testing we want to ensure all our code is working properly.

Features to 0.1 release

Here's a list of features that I put together during last Thursday's meeting - can serve as discussion points for future meetings. Overlaps with some previous issues, but thought it would be convenient to put it all in one place.

  • Simplify grid layout of window into viewer, layerlist, slicing sliders and cmap sliders. Include adding splitters so can chose to display or not.

  • Add selection capability to layer widgets.

  • Link changes in notebooks to updates of the layer widgets. Possibly use vispy or qt event system.

  • Further improve layer widgets. Add custom properties. Add ability to rearrange. Add ability to delete

  • Add keyboard shortcuts.

  • Show additional information about images such as dimensions / x,y cursor position, grayscale value at cursor

  • Add additional layer types for lines and polygons

Plugin mechanism for layers also provides GUI elements

The concept of layer is a pairs (data,visual).
'data' being a data object (image, points, lines,etc)
and the corresponding 'visual' that represents visually
this object as a layer in Napari. Since we want to have sliders and
buttons that control how layers are displayed (min, max, gamma, etc).
It makes sense to provide a path for adding these elements in
a modular way as part of the 'layer registration' so that no hacking
of the UI code would be required for new layers needing new controls
such as sliders... etc...

We can discuss how to do this elegantly without introducing complexity.
I see two main approaches:

i) declarative: layer plugins declaratively specify what we want and it is created for them.
Pro: very clean. Con: can become very complicated because we essentially create a layer to pyQt.

ii) hook-based: we provide entry points and 'receptacles' for the PyQt objects created by the plugin side. For example, controls can be added on all three sides, or attached to each layer list item, etc...
Note: values from existing controls (alpha, brightness) can be used by any layer if they want to...
Pro: much more scalable and all features of pyQt are accessible. Con: reduced encapsulation as we give indirect access to much of the UI.

I thinks scalability wins. Option (ii) is simpler, more scalable, relies on a direct access to a solid api (pyQT), and can be clean if we provide well defined 'receptacles' for dropping the UI elements .

cmap vs colormap on a layer

❓ Questions and Help

As initially posed in #89 - both cmap and colormap exist as properties on a layer, but have slightly different setters. It's not clear to me what the difference is or why we need both. If possible it would be nice to eliminate one. Maybe @kne42 has the answer?

Add basic colormaps

🚀 Feature

As discussed in #89 it would be nice to add some standard colormaps that people might want to use (particularly in biology) like the following:

Blues - black to blue
Reds - black to red
Greens - black to green
Magenta - black to magenta
Yellow - black to yellow
Cyan - black to cyan

It might also be nice to add some standard diverging ones too like red to blue through white or red to blue through black - though I don't think we want to try and make an exhaustive list

Similarly looking through the list of colormaps now it seems like there are some that could be removed as they are less popular

Intensities in are normalized by values in first frame

🐛 Bug

If the first slice of a z-stack is all zeroes, then subsequent slices appear all white. I believe that intensities are being normalized to the range of the first slice.

To Reproduce

Steps to reproduce the behavior:

from napari_gui.elements import Window

img = np.random.randn(100, 100, 2)
img[:,:,0] = 0
# img[:,:,0] = img[:,:,0]*0.1
# img[:,:,1] = 0

win = Window()
viewer = win.add_viewer()
layer = viewer.add_image(img, dict(itype='mono'))
layer.cmap = 'grays'
win.show()

Expected behavior

Intensity scaling should be done for the whole stack at once (or perhaps as a per-slice option), not based on the first slice.

Environment

  • napari-gui Version (e.g., 1.0): 0.0.4+14.g9a08159
  • OS X
  • Python version: 3.6.4

Support visualization of lazy ndarrays

🚀 Feature

Natively support lazily-defined arrays, e.g. memory-mapped arrays, distributed dask arrays, etc.

Motivation

For large arrays that don't fit in memory (e.g., light sheet microscopy data), it's very convenient to use an interface that represents the data as if it were an in-memory array. In python: numpy arrays via np.memmap or dask.array, hdf5 files via h5py; imagej has virtual stacks for the same application.

It would be awesome for a python-native array visualization tool like Napari to recognize and support this kind of data / interface.

Disclaimer: I don't know the napari internals at all, so you might already have this functionally baked in, or it's on your roadmap. But I didn't see a public discussion about this topic, and I haven't seen such a discussion anywhere else, so this seemed the best place to start it :)

Pitch

Suppose I have a 4D dask array distributed along axis 0 (a typical scheme for volumetric microscopy data, which might be stored 1 file : 1 3D timepoint ) and I want to visualize 2D slices from this dataset. I would like to run code like this:

viewer = win.viewer
layer = viewer.add_image(dask_array, meta) 

...and adjust sliders to see different planes and different timepoints, all without having to load the entire dataset into memory. In my example the data happens to be distributed along the 0th (time) axis, so seeking through time involves loading data from disk, while seeking through space (axes 1,2,3) involves displaying data that are already in memory. Alternatively, each image might be really really big and thus chunked in space AND time (i.e., all axes are distributed), and so just displaying a full plane for a single timepoint requires loading multiple chunks of data from disk.

Working with lazy, distributed data adds lots of new complications: to reduce latency, the display software might asynchronously cache data along the distributed axis; the display code will need to know about the data's chunking scheme, the display code may need gui elements to indicate distributed vs local axes of the data, etc.

I know that this scheme is standard for visualization of EM data; but for EM data, most interactive operations (like tracing neurites) are local, in that only a single, small, contiguous block of data needs to be in memory (i.e., displayable at low latency). But for live imaging data -- data with a temporal dimension -- analysts often want the entire temporal context of multiple spatial regions, and the ability to quickly "span" the dataset from start to stop, and the ability to visualize a post-processed version of the data, and a lot of other things that analysts working with dead tissues rarely, if ever, need to do.

So... it would be cool if napari could support this! Since things are early, maybe it's possible include features in the API such that distributed / lazy datasets have a native interface.

Security on GitHub

We all like the idea of GitHub repositories as plugins. However, GitHub has a dramatic security flaw, which is that deleted usernames can immediately be reclaimed by anyone on the internet. (!) This means that if a developer of a trusted plugin closes their account, there is nothing to stop someone from replacing the plugin with the plugin + a bitcoin mining bot, or worse.

None of us are security experts at the moment but this is a big hole, and I wonder if there is anything significant to be done here to address this. (e.g. is there some metadata accessible in the GitHub API that would make this attack not work?)

https://www.theregister.co.uk/2018/02/10/github_account_name_reuse/

Future of Dev branch

❓ Questions and Help

What do we like to see about dev branch in future?
Shall we delete it? Or keep it?
We will be now trying to have feature branches and we might want to try back having a dev branch in future? Are we interested in to keep commit history separately? (we already merge all commits in master, fyi)

In my opinion, we can delete it since we have commits in master already. Wondering your thoughts!

Add names to vispy colormaps

🚀 Feature

As described in #89 currently the vispy colormaps do not have a name field which is the string by which they can be created. It would be useful to have that name field so that we can reference it in the dropdown menu when the colormap is changed in the notebook or on launch.

We could either add this directly in vispy or make a wrapper ourselves. The former is probably slightly nicer, as acknowledged by @jni

Behavior After Deleting All Layers

❓ Questions and Help

Once you delete all layers, various IndexErrors are thrown.
What is the expected behavior instead? accordingly we can plan to take a decision about it.
Please provide your understanding and the behavior you are expecting.

Release schedule

You've all added quite a few features that we'd like to use in starfish (here I'm thinking about 3-d markers and improvements to the index). When will you make the next release? Do you have plans to instantiate a release schedule?

Input args to add_markers

As noted in #88 right now it's awkward to create a new empty markers layer. One solution proposed by @royerloic and @jni is to pass a shape argument like size=my_tuple. This will solve a number of ambiguities and should be implemented in a future PR

Expected behavior of clims range slider

❓ Questions and Help

I'm making this issue to discuss expected behavior of the clims range slider.

I think the slider should only interact with the top most selected visible image layer. If no image layers are selected and visible then the slider should be functionally disabled and visually greyedout. If multiple image layers are selected and visible changes of the slider will only affect the topmost one.

One question is when an image layer is selected what should the min value and the max value of the range slider should be pegged to? Options are

  • [0, 1] if the image is a float
  • [0, 255] if the image is uint8
  • [0, 65,535] for uint16
  • [0, 4,294,967,295] for unit32

As was pointed out in the meeting, little data uses that full range. Many cameras are 12-bit or 14-bit. So another option is to look at the max value of the image and set it to say the biggest power of 2 that is greater? Another option is to only use these values as defaults and instead allow the limits to be set in the meta data field when the image layer is created.

The clims slider can be then initialized to the min and max value of the image passed, but are then adjustable up to the ranges described above.

Thoughts @jni @royerloic @AhmetCanSolak ?

Window, viewer and layer API

Here is a mockup for what the viewer and layer api should look like (roughly):

Create a window:
window = gui.newwindow(name, ...other options...)
Note: We should distinguish the concept of 'window' and 'viewer'.
you can have several viewers in a window, organised either as tabs
or side by side, etc... Tabs are probably the best way to do it.

Create or removes a viewer in a window:
viewer = window.addviewer(name=None, ...other options...) %optional name
viewer = window.removeviewer()

Most of the time we just want a viewer straight away:
viewer = gui.newviewer()
This creates the window on the fly...

Even more often, we want an image displayed in a viewer inside of a window, in a one-liner:
viewer = gui.imshow(...list of numpy arrays..., rgb=None)

semi-internal api for adding/removing a layer of a given type:
viewer.addlayer(ImageLayer(... list of numpy arrays ...))
viewer.removelayer(name or index of layer)

convenience api for common layers:
viewer.addimages(... list of numpy arrays ...)
viewer.addpoints(... list of points as nparray ...)
viewer.addlines(... list of lines as nparray ...)
etc...

get a layer from a viewer:
layer = viewer.getlayer(...index or name of layer...)

set the viewer sliders on a given layer:
layer.setposition( tuple of ints )
Note this must be a complete tuple that also includes the displayed dims!
for simplicity we should also have:
layer.setposition(dimindex,position )

Different layers have different available properties, for example:
layer.cmap = ...
layer.opacity = ...
etc...
In the case of image viewers, we have an api for the different layout modes etc...

We can always retrieve the reference to the data that the layer represents:
data = layer.data
for example the numpy array, etc...

And force the layer to refresh itself and reflect any change of the data:
layer.refresh()
Note: we don't have yet a mechanism to know when numpy arrays have changed
their content for example...

Choose coordinate convention for docs

@jni made a comment about coordinate conventions in the docs at the end of #21 that didn't get addressed. I thought I'd make an issue, as I'm sure people have thoughts they'd like to discuss.

@jni:

As a minor quibble, in examples documentation etc, can we use the coordinate convertions from scikit-image? ie tprc,ch instead of xyzct. (I did just realise that columns conflicts with channels. Oh well.)

Getting Clim Values From Layers

❓ Questions and Help

Hello to all Napari friends.

I am currently trying to implement the clim slider feature.
To initialize the vertical range slider for clim, i need to get the minimum and maximum color values for all selected layers. Hence i am trying to reach to viewer.layers._list list and will be iterating over that list to find min and max color limits. However, we do not have the layers/layerlist in the context of qt/_control.py.

  • Would you like me to pass layers to Controls contructor in _viewer.py?
  • or are we considering to come up with a context management for separate qt widgets?

Idea: use namespace packages in conjunction with plugins

Introduction

As discussed in our last meeting, namespace packages could be a handy way to ship useful core modules separately for use as standalone packages. This could be extended to all plugins that could be useful outside of the broader Napari ecosystem, such as napari-gui.

This would provide consistency in import statements regardless of how a plugin is installed, as well as allow pip-installable plugins to once again reference themselves in absolute imports instead of requiring all imports to be relative.

What are namespace packages?

Namespace packages are best explained by a simple use-case. Imagine two packages foo.a and foo.b. foo.a and foo.b are independent and can be shipped separately. In this case, foo would be considered a namespace package. To achieve this, we have the following structure:

pkg-foo-a/
    foo/
        a/
            __init__.py
            ...
pkg-foo-b/
    foo/
        b/
            __init__.py
            ...

These are known as implicit namespace packages and they were introduced in PEP 420 (available in Python 3.3+).

In Python <3.3, namespace packages were created using the pkgutil.extend_path function under each namespace's __init__.py:

# foo/__init__.py
__path__ = __import__('pkgutil').extend_path(__path__, __name__)

pkgutil-style namespace packages shall henceforth be referred to as pseudo-namespace packages.

Proposal

Implementation of this structure would require two main changes:

Refactoring existing plugins

Plugins will no longer be importable under napari.plugins.<name> but under napari.<name> itself. If installed from pip, it will also be importable under napari.<name>.

For example, the napari-gui repository structure will look like so:

napari-gui/
    napari.yml
    napari/
        gui/
            __init__.py
            ...
    ...

Its specification will change to:

# napari.yml
plugin_info:
  plugin_name: gui
  path: napari/gui/__init__.py
  ...
...

Plugins using setuptools.find_packages() in their setup.pys will find that it no longer works. This can be fixed with a simple list comprehension:

# setup.py
from setuptools import setup, find_packages

setup(
    name='napari-<name>',
    ...
    packages=[f'napari.{pkg}' for pkg in find_packages('napari')]
)

Plugins that do not need to be served as independent pip packages should not place their code under a napari/<name> directory.

Implementation

Only a few functions need to be changed, such as napari/plugins/machinery/_load:get_plugin_namespace.

The contents of napari/plugins/__init__.py will be moved to napari/__init__.py for use with the plugins package only. It will be a pseudo-namespace package, but will only work if no other namespace package specifies napari/__init__.py:

# napari/__init__.py
import sys
import pkgutil


class NapariPluginImporter:
    """MetaPathFinder for Napari plugin imports."""
    @classmethod
    def find_spec(cls, fullname, path=None, target=None):
        ...


sys.meta_path.append(NapariPluginImporter)
__path__ = pkgutil.extend_path(__path__, __name__)
del sys, pkgutil

Whether pip-installed versions or plugin-installed versions have priority is a matter of inserting the importer into the end or the beginning of sys.meta_path, respectfully.

According to the PEP 420 specification:

While looking for a module or package named "foo", for each
directory in the parent path:

  • If <directory>/foo/__init__.py is found, a regular package is
    imported and returned.

  • If not, but <directory>/foo.{py,pyc,so,pyd} is found, a module
    is imported and returned. The exact list of extension varies by
    platform and whether the -O flag is specified. The list here is
    representative.

  • If not, but <directory>/foo is found and is a directory, it is
    recorded and the scan continues with the next directory in the
    parent path.

  • Otherwise the scan continues with the next directory in the parent
    path.

If the scan completes without returning a module or package, and at least one directory was recorded, then a namespace package is created.

This means that if an implicit namespace package is found during the scan before a normal package, the import system will not opt to automatically create the namespace package, but will continue iterating through all candidates until it encounters a valid package, otherwise finally creating the namespace package.

Due to this, implicit namespace packages will never be created if a pseudo-namespace package (which is considered as a normal package by the import system) is present. The code within the package extending its path will be executed, acting it to behave as a namespace package.

Within the import system, parent packages are resolved and executed before child packages. For example, in the statement import foo.bar, first foo will be resolved and executed, then bar will be resolved and executed.

Therefore the plugin core's napari/__init__.py will always be executed before subpackages are loaded, allowing it to load all plugin and pip-installed packages.

Limitations

  1. no other namespace package may define napari/__init__.py
  2. due to this implementation, dynamic path computation will no longer be available

(1) can be resolved if other namespace packages insert similar code in their napari/__init__.py

(2) can be resolved by copying the functionality introduced in the importlib standard library, which provides a special _NamespacePath class that provides this feature

Add a markers layer class

It would be nice to have a Layer class for displaying markers at specified coordinates. For example this could be used to visualize the locations of detected transcripts in FISH assays.

@sofroniewn and I are currently working on a implementation that uses vispy Markers visuals.

Fix circular referencing

❓ Questions and Help

Right now we have a lot of circular referencing of our main objects, i.e. you can type viewer.control_bars.viewer or viewer.layers.viewer. This was raised at our last meeting and was seen as undesirable as right now you can basically get anywhere from anywhere and read / write as you wish. Unfortunately it was not clear how to fix it. Note that this question doesn't directly connect to any of the qt / UI / non-UI stuff we were talking about, but is really just about how different parts of our "model" or "state" reference each other.

I think we'd like to move to a more tree / DAG like structure, but a problem then becomes - how do you share information across different parts of the graph. Maybe our struggles indicate we have not found the right abstractions and modularity.

As a concrete example I'd like to focus on the ControlBars class (but similar arguments would apply to Dimensions and LayersList too). On construction viewer gets passed to the object and appended as a property. When the clims slider values change we go inside viewer and inside layers to find the layer whose clim values need updating. Similarly when the layer selection changes we go inside viewer and inside layers to find the layer whose clim values now need to be sent to the slider. Notice how the communication is bidirectional.

I could imagine a world where we passed the viewer to ControlBars on construction, connected up some events but then did not append it as a property. This might make it more clear how different parts of our model interact with each other - it's just that I can't really see how to make this work (without say also requiring that the LayersList object digs into the ControlBars object too). It could mean that we need to pull more of this functionality that involved both the LayersList and the ControlBars back into the viewer which can see both - but then maybe some of the modularity and motivation for having the separation is lost too.

Curious about any thoughts?
@kne42, @AhmetCanSolak, @jni, @royerloic

Viewer, slicing sliders, and Co

A few features for the viewer's sliders:

  • The number of sliders adapts to the number of dimensions in the images or objects (max)
  • Interval sliders are shown for displayed dimensions so that what is displayed can be cropped
    Very useful for 3D cropping for example...
  • There is a button for max/avg/... projections next to each slider
  • There is 3 small square buttons to toggle 1D, 2D, and 3D viewing.
  • Displayed dimensions can be chosen with 1, 2 or 3 context drop-down menus, depending on how many dimensions are displayed
  • There is a small squared 'play' button next to each slider so that we can animate
    the display or stop it. The speed can be controlled by right clicking and changing
    the speed on some context menu.

Run examples with PyTest

We should include running examples with pytest, not only to ensure that we are providing working examples, but for right now they’re reasonably effective as tests while we create actual unit tests...

Data Sync/Binding

I havn't addressed any of the event handling / linking of gui / object properties. Nor have I addressed any of the image specific properties of the layers / support for marker layers.

I believe this is important to discuss so I will be converting an issue out of it.

Originally posted by @AhmetCanSolak in #41

GUI Style

I am having some experiments with pyqt5 and observed that same code can easily generate different styles for different operating systems. To prevent that there is a style plugin for qt5 called fusion. Do we have concerns on consistency of gui style and behavior across various operating systems? I guess we do. Let me know your thoughts on this issue and maybe discuss this in the next meeting?

Plan for refactoring

❓ Questions and Help

At the meeting last thursday @royerloic proposed a feature freeze and to do some refactoring before things got unwieldy. Although we havn't quite finished merging in the in-progress features I thought I'd create this issue to allow some discussion about the upcoming refactoring - what we want to achieve / how we plan to go about it. It could give us a jump start on the meeting tomorrow, which not everyone may be able to attend.

At the last meeting we identified the following areas as priorities:

  • data passage between gui elements including making things thread safe.
  • getting slicing / indexing working (assuming image.shape is identical). Allowing for switch multiview xy/xz etc. and allowing for switch max_proj / slice / crop.
  • isolating keyboard shortcuts.

What different parts of this are people interested in working on?

A couple things that jump out to me are how we provide different parts of the gui access to data. Right now we're sometimes passing around the layers object, sometimes the viewer object. This could be standardized.

Another is how to link actions in the gui, right now we often have functions that get called but we could maybe use events more.

Finally right now we still have some things in the qt folder in elements and some not. I think there was a plan at some point to remove things from the qt folder and merge the code with things in the elements folder like I think was done with _window. Do people still like this idea?

Any thoughts on priorities and organizational approach from @jni, @kne42, and @AhmetCanSolak?

Using observer pattern to update rendering

We are needed to provide a way to automatically update the rendering in case of a change in the data array we have load. To provide a smooth experience, we could(and probably should) use observer pattern listener to keep container/viewer notified about possible changes.

In the link below you can find a simple demonstration of this pattern, which we can adapt later to our layer and viewer classes in the project.
https://gist.github.com/AhmetCanSolak/6340d5d74e5152468b68f6ae895e3bc5

Implement min/max/gamma filtration

  • Add basic double-slider for existing min and max adjustment (clim) to the GUI

  • Add support for CPU-based gamma adjustment

  • Implement min,max, and gamma in the shader as an optimised option (bool flag)

ND support for all layer types

All layer types must/should support arbitrary n-dimensions.
Let's look at how hard that can be:

i) list of points: trivial (x0, ...xn)
ii) list of lines: trivial ((x0, ...xn), (y0, ...yn))
iii) list of boxes: trivial ((x0, ...xn), (y0, ...yn))
iv) list of triangles: trivial ((x0, ...xn), (y0, ...yn), (z0, ...zn))
v) list of n-dim simplices

It seems that lists of simplices (simplex is n-tuple of n-dim points) is a generic
representation for all these objects.
We also want to provide a 'value' per point, a lut per layer, and a 'radius' per point,
which extends to lines, etc.. as thickness.

Is there anything that is hard to represent(unlikely), or draw(more likely)
for arbitrary n-dim ? @sofroniewn ?

Changing Qt layouts

🚀 Feature

Hello everybody!
I need to introduce a breaking change in code base. I'm proposing to change layouts to more design friendly way. Like having a grid on top level and widget based layouts accordingly. I started to implement already just want your collaboration on that for smoother merge of this feature.

Motivation

Current layouting is not working responsively and hard to manage. Including more gui widgets will cause more and more trouble with current implementation.

Pitch

I want to put a grid layout on top level where we will have stretchable layouts. Those stretchable layouts will include viewer, layerlist, slicing sliders and cmap sliders.

Alternatives

we could serve a fixed resolution gui? which will be terrible i believe.

GUI mockups

I have made a mockup of the gui with the addition of range slides & max projection / play button / gamma slider and colorlimits slider and single letter labels of dimensions. Not included in the mockup right now are UI elements to toggle between axes that are displayed in the image and that are displayed as sliders or any UI elements related to different layers. I will try mocking some of these up too, but wanted to paste the intermediate in here first.

image

Git Flow

@AhmetCanSolak sure, happy for it to be questioned, but I'll point out that the SciPy "master-only" model came after Fernando Perez and others questioned whether git-flow was necessary, or just overhead. Many of these projects started out using git-flow then got to the point that dev could nicely act as master anyway. Specifically, if we have good test coverage and process, master should never be broken. dev to me seems to encourage breaking things.

I also don't like the sound of squashing everything in between releases. A clean history is absolutely critical for finding regressions and when things broke.

Originally posted by @jni in #30 (comment)

Let me transfer this discussion to issue to keep PR away.

add item events

from @kne42

Ah! I forgot that this was a thing (arbitrary kwargs). This calls into question whether we need a custom 'ItemEvent` class at all

To address in future PR

QtLayerList does not update in the background

🐛 Bug

QtLayerList does not visually update when I tamper with viewer.layers until after I click on the window again. I.e., it's not updating in the background for whatever reason.

To Reproduce

Steps to reproduce the behavior:

  1. Create multiple layers and make them identifiable within the layer list.
  2. Perform any sort of deleting or reordering operation on the layer list.

Expected behavior

The layer list GUI should be updating in the background.

Environment

  • napari-gui Version (e.g., 1.0): a40163e
  • OS (e.g., Linux): MacOS Mojave
  • Python version: 3.6.5

Additional context

I will note that adding layers has no such problem (although it is rather slow).

Idea: button right of each slider toggles 'max projection'

One of the most useful operations on n-dim images is max projection.
It lets you collapse a dimension which facilitates visualisation.
We can have this feature in a very simple and transparent manner
by having a small square botton next to each dim slider toggle between
'slice mode' and 'projection mode'. When in projection mode, the slider
is 'grayed out' (deactivated) and a max projection is computed on the fly
or cached... we have to see what is better performance wise...

Way to get at extra layer properties.

Should extra layer properties be available by double clicking on a layer widget and opening a popup box / or folddown menu or by clicking a sandwhich like menu icon - or another option. Came up in discussion of #54

windows install mess?

🐛 Bug

The symlink in the repo doesn't work for windows. in my conda environment, pip install exits without error but does not result in an installed package available in python (also demos don't run). python setup.py install fails with the error below.

To Reproduce

Steps to reproduce the behavior:

  1. clone repo, cd into napari-gui
  2. python setup.py install
python setup.py install
running install
running bdist_egg
running egg_info
creating napari_gui.egg-info
writing napari_gui.egg-info\PKG-INFO
writing dependency_links to napari_gui.egg-info\dependency_links.txt
writing requirements to napari_gui.egg-info\requires.txt
writing top-level names to napari_gui.egg-info\top_level.txt
writing manifest file 'napari_gui.egg-info\SOURCES.txt'
reading manifest file 'napari_gui.egg-info\SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'napari_gui\_version.py'
warning: no files found matching 'napari_gui\elements\qt\icons'
writing manifest file 'napari_gui.egg-info\SOURCES.txt'
installing library code to build\bdist.win-amd64\egg
running install_lib
running build_py
creating build
creating build\lib
creating build\lib\examples
copying examples\demo.py -> build\lib\examples
copying examples\slider.py -> build\lib\examples
copying examples\__init__.py -> build\lib\examples
UPDATING build\lib\napari_gui/_version.py
error: [WinError 3] The system cannot find the path specified: 'build\\lib\\napari_gui/_version.py'

Expected behavior

expected behavior (i.e. demo.py runs) can be obtained by creating a Windows shortcut and then manually copying the contents from gui to the build\lib\napari_gui directory. after this, running python setup.py install works and the demo runs.

Environment

  • napari-gui==0.0.4+45.g6e15cf8
  • OS (e.g., Linux): Windows 10
  • Python version: 3.6.1

Where do qt elements get connected to functions

❓ Questions and Help

My question has two main parts to it. The first is where to qt elements get connected to functions? What I mean by this is right now take one of the buttons like out QAddLayerButton this needs to get connected to the function that actually adds the layer. It was previously done inside the _layer_buttons file inside the qt folder, but now in the latest on #90 I have moved it to inside the _viewer file which is not inside the qt folder. Basically - should this sort of logic be happening on files inside the qt folder or on files outside the qt folder. I lean towards the latter - I think it keeps the files in the qt folder then much simpler, and not having to do with the logic of the gui but just the actual elements that are getting rendered.

As a second part of this question - should the viewer or higher level object be passed as an input to the classes in the qt folder. I.e. should dimensions be passed to the QtDimensions class in the qt folder. Right now we do it in a whole bunch of places, but maybe as part of the refactor we should prevent this. Preventing this then forces us to put all the connection logic into the files outside the qt folder and means the files in the qt folder are just responsible for laying out the widgets and any self contained styling and logic.

What are peoples thoughts about this? @AhmetCanSolak @kne42 @royerloic @jni

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.