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?
Hi @jacopoabramo, and thanks for trying it out :)
Am I doing something wrong?
nope! you've just exposed one of the incorrect assumptions that we made so far, which is that the config would have a camera device (in our _refresh_camera_options method). We could/should "fix" this specific issue pretty easily by just making sure to try/catch the call to self._mmc.getDevicePropertyNames(cam_device) ...
however, this kinda points to a broader issue for your specific use case at the moment (thinking about your image.sc post)... which is that napari-micromanager is, at this point, still just trying to get the basic use case of a GUI-control for a camera and conventional scope. As mentioned over there, for now, I suspect that you'll want to go a bit lower-level and use the core object provided by pymmcore-plus.
I'm happy to help get you started with getting your custom camera control to show images in napari, while still being able to use mmcore for piezo and other device control. Maybe you can provide a basic workflow you're aiming for, and a little info about how you snap images with your non-mm camera?
one more thing, @fdrgsp just reminded me that we actually do catch that case in the main branch:
https://github.com/tlambert03/napari-micromanager/blob/fc3a9fb5fe8edf958ca236722d47c8f16ecb8b1b/micromanager_gui/main_window.py#L279-L281
so, you might try again from main ... or I can cut another release to pypi.
pip install -U git+https://github.com/tlambert03/napari-micromanager.git
I also just pushed a new rc to pypi... so you should be able to pip install -U napari-micromanager in about 10 mins
Hi @tlambert03 , thank you for the quick reply!
I've also read your post on image.sc, thanks for that too. I'll try to briefly explain my current situation. We have a Ximea xiB-64 camera, which - as I've explained - has some performance issues when used with Micro-Manager. To go around that, I've worked on napari-live-recording, a plugin to test new cameras and/or to use with small/quick setups. These are the controls I've implemented so far (some of them are on a development branch)
- image snap: napari adds a layer with a snap image from the camera;
- album snap: napari adds a layer with a snap which gradually creates a stack of images everytime you press the button;
- live recording: napari adds a layer which is updated every 60 Hz to live-stream the camera frames;
- recording: the camera records a stack of N frames (which can also be post-processed with a user-defined function if necessary), and then adds the recorded stack on napari
There are also some other controls integrated (at least for the Ximea camera - exposure time, bytes per pixel).
I was focusing on the last point of the list, the recording functionality, as what I wanted to achieve was something similar to a multidimensional acquisition available in Micro-Manager: provide a sets of points which moves an XY piezo stage, and after each movement capture an image. Tecnically I could write my own user-defined function to do so, but when I saw Micro-Manager being ported to napari I thought it to be easier to try and send these instructions directly to Micro-Manager, and after each movement capture an image with the live recording plugin.
I hope my explanation was clear!
EDIT: the camera snap is performed by directly using the Python APIs provided by Ximea in this case. Each camera the plugin supports is inherited from a common interface, with - at the moment - limited functionalities. So based on the user choice a new camera object is instantiated starting from the same interface. It's maybe not great but so far it's doing its job and I'm hoping to add new cameras to test it out.
what I wanted to achieve was something similar to a multidimensional acquisition available in Micro-Manager: provide a sets of points which moves an XY piezo stage, and after each movement capture an image.
cool, thanks. makes sense! Let me give a high level overview of the moving parts here, which will hopefully clarify where you might start to use some of the stack here. There's three "moving parts" (and some current limitations, which I'll get to):
-
pymmcore-plus- this is a wrapper onpymmcore, that provides a number of useful things for the purpose of gui/event-driven micromanager control- inter-process communication via
RemoteMMCore, so you don't choke up the main thread waiting for things like acquisitions, etc... - callbacks, re-routed from mmcore's callback system, via psygnal, so you can do stuff like
@mmcore.propertyChanged.connect def prop_changed(device, prop, value): print(f"{device}.{prop} changed to {value!r}")- a
run_mdamethod that acts as the multi-dimensional acquisition engine to drive scope over a set of events.
- inter-process communication via
-
useq-schema - this is a backend agnostic schema for describing a sequence of microscope control events. it lets you define an experiment like this:
it still needs a lot of work/input to make it more flexible beyond the standard 6d experiment type, but so far i've been quite happy with forcing the definition of the experiment to something static like that. note that themda = MDASequence( stage_positions=[(100, 100, 30), (200, 150, 35)], channels=["DAPI", "FITC"], time_plan={'interval': 1, 'loops': 20}, z_plan={"range": 4, "step": 0.5}, axis_order='tpcz', )run_mdamethod in CMMCore accepts an instance of MDASequence. - napari-micromanager - This sits on top of those two things and basically provides a GUI for the most common things. It constructs an
MDASequencebased on the users interaction with the GUI, and then passes it to pymmcore-plusrun_mdamethod. It then connects various callbacks to the core object's signals, so that it can react in the main gui. For example, after every frame in an MDA, therun_mdamethod emits a frameReady signal... napari-micromanager connects to that signal and shows the image in the viewer in the_on_mda_framemethod.
... SO
what we'd need to do for your case is just make these three lines in run_mda more accessible, so that the user could define their own acquisition method and signals. That is, we need a hook there or something for you to call capture_image from your library.
Note: this also really should be reflected upstream in useq-schema, that is: an "acquisition event" should be more pluggable (e.g. beyond just a channel config and name)
If you want to start hacking at it, I might suggest starting just with pymmcore-plus using something like this example to build a simple experiment, and then think about how you'd want to hook into that run_mda method. (that method has always been a spot where we've planned to open up hooks, like image processing, etc... so it's a natural next step)
hey @jacopoabramo, can you let me know if this is resolved on your end? A lot has obviously changed in the meantime, but I see that you do depend on pymmcore-plus in your plugin, so don't want to close it without making sure you're good
Hi @tlambert03, sorry for the late reply - was cought up in a lot of stuff. I'll close the issue since the plugin interface to pymmcore-plus seems to be stable