Skip to content

Commit

Permalink
updating the widgets and controller
Browse files Browse the repository at this point in the history
  • Loading branch information
beniroquai committed Jun 19, 2024
1 parent 1cecade commit ea89421
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 26 deletions.
5 changes: 3 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ install_requires =
magicgui
qtpy
scikit-image
arkitekt
mikro
reaktion

python_requires = >=3.8
include_package_data = True
Expand All @@ -55,8 +58,6 @@ testing =
pytest # https://docs.pytest.org/en/latest/contents.html
pytest-cov # https://pytest-cov.readthedocs.io/en/latest/
pytest-qt # https://pytest-qt.readthedocs.io/en/latest/
imswitch
pyqt5


[options.package_data]
Expand Down
173 changes: 171 additions & 2 deletions src/imswitch_arkitekt/imswitch_arkitekt_controller.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,182 @@
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 imswitch
from imswitch.imcommon.controller import MainController
from mikro.api.schema import (
from_xarray,
)
from arkitekt import App
from rekuest.qt.builders import qtinloopactifier, qtwithfutureactifier
import xarray as xr

from imswitch.imcommon.model.logging import initLogger
from imswitch.imcontrol.controller.controllers.LaserController import LaserController
from rekuest.widgets import SliderWidget
from mikro.api.schema import (
RepresentationFragment,
OmeroRepresentationInput,
PhysicalSizeInput,
ChannelInput,
)
import numpy as np
from typing import Any, Dict, List, Optional, Tuple

from koil.qt import QtFuture


