Source code for pymodaq.utils.scanner.scanner

from __future__ import annotations
from typing import Tuple, List, TYPE_CHECKING
from collections import OrderedDict


from qtpy import QtWidgets, QtCore
from qtpy.QtCore import QObject, Signal, Slot

from pymodaq.utils.logger import set_logger, get_module_name
from pymodaq.utils.config import Config
from pymodaq.utils.scanner.scan_factory import ScannerFactory, ScannerBase
from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter
import pymodaq.utils.daq_utils as utils
from pymodaq.utils.scanner.utils import ScanInfo
from pymodaq.utils.plotting.scan_selector import Selector
from pymodaq.utils.data import DataToExport, DataActuator

if TYPE_CHECKING:
    from pymodaq.control_modules.daq_move import DAQ_Move


logger = set_logger(get_module_name(__file__))
config = Config()
scanner_factory = ScannerFactory()


[docs]class Scanner(QObject, ParameterManager): """Main Object to define a PyMoDAQ scan and create a UI to set it Parameters ---------- parent_widget: QtWidgets.QWidget scanner_items: list of GraphicItems used by ScanSelector for chosing scan area or linear traces actuators: List[DAQ_Move] list actuators names See Also -------- ScanSelector, ScannerBase, TableModelSequential, TableModelTabular, pymodaq_types.TableViewCustom """ scanner_updated_signal = Signal() settings_name = 'scanner' params = [ {'title': 'Calculate positions:', 'name': 'calculate_positions', 'type': 'action'}, {'title': 'N steps:', 'name': 'n_steps', 'type': 'int', 'value': 0, 'readonly': True}, {'title': 'Scan type:', 'name': 'scan_type', 'type': 'list', 'limits': scanner_factory.scan_types()}, {'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'list', 'limits': scanner_factory.scan_sub_types(scanner_factory.scan_types()[0])}, ] def __init__(self, parent_widget: QtWidgets.QWidget = None, scanner_items=OrderedDict([]), actuators: List[DAQ_Move] = []): QObject.__init__(self) ParameterManager.__init__(self) if parent_widget is None: parent_widget = QtWidgets.QWidget() self.parent_widget = parent_widget self._scanner_settings_widget = None self.connect_things() self._scanner: ScannerBase = None self.setup_ui() self.actuators = actuators if self._scanner is not None: self.settings.child('n_steps').setValue(self._scanner.evaluate_steps()) def setup_ui(self): self.parent_widget.setLayout(QtWidgets.QVBoxLayout()) self.parent_widget.layout().setContentsMargins(0, 0, 0, 0) self.parent_widget.layout().addWidget(self.settings_tree) self._scanner_settings_widget = QtWidgets.QWidget() self._scanner_settings_widget.setLayout(QtWidgets.QVBoxLayout()) self._scanner_settings_widget.layout().setContentsMargins(0, 0, 0, 0) self.parent_widget.layout().addWidget(self._scanner_settings_widget) self.settings_tree.setMinimumHeight(110) self.settings_tree.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) def set_scanner(self): try: self._scanner: ScannerBase = scanner_factory.get(self.settings['scan_type'], self.settings['scan_sub_type'], actuators=self.actuators) while True: child = self._scanner_settings_widget.layout().takeAt(0) if not child: break child.widget().deleteLater() QtWidgets.QApplication.processEvents() self._scanner_settings_widget.layout().addWidget(self._scanner.settings_tree) self._scanner.settings.sigTreeStateChanged.connect(self._update_steps) except ValueError as e: pass @property def scanner(self) -> ScannerBase: return self._scanner
[docs] def get_scanner_sub_settings(self): """Get the current ScannerBase implementation's settings""" return self._scanner.settings
[docs] def value_changed(self, param: Parameter): if param.name() == 'scan_type': self.settings.child('scan_sub_type').setOpts( limits=scanner_factory.scan_sub_types(param.value())) if param.name() in ['scan_type', 'scan_sub_type']: self.set_scanner() self.settings.child('scan_type').setOpts(tip=self._scanner.__doc__) self.settings.child('scan_sub_type').setOpts(tip=self._scanner.__doc__) self.settings.child('n_steps').setValue(self._scanner.evaluate_steps())
@property def actuators(self): """list of str: Returns as a list the name of the selected actuators to describe the actual scan""" return self._actuators @actuators.setter def actuators(self, act_list): self._actuators = act_list self.set_scanner()
[docs] def set_scan_type_and_subtypes(self, scan_type: str, scan_subtype: str): """Convenience function to set the main scan type Parameters ---------- scan_type: str one of registered Scanner main identifier scan_subtype: list of str or None one of registered Scanner second identifier for a given main identifier See Also -------- ScannerFactory """ if scan_type in scanner_factory.scan_types(): self.settings.child('scan_type').setValue(scan_type) if scan_subtype is not None: if scan_subtype in scanner_factory.scan_sub_types(scan_type): self.settings.child('scan_sub_type').setValue(scan_subtype)
def set_scan_from_settings(self, settings: Parameter, scanner_settings: Parameter): self.set_scan_type_and_subtypes(settings['scan_type'], settings['scan_sub_type']) self.settings.restoreState(settings.saveState()) self._scanner.settings.restoreState(scanner_settings.saveState()) @property def scan_type(self) -> str: return self.settings['scan_type'] @property def scan_sub_type(self) -> str: return self.settings['scan_sub_type'] def connect_things(self): self.settings.child('calculate_positions').sigActivated.connect(self.set_scan) self.scanner_updated_signal.connect(self.save_scanner_settings) def save_scanner_settings(self): self._scanner.save_scan_parameters()
[docs] def get_scan_info(self) -> ScanInfo: """Get a summary of the configured scan as a ScanInfo object""" return ScanInfo(self._scanner.n_steps, positions=self._scanner.positions, axes_indexes=self._scanner.axes_indexes, axes_unique=self._scanner.axes_unique, selected_actuators=[act.title for act in self.actuators])
def get_nav_axes(self): return self._scanner.get_nav_axes() def get_scan_shape(self): return self._scanner.get_scan_shape()
[docs] def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]: """To be reimplemented. Calculations of indexes within the scan""" return self._scanner.get_indexes_from_scan_index(scan_index)
def _update_steps(self): self.settings.child('n_steps').setValue(self.n_steps) @property def n_steps(self): return self._scanner.evaluate_steps() @property def n_axes(self): return self._scanner.n_axes @property def positions(self): return self._scanner.positions
[docs] def positions_at(self, index: int) -> DataToExport: """ Extract the actuators positions at a given index in the scan as a DataToExport of DataActuators""" dte = DataToExport('scanner') for ind, pos in enumerate(self.positions[index]): dte.append(DataActuator(self.actuators[ind].title, data=float(pos))) return dte
@property def axes_indexes(self): return self._scanner.axes_indexes @property def axes_unique(self): return self._scanner.axes_unique @property def distribution(self): return self._scanner.distribution
[docs] def set_scan(self): """Process the settings options to calculate the scan positions Returns ------- bool: True if the processed number of steps if **higher** than the configured number of steps """ oversteps = config('scan', 'steps_limit') if self._scanner.evaluate_steps() > oversteps: return True self._scanner.set_scan() self.settings.child('n_steps').setValue(self.n_steps) self.scanner_updated_signal.emit() return False
def update_from_scan_selector(self, scan_selector: Selector): self._scanner.update_from_scan_selector(scan_selector)
def main(): from pymodaq.utils.parameter import ParameterTree app = QtWidgets.QApplication(sys.argv) class MoveMock: def __init__(self, ind: int = 0): self.title = f'act_{ind}' self.units = f'units_{ind}' actuators = [MoveMock(ind) for ind in range(3)] params = [{'title': 'Actuators', 'name': 'actuators', 'type': 'itemselect', 'value': dict(all_items=[act.title for act in actuators], selected=[]),'checkbox':True}, {'title': 'Set Scan', 'name': 'set_scan', 'type': 'action'}, ] settings = Parameter.create(name='settings', type='group', children=params) settings_tree = ParameterTree() settings_tree.setParameters(settings) widget_main = QtWidgets.QWidget() widget_main.setLayout(QtWidgets.QVBoxLayout()) #widget_main.layout().setContentsMargins(0, 0, 0, 0) widget_scanner = QtWidgets.QWidget() widget_main.layout().addWidget(settings_tree) widget_main.layout().addWidget(widget_scanner) scanner = Scanner(widget_scanner, actuators=actuators) def update_actuators(param): scanner.actuators = [utils.find_objects_in_list_from_attr_name_val(actuators, 'title', act_str, return_first=True)[0] for act_str in param.value()['selected']] def print_info(): print('info:') print(scanner.get_scan_info()) print('positions:') print(scanner.positions) print('nav:') print(scanner.get_nav_axes()) settings.child('actuators').sigValueChanged.connect(update_actuators) settings.child('set_scan').sigActivated.connect(scanner.set_scan) scanner.scanner_updated_signal.connect(print_info) widget_main.show() sys.exit(app.exec_()) if __name__ == '__main__': import sys from qtpy import QtWidgets main()