Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ package-url = 'https://github.com/IumiShaping/pymodaq_plugins_optical_2D_shaping
name = "pymodaq_plugins_beam_shaping"
description = "PyMoDAQ's extension plugin to run optical shaping from various shapers (focusing on amplitude and phase shaping) and the image propagation"
dependencies = [
"pymodaq>=5.3.2",
"pymodaq_utils>=5.3.1",
"pymodaq_gui>=5.3.0",
"pymodaq_data>=5.3.0",
"pymodaq>=5.3.2,<5.4",
"pymodaq_utils>=5.3.1,<5.4",
"pymodaq_gui>=5.3.0,<5.4",
"pymodaq_data>=5.3.0,<5.4",
'pyfftw',
'scikit-image',
'opencv-python',
Expand All @@ -36,7 +36,7 @@ maintainers = [
dynamic = ["version", "urls", "entry-points"]
readme = "README.rst"
license = { file="LICENSE" }
requires-python = ">=3.8"
requires-python = ">=3.10"

classifiers = [
"Development Status :: 5 - Production/Stable",
Expand Down Expand Up @@ -66,7 +66,7 @@ pytorch = [
requires = [
"hatchling>=1.9.0",
"hatch-vcs", "toml",
"pymodaq_utils>=0.0.6",
"pymodaq_utils>=5.3.6",
]
build-backend = "hatchling.build"

Expand Down
82 changes: 55 additions & 27 deletions src/pymodaq_plugins_beam_shaping/algorithms/algorithm_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from pymodaq_plugins_beam_shaping.algorithms.algo_config import AlgoConfig
from pymodaq_plugins_beam_shaping.utilities.h5saving import ShapingSaver

from pymodaq_plugins_beam_shaping.utilities import sizing
from pymodaq_plugins_beam_shaping.utilities import sizing, regions

algo_factory = AlgorithmFactory()
phase_factory = PhaseFactory()
Expand Down Expand Up @@ -96,16 +96,14 @@ class AlgoApp(CustomApp):
{'title': 'Apply Mask', 'name': 'apply_mask', 'type': 'bool', 'value': False},
{'title': 'Mask Type', 'name': 'mask_type', 'type': 'list', 'value': str(MaskType.SQUARE),
'limits': MaskType.names()},
{'title': 'Slices', 'name': 'slices', 'type': 'str',
'value': '(slice(277, 1770, None), slice(252, 1795, None))'},
{'title': 'Slices (um):', 'name': 'slices', 'type': 'str', 'value': ''},
]},
{'title': 'Intermediate Masking', 'name': str(ApplyMaskTo.INTERMEDIATE), 'type': 'group',
'visible': False, 'children': [
{'title': 'Apply Mask', 'name': 'apply_mask', 'type': 'bool', 'value': False},
{'title': 'Mask Type', 'name': 'mask_type', 'type': 'list', 'value': str(MaskType.ELLIPTICAL),
'limits': MaskType.names()},
{'title': 'Slices', 'name': 'slices', 'type': 'str',
'value': '(slice(162, 882, None), slice(545, 1825, None))'},
{'title': 'Slices (um):', 'name': 'slices', 'type': 'str', 'value': ''},
]}
]

Expand Down Expand Up @@ -145,10 +143,18 @@ def __init__(self, dockarea, toolbar: QtWidgets.QToolBar=None):
'visible': self.settings['stopping', 'stopping_type'] == stop,
'children': stopping_factory.get_stop_class(stop).params})

self.module_and_data_saver: ShapingSaver = None
self._module_and_data_saver: ShapingSaver = None
self._h5saver: H5Saver = None


@property
def module_and_data_saver(self) -> ShapingSaver:
return self._module_and_data_saver

@module_and_data_saver.setter
def module_and_data_saver(self, saver: ShapingSaver):
self._module_and_data_saver = saver

@property
def h5saver(self) -> H5Saver:
if self._h5saver is None:
Expand Down Expand Up @@ -268,6 +274,7 @@ def set_target_field(self, field: Field):
self._algorithm.set_target_field(field)

self._target_field = field
self.check_slices()

def set_input_field(self, field: Field):

Expand Down Expand Up @@ -328,7 +335,7 @@ def set_algorithm(self, algo_name: str = None):

self.config_saver_loader.base_path = [self._algorithm.ALGO_NAME]
self.set_settings_values()
self.update_target_slices(eval(self.settings[str(ApplyMaskTo.TARGET), 'slices']))
self.check_slices()

