-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pull request for plugin feature #59
Comments
@beniroquai forgot to add the tag |
Certainly! Thanks for the reminder. How should I do it anyway? Cherry-pick the files I modified from the imswitch/imswitch master and then document the sample plugins? First Plugin:https://github.com/openUC2/imswitch-flimlabs ImSwitch Cookie Cutter (without manager yet)https://github.com/openUC2/cookiecutter-imswitch Both use the openUC2 branch, but if we replace openuc2 by imswitch it should work either way. We could have a flag - or long term just imswitch ;) |
Definetely worth putting just ImSwitch for the plugins. |
Right now the plugin works for controller/widget coupling only right? Not for device managers? |
Now this becomes odd, but anyway. I created a new branch that is based on the The reference plugin so far is the https://github.com/openUC2/imswitch-flimlabs plugin. What's next?
you could load cameras if I'm not mistaking. I guess you could not yet create new basemanagers, like a new type of device layer. Haven't fully thought about it yet. Explanation:If loading a controller/widget fails, it looks for relevant plugins: For Widgets: try:
self.controllers[widgetKey] = self.__factory.createController(
(getattr(controllers, f'{widgetKey}Controller')
if widgetKey != 'Scan' else
getattr(controllers, f'{widgetKey}Controller{self.__setupInfo.scan.scanWidgetType}')), widget
)
except Exception as e:
#try to get it from the plugins
for entry_point in pkg_resources.iter_entry_points(f'imswitch.implugins'):
if entry_point.name == f'{widgetKey}_controller':
packageController = entry_point.load()
self.controllers[widgetKey] = self.__factory.createController(packageController, widget)
break
self.__logger.debug(e)
raise ValueError(f'No controller found for widget {widgetKey}') For Managers (in multimanager only): try:
package = importlib.import_module(
pythontools.joinModulePath(f'{currentPackage}.{subManagersPackage}',
managedDeviceInfo.managerName)
)
manager = getattr(package, managedDeviceInfo.managerName)
self._subManagers[managedDeviceName] = manager(
managedDeviceInfo, managedDeviceName, **lowLevelManagers)
except:
# try to import from the implugins
for entry_point in pkg_resources.iter_entry_points(f'imswitch.implugins.{subManagersPackage}'):
manager = entry_point.load()
self._subManagers[managedDeviceName] = manager(
managedDeviceInfo, managedDeviceName, **lowLevelManagers) For Controllers try:
self.widgets[widgetKey] = self.factory.createWidget(
getattr(widgets, f'{widgetKey}Widget')
if widgetKey != 'Scan' else
getattr(widgets, f'{widgetKey}Widget{self.viewSetupInfo.scan.scanWidgetType}')
)
except:
# try to get it from the plugins
for entry_point in pkg_resources.iter_entry_points(f'imswitch.implugins'):
if entry_point.name == f'{widgetKey}_widget':
packageWidget = entry_point.load()
self.widgets[widgetKey] = self.factory.createWidget(packageWidget)
break The commit e47f62f certainly needs some cleanup. Also we would need to document the proper entry-point descriptions (e.g. @jacopoabramo stage is yours ;) |
I think that using the multi-managers as entry-points for device manager is a perfect match. This introduces a level of categorization for which I'm not a fan of, but to keep an imswitch compatible infrastracture I believe this is the way to go. I believe there are several key factors to take into account to make sure that this works properly, starting from removing the pyqt signals from the presenter-model layers just as we discussed. I'll prepare a project for this, I believe it's the one with the highest priority. |
Have you tried yourself already? |
One more addition would be a detector plugin. This is only for the records so that we won't loose it: from imswitch.imcontrol.model.managers.detectors.DetectorManager import DetectorManager, DetectorAction, DetectorNumberParameter
from imswitch.imcontrol.controller.basecontrollers import ImConWidgetController
from imswitch.imcontrol.view.ImConMainView import _DockInfo
import numpy as np
from typing import Any, Dict, List, Optional, Tuple
rightDockInfos = {
'Autofocus': _DockInfo(name='Autofocus', yPosition=0),
}
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
'''
class CameraPlugin(ImConWidgetController):
"""Linked to CameraPluginWidget."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
'''
class cameraplugin(DetectorManager):
def __init__(self, detectorInfo=None, name="Test", **kwargs):
# Initialize any additional camera-specific setup here
if detectorInfo is not None:
self.detectorInfo = detectorInfo
model = "Test"
fullShape = [100, 100]
supportedBinnings = [1]
else:
self.detectorInfo = {
"name": "CameraPlugin",
"fullShape": [100, 100],
"supportedBinnings": [1, 2, 4],
"model": "Test",
"forAcquisition": True,
}
self.detectorInfo = dotdict(self.detectorInfo)
fullShape = self.detectorInfo.fullShape
supportedBinnings = self.detectorInfo.supportedBinnings
model = self.detectorInfo.model
# Prepare parameters
parameters = {
'exposure': DetectorNumberParameter(group='Misc', value=100, valueUnits='ms',
editable=True),
'gain': DetectorNumberParameter(group='Misc', value=1, valueUnits='arb.u.',
editable=True),
'brightness': DetectorNumberParameter(group='Misc', value=1, valueUnits='arb.u.',
editable=True),
}
# Prepare actions
actions = {
}
super().__init__(self.detectorInfo, name, fullShape, supportedBinnings, model,
parameters=parameters, actions=actions, croppable=True)
def pixelSizeUm(self) -> List[int]:
# Return the pixel size in micrometers for Z, Y, X dimensions
return [1, 1.1, 1.1] # Example values
def crop(self, hpos: int, vpos: int, hsize: int, vsize: int) -> None:
# Implement the cropping logic here
pass
def getLatestFrame(self) -> np.ndarray:
# Return the latest frame captured by the camera
# This is just a placeholder implementation
return np.random.rand(self.fullShape[0], self.fullShape[1]) # Example: return a random image
def getChunk(self) -> np.ndarray:
# Return a chunk of frames since the last call or flush
# This is just a placeholder implementation
return np.random.rand(5, self.fullShape[0], self.fullShape[1]) # Example: return a stack of 5 random images
def flushBuffers(self) -> None:
# Flush the internal buffers
pass
def startAcquisition(self) -> None:
# Start image acquisition
pass
def stopAcquisition(self) -> None:
# Stop image acquisition
pass
@property
def pixelSizeUm(self):
return [1, 1, 1] |
So, the Managers are added too. The attempt to integrate the dataclasses into SetupInfo seems a bit more involved for now. |
Before creating a pull request, write documentation on how a plugin is structured, types of possible plugins (widget/controller couple, hardware I presume?) and how to add a plugin to the configuration file.
The text was updated successfully, but these errors were encountered: