Giter VIP home page Giter VIP logo

napari-micromanager's Introduction

napari-micromanager

License PyPI Python Version Tests codecov

GUI interface between napari and micromanager powered by pymmcore-plus and pymmcore-widgets


napari-micromanager

Installation

You can install napari-micromanager via pip:

pip install napari-micromanager

You will also need a Qt backend such as PySide2/6, or PyQt5/6. If you've previously installed napari into this environment with pip install napari[all], then you will likely already have it. If not, you will also need to install a Qt backend of your choice:

pip install pyqt5  # or any of {pyqt5, pyqt6, pyside2, pyside6}

Getting micromanager adapters:

The easiest way to get the micromanager adapters is to use:

mmcore install

this will install micromanager to the pymmcore_plus folder in your site-package; use this to see where:

python -c "from pymmcore_plus import find_micromanager; print(find_micromanager())"

alternatively, you can direct pymmcore_plus to your own micromanager installation with the MICROMANAGER_PATH environment variable:

export MICROMANAGER_PATH='/path/to/Micro-Manager-...'

Contributing

Contributions are very welcome.

Launching napari with plugin

You can launch napari and automatically load this plugin using the launch-dev.py script:

python launch-dev.py

Alternatively you can run:

napari -w napari-micromanager

License

Distributed under the terms of the BSD-3 license, "napari-micromanager" is free and open source software

Issues

If you encounter any problems, please file an issue along with a detailed description.

napari-micromanager's People

Contributors

alexcoul avatar dependabot[bot] avatar fdrgsp avatar ianhi avatar pre-commit-ci[bot] avatar rileymshea avatar rixius 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

Watchers

 avatar  avatar  avatar

napari-micromanager's Issues

Stage start moving before stage up to load position

Hello,

When I move the stage from well to well I try to bring the focus device to the load position between the move but the stage start moving before the focus is done moving up, here the function I use:

def move_load_pos(core=None, XY=None):
    #Move stage to load position
    previous_z = core.getZPosition()
    core.setZPosition(0)
    core.waitForDevice(core.getFocusDevice())
    #Move to XY position
    core.setXYPosition(XY[0] ,XY[1])
    #Bring the stage back down
    core.waitForDevice(core.getXYStageDevice())
    core.setZPosition(previous_z)
    core.waitForDevice(core.getFocusDevice())

Do you see any reason for that to happens? I could definitely add some time delays.

Thanks for all your help!

wrong checking option in mda

If you toggle the stage positoins check box in the multid widget you will get this error:

TypeError                                 Traceback (most recent call last)
File ~/mambaforge/envs/napmicr-test/lib/python3.9/site-packages/micromanager_gui/multid_widget.py:241, in MultiDWidget.toggle_checkbox_save_pos(self=)
    238     self.checkBox_save_pos.setEnabled(True)
    240 else:
--> 241     self.checkBox_save_pos.setCheckState(False)
        self.checkBox_save_pos = 
        self = 
    242     self.checkBox_save_pos.setEnabled(False)

TypeError: 'PySide2.QtWidgets.QCheckBox.setCheckState' called with wrong argument types:
  PySide2.QtWidgets.QCheckBox.setCheckState(bool)
Supported signatures:
  PySide2.QtWidgets.QCheckBox.setCheckState(PySide2.QtCore.Qt.CheckState)

which is these lines:

https://github.com/tlambert03/napari-micromanager/blob/db0e98bc53e293e826fc3e82473c9e210fbbccaa/micromanager_gui/multid_widget.py#L240-L242

attn: @fdrgsp

rethinking saving mechanism

Currently saving here defaults to a basic tiff saving. Eventually that will probably live in pymmcore-plus, but that would still limit the saving option in the MDA dialog to a single method of saving.

Given that anyone will be able to create objects to handle saving (e.g. my raman zarr writer) it would be amazing to provide a mechanism for napari-micromanager to allow users to select from any supplied package.

I imagine this going like this:

  1. pip install my-writer
  2. napari-micro discovers the provided writers via entrypoints (or pluggy or ???)
  3. Option for that writer provided in the MDA dialog
  4. use magicgui to generate dialog to set any creation options for that writer

Fixes for live mode

We can hold off on this for a bit till happy with #111 but I suspect we have some bugs in live mode.

See extensive explanation here: micro-manager/mmCoreAndDevices#171 (comment)

In particular we need to:

  1. Be even more careful than we currently are when updating the exposure
  2. Not bother with passing a number to startContinuousSequenceAcquistion because it doesn't actually affect anything.