self.algo_changed.emit(self._algorithm)

Expand Down Expand Up @@ -357,7 +364,7 @@ def ini_algo(self):
if self.is_action_checked('ini_algo'):
self.get_action('algorithms').widget.setEnabled(False)
#self.set_action_enabled('ini_algo', False)
#self.set_algorithm()
self.set_algorithm()

self.runner_thread = QtCore.QThread()
runner = AlgoRunner(self._algorithm)
Expand Down Expand Up @@ -402,7 +409,7 @@ def setup_docks_and_widgets(self):
self.settings_widget = QtWidgets.QWidget()
self.settings_widget.setLayout(QtWidgets.QVBoxLayout())
self.settings_widget.layout().setContentsMargins(0, 0, 0, 0)
splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter = QtWidgets.QSplitter(QtCore.Qt.Orientation.Vertical)
self.settings_widget.layout().addWidget(splitter)
splitter.addWidget(self.settings_tree)

Expand Down Expand Up @@ -521,38 +528,59 @@ def compute_phase(self):
def apply_mask(self, apply_to: ApplyMaskTo) -> bool:
return self.settings[str(apply_to), 'apply_mask']

def get_plane_pixel_sizes(self, apply_to: ApplyMaskTo) -> Union[None, tuple]:
""" Pixel sizes of the plane the mask applies to, None if not (yet) defined"""
try:
if str(apply_to) == str(ApplyMaskTo.INTERMEDIATE):
return self._algorithm.intermediate_pixel_sizes
return self._target_field.pixels_sizes
except (AttributeError, ValueError, TypeError):
return None

def get_mask_region(self, apply_to: ApplyMaskTo) -> Union[None, tuple[slice, slice]]:
""" Get the mask region as slices in real units (um), None if not defined"""
return regions.parse_slices(self.settings[str(apply_to), 'slices'])

def get_mask_as_slices(self, apply_to: ApplyMaskTo) -> Union[None, tuple[slice, slice]]:
""" Get the mask region as slices of indexes to be used on the arrays of the corresponding plane"""
if self.apply_mask(apply_to):
slices = eval(self.settings[str(apply_to), 'slices'])
if hasattr(slices, '__iter__'):
for _slice in slices:
if not isinstance(_slice, slice):
return None
return slices
region = self.get_mask_region(apply_to)
pixel_sizes = self.get_plane_pixel_sizes(apply_to)
if region is None or pixel_sizes is None:
return None
return regions.slices_to_indexes(region, pixel_sizes, sizing.get_effective_needed_field_size())
else:
return None

def get_mask_type(self, apply_to: ApplyMaskTo) -> MaskType:
return MaskType[self.settings[str(apply_to), 'mask_type']]

def update_intermediate_slices(self, slices: tuple[slice, slice]):
self.settings.child(str(ApplyMaskTo.INTERMEDIATE), 'slices').setValue(str(slices))
self.update_slices(ApplyMaskTo.INTERMEDIATE, slices)

def update_target_slices(self, slices: tuple[slice, slice]):
slices = self.constrains_slices(slices)
self.settings.child(str(ApplyMaskTo.TARGET), 'slices').setValue(str(slices))

@staticmethod
def constrains_slices(slices: tuple[slice, slice]) -> tuple[slice, slice]:
position, size = sizing.get_effective_area_pos_size_in_pxls()
slices = (slice(int(max(slices[0].start, position[0])), int(min(slices[0].stop, position[0] + size[0]))),
slice(int(max(slices[1].start, position[1])), int(min(slices[1].stop, position[1] + size[1])))
)
return slices
self.update_slices(ApplyMaskTo.TARGET, slices)

def update_slices(self, apply_to: ApplyMaskTo, slices: tuple[slice, slice]):
""" Set the mask region (slices in m) constrained to the effective area of the plane"""
pixel_sizes = self.get_plane_pixel_sizes(apply_to)
if pixel_sizes is not None:
slices = regions.constrain_slices(slices, pixel_sizes)
self.settings.child(str(apply_to), 'slices').setValue(regions.slices_to_str(slices))

def check_slices(self):
""" Set the default region (2/3 of the effective area, centered) if the current one is not valid"""
for apply_to in (ApplyMaskTo.TARGET, ApplyMaskTo.INTERMEDIATE):
pixel_sizes = self.get_plane_pixel_sizes(apply_to)
if pixel_sizes is None:
continue
region = self.get_mask_region(apply_to)
if region is None or not regions.are_slices_in_area(region, pixel_sizes):
self.update_slices(apply_to, regions.default_slices(pixel_sizes))

def value_changed(self, param: Parameter):
for applied in ApplyMaskTo.values():
if applied in putils.get_param_path(param):
if applied in putils.get_param_path(param) and self._algorithm is not None:
self._algorithm.update_mask = True
if param.name() == 'ini_phase_factory':
for param_child in self.settings.child('ini_phase_group', 'phase_params').children():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ class DAQ_Move_FocalLength(DAQ_Move_base):
is_multiaxes = False
_axis_names: Union[List[str], Dict[str, int]] = ['Focal']
_controller_units: Union[str, List[str]] = 'cm'
_epsilon: Union[float, List[float]] = 0.1
_epsilons: Union[float, List[float]] = 0.1
data_actuator_type = DataActuatorType.DataActuator

params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)
params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilons)

def ini_attributes(self):
self.controller: 'Correction' = None
Expand Down Expand Up @@ -84,8 +84,8 @@ def move_rel(self, value: DataActuator):
----------
value: (float) value of the relative target positioning
"""
value = self.check_bound(self.current_position + value) - self.current_position
self.target_value = value + self.current_position
value = self.check_bound(self.current_value + value) - self.current_value
self.target_value = value + self.current_value
value = self.set_position_relative_with_scaling(value)

self.move_abs(self.target_value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ class DAQ_Move_MockAutoFocus(DAQ_Move_base):
is_multiaxes = False
_axis_names: Union[List[str], Dict[str, int]] = ['Focus']
_controller_units: Union[str, List[str]] = ''
_epsilon: Union[float, List[float]] = 0.1
_epsilons: Union[float, List[float]] = 0.1
data_actuator_type = DataActuatorType.DataActuator

params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)
params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilons)

def ini_attributes(self):
self.controller: Optional[Autofocus] = None
Expand Down Expand Up @@ -86,8 +86,8 @@ def move_rel(self, value: DataActuator):
----------
value: (float) value of the relative target positioning
"""
value = self.check_bound(self.current_position + value) - self.current_position
self.target_value = value + self.current_position
value = self.check_bound(self.current_value + value) - self.current_value
self.target_value = value + self.current_value
value = self.set_position_relative_with_scaling(value)

self.move_abs(self.target_value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ class DAQ_Move_MockShaper(DAQ_Move_base):
is_multiaxes = False
_axis_names: Union[List[str], Dict[str, int]] = ['']
_controller_units: Union[str, List[str]] = ''
_epsilon: Union[float, List[float]] = 0.1
_epsilons: Union[float, List[float]] = 0.1
data_actuator_type = DataActuatorType.DataActuator

params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)
params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilons)


def ini_attributes(self):
Expand Down Expand Up @@ -116,8 +116,8 @@ def move_rel(self, value: DataActuator):
----------
value: (float) value of the relative target positioning
"""
value = self.check_bound(self.current_position + value) - self.current_position
self.target_value = value + self.current_position
value = self.check_bound(self.current_value + value) - self.current_value
self.target_value = value + self.current_value
value = self.set_position_relative_with_scaling(value)

self.move_abs(self.target_value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ class DAQ_Move_Zernike(DAQ_Move_base):
for m in range(-n, n+2, 2):
_axis_names.append(f'{n}{m}')
_controller_units: Union[str, List[str]] = ''
_epsilon: Union[float, List[float]] = 0.1
_epsilons: Union[float, List[float]] = 0.1
data_actuator_type = DataActuatorType.DataActuator

params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)
params = [] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilons)

def ini_attributes(self):
self.controller: 'Correction' = None
Expand Down Expand Up @@ -94,8 +94,8 @@ def move_rel(self, value: DataActuator):
----------
value: (float) value of the relative target positioning
"""
value = self.check_bound(self.current_position + value) - self.current_position
self.target_value = value + self.current_position
value = self.check_bound(self.current_value + value) - self.current_value
self.target_value = value + self.current_value
value = self.set_position_relative_with_scaling(value)

self.move_abs(self.target_value)
Expand Down
33 changes: 19 additions & 14 deletions src/pymodaq_plugins_beam_shaping/extensions/spatial_beam_shaping.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pymodaq_gui.utils.layout import save_layout_state, load_layout_state
from pymodaq_gui.parameter.ioxml import xml_string_to_parameter