class imswitch_arkitekt_controller(ImConWidgetController):
"""Linked to CameraPluginWidget."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__logger = initLogger(self)
self.__logger.debug("Initializing imswitch arkitekt controller")

# An actifier that will be used to register the functions
# actifiers are used to defined the asynchronous wrapping of the function
# i.e should the function be run in the main thread or in a separate thread
# if not provided arkitekt will offload the function to a separate thread
self.__app = self._widget.global_app
if not imswitch.IS_HEADLESS:
self._widget.magic_bar.app_up.connect(self.on_app_up)
self._widget.magic_bar.app_down.connect(self.on_app_down)

# here we are using the qtinloopactifier which will run the function in the main thread of
# the qt application and return the result of that functioncall, this is often not a good idea
self.qt_actifier = qtinloopactifier
# A future actifier can be used to pass the function a future object that can be resolved
# at a later time, this is useful for functions that need to wait for a signal to be emitted
# before they can continue
self.qt_future_activier = qtwithfutureactifier

# Will be run in the main thread
self.__app.rekuest.register(builder=self.qt_actifier)(self.call_run_scan)
self.__app.rekuest.register(
builder=self.qt_actifier, widgets={"value": SliderWidget(min=0, max=1024)}
)(self.set_laser_value)

# Will be run in a separate thread
self.__app.rekuest.register()(self.snap_image)

# WIll run in the main thread, but will be passed a future object that can be
# resolved at a later time
self.__app.rekuest.register(builder=self.qt_future_activier)(self.on_do_stuff_async)

#@property
#def api(self):
# return self.__api

#@property
#def shortcuts(self):
# return self.__shortcuts

def on_do_stuff_async(self, qtfuture: QtFuture, hello: str) -> str:
"""Do Stuff Async
This function will be run in the main thread, but will be passed a future object
that can be resolved at a later time. This is useful for functions that need to
wait for a signal to be emitted before they can continue.
Args:
qtfuture (QFuture): The future object that can be resolved at a later time.
hello (str): A string that will be printed to the console.
Returns:
str: A string that will be returned to the caller.
"""
print(hello)

# YOu can store the future object and resolve it at a later time

self._to_be_resolved_future = qtfuture


#here we are resolving the future object with the value 42 immediately
# This is not very useful, but you can imagine that you would resolve the future
# object when a signal is emitted or when some other condition is met
qtfuture.resolve("42")

return None

def call_run_scan(self):
"""Run Scan
Runs the currently active scan that is open on imswitch.
"""
controlMainController = self._MikroMainController__moduleMainControllers[
"imcontrol"
]
scanController = controlMainController.controllers["Scan"]
scanController.runScan()

def snap_image(
self, xdims: int = 1000, ydims: int = 1000
) -> RepresentationFragment:
"""Snap Image
Snaps a single image from the camera.
Args:
xdims (int): The dimensions of the image in the x direction.
ydims (int): The dimensions of the image in the y direction.
Returns:
RepresentationFragment: The generated image.
"""
detectorName = self._master.detectorsManager.getAllDeviceNames()[0]
detectorManager = self._controller._master.detectorsManager[detectorName]
latestimg = detectorManager.getLatestFrame().copy()
active_channels = [detectorName]
if len(latestimg.shape) == 2:
latestimg = latestimg[:, :, np.newaxis]
active_channels = ["channel1"]

metadata = OmeroRepresentationInput(
physicalSize=PhysicalSizeInput(x=2, y=2),
channels=[ChannelInput(name=channel) for channel in active_channels],
)

return from_xarray(
xr.DataArray(latestimg, dims=["x", "y", "c"]),
name="Randomly generated image",
omero=metadata,
)

def set_laser_value(self, lasername: str, value: int):
"""Set Laser Value
Sets the value of the laser.
Args:
lasername (str): The lasername value
value (int): the value to set the laser to
"""
allIlluNames = self._master.lasersManager.getAllDeviceNames()
laserSource = self._master.lasersManager[allIlluNames[0]]
laserSource.setEnabled(True)
laserSource.setLaserValue(lasername, value)

def on_app_up(self):

print("App up")

def on_app_down(self):
print("App down")

def closeEvent(self):
self.__logger.debug("Shutting down")


# Copyright (C) 2020-2021 ImSwitch developers
# This file is part of ImSwitch.
#
# ImSwitch is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ImSwitch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
19 changes: 4 additions & 15 deletions src/imswitch_arkitekt/imswitch_arkitekt_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,11 @@ def __init__(self, pluginInfo, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__logger = initLogger(self)

self.global_app = None

if pluginInfo is None:
# import imswitch_sim_info.py
return

self.__pluginInfo = pluginInfo
self.__wavelength = self.__pluginInfo.wavelength
self.__pixelsize = self.__pluginInfo.pixelSize
self.__angleMount = self.__pluginInfo.angleMount
self.__simSize = (self.__pluginInfo.width, self.__pluginInfo.height)
self.__patternsDir = self.__pluginInfo.patternsDir
self.isSimulation = self.__pluginInfo.isSimulation
self.nRotations = self.__pluginInfo.nRotations
self.nPhases = self.__pluginInfo.nPhases
self.simMagnefication = self.__pluginInfo.nPhases
self.isFastAPISIM = self.__pluginInfo.isFastAPISIM
self.simPixelsize = self.__pluginInfo.simPixelsize
self.simNA = self.__pluginInfo.simNA
self.simN = self.__pluginInfo.simN # refr
self.simETA = self.__pluginInfo.simETA
def set_global_app(self, app):
self.global_app = app
69 changes: 62 additions & 7 deletions src/imswitch_arkitekt/imswitch_arkitekt_widget.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,69 @@
from imswitch.imcontrol.view.widgets.basewidgets import Widget
from imswitch.imcommon.model import initLogger
from arkitekt.qt.magic_bar import MagicBar
from arkitekt.builders import publicscheduleqt
import numpy as np
from typing import Any, Dict, List, Optional, Tuple
from dataclasses import dataclass
from qtpy import QtCore, QtWidgets

class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__

global_app = None
class imswitch_arkitekt_widget(Widget):
"""Linked to CameraPluginWidget."""
"""Linked to the Arkitekt Controller ."""

sigLoadParamsFromHDF5 = QtCore.Signal()
sigPickSetup = QtCore.Signal()
sigClosing = QtCore.Signal()

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# initialize the logger
self.__logger = initLogger(self)
self.__logger.debug("Initializing")

# create the main widget layout
self.cwidget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
self.cwidget.setLayout(layout)

# Initialize the Magic Bar (Arkitekt) to have the menu available
identifier = "github.io.jhnnsrs.mikro-napari"
version = "latest"
logo = "https://avatars.githubusercontent.com/u/127734217?s=200&v=4"
settings = QtCore.QSettings("imswitch", f"{identifier}:{version}")
self.global_app = publicscheduleqt(
identifier, version, parent=self.cwidget, logo=logo, settings=settings
)
self.magic_bar = MagicBar(self.global_app, dark_mode=True)


self.loadParamsAction = QtWidgets.QAction(
"Load parameters from saved HDF5 file…", self
)

# Buttons
self.loadParamsButton = QtWidgets.QPushButton("Load Parameters")
self.loadParamsButton.clicked.connect(self.sigLoadParamsFromHDF5.emit)

self.pickSetupButton = QtWidgets.QPushButton("Pick Setup")
self.pickSetupButton.clicked.connect(self.sigPickSetup.emit)

self.fileSelectButton = QtWidgets.QPushButton("Select File")
self.fileSelectButton.clicked.connect(self.sigPickSetup.emit)

# Adding widgets to layout
layout.addWidget(self.magic_bar)
layout.addWidget(self.loadParamsButton)
layout.addWidget(self.pickSetupButton)
layout.addWidget(self.fileSelectButton)
layout.addStretch()

self.setLayout(layout)



@dataclass
class _DockInfo:
name: str
yPosition: int

0 comments on commit ea89421

Please sign in to comment.