Napari + Napari Micromanager plugin too large for my screen

Hello,

I have a small issue on a MacBook Pro (13inches), the napari-micromanager main window is too large for my screen, and unless I float the panel, I can't make the napari window smaller. Basically I don't have access to the bottom of the page:
Screen Shot 2022-06-21 at 7 54 22 PM
Not sure if there is something I could do on my side, outside accepting it :)

Thanks!

Sample explorer with z stack + multiple points

Hello (again),

I wonder if there is an elegant way to couple the multi-D Acquisition with the sample explorer?
For example, if one wants to acquire 4x4 tiles with z stack at multiple positions.

Thanks!

Feature freeze

I'd like to propose that we freeze feature development for a little bit (not including any existing PRs) and focus on cleaning everything up. Let's get the number of PRs back down (preferably to zero). Then work on refactoring the code, removing duplication, adding tests, speeding up tests, improving component isolation. We've got a solid amount of functionality at this point, but things are starting to become less manageable. It would be a good time to step back and do some cleanup.

What do you think @fdrgsp @ianhi?

Generalize `Objective`

Currently the device name for the objective is hardcoded as "Objective" https://github.com/tlambert03/napari-micromanager/blob/5a8145de33bdd8df7a3d8c4d9f0594fc10a39dd0/micromanager_gui/main_window.py#L266-L269

however this device can be named anything. For example the default micromanager hardware wizard for the Nikon TE-2000 calls it the nosepiece which results in napari-micromanager not allowing switching options.

Two proposed solutions:

  1. Require that users create an Objective config group and use that (a la channel)
  2. Require that the device be named Objective (current state of affairs)
  3. Search through all StateDevices and match common names ([O,o]jective, [N,n]osepiece)

pycro-manager backend

Hi @tlambert03, I wanted to let you know the changes we discussed before are now fully implemented and tested, and pycromanager can be used as a backend acquisition system with no GUI of its own. If you continue adding features to this project, I think it would be awesome to integrate a pycromanager backend option, because 1) It would being a huge upgrade over the default pycromanager viewer. 2) It will eventually just work with the rest of Micro-Manager as well, since the pycromanager acquisition engine is slated to replace the Clojure engine running MDAs from the the micromanager GUI. 3) It would add in all the other nice features of pycromanager, like the flexible acquisition system, the lighting-fast data saving, and opening enormous memory-mapped datasets in Napari with a few lines of code.

The way this works is through the combination of headless mode, which allows you to launch the core and acquisition as a silent backend, and saved image callbacks, which allows you to specify a function that gets called every time a new image is written to disk, so they it can be read directly in python. The idea is you can use these to update the viewer every time a new image arrives.

The current downsides of the pycromanager as a backend are that live mode and on-the-fly image processing are limited to 100MB/s (though they can be implemented much faster by saving to disk first and then reading). This is something I'm currently working on fixing.

Here is an example of how to use it:

from pycromanager import Acquisition, multi_d_acquisition_events, start_headless

mm_app_path = '/path/to/micromanager'
config_file = mm_app_path + "/MMConfig_demo.cfg"

# Start the Java process
start_headless(mm_app_path, config_file, timeout=5000)

save_dir = r"C:\Users\henry\Desktop\data"

def image_saved_fn(axes, dataset):
    pixels = dataset.read_image(**axes)
    # TODO: use the pixels for something, like post-processing or a custom image viewer

with Acquisition(directory=save_dir, name="test", show_display=False,
                image_saved_fn=image_saved_fn,
                 ) as acq:
    events = multi_d_acquisition_events(
        num_time_points=5,
        z_start=0, z_end=6, z_step=0.4,
    )
    acq.acquire(events)

New gui with superqt QCollapsibles

@tlambert03 @ianhi

In relation to @tlambert03 suggestion in #90, I played a bit with the superqt QCollapsible.

In both I removed the camera bit depth and binning controls since I figured that we can set it through the property browser and we will be able to control it through the group and preset table (once ready).

I also added the shutter widget in the gui (since I want to open a PR to implement shutters control)

What do you think? Which one of these you like better? (in the first one I used the collapsible only for the stages).

720p_only_stages.mov
720p_collapsables.mov

Linux Support

I have run the full pytest suite under the latest branch in Ubuntu Linux and everything passes.

Notes

The main wrinkle with linux is that micro-manager or at least mmcore need to be compiled from source, but it was relatively straightforward with conda and apt on Ubuntu 22.04. https://github.com/micro-manager/mmCoreAndDevices#building-on-mac-and--linux

The only other thing I had to get all the tests to pass was to copy the MMConfig_demo.cfg to /usr/local/lib/micro-manager.

I believe it should be possible either via a github actions caching directive to cache the compilation of micro-manager/mmcore(and apt/conda pre-reqs), so re-compilation only occurs on new releases of mmcore or some other infrequent heuristic.


I'm willing to put a PR together that integrates an Ubuntu runner into the github actions matrix.

icons do not show up when starting napari with plugin from command line

I am running python 3.9.9, napari version 0.4.12, and napari-micromanager installed from github version 0.0.1rc6.dev9+g69c3bed.d20220117 on Windows 10 Pro version 10.0.19041

When I start napari and the napari-micromanager plugin using
napari -w micromanager
the icons (e.g. the up/down/right/left arrows for the stage and camera icon on the live button) are not shown. Instead there are blank square placeholders. This may be a more general napari issue, because the napari buttons (e.g. layers buttons, toggle n-display, etc) also do not show any icons.

On the other hand, if I first launch napari from the command line
napari
and then navigate to the plugins menu and select the micromanager plugin, the icons appear as expected

Allow for multiple focus devices

Microscopes can have multiple focus devices (e.g. stage controls + perfect focus). Micromanager handles this by allowing toggling between them:
image

napari-micromanger should also allow choosing which device is used.

Contrast Limits - Autoadjust + histogram

Problem

Currently the contrast limits are set by whatever the first image is. This results in behavior like this:
Peek 2021-07-06 23-47

1st snap was of the random noise of CY5 with demo cam, second is switching to FITC which has greater intensity which then saturates and the Napari contrast slider don't auto adjust.