from pymodaq.extensions.custom_ext import CustomExt
from pymodaq.utils.custom_ext import CustomExt
from pymodaq.utils.gui_utils.loader_utils import create_extension
from pymodaq.utils.data import DataToExport, DataCalculated, DataDim

Expand All @@ -30,6 +30,7 @@
from pymodaq_plugins_beam_shaping.algorithms import AlgorithmFactory, AlgoBase
from pymodaq_plugins_beam_shaping.utilities import sizing
from pymodaq_plugins_beam_shaping.algorithms.utils import ApplyMaskTo, LensSetup
from pymodaq_plugins_beam_shaping.utilities import regions
from pymodaq_plugins_beam_shaping.utilities.calibrating import BeamShapingCalibration, Calibration
from pymodaq_plugins_beam_shaping.utilities.data import DataShaper
from pymodaq_plugins_beam_shaping.utilities.shaper import Shaper, ModulatorType, get_shaper
Expand Down Expand Up @@ -474,6 +475,7 @@ def setup_actions(self):

def update_ui_from_algo(self, algo: AlgoBase):
self.set_action_visible('show_intermediate', algo.SETUP_TYPE == LensSetup.FourF)
self.show_set_target_roi_select()

def plot_target(self, field: Field):
self.target_viewers.show_data(DataToExport('Target', data=[
Expand Down Expand Up @@ -509,15 +511,16 @@ def do_things_after_ui_setup(self):
self._algorithm.modulator_field_signal.connect(self.update_modulator_field)
self._input_field_loader.field_signal.connect(self._algorithm.set_input_field)
self._target_field_loader.field_signal.connect(self._algorithm.set_target_field)
self._target_field_loader.field_signal.connect(lambda field: self.show_set_target_roi_select())

self.set_menu(self._algorithm.menu) #adds this to the menu dict (automatically added in the sharedUI)

self.intermediate_viewer.roi_select_signal.connect(
lambda roi: self._algorithm.update_intermediate_slices(roi.to_slices()))
lambda roi: self._algorithm.update_intermediate_slices(regions.from_axis_units(roi.to_slices(as_integer=False))))
self.show_set_target_roi_select()

for viewer in (self._target_field_loader.amp_viewer, self._target_field_loader.phase_viewer):
viewer.roi_select_signal.connect(lambda roi: self._algorithm.update_target_slices(roi.to_slices()))
viewer.roi_select_signal.connect(lambda roi: self._algorithm.update_target_slices(regions.from_axis_units(roi.to_slices(as_integer=False))))

if layout_path.joinpath('shaping.dock').is_file():
try:
Expand All @@ -529,13 +532,15 @@ def do_things_after_ui_setup(self):


def show_set_target_roi_select(self):
slices = self._algorithm.constrains_slices(eval(self._algorithm.settings[ApplyMaskTo.TARGET, 'slices']))
self._target_field_loader.amp_viewer.view.set_action_checked('ROIselect', True)
pos = [slices[0].start, slices[1].start]
size = [slices[0].stop - slices[0].start, slices[1].stop - slices[0].start]
self._target_field_loader.amp_viewer.view.show_ROI_select(
size=size[::-1],
pos=pos[::-1])
slices = self._algorithm.get_mask_region(ApplyMaskTo.TARGET)
if slices is None:
return
slices = regions.to_axis_units(slices) # the ROI is in the units of the axes
view = self._target_field_loader.amp_viewer.view
view.set_action_checked('ROIselect', True)
view.show_ROI_select() # position and size are not taken into account by the viewer, set below
view.ROIselect.setPos((slices[1].start, slices[0].start))
view.ROIselect.setSize((slices[1].stop - slices[1].start, slices[0].stop - slices[0].start))

def connect_things(self):
logger.debug('connecting things')
Expand Down Expand Up @@ -666,15 +671,15 @@ def _quit_fun(self) -> bool:
def main():
import sys
from pymodaq_gui.qt_utils import mkQApp
from pymodaq.dashboard import create_load_dashboard
from pymodaq.dashboard import load_dashboard_with_arguments
from pymodaq.utils.gui_utils.loader_utils import create_extension

app = mkQApp('BeamShaping')

win, dashboard = create_load_dashboard()
win, dashboard, _ = load_dashboard_with_arguments(show_dashboard=False, load_extension=False)
win.mainwindow.setVisible(False)

win_ext, scan = create_extension(dashboard, BeamShaping)
win_ext.show()
win_ext, ext = create_extension(dashboard, BeamShaping, show_extension=True)

sys.exit(app.exec())

Expand Down
Loading
Loading