Proposed Solution

  1. First pass. Implement something like micromanager auto adjust contrast limits
  2. Also provide a small histogram (matplotlib based?) as a widget (maybe in the bottom right?) (x-ref napari/napari#675)

Improve Live mode

Live mode is not optimal for example I experienced the below lagginess in live mode:

VID_20210812_172007796.mp4

It subsequently got much worse, not sure about reproducing as this could also be an issue with pymmcore-plus. I will try to try on a different machine soon, but wanted to open in case anyone else is having

Keeping LED on during z stack

Hi,
In micro-manager there is an option to keep shutter open during a z-stack, is it possible to do that in napari-micro/pymmcore? Otherwise the LED is turn on and off which slightly slow down the acquisition.

Thanks!

Edit, Doing some digging I found that is a know things to add:
pymmcore-plus/useq-schema#38

However, is there a way to programmatically added to an MDA engine?

Properly connect gui state and core state

as observed by @ianhi in #20, the gui was written before there was a good way to receive callbacks from the core object. We need to go back and use event-based gui-state updates instead of on-demand updates.

Camera test failing

tests/test_cam_objective.py::test_crop_camera[local] FAILED [ 0%]

e.g. https://github.com/tlambert03/napari-micromanager/runs/5542877632?check_suite_focus=true

=================================== FAILURES ===================================
___________________________ test_crop_camera[local] ____________________________
CALL ERROR: Exceptions caught in Qt event loop:
________________________________________________________________________________
Traceback (most recent call last):
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 145, in __getitem__
    return self.__getitem__(self.index(key))
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 238, in index
    raise ValueError(
ValueError: 'Camera_ROI' is not in list

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 110, in camera_centered_crop
    cam_roi_layer = self.viewer.layers[CAM_ROI_LAYER]
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 147, in __getitem__
    raise KeyError(str(e)) from e
KeyError: "'Camera_ROI' is not in list"

During handling of the above exception, another exception occurred:

numpy.core._exceptions._UFuncNoLoopError: ufunc 'equal' did not contain a loop with signature matching types (<class 'numpy.dtype[str_]'>, <class 'numpy.dtype[float64]'>) -> None

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 95, in _on_roi_cbox_change
    self.camera_centered_crop()
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 114, in camera_centered_crop
    cam_roi_layer = self.add_roi_layer()
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 74, in add_roi_layer
    return self.viewer.add_shapes(
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/components/viewer_model.py", line 4, in add_shapes
    import itertools
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/shapes/shapes.py", line 583, in __init__
    self._text = TextManager._from_layer(
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/utils/text_manager.py", line 223, in _from_layer
    return cls(**kwargs)
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/utils/text_manager.py", line 75, in __init__
    self._set_text(text, n_text, properties=properties)
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/utils/text_manager.py", line 91, in _set_text
    self.values = np.empty(0)
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/evented_model.py", line 233, in __setattr__
    if not are_equal(after, before):
  File "<__array_function__ internals>", line 180, in array_equal
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/numpy/core/numeric.py", line 2449, in array_equal
    return bool(asarray(a1 == a2).all())
FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
________________________________________________________________________________
Traceback (most recent call last):
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 145, in __getitem__
    return self.__getitem__(self.index(key))
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 238, in index
    raise ValueError(
ValueError: 'Camera_ROI' is not in list

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 130, in _on_crop_pushed
    cam_roi_layer = self.viewer.layers[CAM_ROI_LAYER]
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 147, in __getitem__
    raise KeyError(str(e)) from e
KeyError: "'Camera_ROI' is not in list"

During handling of the above exception, another exception occurred:

numpy.core._exceptions._UFuncNoLoopError: ufunc 'equal' did not contain a loop with signature matching types (<class 'numpy.dtype[str_]'>, <class 'numpy.dtype[float64]'>) -> None

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 133, in _on_crop_pushed
    self.make_rectangle_roi_layer()
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 82, in make_rectangle_roi_layer
    cam_roi_layer = self.add_roi_layer()
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 74, in add_roi_layer
    return self.viewer.add_shapes(
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/components/viewer_model.py", line 4, in add_shapes
    import itertools
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/shapes/shapes.py", line 583, in __init__
    self._text = TextManager._from_layer(
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/utils/text_manager.py", line 223, in _from_layer
    return cls(**kwargs)
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/utils/text_manager.py", line 75, in __init__
    self._set_text(text, n_text, properties=properties)
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/layers/utils/text_manager.py", line 91, in _set_text
    self.values = np.empty(0)
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/evented_model.py", line 233, in __setattr__
    if not are_equal(after, before):
  File "<__array_function__ internals>", line 180, in array_equal
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/numpy/core/numeric.py", line 2449, in array_equal
    return bool(asarray(a1 == a2).all())
FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
________________________________________________________________________________
---------------------------- Captured stderr setup -----------------------------
2022-03-14 18:56:52.693 | DEBUG    | pymmcore_plus._util:find_micromanager:33 - using MM path from local install: /Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/pymmcore_plus/Micro-Manager-latest_mac
2022-03-14 18:56:52.693 | INFO     | pymmcore_plus.core._mmcore_plus:setDeviceAdapterSearchPaths:152 - setting adapter search paths: ['/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/pymmcore_plus/Micro-Manager-latest_mac']
2022-03-14 18:57:03.892 | DEBUG    | pymmcore_plus._util:find_micromanager:33 - using MM path from local install: /Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/pymmcore_plus/Micro-Manager-latest_mac
----------------------------- Captured stderr call -----------------------------
Exceptions caught in Qt event loop:
________________________________________________________________________________
Traceback (most recent call last):
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 145, in __getitem__
    return self.__getitem__(self.index(key))
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 238, in index
    raise ValueError(
ValueError: 'Camera_ROI' is not in list

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/runner/work/napari-micromanager/napari-micromanager/micromanager_gui/_camera_roi.py", line 110, in camera_centered_crop
    cam_roi_layer = self.viewer.layers[CAM_ROI_LAYER]
  File "/Users/runner/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/site-packages/napari/utils/events/containers/_typed.py", line 147, in __getitem__
    raise KeyError(str(e)) from e
KeyError: "'Camera_ROI' is not in list"

More thoughts/questions on project scope

I think that the way this library special cases the single body, multi axis, single camera microscope is part of what makes the UI very easy to use. While this gives real gains in terms of the UI just magically working, it is also limiting for other cases. If the hope is to one day support other styles of microscope or greater generality then are there design decisions that should be made now that will make that easier in the future? Or is that perhaps not the hope?

(It's that special casing this style of setup vs others is the scope that I was trying to grapple with in https://github.com/tlambert03/napari-micromanager/pull/40#issuecomment-910864118.)

core_widget vs gui_object

What's the line between these two? In #150 I made the exposure widget a core_widget because it's following a core property. So maybe core_widget handle the interactions with core, and gui_objects are responsible for creating and laying out those widgets?

Change what is displayed when no devices are loaded

In general I think it would be cool if we didn't display any controls for which there are no relevant devices loaded.

But what I to propose here is:

  1. Hiding all controls except for the load config options
  2. Once a config has been loaded relegate the load config dialog to the menu being introduced in #131
    • save that vertical space!

add to pypi

Now that I'm trying to use this on micrscopes it would be nice to be able to pip install specific versions.

Rotate / Flip live image

Hello,

The camera chip I am using is not square and I would like to Flip and rotate the image, I can do that after acquisition in the engine but I wonder if there is an option to rotate the image on the live view as well?

Thanks!
Cedric

Stop "live acquisition"

Hello,

Quick question, I wonder if there is a "clean" way to stop the live acquisition programmatically?
Doing core.stopSequenceAcquisition(core.getCameraDevice()) does stop the sequence but doesn't toggle the Live button to Stop and I can't clear the preview layer in napari.

Shutters and channel setting

Hi,

I have 2 problems with shutter and channel setting.
・In the main window, the shutter button in microscope is grayed out and can't be selected.
・I can't add channels in the Snap/Live or multi-D acquisition tabs.

Micro-manager configuration worked in imageJ, but it didn't work as a plug-in tool on the Napari platform.

Is there any reason why this would be the case?

Thanks for all your help!

MDA Image layer can overflow memory

Problem

Currently there are no guards on the size of the MDA image layer:
https://github.com/tlambert03/napari-micromanager/blob/10a4b229fd3b0d1438ee3a2d750cb7d13ab3d2bf/micromanager_gui/main_window.py#L308-L313

given that MDAs can easily exceed the size of a standard computer's RAM I think this is setting us up for some crashes deep into an acquisition (probably by the time everyone has already gone home...).

Solution

not sure what's best here. We could create a zarr file in a temp directory and display that - storing the images as they come in.

Replicate Qt signal thread behavior

Description

psygnal does not work across threads when used in a pyqt application. (working towards a small example but I know that this is true from pymmcore-plus + napari).

To fix this I suspect that it will be necessary to implement the different types of connections described here:

https://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads

@tlambert03 you also wrote down some thoughts on this here: https://github.com/tlambert03/pymmcore-plus/issues/17#issuecomment-880725024

Create a tab for handling arbitrary config groups

Problem

Currently there is no way to modify a config group that is not special cased into the default UI (e.g. Channel). In contrast micromanager doesn't special case any of the config groups and instead devotes a large amount of UI real estate to changing the config groups:
image

Proposed Solution

Either add a new tab to the bottom element (the arrow) or fill in the space (the box) with a config switching widget:
image

remove tifffile dependency

Update - we implemented option 1. Next steps are to go for option 3 and not need to use tiffffile here


If you check the save option in MDA then you can end up importing tiffffile:
https://github.com/tlambert03/napari-micromanager/blob/157ec63eebb3d370d89f3fecef9025cd33e6333d/micromanager_gui/_saving.py#L7

even though this is not included in the dependencies:

https://github.com/tlambert03/napari-micromanager/blob/157ec63eebb3d370d89f3fecef9025cd33e6333d/setup.cfg#L30-L36

We should either:

  1. Add tifffile to deps
  2. Guard the import and raise an error to the user when the click save in the MDA dialog
  3. restructure the MDA widget to only be responsible for generating the mda sequence and displaying it in napari. Other libraries should be responsible for the saving (e.g. https://github.com/ianhi/pymmcore-MDA-writers) and the MDA widget could provide an interface to them.

My choice would be start with 1 as it's a quick fix to a potential bug and eventually do 3.

Use `channelGroup` to determine the config group used for the channel

There is core.getChannelGroup which should be used instead of hardcoding "Channel" https://github.com/tlambert03/napari-micromanager/blob/57ee6672aad52e171058376f2a115305f2dabc84/micromanager_gui/main_window.py#L237-L239

However, we should keep the hardcoded Channel as a fallback because it's not clear if it's even possible to set this via a config file (and micromanager doesn't seem to save it into the config) see https://forum.image.sc/t/how-to-set-channelgroup-in-config-file/55746

Allow users to select MDA engine

Now that MDA Engines are userswappable we should consider provide a UI mechanism for choosing a new one. Similar to #156 this would require some sort of plugin/entrypoint architecture

Proposal: Aim for an independent pymmcore-widgets package, use it here

In doing some of the cleanup, I've been thinking perhaps it would be good to work towards creating a new package (called something like pymmcore-widgets) that provides a number of small components that one could use to compose an interface for micromanager however they want.

It might even be possible to have a declarative "UI" config that a user could provide to customize their GUI. So, just as the mmconfig.cfg file is essentially an instance of the micromanager "schema", one could also imagine a UI config file (yaml, json, or some other markup language) that declares which devices or config groups should be visible in the GUI, and how they should be laid out.

napari-micromanager then is essentially "one version" of an MM layout, with additional logic for interacting with napari.

See #120 for a start on how this could look (with each DeviceType having a class that one can build off of). While that works well for state devices, I'm not immediately sure how far the concept can be stretched before one might as well have used a full blown device property browser.

I also think that what @ianhi started in https://github.com/tlambert03/napari-micromanager/pull/94 would be useful here too. Essentially just a ConfigurationGroup widget.

thoughts @ianhi @fdrgsp on that new package?

CC @dpshepherd and @ptbrown1729 - curious to get your thoughts. this additional repo also seems like something that you would benefit from and perhaps want to contribute to. I'm glad to see you've forked this package and are developing things that are useful for you (I can definitely see how the current repo design is too "pre-decided"). It would be great if we could somehow get to a place where we have a common set of device widgets that we just arrange however we need (but let me know if you think even that is a bit naïve)

While acquiring a multi-D acquisition on only "emit" the last image to napari viewer

Hello,
I am not clear on how emit work in:

self._prep_hardware(event)
self._mmc.snapImage()
self.img = self._mmc.getImage()
self._events.frameReady.emit(self.img, event)

Trying to stream all the data during acquisition seriously slows down the acquisition and often end up in failure (the data are quite big). However, the user would like to have an idea on how the acquisition is going I then wonder if it's possible to only emit the current image and remove the previous image in napari. Obviously if I tried to directly add_image to the viewer I end up with a thread error.

Thank you for your help!

Setup error in loading MM config file

Hi everyone. I've tried to install this plugin on my setup but I've got some errors popping up after loading the .cfg file.

Steps to reproduce

  • Select the following MM config file:
# Generated by Configurator on Wed Oct 27 15:31:13 CEST 2021

# Reset
Property,Core,Initialize,0

# Devices
Device,COM3,SerialManager,COM3
Device,MicroDrive XY Stage,MCL_MicroDrive,MicroDrive XY Stage
Device,MicroDrive Z Stage,MCL_MicroDrive,MicroDrive Z Stage
Device,MCL NanoDrive Z Stage,MCL_NanoDrive,MCL NanoDrive Z Stage
Device,iBeamSmartCW,Toptica_iBeamSmartCW,iBeamSmartCW

# Pre-init settings for devices
Property,MicroDrive XY Stage,EncodersPresent,No
Property,MicroDrive Z Stage,EncodersPresent,No
Property,iBeamSmartCW,Description,iBeam smart Laser Controller
Property,iBeamSmartCW,Port,COM3

# Pre-init settings for COM ports
Property,COM3,AnswerTimeout,500.0000
Property,COM3,BaudRate,115200
Property,COM3,DTR,Disable
Property,COM3,DataBits,8
Property,COM3,DelayBetweenCharsMs,0.0000
Property,COM3,Fast USB to Serial,Disable
Property,COM3,Handshaking,Off
Property,COM3,Parity,None
Property,COM3,StopBits,1
Property,COM3,Verbose,1

# Hub (parent) references

# Initialize
Property,Core,Initialize,1

# Delays

# Focus directions
FocusDirection,MicroDrive Z Stage,0
FocusDirection,MCL NanoDrive Z Stage,0

# Roles
Property,Core,Focus,MCL NanoDrive Z Stage
Property,Core,AutoShutter,1

# Camera-synchronized devices

# Labels

# Configuration presets
# Group: Channel

# Group: System
# Preset: Startup



# PixelSize settings
  • Click "Load"

Traceback

RuntimeError                              Traceback (most recent call last)
~\AppData\Local\Programs\Python\Python39\lib\site-packages\micromanager_gui\main_window.py in _on_system_configuration_loaded(self=)
    252 
    253     def _on_system_configuration_loaded(self):
--> 254         self._refresh_camera_options()
        self._refresh_camera_options = >
    255         self._refresh_objective_options()
    256         self._refresh_channel_list()

~\AppData\Local\Programs\Python\Python39\lib\site-packages\micromanager_gui\main_window.py in _refresh_camera_options(self=)
    224     def _refresh_camera_options(self):
    225         cam_device = self._mmc.getCameraDevice()
--> 226         cam_props = self._mmc.getDevicePropertyNames(cam_device)
        cam_props = undefined
        self._mmc.getDevicePropertyNames = 
        cam_device = ''
    227         if "Binning" in cam_props:
    228             bin_opts = self._mmc.getAllowedPropertyValues(cam_device, "Binning")

~\AppData\Local\Programs\Python\Python39\lib\site-packages\Pyro5\client.py in __call__(self=, *args=('',), **kwargs={})
    474         for attempt in range(self.__max_retries + 1):
    475             try:
--> 476                 return self.__send(self.__name, args, kwargs)
        self.__send = 
        self.__name = 
        args = ('',)
        kwargs = {}
    477             except (errors.ConnectionClosedError, errors.TimeoutError):
    478                 # only retry for recoverable network errors

~\AppData\Local\Programs\Python\Python39\lib\site-packages\Pyro5\client.py in _pyroInvoke(self=, methodname='getDevicePropertyNames', vargs=('',), kwargs={}, flags=0, objectId='pymmcore_plus.CMMCorePlus')
    241                     return _StreamResultIterator(streamId, self)
    242                 if msg.flags & protocol.FLAGS_EXCEPTION:
--> 243                     raise data  # if you see this in your traceback, you should probably inspect the remote traceback as well
        data = RuntimeError('No device with label ""')
    244                 else:
    245                     return data

RuntimeError: No device with label ""

Setup

napari: 0.4.12
 Platform: Windows-10-10.0.19043-SP0
Python: 3.9.7 (tags/v3.9.7:1016ef3, Aug 30 2021, 20:19:38) [MSC v.1929 64 bit (AMD64)]
Qt: 5.15.2
PyQt5: 5.15.5
NumPy: 1.21.2
SciPy: 1.7.1
Dask: 2021.09.1
VisPy: 0.9.2

OpenGL:
- GL version: 4.6.0 - Build 27.20.100.9127
- MAX_TEXTURE_SIZE: 16384

Screens:
- screen 1: resolution 1920x1080, scale 1.0

Plugins:
- console: 0.0.4
- micromanager: 0.0.1rc3
- napari-live-recording: 0.1.5
- scikit-image: 0.4.12
- svg: 0.1.5

Am I doing something wrong?

pixelSizeConfiguration considerations

Opening this issue to get some feedback from @fdrgsp on the design goals we should be having for maintenance of pixel size configuration in general. We should also compare/contrast with how this would be done in micromanager (there's always merit in maintaining consistency between the two, unless we decide that we can definitely offer something better).

From what I can tell, this is the goal

  1. we offer an input somewhere that lets the user enter their camera pixel size.
  2. when either the camera pixel size or the objective changes, we:
    a. try to glean what the magnification of the objective is from the objective label (this is definitely something not done in micromanager, and probably error prone... might be better to have an explicit API rather than do the magic).
    b. define a new pixelSizeConfig entry for the objective
    c. setPixelSizeUm using the mag and camera pixel
    d. set the config as current...

I definitely like the general goal here, but I think we need an independent object, probably one that doesn't really need widgets, which monitors core property changes for the objective and whatever means we use to retrieve camera pixel size.

I also don't think we need that pixel size entry to be part of the main window.

About periodic execution of snap and image processing

I would like to capture an image periodically (e.g., once every 5 minutes). Is there an easy way to do this?
Also, I would like to process the images when they are acquired. How do I connect the image acquisition to the image processing?
Thanks for your help!

Development tips?

Any tips+tricks for developing a napari plugin? In particular is there an easy way to start up napari and immediately load the plugin? Every time I want to test something I am opening napari from the terminal, loading the plugin and then loading the demo config.

Generalize (or hide) the autofocus up/down

Currently there is a PFS-Offset control add by @fdrgsp in #68

But this is specific to Nikon scopes, and not every scope will have autofocus. So this element should only be added if one is available (this is what MM does)

Or potentially there should be a dropdown for setting the Focus Device and we should only have a widget for the core focus device.

Two other issues:

  1. The up and down buttons are missing arrows
  2. This only added a GUI element, but there doesn't seem to be any code/callbacks connected to it.
    image

core.waitForSystem() doesn't seem to be waiting

Hello,

I think I mentioned that before but it seems that the core.waitForSystem() doesn't seem to be waiting enough. For example, If I start from the load position on a stage and start an experiment, it would start the acquisition before the stage is all the way down. However, if I add a time.sleep(2.2) after self._prep_hardware(event) it works fine.

notifyAccessibilityUpdate warning when clicking on stage positions in MDA widget

WARNING: Cannot create accessible child interface for object:  QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget")  index:  6
WARNING: Cannot create accessible child interface for object:  QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget")  index:  6
WARNING: Cannot create accessible child interface for object:  QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget")  index:  6
WARNING: Invalid child in QAccessibleEvent: QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget") child: 6
WARNING: QCocoaAccessibility::notifyAccessibilityUpdate: invalid element
WARNING: Cannot create accessible child interface for object:  QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget")  index:  6
WARNING: Cannot create accessible child interface for object:  QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget")  index:  6
WARNING: Cannot create accessible child interface for object:  QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget")  index:  6
WARNING: Invalid child in QAccessibleEvent: QTableWidget(0x7ffaa3ce7320, name = "stage_tableWidget") child: 6
WARNING: QCocoaAccessibility::notifyAccessibilityUpdate: invalid element

GUI is disabled when opened programmatically

Due to the _set_enabled behavior if you modify the launch-dev script to have the config be loaded before creating the widget (a reasonable thing to do one when scripting)

from pymmcore_plus import CMMCorePlus
from pathlib import Path

import napari
from useq import MDASequence

core = CMMCorePlus.instance()
core.loadSystemConfiguration(str(Path(__file__).parent / "tests" / "test_config.cfg"))

v = napari.Viewer()
dw, main_window = v.window.add_plugin_dock_widget("napari-micromanager")


sequence = MDASequence(
    channels=["Cy5", {"config": "FITC", "exposure": 50}],
    time_plan={"interval": 2, "loops": 5},
    z_plan={"range": 4, "step": 0.5},
    axis_order="tpcz",
    stage_positions=[(222, 1, 1), (111, 0, 0)],
)

main_window.mda.set_state(sequence)

napari.run()

then the entire GUI is still disabled.

I think the solution is to remove the top level concept of enabled/disabled and push that down to a per widget basis. They should check for the relevant loaded devices at creation and then listen the appropriate signals to (de)activate themselves as approriate.

`_on_system_configuration_loaded` should not modify system state

Noticed today that when I opened napari-micromanager (manually called _on_system_configuration_loaded because it was already loaded remotely) that the objective position changed from where it already was. This seems to be because the objective_combobox was populated with a list default order but didn't check the current state of the objectives so it ended up calling change_objective.

I have two proposed improvements:

  1. At the end of init the system state should be queried to account for the config already being loaded
  2. refresh_objective and friends should take care to not accidentally change the current state via the callbacks on their widgets.

Zarr backing MDA display can overflow disk space - maybe make location configurable

In #145 (review) @tlambert03 noted

In the future, it'd be nice to bring back the in-memory behavior as well if someone knows they won't be exceeding their RAM (along with checks to let you know whether you would be).

In that same vein it turns out you can also muck things up by running out of hard drive space :(
image

We have a multi-terabyte data drive on that computer which is where I save images, but the zarr location isn't configurable to use that directory. Interestingly since the saving mechanism is separate and uses the other drive it appears to continue functioning.

I think there are two opportunities for improvement here:

  1. Prevention: Allow some configurability in where the temp files are made
  2. Damage control: Catch and raise a Napari dialog with the error.

[Bug] no protection against moving when there are no XYZ devices

Problem

The XY (or Z) buttons have no protection against being called even if there is no valid device. So if you click them then they will throw this error:

RuntimeError                              Traceback (most recent call last)
File ~/Documents/oss/micro/napari-micromanager/micromanager_gui/main_window.py:497, in MainWindow.stage_y_up(self=<micromanager_gui.main_window.MainWindow object>)
    496 def stage_y_up(self):
--> 497     self._mmc.setRelativeXYPosition(
        self._mmc = <CMMCorePlus at 0x7fb4f961cf40>
        self = <micromanager_gui.main_window.MainWindow object at 0x7fb4f9fa8f70>
        self.xy_step_size_SpinBox = <PyQt5.QtWidgets.QSpinBox object at 0x7fb4f9595280>
    498         0.0,
    499         float(self.xy_step_size_SpinBox.value()),
    500     )
    501     if self.snap_on_click_xy_checkBox.isChecked():
    502         self.snap()

RuntimeError: No device with label ""

Solution

disable those buttons if there is no device available to be moved.

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.