pymodaq 3.6.13__py3-none-any.whl → 4.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymodaq might be problematic. Click here for more details.
- pymodaq/__init__.py +13 -6
- pymodaq/control_modules/__init__.py +0 -7
- pymodaq/control_modules/daq_move.py +965 -2
- pymodaq/control_modules/daq_move_ui.py +319 -0
- pymodaq/control_modules/daq_viewer.py +1573 -3
- pymodaq/control_modules/daq_viewer_ui.py +393 -0
- pymodaq/control_modules/mocks.py +51 -0
- pymodaq/control_modules/move_utility_classes.py +709 -8
- pymodaq/control_modules/utils.py +256 -0
- pymodaq/control_modules/viewer_utility_classes.py +663 -6
- pymodaq/daq_utils.py +89 -0
- pymodaq/dashboard.py +91 -72
- pymodaq/examples/custom_app.py +12 -11
- pymodaq/examples/custom_viewer.py +10 -10
- pymodaq/examples/function_plotter.py +16 -13
- pymodaq/examples/nonlinearscanner.py +8 -6
- pymodaq/examples/parameter_ex.py +7 -7
- pymodaq/examples/preset_MockCamera.xml +1 -0
- pymodaq/extensions/__init__.py +16 -0
- pymodaq/extensions/console.py +76 -0
- pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
- pymodaq/extensions/daq_scan.py +1339 -0
- pymodaq/extensions/daq_scan_ui.py +240 -0
- pymodaq/extensions/h5browser.py +23 -0
- pymodaq/{pid → extensions/pid}/__init__.py +4 -2
- pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
- pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
- pymodaq/{pid → extensions/pid}/utils.py +52 -6
- pymodaq/extensions/utils.py +40 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
- pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
- pymodaq/post_treatment/load_and_plot.py +219 -0
- pymodaq/post_treatment/process_to_scalar.py +263 -0
- pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
- pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
- pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
- pymodaq/resources/VERSION +1 -1
- pymodaq/resources/config_template.toml +32 -13
- pymodaq/resources/preset_default.xml +1 -1
- pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
- pymodaq/utils/__init__.py +0 -29
- pymodaq/utils/abstract/__init__.py +48 -0
- pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
- pymodaq/utils/array_manipulation.py +379 -8
- pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
- pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
- pymodaq/utils/config.py +448 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +828 -8
- pymodaq/utils/data.py +1873 -7
- pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
- pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
- pymodaq/{daq_utils → utils}/enums.py +12 -7
- pymodaq/utils/exceptions.py +37 -0
- pymodaq/utils/factory.py +82 -0
- pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
- pymodaq/utils/gui_utils/custom_app.py +129 -0
- pymodaq/utils/gui_utils/file_io.py +66 -0
- pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
- pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
- pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
- pymodaq/utils/gui_utils/widgets/label.py +24 -0
- pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
- pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
- pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
- pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
- pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
- pymodaq/utils/h5modules/__init__.py +1 -0
- pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
- pymodaq/utils/h5modules/browsing.py +683 -0
- pymodaq/utils/h5modules/data_saving.py +839 -0
- pymodaq/utils/h5modules/h5logging.py +110 -0
- pymodaq/utils/h5modules/module_saving.py +350 -0
- pymodaq/utils/h5modules/saving.py +914 -0
- pymodaq/utils/h5modules/utils.py +85 -0
- pymodaq/utils/logger.py +64 -6
- pymodaq/utils/managers/action_manager.py +460 -0
- pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
- pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
- pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
- pymodaq/utils/managers/parameter_manager.py +110 -0
- pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
- pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
- pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
- pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
- pymodaq/utils/math_utils.py +546 -10
- pymodaq/{daq_utils → utils}/messenger.py +5 -1
- pymodaq/utils/parameter/__init__.py +2 -15
- pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
- pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
- pymodaq/utils/parameter/utils.py +206 -11
- pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
- pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
- pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
- pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
- pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
- pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
- pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
- pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
- pymodaq/utils/plotting/navigator.py +355 -0
- pymodaq/utils/plotting/scan_selector.py +480 -0
- pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
- pymodaq/utils/plotting/utils/filter.py +538 -0
- pymodaq/utils/plotting/utils/lineout.py +224 -0
- pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
- pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
- pymodaq/utils/plotting/widgets.py +76 -0
- pymodaq/utils/scanner/__init__.py +10 -0
- pymodaq/utils/scanner/scan_factory.py +204 -0
- pymodaq/utils/scanner/scanner.py +271 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
- pymodaq/utils/scanner/scanners/sequential.py +192 -0
- pymodaq/utils/scanner/scanners/tabular.py +294 -0
- pymodaq/utils/scanner/utils.py +83 -0
- pymodaq/utils/slicing.py +47 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +51 -0
- pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
- pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
- pymodaq/utils/units.py +216 -0
- pymodaq-4.0.1.dist-info/METADATA +159 -0
- {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
- {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
- pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
- pymodaq/daq_move/daq_move_gui.py +0 -279
- pymodaq/daq_move/daq_move_gui.ui +0 -534
- pymodaq/daq_move/daq_move_main.py +0 -1042
- pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
- pymodaq/daq_move/utility_classes.py +0 -686
- pymodaq/daq_scan.py +0 -2160
- pymodaq/daq_utils/array_manipulation.py +0 -386
- pymodaq/daq_utils/config.py +0 -273
- pymodaq/daq_utils/conftests.py +0 -7
- pymodaq/daq_utils/custom_parameter_tree.py +0 -9
- pymodaq/daq_utils/daq_enums.py +0 -133
- pymodaq/daq_utils/daq_utils.py +0 -1402
- pymodaq/daq_utils/exceptions.py +0 -71
- pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
- pymodaq/daq_utils/gui_utils/file_io.py +0 -75
- pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
- pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
- pymodaq/daq_utils/h5exporters.py +0 -242
- pymodaq/daq_utils/h5modules.py +0 -1559
- pymodaq/daq_utils/h5utils.py +0 -241
- pymodaq/daq_utils/managers/action_manager.py +0 -236
- pymodaq/daq_utils/managers/parameter_manager.py +0 -57
- pymodaq/daq_utils/math_utils.py +0 -705
- pymodaq/daq_utils/parameter/__init__.py +0 -1
- pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
- pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
- pymodaq/daq_utils/parameter/utils.py +0 -136
- pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
- pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
- pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
- pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
- pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
- pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
- pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
- pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
- pymodaq/daq_utils/plotting/items/__init__.py +0 -0
- pymodaq/daq_utils/plotting/navigator.py +0 -500
- pymodaq/daq_utils/plotting/scan_selector.py +0 -289
- pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
- pymodaq/daq_utils/plotting/utils/filter.py +0 -236
- pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
- pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
- pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
- pymodaq/daq_utils/scanner.py +0 -1289
- pymodaq/daq_utils/tree_layout/__init__.py +0 -0
- pymodaq/daq_viewer/__init__.py +0 -0
- pymodaq/daq_viewer/daq_gui_settings.py +0 -237
- pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
- pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
- pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
- pymodaq/daq_viewer/utility_classes.py +0 -673
- pymodaq/examples/logger_image/__init__.py +0 -0
- pymodaq/examples/logger_image/logger_displayer.py +0 -121
- pymodaq/examples/logger_image/setup.svg +0 -3119
- pymodaq/examples/logger_image/setup_svg.py +0 -114
- pymodaq/h5browser.py +0 -39
- pymodaq/utils/scanner.py +0 -15
- pymodaq-3.6.13.dist-info/METADATA +0 -39
- pymodaq-3.6.13.dist-info/entry_points.txt +0 -8
- pymodaq-3.6.13.dist-info/top_level.txt +0 -1
- /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
- /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
- /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
- /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
- /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
- /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
- /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
- /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
- /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
- /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
- /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
- {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,1339 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""Automated scanning module functionalities for PyMoDAQ
|
|
5
|
+
|
|
6
|
+
Contains all objects related to the DAQScan module, to do automated scans, saving data...
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
from collections import OrderedDict
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import sys
|
|
14
|
+
import tempfile
|
|
15
|
+
from typing import List, Tuple, TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from qtpy import QtWidgets, QtCore, QtGui
|
|
19
|
+
from qtpy.QtCore import QObject, Slot, QThread, Signal, QDateTime, QDate, QTime
|
|
20
|
+
|
|
21
|
+
from pymodaq.utils import data as data_mod
|
|
22
|
+
from pymodaq.utils.logger import set_logger, get_module_name
|
|
23
|
+
from pymodaq.utils.config import Config, get_set_preset_path
|
|
24
|
+
from pymodaq.utils.parameter import ioxml
|
|
25
|
+
from pymodaq.utils.plotting.data_viewers.viewer import ViewersEnum
|
|
26
|
+
from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter, ParameterTree
|
|
27
|
+
|
|
28
|
+
from pymodaq.utils import exceptions
|
|
29
|
+
from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
|
|
30
|
+
from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
|
|
31
|
+
from pymodaq.utils.plotting.navigator import Navigator
|
|
32
|
+
from pymodaq.utils.plotting.scan_selector import ScanSelector, SelectorItem
|
|
33
|
+
from pymodaq.utils.scanner.scanner import Scanner, scanner_factory #, adaptive, adaptive_losses
|
|
34
|
+
from pymodaq.utils.managers.batchscan_manager import BatchScanner
|
|
35
|
+
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
36
|
+
from pymodaq.post_treatment.load_and_plot import LoaderPlotter
|
|
37
|
+
from pymodaq.utils.messenger import messagebox
|
|
38
|
+
from pymodaq.extensions.daq_scan_ui import DAQScanUI
|
|
39
|
+
|
|
40
|
+
from pymodaq.utils import daq_utils as utils
|
|
41
|
+
from pymodaq.utils import gui_utils as gutils
|
|
42
|
+
from pymodaq.utils.h5modules.saving import H5Saver
|
|
43
|
+
from pymodaq.utils.h5modules import module_saving, data_saving
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from pymodaq.dashboard import DashBoard
|
|
47
|
+
|
|
48
|
+
config = Config()
|
|
49
|
+
logger = set_logger(get_module_name(__file__))
|
|
50
|
+
|
|
51
|
+
SHOW_POPUPS = config('scan', 'show_popups')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ScanDataTemp:
|
|
55
|
+
"""Convenience class to hold temporary data to be plotted in the live plots"""
|
|
56
|
+
def __init__(self, scan_index: int, indexes: Tuple[int], data: data_mod.DataToExport):
|
|
57
|
+
self.scan_index = scan_index
|
|
58
|
+
self.indexes = indexes
|
|
59
|
+
self.data = data
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class DAQScan(QObject, ParameterManager):
|
|
63
|
+
"""
|
|
64
|
+
Main class initializing a DAQScan module with its dashboard and scanning control panel
|
|
65
|
+
"""
|
|
66
|
+
settings_name = 'daq_scan_settings'
|
|
67
|
+
command_daq_signal = Signal(utils.ThreadCommand)
|
|
68
|
+
status_signal = Signal(str)
|
|
69
|
+
live_data_1D_signal = Signal(list)
|
|
70
|
+
|
|
71
|
+
params = [
|
|
72
|
+
{'title': 'Time Flow:', 'name': 'time_flow', 'type': 'group', 'expanded': False, 'children': [
|
|
73
|
+
{'title': 'Wait time step (ms)', 'name': 'wait_time', 'type': 'int', 'value': 0,
|
|
74
|
+
'tip': 'Wait time in ms after each step of acquisition (move and grab)'},
|
|
75
|
+
{'title': 'Wait time between (ms)', 'name': 'wait_time_between', 'type': 'int',
|
|
76
|
+
'value': 0,
|
|
77
|
+
'tip': 'Wait time in ms between move and grab processes'},
|
|
78
|
+
{'title': 'Timeout (ms)', 'name': 'timeout', 'type': 'int', 'value': 10000},
|
|
79
|
+
]},
|
|
80
|
+
{'title': 'Scan options', 'name': 'scan_options', 'type': 'group', 'children': [
|
|
81
|
+
{'title': 'Naverage:', 'name': 'scan_average', 'type': 'int', 'value': 1, 'min': 1},
|
|
82
|
+
{'title': 'Group 0D data:', 'name': 'group0D', 'type': 'bool', 'value': True},
|
|
83
|
+
{'title': 'Sort 1D scan data:', 'name': 'sort_scan1D', 'type': 'bool', 'value': False},]},
|
|
84
|
+
|
|
85
|
+
{'title': 'Plotting options', 'name': 'plot_options', 'type': 'group', 'children': [
|
|
86
|
+
{'title': 'Get Probe signals', 'name': 'plot_probe', 'type': 'bool_push'},
|
|
87
|
+
{'title': 'Plot 0Ds:', 'name': 'plot_0d', 'type': 'itemselect'},
|
|
88
|
+
{'title': 'Plot 1Ds:', 'name': 'plot_1d', 'type': 'itemselect'},
|
|
89
|
+
{'title': 'Prepare Viewers', 'name': 'prepare_viewers', 'type': 'bool_push'},
|
|
90
|
+
{'title': 'Plot at each step?', 'name': 'plot_at_each_step', 'type': 'bool', 'value': True},
|
|
91
|
+
{'title': 'Refresh Plots (ms)', 'name': 'refresh_live', 'type': 'int', 'value': 1000, 'visible': False},
|
|
92
|
+
]},
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
def __init__(self, dockarea: gutils.DockArea = None, dashboard: DashBoard = None):
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
dockarea: DockArea
|
|
101
|
+
instance of the modified pyqtgraph Dockarea
|
|
102
|
+
dashboard: DashBoard
|
|
103
|
+
instance of the pymodaq dashboard
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
logger.info('Initializing DAQScan')
|
|
108
|
+
QObject.__init__(self)
|
|
109
|
+
ParameterManager.__init__(self)
|
|
110
|
+
|
|
111
|
+
self.title = __class__.__name__
|
|
112
|
+
|
|
113
|
+
self.dockarea: gutils.DockArea = dockarea
|
|
114
|
+
self.dashboard: DashBoard = dashboard
|
|
115
|
+
if dashboard is None:
|
|
116
|
+
raise Exception('No valid dashboard initialized')
|
|
117
|
+
|
|
118
|
+
self.mainwindow = self.dockarea.parent()
|
|
119
|
+
self.ui: DAQScanUI = DAQScanUI(self.dockarea)
|
|
120
|
+
|
|
121
|
+
self.wait_time = 1000
|
|
122
|
+
|
|
123
|
+
self.navigator: Navigator = None
|
|
124
|
+
self.scan_selector: ScanSelector = None
|
|
125
|
+
|
|
126
|
+
self.ind_scan = 0
|
|
127
|
+
self.ind_average = 0
|
|
128
|
+
|
|
129
|
+
self._metada_dataset_set = False
|
|
130
|
+
|
|
131
|
+
self.curvilinear_values = []
|
|
132
|
+
self.plot_colors = utils.plot_colors
|
|
133
|
+
|
|
134
|
+
self.scan_thread: QThread = None
|
|
135
|
+
|
|
136
|
+
self.modules_manager = ModulesManager(self.dashboard.detector_modules, self.dashboard.actuators_modules)
|
|
137
|
+
self.modules_manager.settings.child('data_dimensions').setOpts(expanded=False)
|
|
138
|
+
self.modules_manager.settings.child('actuators_positions').setOpts(expanded=False)
|
|
139
|
+
self.modules_manager.detectors_changed.connect(self.clear_plot_from)
|
|
140
|
+
|
|
141
|
+
self.h5saver = H5Saver()
|
|
142
|
+
self.module_and_data_saver = module_saving.ScanSaver(self)
|
|
143
|
+
self.module_and_data_saver.h5saver = self.h5saver
|
|
144
|
+
|
|
145
|
+
self.extended_saver: data_saving.DataToExportExtendedSaver = None
|
|
146
|
+
self.h5temp: H5Saver = None
|
|
147
|
+
self.temp_path: tempfile.TemporaryDirectory = None
|
|
148
|
+
|
|
149
|
+
self.h5saver.settings.child('do_save').hide()
|
|
150
|
+
self.h5saver.settings.child('custom_name').hide()
|
|
151
|
+
self.h5saver.new_file_sig.connect(self.create_new_file)
|
|
152
|
+
|
|
153
|
+
self.scanner = Scanner(actuators=self.modules_manager.actuators) # , adaptive_losses=adaptive_losses)
|
|
154
|
+
self.scan_parameters = None
|
|
155
|
+
|
|
156
|
+
self.batcher: BatchScanner = None
|
|
157
|
+
self.batch_started = False
|
|
158
|
+
self.ind_batch = 0
|
|
159
|
+
|
|
160
|
+
self.modules_manager.actuators_changed[list].connect(self.update_actuators)
|
|
161
|
+
|
|
162
|
+
self.setup_ui()
|
|
163
|
+
self.ui.command_sig.connect(self.process_ui_cmds)
|
|
164
|
+
|
|
165
|
+
self.create_dataset_settings()
|
|
166
|
+
self.setup_modules(self.dashboard.title)
|
|
167
|
+
self.set_config()
|
|
168
|
+
|
|
169
|
+
self.live_plotter = LoaderPlotter(self.dockarea)
|
|
170
|
+
self.live_timer = QtCore.QTimer()
|
|
171
|
+
self.live_timer.timeout.connect(self.update_live_plots)
|
|
172
|
+
|
|
173
|
+
self.ui.enable_start_stop(True)
|
|
174
|
+
logger.info('DAQScan Initialized')
|
|
175
|
+
|
|
176
|
+
def plot_from(self):
|
|
177
|
+
self.modules_manager.get_det_data_list()
|
|
178
|
+
data0D = self.modules_manager.settings['data_dimensions', 'det_data_list0D']
|
|
179
|
+
data1D = self.modules_manager.settings['data_dimensions', 'det_data_list1D']
|
|
180
|
+
data0D['selected'] = data0D['all_items']
|
|
181
|
+
data1D['selected'] = data1D['all_items']
|
|
182
|
+
self.settings.child('plot_options', 'plot_0d').setValue(data0D)
|
|
183
|
+
self.settings.child('plot_options', 'plot_1d').setValue(data1D)
|
|
184
|
+
|
|
185
|
+
def setup_ui(self):
|
|
186
|
+
self.ui.populate_toolbox_widget([self.settings_tree, self.h5saver.settings_tree],
|
|
187
|
+
['General Settings', 'Save Settings'])
|
|
188
|
+
|
|
189
|
+
self.ui.set_scanner_settings(self.scanner.parent_widget)
|
|
190
|
+
self.ui.set_modules_settings(self.modules_manager.settings_tree)
|
|
191
|
+
|
|
192
|
+
self.plotting_settings_tree = ParameterTree()
|
|
193
|
+
self.plotting_settings_tree.setParameters(self.settings.child('plot_options'))
|
|
194
|
+
self.ui.set_plotting_settings(self.plotting_settings_tree)
|
|
195
|
+
|
|
196
|
+
################
|
|
197
|
+
# CONFIG/SETUP UI / EXIT
|
|
198
|
+
|
|
199
|
+
def set_config(self):
|
|
200
|
+
self.settings.child('time_flow', 'wait_time').setValue(config['scan']['timeflow']['wait_time'])
|
|
201
|
+
self.settings.child('time_flow', 'wait_time_between').setValue(config['scan']['timeflow']['wait_time'])
|
|
202
|
+
self.settings.child('time_flow', 'timeout').setValue(config['scan']['timeflow']['timeout'])
|
|
203
|
+
|
|
204
|
+
self.settings.child('scan_options', 'scan_average').setValue(config['scan']['Naverage'])
|
|
205
|
+
self.settings.child('scan_options', 'sort_scan1D').setValue(config['scan']['sort1D'])
|
|
206
|
+
|
|
207
|
+
def setup_modules(self, filename):
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
try:
|
|
212
|
+
# todo update with v4 layout
|
|
213
|
+
pass
|
|
214
|
+
######################################################################
|
|
215
|
+
# set scan selector
|
|
216
|
+
# items = OrderedDict()
|
|
217
|
+
# if self.navigator is not None:
|
|
218
|
+
# items["Navigator"] = dict(viewers=[self.navigator.viewer], names=["Navigator"])
|
|
219
|
+
# for det in self.modules_manager.detectors_all:
|
|
220
|
+
# if len([view for view in det.ui.viewers if view.viewer_type == 'Data2D']) != 0:
|
|
221
|
+
# items[det.title] = dict(viewers=[view for view in det.ui.viewers if view.viewer_type == 'Data2D'],
|
|
222
|
+
# names=[view.title for view in det.ui.viewers if
|
|
223
|
+
# view.viewer_type == 'Data2D'], )
|
|
224
|
+
# items["DAQScan"] = dict(viewers=[self.ui.scan2D_graph], names=["DAQScan"])
|
|
225
|
+
#
|
|
226
|
+
# if self.navigator is not None:
|
|
227
|
+
# items = OrderedDict(Navigator=dict(viewers=[self.navigator.viewer], names=["Navigator"]))
|
|
228
|
+
# items.update(self.scanner.scan_selector.viewers_items)
|
|
229
|
+
#
|
|
230
|
+
# self.scanner.viewers_items = items
|
|
231
|
+
#
|
|
232
|
+
# self.scanner.scan_selector.widget.setVisible(False)
|
|
233
|
+
# self.scanner.scan_selector.settings.child('scan_options', 'scan_type').hide()
|
|
234
|
+
#
|
|
235
|
+
# self.scanner.scan_selector.widget.setVisible(False)
|
|
236
|
+
# self.scanner.scan_selector.show_scan_selector(visible=False)
|
|
237
|
+
#
|
|
238
|
+
# self.show_average_dock(False)
|
|
239
|
+
#
|
|
240
|
+
# self.ui.scan_dock.setEnabled(True)
|
|
241
|
+
# self.file_menu.setEnabled(True)
|
|
242
|
+
# self.settings_menu.setEnabled(True)
|
|
243
|
+
# self.create_new_file(True)
|
|
244
|
+
|
|
245
|
+
except Exception as e:
|
|
246
|
+
logger.exception(str(e))
|
|
247
|
+
# self.update_status(getLineInfo()+str(e), self.wait_time, log_type='log')
|
|
248
|
+
|
|
249
|
+
def create_average_dock(self):
|
|
250
|
+
#todo update with v4 layout
|
|
251
|
+
self.ui.average_dock = gutils.dock.Dock("Averaging")
|
|
252
|
+
average_tab = QtWidgets.QTabWidget()
|
|
253
|
+
average1D_widget = QtWidgets.QWidget()
|
|
254
|
+
average2D_widget = QtWidgets.QWidget()
|
|
255
|
+
|
|
256
|
+
# %% init the 1D viewer
|
|
257
|
+
self.ui.average1D_graph = Viewer1D(average1D_widget)
|
|
258
|
+
|
|
259
|
+
# %% init the 2D viewer
|
|
260
|
+
self.ui.average2D_graph = Viewer2D(average2D_widget)
|
|
261
|
+
|
|
262
|
+
average_tab.addTab(average1D_widget, '1D plot Average')
|
|
263
|
+
average_tab.addTab(average2D_widget, '2D plot Average')
|
|
264
|
+
|
|
265
|
+
self.ui.average_dock.addWidget(average_tab)
|
|
266
|
+
self.dockarea.addDock(self.ui.average_dock, 'right', self.ui.scan_dock)
|
|
267
|
+
|
|
268
|
+
self.ui.average_dock.setVisible(False)
|
|
269
|
+
|
|
270
|
+
def process_ui_cmds(self, cmd: utils.ThreadCommand):
|
|
271
|
+
"""Process commands sent by actions done in the ui
|
|
272
|
+
|
|
273
|
+
Parameters
|
|
274
|
+
----------
|
|
275
|
+
cmd: ThreadCommand
|
|
276
|
+
Possible values are:
|
|
277
|
+
* quit
|
|
278
|
+
* ini_positions
|
|
279
|
+
* start
|
|
280
|
+
* start_batch
|
|
281
|
+
* stop
|
|
282
|
+
* move_at
|
|
283
|
+
* show_log
|
|
284
|
+
* load
|
|
285
|
+
* save
|
|
286
|
+
* show_file
|
|
287
|
+
* navigator
|
|
288
|
+
* batch
|
|
289
|
+
* viewers_changed
|
|
290
|
+
"""
|
|
291
|
+
if cmd.command == 'quit':
|
|
292
|
+
self.quit_fun()
|
|
293
|
+
elif cmd.command == 'ini_positions':
|
|
294
|
+
self.set_ini_positions()
|
|
295
|
+
elif cmd.command == 'start':
|
|
296
|
+
self.start_scan()
|
|
297
|
+
elif cmd.command == 'start_batch':
|
|
298
|
+
self.start_scan_batch()
|
|
299
|
+
elif cmd.command == 'stop':
|
|
300
|
+
self.stop_scan()
|
|
301
|
+
elif cmd.command == 'move_at':
|
|
302
|
+
self.move_to_crosshair()
|
|
303
|
+
elif cmd.command == 'show_log':
|
|
304
|
+
self.show_log()
|
|
305
|
+
elif cmd.command == 'load':
|
|
306
|
+
self.load_file()
|
|
307
|
+
elif cmd.command == 'save':
|
|
308
|
+
self.save_file()
|
|
309
|
+
elif cmd.command == 'show_file':
|
|
310
|
+
self.show_file_content()
|
|
311
|
+
elif cmd.command == 'navigator':
|
|
312
|
+
self.show_navigator()
|
|
313
|
+
elif cmd.command == 'batch':
|
|
314
|
+
self.show_batcher(self.ui.menubar)
|
|
315
|
+
elif cmd.command == 'viewers_changed':
|
|
316
|
+
...
|
|
317
|
+
|
|
318
|
+
def show_log(self):
|
|
319
|
+
"""Open the log file in the default text editor"""
|
|
320
|
+
import webbrowser
|
|
321
|
+
webbrowser.open(logger.parent.handlers[0].baseFilename)
|
|
322
|
+
|
|
323
|
+
def quit_fun(self):
|
|
324
|
+
"""
|
|
325
|
+
Quit the current instance of DAQ_scan
|
|
326
|
+
|
|
327
|
+
See Also
|
|
328
|
+
--------
|
|
329
|
+
quit_fun
|
|
330
|
+
"""
|
|
331
|
+
try:
|
|
332
|
+
if self.temp_path is not None:
|
|
333
|
+
try:
|
|
334
|
+
self.h5temp.close()
|
|
335
|
+
self.temp_path.cleanup()
|
|
336
|
+
except Exception as e:
|
|
337
|
+
logger.exception(str(e))
|
|
338
|
+
|
|
339
|
+
self.h5saver.close_file()
|
|
340
|
+
self.ui.average_dock.close()
|
|
341
|
+
self.ui.scan_dock.close()
|
|
342
|
+
self.dockarea.close()
|
|
343
|
+
|
|
344
|
+
except Exception as e:
|
|
345
|
+
logger.exception(str(e))
|
|
346
|
+
|
|
347
|
+
def create_dataset_settings(self):
|
|
348
|
+
# params about dataset attributes and scan attibutes
|
|
349
|
+
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
350
|
+
params_dataset = [{'title': 'Dataset information', 'name': 'dataset_info', 'type': 'group', 'children': [
|
|
351
|
+
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config['user']['name']},
|
|
352
|
+
{'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
|
|
353
|
+
{'title': 'Sample:', 'name': 'sample', 'type': 'str', 'value': ''},
|
|
354
|
+
{'title': 'Experiment type:', 'name': 'experiment_type', 'type': 'str', 'value': ''},
|
|
355
|
+
{'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''}]}]
|
|
356
|
+
|
|
357
|
+
params_scan = [{'title': 'Scan information', 'name': 'scan_info', 'type': 'group', 'children': [
|
|
358
|
+
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config['user']['name']},
|
|
359
|
+
{'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
|
|
360
|
+
{'title': 'Scan type:', 'name': 'scan_type', 'type': 'str', 'value': ''},
|
|
361
|
+
{'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'str', 'value': ''},
|
|
362
|
+
{'title': 'Scan name:', 'name': 'scan_name', 'type': 'str', 'value': '', 'readonly': True},
|
|
363
|
+
{'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''},
|
|
364
|
+
]}]
|
|
365
|
+
|
|
366
|
+
self.dataset_attributes = Parameter.create(name='Attributes', type='group', children=params_dataset)
|
|
367
|
+
self.scan_attributes = Parameter.create(name='Attributes', type='group', children=params_scan)
|
|
368
|
+
###################
|
|
369
|
+
# external modules
|
|
370
|
+
|
|
371
|
+
def show_batcher(self, menubar):
|
|
372
|
+
self.batcher = BatchScanner(self.dockarea, self.modules_manager.actuators_all,
|
|
373
|
+
self.modules_manager.detectors_all)
|
|
374
|
+
self.batcher.create_menu(menubar)
|
|
375
|
+
self.batcher.setupUI()
|
|
376
|
+
self.ui.set_action_visible('start_batch', True)
|
|
377
|
+
|
|
378
|
+
def start_scan_batch(self):
|
|
379
|
+
self.batch_started = True
|
|
380
|
+
self.ind_batch = 0
|
|
381
|
+
self.loop_scan_batch()
|
|
382
|
+
|
|
383
|
+
def loop_scan_batch(self):
|
|
384
|
+
if self.ind_batch >= len(self.batcher.scans_names):
|
|
385
|
+
self.stop_scan()
|
|
386
|
+
return
|
|
387
|
+
self.scanner = self.batcher.get_scan(self.batcher.scans_names[self.ind_batch])
|
|
388
|
+
actuators, detectors = self.batcher.get_act_dets()
|
|
389
|
+
self.set_scan_batch(actuators[self.batcher.scans_names[self.ind_batch]],
|
|
390
|
+
detectors[self.batcher.scans_names[self.ind_batch]])
|
|
391
|
+
self.start_scan()
|
|
392
|
+
|
|
393
|
+
def set_scan_batch(self, actuators, detectors):
|
|
394
|
+
self.modules_manager.selected_detectors_name = detectors
|
|
395
|
+
self.modules_manager.selected_actuators_name = actuators
|
|
396
|
+
QtWidgets.QApplication.processEvents()
|
|
397
|
+
|
|
398
|
+
def show_file_attributes(self, type_info='dataset'):
|
|
399
|
+
"""
|
|
400
|
+
Switch the type_info value.
|
|
401
|
+
|
|
402
|
+
In case of :
|
|
403
|
+
* *scan* : Set parameters showing top false
|
|
404
|
+
* *dataset* : Set parameters showing top false
|
|
405
|
+
* *managers* : Set parameters showing top false. Add the save/cancel buttons to the accept/reject dialog (to save managers parameters in a xml file).
|
|
406
|
+
|
|
407
|
+
Finally, in case of accepted managers type info, save the managers parameters in a xml file.
|
|
408
|
+
|
|
409
|
+
=============== =========== ====================================
|
|
410
|
+
**Parameters** **Type** **Description**
|
|
411
|
+
*type_info* string The file type information between
|
|
412
|
+
* scan
|
|
413
|
+
* dataset
|
|
414
|
+
* managers
|
|
415
|
+
=============== =========== ====================================
|
|
416
|
+
|
|
417
|
+
See Also
|
|
418
|
+
--------
|
|
419
|
+
custom_tree.parameter_to_xml_file, create_menu
|
|
420
|
+
"""
|
|
421
|
+
if SHOW_POPUPS:
|
|
422
|
+
dialog = QtWidgets.QDialog()
|
|
423
|
+
vlayout = QtWidgets.QVBoxLayout()
|
|
424
|
+
tree = ParameterTree()
|
|
425
|
+
tree.setMinimumWidth(400)
|
|
426
|
+
tree.setMinimumHeight(500)
|
|
427
|
+
if type_info == 'scan':
|
|
428
|
+
tree.setParameters(self.scan_attributes, showTop=False)
|
|
429
|
+
elif type_info == 'dataset':
|
|
430
|
+
tree.setParameters(self.dataset_attributes, showTop=False)
|
|
431
|
+
|
|
432
|
+
vlayout.addWidget(tree)
|
|
433
|
+
dialog.setLayout(vlayout)
|
|
434
|
+
buttonBox = QtWidgets.QDialogButtonBox(parent=dialog)
|
|
435
|
+
buttonBox.addButton('Cancel', buttonBox.RejectRole)
|
|
436
|
+
buttonBox.addButton('Apply', buttonBox.AcceptRole)
|
|
437
|
+
buttonBox.rejected.connect(dialog.reject)
|
|
438
|
+
buttonBox.accepted.connect(dialog.accept)
|
|
439
|
+
|
|
440
|
+
vlayout.addWidget(buttonBox)
|
|
441
|
+
dialog.setWindowTitle('Fill in information about this {}'.format(type_info))
|
|
442
|
+
res = dialog.exec()
|
|
443
|
+
else:
|
|
444
|
+
res = True
|
|
445
|
+
return res
|
|
446
|
+
|
|
447
|
+
def show_file_content(self):
|
|
448
|
+
try:
|
|
449
|
+
self.h5saver.init_file(addhoc_file_path=self.h5saver.settings.child(('current_h5_file')).value())
|
|
450
|
+
self.h5saver.show_file_content()
|
|
451
|
+
except Exception as e:
|
|
452
|
+
logger.exception(str(e))
|
|
453
|
+
|
|
454
|
+
def show_navigator(self):
|
|
455
|
+
|
|
456
|
+
if self.navigator is None:
|
|
457
|
+
# loading navigator
|
|
458
|
+
self.navigator_dock = gutils.Dock('Navigator')
|
|
459
|
+
widgnav = QtWidgets.QWidget()
|
|
460
|
+
self.navigator_dock.addWidget(widgnav)
|
|
461
|
+
self.dockarea.addDock(self.navigator_dock)
|
|
462
|
+
self.navigator_dock.float()
|
|
463
|
+
|
|
464
|
+
self.navigator = Navigator(widgnav)
|
|
465
|
+
|
|
466
|
+
self.navigator.log_signal[str].connect(self.dashboard.add_status)
|
|
467
|
+
self.navigator.settings.child('settings', 'Load h5').hide()
|
|
468
|
+
self.navigator.set_action_visible('load_scan', False)
|
|
469
|
+
|
|
470
|
+
self.navigator.sig_double_clicked.connect(self.move_at)
|
|
471
|
+
self.navigator.h5saver = self.h5saver
|
|
472
|
+
self.navigator.list_2D_scans()
|
|
473
|
+
|
|
474
|
+
self.show_scan_selector()
|
|
475
|
+
|
|
476
|
+
def show_scan_selector(self):
|
|
477
|
+
viewer_items = []
|
|
478
|
+
if self.navigator is not None:
|
|
479
|
+
viewer_items.append(SelectorItem(self.navigator.viewer, name='Navigator'))
|
|
480
|
+
#
|
|
481
|
+
# for viewer in self.live_plotter.viewers:
|
|
482
|
+
# viewer_items.update({viewer.title: dict(viewers=[viewer], names=[viewer.title])})
|
|
483
|
+
self.scan_selector = ScanSelector(viewer_items)
|
|
484
|
+
|
|
485
|
+
self.ui.add_scanner_settings(self.scan_selector.settings_tree)
|
|
486
|
+
|
|
487
|
+
self.scan_selector.scan_select_signal.connect(self.scanner.update_from_scan_selector)
|
|
488
|
+
|
|
489
|
+
################
|
|
490
|
+
# LOADING SAVING
|
|
491
|
+
|
|
492
|
+
def load_file(self):
|
|
493
|
+
self.h5saver.load_file(self.h5saver.h5_file_path)
|
|
494
|
+
|
|
495
|
+
def save_file(self):
|
|
496
|
+
if not os.path.isdir(self.h5saver.settings['base_path']):
|
|
497
|
+
os.mkdir(self.h5saver.settings['base_path'])
|
|
498
|
+
filename = gutils.file_io.select_file(self.h5saver.settings['base_path'], save=True, ext='h5')
|
|
499
|
+
self.h5saver.h5_file.copy_file(str(filename))
|
|
500
|
+
|
|
501
|
+
def save_metadata(self, node, type_info='dataset_info'):
|
|
502
|
+
"""
|
|
503
|
+
Switch the type_info value with :
|
|
504
|
+
* *'dataset_info'* : Give the params attributes the dataset_attributes values
|
|
505
|
+
* *'dataset'* : Give the params attributes the scan_attributes values
|
|
506
|
+
|
|
507
|
+
|
|
|
508
|
+
| Once done, course the params and add string casted date/time metadata as an element of attributes array.
|
|
509
|
+
| Save the contents of given parameter object into a xml string unde the attributes settings.
|
|
510
|
+
|
|
511
|
+
=============== =================== =========================================
|
|
512
|
+
**Parameters** **Type** **Description**
|
|
513
|
+
*node* pytables h5 node Root node to be treated
|
|
514
|
+
*type_info* string File type info between :
|
|
515
|
+
* 'dataset_info'
|
|
516
|
+
* 'scan_info'
|
|
517
|
+
=============== =================== =========================================
|
|
518
|
+
|
|
519
|
+
See Also
|
|
520
|
+
--------
|
|
521
|
+
custom_tree.parameter_to_xml_string
|
|
522
|
+
"""
|
|
523
|
+
|
|
524
|
+
attr = node.attrs
|
|
525
|
+
if type_info == 'dataset_info':
|
|
526
|
+
attr['type'] = 'dataset'
|
|
527
|
+
params = self.dataset_attributes
|
|
528
|
+
else:
|
|
529
|
+
attr['type'] = 'scan'
|
|
530
|
+
params = self.scan_attributes
|
|
531
|
+
for child in params.child(type_info).children():
|
|
532
|
+
if type(child.value()) is QDateTime:
|
|
533
|
+
attr[child.name()] = child.value().toString('dd/mm/yyyy HH:MM:ss')
|
|
534
|
+
else:
|
|
535
|
+
attr[child.name()] = child.value()
|
|
536
|
+
if type_info == 'dataset_info':
|
|
537
|
+
# save contents of given parameter object into an xml string under the attribute settings
|
|
538
|
+
settings_str = b'<All_settings title="All Settings" type="group">' + \
|
|
539
|
+
ioxml.parameter_to_xml_string(params) + \
|
|
540
|
+
ioxml.parameter_to_xml_string(self.settings)
|
|
541
|
+
# ioxml.parameter_to_xml_string(
|
|
542
|
+
# self.dashboard.preset_manager.preset_params) +\
|
|
543
|
+
settings_str += b'</All_settings>'
|
|
544
|
+
attr['settings'] = settings_str
|
|
545
|
+
|
|
546
|
+
elif type_info == 'scan_info':
|
|
547
|
+
settings_all = [ioxml.parameter_to_xml_string(params),
|
|
548
|
+
ioxml.parameter_to_xml_string(self.settings),
|
|
549
|
+
ioxml.parameter_to_xml_string(self.h5saver.settings),
|
|
550
|
+
ioxml.parameter_to_xml_string(self.scanner.settings)]
|
|
551
|
+
|
|
552
|
+
settings_str = b'<All_settings title="All Settings" type="group">'
|
|
553
|
+
for set in settings_all:
|
|
554
|
+
if len(settings_str + set) < 60000:
|
|
555
|
+
# size limit for any object header (including all the other attributes) is 64kb
|
|
556
|
+
settings_str += set
|
|
557
|
+
else:
|
|
558
|
+
break
|
|
559
|
+
settings_str += b'</All_settings>'
|
|
560
|
+
attr['settings'] = settings_str
|
|
561
|
+
|
|
562
|
+
def create_new_file(self, new_file):
|
|
563
|
+
if new_file:
|
|
564
|
+
self._metada_dataset_set = False
|
|
565
|
+
self.h5saver.init_file(update_h5=new_file)
|
|
566
|
+
self.module_and_data_saver.h5saver = self.h5saver
|
|
567
|
+
res = self.update_file_settings()
|
|
568
|
+
if new_file:
|
|
569
|
+
self.ui.enable_start_stop()
|
|
570
|
+
return res
|
|
571
|
+
|
|
572
|
+
def update_file_settings(self):
|
|
573
|
+
try:
|
|
574
|
+
res = True
|
|
575
|
+
if not self._metada_dataset_set:
|
|
576
|
+
res = self.set_metadata_about_dataset()
|
|
577
|
+
self.save_metadata(self.h5saver.raw_group, 'dataset_info')
|
|
578
|
+
|
|
579
|
+
if self.navigator is not None:
|
|
580
|
+
self.navigator.update_h5file(self.h5saver.h5_file)
|
|
581
|
+
self.navigator.settings.child('settings', 'filepath').setValue(self.h5saver.h5_file.filename)
|
|
582
|
+
|
|
583
|
+
# # set attributes to the current group, such as scan_type....
|
|
584
|
+
# self.scan_attributes.child('scan_info', 'scan_type').setValue(
|
|
585
|
+
# self.scanner.settings.child('scan_type').value())
|
|
586
|
+
# self.scan_attributes.child('scan_info', 'scan_sub_type').setValue(
|
|
587
|
+
# self.scanner.settings.child('scan_sub_type').value())
|
|
588
|
+
#
|
|
589
|
+
# scan_node = self.module_and_data_saver.get_set_node()
|
|
590
|
+
# self.scan_attributes.child('scan_info', 'scan_name').setValue(scan_node.name)
|
|
591
|
+
# self.scan_attributes.child('scan_info', 'description').setValue('')
|
|
592
|
+
#
|
|
593
|
+
# res = self.set_metadata_about_current_scan()
|
|
594
|
+
|
|
595
|
+
return res
|
|
596
|
+
|
|
597
|
+
except Exception as e:
|
|
598
|
+
logger.exception(str(e))
|
|
599
|
+
|
|
600
|
+
def update_scan_info(self):
|
|
601
|
+
# set attributes to the current group, such as scan_type....
|
|
602
|
+
self.scan_attributes.child('scan_info', 'scan_type').setValue(
|
|
603
|
+
self.scanner.settings.child('scan_type').value())
|
|
604
|
+
self.scan_attributes.child('scan_info', 'scan_sub_type').setValue(
|
|
605
|
+
self.scanner.settings.child('scan_sub_type').value())
|
|
606
|
+
scan_node = self.module_and_data_saver.get_set_node(new=False)
|
|
607
|
+
if scan_node.attrs['scan_done']:
|
|
608
|
+
scan_name = self.module_and_data_saver.get_next_node_name()
|
|
609
|
+
else:
|
|
610
|
+
scan_name = scan_node.name
|
|
611
|
+
self.scan_attributes.child('scan_info', 'scan_name').setValue(scan_name)
|
|
612
|
+
self.scan_attributes.child('scan_info', 'description').setValue('')
|
|
613
|
+
|
|
614
|
+
res = self.set_metadata_about_current_scan()
|
|
615
|
+
return res
|
|
616
|
+
|
|
617
|
+
# PROCESS MODIFICATIONS
|
|
618
|
+
def update_actuators(self, actuators: List[str]):
|
|
619
|
+
self.scanner.actuators = self.modules_manager.actuators
|
|
620
|
+
|
|
621
|
+
def move_to_crosshair(self, *args, **kwargs):
|
|
622
|
+
if self.ui.is_action_checked('move_at'):
|
|
623
|
+
self.modules_manager.connect_actuators()
|
|
624
|
+
self.live_plotter.connect_double_clicked(self.move_at)
|
|
625
|
+
else:
|
|
626
|
+
self.live_plotter.disconnect(self.move_at)
|
|
627
|
+
self.modules_manager.connect_actuators(False)
|
|
628
|
+
|
|
629
|
+
def move_at(self, posx: float, posy: float = None):
|
|
630
|
+
if logging.getLevelName(logger.level) == 'DEBUG':
|
|
631
|
+
print(f'clicked at: {posx}, {posy}')
|
|
632
|
+
positions = [posx, posy]
|
|
633
|
+
positions = positions[:self.scanner.n_axes]
|
|
634
|
+
self.modules_manager.move_actuators(positions)
|
|
635
|
+
|
|
636
|
+
def value_changed(self, param):
|
|
637
|
+
"""
|
|
638
|
+
|
|
639
|
+
"""
|
|
640
|
+
if param.name() == 'scan_average':
|
|
641
|
+
self.ui.show_average_step(param.value() > 1)
|
|
642
|
+
elif param.name() == 'prepare_viewers':
|
|
643
|
+
self.prepare_viewers()
|
|
644
|
+
elif param.name() == 'plot_probe':
|
|
645
|
+
self.plot_from()
|
|
646
|
+
elif param.name() == 'plot_at_each_step':
|
|
647
|
+
self.settings.child('plot_options', 'refresh_live').show(not param.value())
|
|
648
|
+
|
|
649
|
+
def clear_plot_from(self):
|
|
650
|
+
self.settings.child('plot_options', 'plot_0d').setValue(dict(all_items=[], selected=[]))
|
|
651
|
+
self.settings.child('plot_options', 'plot_1d').setValue(dict(all_items=[], selected=[]))
|
|
652
|
+
|
|
653
|
+
def prepare_viewers(self):
|
|
654
|
+
|
|
655
|
+
viewers_enum = [ViewersEnum('Data0D').increase_dim(self.scanner.n_axes)
|
|
656
|
+
for _ in range(len(self.settings['plot_options', 'plot_0d']['selected']))]
|
|
657
|
+
data_names = self.settings['plot_options', 'plot_0d']['selected']
|
|
658
|
+
|
|
659
|
+
if self.settings['scan_options', 'group0D'] and len(viewers_enum) > 0 and ViewersEnum('Data1D') in viewers_enum:
|
|
660
|
+
viewers_enum = [ViewersEnum('Data1D')]
|
|
661
|
+
data_names = [self.live_plotter.grouped_data1D_fullname]
|
|
662
|
+
viewers_enum.extend([ViewersEnum('Data1D').increase_dim(self.scanner.n_axes)
|
|
663
|
+
for _ in range(len(self.settings['plot_options', 'plot_1d']['selected']))])
|
|
664
|
+
data_names.extend(self.settings['plot_options', 'plot_1d']['selected'])
|
|
665
|
+
self.live_plotter.prepare_viewers(viewers_enum, viewers_name=data_names)
|
|
666
|
+
|
|
667
|
+
def update_status(self, txt, wait_time=0, log_type=None):
|
|
668
|
+
"""
|
|
669
|
+
Show the txt message in the status bar with a delay of wait_time ms.
|
|
670
|
+
|
|
671
|
+
=============== =========== =======================
|
|
672
|
+
**Parameters** **Type** **Description**
|
|
673
|
+
*txt* string The message to show
|
|
674
|
+
*wait_time* int the delay of showing
|
|
675
|
+
*log_type* string the type of the log
|
|
676
|
+
=============== =========== =======================
|
|
677
|
+
"""
|
|
678
|
+
self.ui.display_status(txt, wait_time)
|
|
679
|
+
self.status_signal.emit(txt)
|
|
680
|
+
logger.info(txt)
|
|
681
|
+
|
|
682
|
+
def save_scan(self):
|
|
683
|
+
#todo update with v4 layout
|
|
684
|
+
pass
|
|
685
|
+
|
|
686
|
+
@Slot(list)
|
|
687
|
+
def thread_status(self, status): # general function to get datas/infos from all threads back to the main
|
|
688
|
+
"""
|
|
689
|
+
| General function to get datas/infos from all threads back to the main.
|
|
690
|
+
|
|
|
691
|
+
|
|
692
|
+
Switch the status with :
|
|
693
|
+
* *"Update status"* : Update the status bar with the status attribute txt message
|
|
694
|
+
* *"Update_scan_index"* : Set the value of the User Interface - indice_scan_sb attribute.
|
|
695
|
+
* *"Scan_done"* : Save the scan and init the positions
|
|
696
|
+
* *"Timeout"* : Set the "Timeout occured" in the User Interface-log message
|
|
697
|
+
|
|
698
|
+
See Also
|
|
699
|
+
--------
|
|
700
|
+
update_status, save_scan, set_ini_positions
|
|
701
|
+
"""
|
|
702
|
+
if status[0] == "Update_Status":
|
|
703
|
+
self.update_status(status[1], wait_time=self.wait_time)
|
|
704
|
+
|
|
705
|
+
elif status[0] == "Update_scan_index":
|
|
706
|
+
# status[1] = [ind_scan,ind_average]
|
|
707
|
+
self.ind_scan = status[1][0]
|
|
708
|
+
self.ui.set_scan_step(status[1][0] + 1)
|
|
709
|
+
self.ind_average = status[1][1]
|
|
710
|
+
self.ui.set_scan_step_average(status[1][1] + 1)
|
|
711
|
+
|
|
712
|
+
elif status[0] == "Scan_done":
|
|
713
|
+
self.modules_manager.reset_signals()
|
|
714
|
+
self.live_timer.stop()
|
|
715
|
+
self.ui.set_scan_done()
|
|
716
|
+
scan_node = self.module_and_data_saver.get_set_node()
|
|
717
|
+
scan_node.attrs['scan_done'] = True
|
|
718
|
+
self.save_scan()
|
|
719
|
+
if not self.batch_started:
|
|
720
|
+
if not self.dashboard.overshoot:
|
|
721
|
+
self.set_ini_positions()
|
|
722
|
+
self.ui.set_action_enabled('ini_positions', True)
|
|
723
|
+
self.ui.set_action_enabled('start', True)
|
|
724
|
+
|
|
725
|
+
# reactivate module controls usiong remote_control
|
|
726
|
+
if hasattr(self.dashboard, 'remote_manager'):
|
|
727
|
+
remote_manager = getattr(self.dashboard, 'remote_manager')
|
|
728
|
+
remote_manager.activate_all(True)
|
|
729
|
+
if self.navigator is not None:
|
|
730
|
+
self.navigator.list_2D_scans()
|
|
731
|
+
else:
|
|
732
|
+
self.ind_batch += 1
|
|
733
|
+
self.loop_scan_batch()
|
|
734
|
+
|
|
735
|
+
elif status[0] == "Timeout":
|
|
736
|
+
self.ui.set_permanent_status('Timeout occurred')
|
|
737
|
+
|
|
738
|
+
############
|
|
739
|
+
# PLOTTING
|
|
740
|
+
|
|
741
|
+
def save_temp_live_data(self, scan_data: ScanDataTemp):
|
|
742
|
+
if scan_data.scan_index == 0:
|
|
743
|
+
nav_axes = self.scanner.get_nav_axes()
|
|
744
|
+
|
|
745
|
+
#
|
|
746
|
+
# for nav_axe in nav_axes:
|
|
747
|
+
# nav_axe.index += 1
|
|
748
|
+
# nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1, self.Naverage),
|
|
749
|
+
# index=0))
|
|
750
|
+
self.extended_saver.add_nav_axes(self.h5temp.raw_group, nav_axes)
|
|
751
|
+
|
|
752
|
+
self.extended_saver.add_data(self.h5temp.raw_group, scan_data.data, scan_data.indexes,
|
|
753
|
+
distribution=self.scanner.distribution)
|
|
754
|
+
if self.settings['plot_options', 'plot_at_each_step']:
|
|
755
|
+
self.update_live_plots()
|
|
756
|
+
|
|
757
|
+
def update_live_plots(self):
|
|
758
|
+
|
|
759
|
+
if self.settings['scan_options', 'scan_average'] > 1:
|
|
760
|
+
average_axis = 0
|
|
761
|
+
else:
|
|
762
|
+
average_axis = None
|
|
763
|
+
try:
|
|
764
|
+
self.live_plotter.load_plot_data(group_1D=self.settings['scan_options', 'group0D'],
|
|
765
|
+
average_axis=average_axis, average_index=self.ind_average,
|
|
766
|
+
target_at=self.scanner.positions[self.ind_scan])
|
|
767
|
+
except Exception as e:
|
|
768
|
+
logger.exception(str(e))
|
|
769
|
+
#################
|
|
770
|
+
# SCAN FLOW
|
|
771
|
+
|
|
772
|
+
def set_scan(self, scan=None) -> bool:
|
|
773
|
+
"""
|
|
774
|
+
Sets the current scan given the selected settings. Makes some checks, increments the h5 file scans.
|
|
775
|
+
In case the dialog is cancelled, return False and aborts the scan
|
|
776
|
+
"""
|
|
777
|
+
try:
|
|
778
|
+
# set the filename and path
|
|
779
|
+
if self.h5saver.h5_file is None: # only the first time start scan is called
|
|
780
|
+
self.create_new_file(True)
|
|
781
|
+
res = self.update_scan_info()
|
|
782
|
+
if not res:
|
|
783
|
+
return False
|
|
784
|
+
|
|
785
|
+
is_oversteps = self.scanner.set_scan()
|
|
786
|
+
if is_oversteps:
|
|
787
|
+
messagebox(text=f"An error occurred when establishing the scan steps. Actual settings "
|
|
788
|
+
f"gives approximately {int(self.scanner.n_steps)} steps."
|
|
789
|
+
f" Please check the steps number "
|
|
790
|
+
f"limit in the config file ({config['scan']['steps_limit']}) or modify"
|
|
791
|
+
f" your scan settings.")
|
|
792
|
+
|
|
793
|
+
if self.modules_manager.Nactuators != self.scanner.n_axes:
|
|
794
|
+
messagebox(text="There are not enough or too much selected move modules for this scan")
|
|
795
|
+
return False
|
|
796
|
+
|
|
797
|
+
if self.scanner.scan_sub_type == 'Adaptive':
|
|
798
|
+
#todo include this in scanners objects for the adaptive scanners
|
|
799
|
+
if len(self.modules_manager.get_selected_probed_data('0D')) == 0:
|
|
800
|
+
messagebox(text="In adaptive mode, you have to pick a 0D signal from which the algorithm will"
|
|
801
|
+
" determine the next positions to scan, see 'probe_data' in the modules selector"
|
|
802
|
+
" panel")
|
|
803
|
+
return False
|
|
804
|
+
|
|
805
|
+
self.ui.n_scan_steps = self.scanner.n_steps
|
|
806
|
+
|
|
807
|
+
# check if the modules are initialized
|
|
808
|
+
for module in self.modules_manager.actuators:
|
|
809
|
+
if not module.initialized_state:
|
|
810
|
+
raise exceptions.DAQ_ScanException('module ' + module.title + " is not initialized")
|
|
811
|
+
|
|
812
|
+
for module in self.modules_manager.detectors:
|
|
813
|
+
if not module.initialized_state:
|
|
814
|
+
raise exceptions.DAQ_ScanException('module ' + module.title + " is not initialized")
|
|
815
|
+
|
|
816
|
+
self.ui.enable_start_stop(True)
|
|
817
|
+
return True
|
|
818
|
+
|
|
819
|
+
except Exception as e:
|
|
820
|
+
logger.exception(str(e))
|
|
821
|
+
self.ui.enable_start_stop(False)
|
|
822
|
+
|
|
823
|
+
def set_metadata_about_current_scan(self):
|
|
824
|
+
"""
|
|
825
|
+
Set the date/time and author values of the scan_info child of the scan_attributes tree.
|
|
826
|
+
Show the 'scan' file attributes.
|
|
827
|
+
|
|
828
|
+
See Also
|
|
829
|
+
--------
|
|
830
|
+
show_file_attributes
|
|
831
|
+
"""
|
|
832
|
+
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
833
|
+
self.scan_attributes.child('scan_info', 'date_time').setValue(date)
|
|
834
|
+
self.scan_attributes.child('scan_info', 'author').setValue(
|
|
835
|
+
self.dataset_attributes.child('dataset_info', 'author').value())
|
|
836
|
+
if not self.batch_started:
|
|
837
|
+
res = self.show_file_attributes('scan')
|
|
838
|
+
else:
|
|
839
|
+
res = True
|
|
840
|
+
return res
|
|
841
|
+
|
|
842
|
+
def set_metadata_about_dataset(self):
|
|
843
|
+
"""
|
|
844
|
+
Set the date value of the data_set_info-date_time child of the data_set_attributes tree.
|
|
845
|
+
Show the 'dataset' file attributes.
|
|
846
|
+
|
|
847
|
+
See Also
|
|
848
|
+
--------
|
|
849
|
+
show_file_attributes
|
|
850
|
+
"""
|
|
851
|
+
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
852
|
+
self.dataset_attributes.child('dataset_info', 'date_time').setValue(date)
|
|
853
|
+
res = self.show_file_attributes('dataset')
|
|
854
|
+
self._metada_dataset_set = True
|
|
855
|
+
return res
|
|
856
|
+
|
|
857
|
+
def start_scan(self):
|
|
858
|
+
"""
|
|
859
|
+
Start an acquisition calling the set_scan function.
|
|
860
|
+
Emit the command_DAQ signal "start_acquisition".
|
|
861
|
+
|
|
862
|
+
See Also
|
|
863
|
+
--------
|
|
864
|
+
set_scan
|
|
865
|
+
"""
|
|
866
|
+
self.ui.display_status('Starting acquisition')
|
|
867
|
+
self.dashboard.overshoot = False
|
|
868
|
+
#deactivate double_clicked
|
|
869
|
+
if self.ui.is_action_checked('move_at'):
|
|
870
|
+
self.ui.get_action('move_at').trigger()
|
|
871
|
+
|
|
872
|
+
res = self.set_scan()
|
|
873
|
+
if res:
|
|
874
|
+
# deactivate module controls using remote_control
|
|
875
|
+
if hasattr(self.dashboard, 'remote_manager'):
|
|
876
|
+
remote_manager = getattr(self.dashboard, 'remote_manager')
|
|
877
|
+
remote_manager.activate_all(False)
|
|
878
|
+
|
|
879
|
+
self.module_and_data_saver.h5saver = self.h5saver
|
|
880
|
+
new_scan = self.module_and_data_saver.get_set_node(new=False).attrs['scan_done']
|
|
881
|
+
scan_node = self.module_and_data_saver.get_set_node(new=new_scan)
|
|
882
|
+
self.save_metadata(scan_node, 'scan_info')
|
|
883
|
+
|
|
884
|
+
self._init_live()
|
|
885
|
+
|
|
886
|
+
# mandatory to deal with multithreads
|
|
887
|
+
if self.scan_thread is not None:
|
|
888
|
+
self.command_daq_signal.disconnect()
|
|
889
|
+
if self.scan_thread.isRunning():
|
|
890
|
+
self.scan_thread.terminate()
|
|
891
|
+
while not self.scan_thread.isFinished():
|
|
892
|
+
QThread.msleep(100)
|
|
893
|
+
self.scan_thread = None
|
|
894
|
+
|
|
895
|
+
self.scan_thread = QThread()
|
|
896
|
+
|
|
897
|
+
scan_acquisition = DAQScanAcquisition(self.settings, self.scanner, self.h5saver.settings,
|
|
898
|
+
self.modules_manager,
|
|
899
|
+
module_saver=self.module_and_data_saver)
|
|
900
|
+
if config['scan']['scan_in_thread']:
|
|
901
|
+
scan_acquisition.moveToThread(self.scan_thread)
|
|
902
|
+
self.command_daq_signal[utils.ThreadCommand].connect(scan_acquisition.queue_command)
|
|
903
|
+
scan_acquisition.scan_data_tmp[ScanDataTemp].connect(self.save_temp_live_data)
|
|
904
|
+
scan_acquisition.status_sig[list].connect(self.thread_status)
|
|
905
|
+
|
|
906
|
+
self.scan_thread.scan_acquisition = scan_acquisition
|
|
907
|
+
self.scan_thread.start()
|
|
908
|
+
|
|
909
|
+
self.ui.set_action_enabled('ini_positions', False)
|
|
910
|
+
self.ui.set_action_enabled('start', False)
|
|
911
|
+
self.ui.set_scan_done(False)
|
|
912
|
+
if not self.settings['plot_options', 'plot_at_each_step']:
|
|
913
|
+
self.live_timer.start(self.settings['plot_options', 'refresh_live'])
|
|
914
|
+
self.command_daq_signal.emit(utils.ThreadCommand('start_acquisition'))
|
|
915
|
+
self.ui.set_permanent_status('Running acquisition')
|
|
916
|
+
logger.info('Running acquisition')
|
|
917
|
+
|
|
918
|
+
def _init_live(self):
|
|
919
|
+
Naverage = self.settings['scan_options', 'scan_average']
|
|
920
|
+
if Naverage > 1:
|
|
921
|
+
scan_shape = [Naverage]
|
|
922
|
+
scan_shape.extend(self.scanner.get_scan_shape())
|
|
923
|
+
else:
|
|
924
|
+
scan_shape = self.scanner.get_scan_shape()
|
|
925
|
+
if self.temp_path is not None:
|
|
926
|
+
try:
|
|
927
|
+
self.h5temp.close()
|
|
928
|
+
self.temp_path.cleanup()
|
|
929
|
+
except Exception as e:
|
|
930
|
+
logger.exception(str(e))
|
|
931
|
+
|
|
932
|
+
self.h5temp = H5Saver()
|
|
933
|
+
self.temp_path = tempfile.TemporaryDirectory(prefix='pymo')
|
|
934
|
+
addhoc_file_path = Path(self.temp_path.name).joinpath('temp_data.h5')
|
|
935
|
+
self.h5temp.init_file(custom_naming=True, addhoc_file_path=addhoc_file_path)
|
|
936
|
+
self.extended_saver: data_saving.DataToExportExtendedSaver =\
|
|
937
|
+
data_saving.DataToExportExtendedSaver(self.h5temp, extended_shape=scan_shape)
|
|
938
|
+
self.live_plotter.h5saver = self.h5temp
|
|
939
|
+
|
|
940
|
+
self.prepare_viewers()
|
|
941
|
+
QtWidgets.QApplication.processEvents()
|
|
942
|
+
|
|
943
|
+
def set_ini_positions(self):
|
|
944
|
+
"""
|
|
945
|
+
Send the command_DAQ signal with "set_ini_positions" list item as an attribute.
|
|
946
|
+
"""
|
|
947
|
+
self.command_daq_signal.emit(utils.ThreadCommand("set_ini_positions"))
|
|
948
|
+
|
|
949
|
+
def stop_scan(self):
|
|
950
|
+
"""
|
|
951
|
+
Emit the command_DAQ signal "stop_acquisition".
|
|
952
|
+
|
|
953
|
+
See Also
|
|
954
|
+
--------
|
|
955
|
+
set_ini_positions
|
|
956
|
+
"""
|
|
957
|
+
self.ui.set_permanent_status('Stoping acquisition')
|
|
958
|
+
self.command_daq_signal.emit(utils.ThreadCommand("stop_acquisition"))
|
|
959
|
+
|
|
960
|
+
if not self.dashboard.overshoot:
|
|
961
|
+
self.set_ini_positions() # do not set ini position again in case overshoot fired
|
|
962
|
+
status = 'Data Acquisition has been stopped by user'
|
|
963
|
+
else:
|
|
964
|
+
status = 'Data Acquisition has been stopped due to overshoot'
|
|
965
|
+
|
|
966
|
+
self.update_status(status, log_type='log')
|
|
967
|
+
self.ui.set_permanent_status('')
|
|
968
|
+
|
|
969
|
+
self.ui.set_action_enabled('ini_positions', True)
|
|
970
|
+
self.ui.set_action_enabled('start', True)
|
|
971
|
+
|
|
972
|
+
def do_scan(self, start_scan=True):
|
|
973
|
+
"""Public method to start the scan programmatically"""
|
|
974
|
+
if start_scan:
|
|
975
|
+
if not self.ui.is_action_enabled('start'):
|
|
976
|
+
self.ui.get_action('set_scan').trigger()
|
|
977
|
+
QtWidgets.QApplication.processEvents()
|
|
978
|
+
self.ui.get_action('start').trigger()
|
|
979
|
+
else:
|
|
980
|
+
self.ui.get_action('stop').trigger()
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
class DAQScanAcquisition(QObject):
|
|
984
|
+
"""
|
|
985
|
+
=========================== ========================================
|
|
986
|
+
|
|
987
|
+
=========================== ========================================
|
|
988
|
+
|
|
989
|
+
"""
|
|
990
|
+
scan_data_tmp = Signal(ScanDataTemp)
|
|
991
|
+
status_sig = Signal(list)
|
|
992
|
+
|
|
993
|
+
def __init__(self, scan_settings: Parameter = None, scanner: Scanner = None,
|
|
994
|
+
h5saver_settings: Parameter = None, modules_manager: ModulesManager = None,
|
|
995
|
+
module_saver: module_saving.ScanSaver = None):
|
|
996
|
+
|
|
997
|
+
"""
|
|
998
|
+
DAQScanAcquisition deal with the acquisition part of daq_scan, that is transferring commands to modules,
|
|
999
|
+
getting back data, saviong and letting know th UI about the scan status
|
|
1000
|
+
|
|
1001
|
+
"""
|
|
1002
|
+
|
|
1003
|
+
super().__init__()
|
|
1004
|
+
|
|
1005
|
+
self.scan_settings = scan_settings
|
|
1006
|
+
self.modules_manager = modules_manager
|
|
1007
|
+
self.scanner = scanner
|
|
1008
|
+
|
|
1009
|
+
self.stop_scan_flag = False
|
|
1010
|
+
self.Naverage = self.scan_settings['scan_options', 'scan_average']
|
|
1011
|
+
self.ind_average = 0
|
|
1012
|
+
self.ind_scan = 0
|
|
1013
|
+
|
|
1014
|
+
self.isadaptive = self.scanner.scan_sub_type == 'Adaptive'
|
|
1015
|
+
|
|
1016
|
+
self.modules_manager.timeout_signal.connect(self.timeout)
|
|
1017
|
+
self.timeout_scan_flag = False
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
self.move_done_flag = False
|
|
1021
|
+
self.det_done_flag = False
|
|
1022
|
+
|
|
1023
|
+
self.det_done_datas = data_mod.DataToExport('ScanData')
|
|
1024
|
+
|
|
1025
|
+
self.h5saver = H5Saver()
|
|
1026
|
+
self.h5saver.settings.restoreState(h5saver_settings.saveState())
|
|
1027
|
+
self.h5saver.init_file(addhoc_file_path=self.h5saver.settings['current_h5_file'])
|
|
1028
|
+
|
|
1029
|
+
self.module_and_data_saver: module_saving.ScanSaver = module_saver
|
|
1030
|
+
|
|
1031
|
+
# update the DAQ_Viewer's detector saver to DetectorExtendedSaver to take into account extended
|
|
1032
|
+
# arrays due to scan shape and eventual averaging
|
|
1033
|
+
scan_shape = self.scanner.get_scan_shape()
|
|
1034
|
+
if self.Naverage > 1:
|
|
1035
|
+
self.scan_shape = [self.Naverage]
|
|
1036
|
+
self.scan_shape.extend(scan_shape)
|
|
1037
|
+
else:
|
|
1038
|
+
self.scan_shape = scan_shape
|
|
1039
|
+
|
|
1040
|
+
for det in self.modules_manager.detectors:
|
|
1041
|
+
det.module_and_data_saver = module_saving.DetectorExtendedSaver(det, self.scan_shape)
|
|
1042
|
+
self.module_and_data_saver.h5saver = self.h5saver # will update its h5saver and all submodules's h5saver
|
|
1043
|
+
|
|
1044
|
+
@Slot(utils.ThreadCommand)
|
|
1045
|
+
def queue_command(self, command):
|
|
1046
|
+
"""Process the commands sent by the main ui
|
|
1047
|
+
|
|
1048
|
+
Parameters
|
|
1049
|
+
----------
|
|
1050
|
+
command: utils.ThreadCommand
|
|
1051
|
+
"""
|
|
1052
|
+
if command.command == "start_acquisition":
|
|
1053
|
+
self.start_acquisition()
|
|
1054
|
+
|
|
1055
|
+
elif command.command == "stop_acquisition":
|
|
1056
|
+
self.stop_scan_flag = True
|
|
1057
|
+
|
|
1058
|
+
elif command.command == "set_ini_positions":
|
|
1059
|
+
self.set_ini_positions()
|
|
1060
|
+
|
|
1061
|
+
elif command.command == "move_stages":
|
|
1062
|
+
self.modules_manager.move_actuators(command.attribute)
|
|
1063
|
+
|
|
1064
|
+
def set_ini_positions(self):
|
|
1065
|
+
"""
|
|
1066
|
+
| Set the positions from the scan_move attribute.
|
|
1067
|
+
|
|
|
1068
|
+
| Move all activated modules to specified positions.
|
|
1069
|
+
| Check the module corresponding to the name assigned in pos.
|
|
1070
|
+
|
|
1071
|
+
See Also
|
|
1072
|
+
--------
|
|
1073
|
+
DAQ_Move_main.daq_move.move_Abs
|
|
1074
|
+
"""
|
|
1075
|
+
try:
|
|
1076
|
+
if self.scanner.scan_sub_type != 'Adaptive':
|
|
1077
|
+
self.modules_manager.move_actuators(list(self.scanner.positions[0]))
|
|
1078
|
+
|
|
1079
|
+
except Exception as e:
|
|
1080
|
+
logger.exception(str(e))
|
|
1081
|
+
|
|
1082
|
+
def start_acquisition(self):
|
|
1083
|
+
try:
|
|
1084
|
+
#todo hoaw to apply newlayout to adaptive mode?
|
|
1085
|
+
|
|
1086
|
+
self.modules_manager.connect_actuators()
|
|
1087
|
+
self.modules_manager.connect_detectors()
|
|
1088
|
+
|
|
1089
|
+
self.stop_scan_flag = False
|
|
1090
|
+
|
|
1091
|
+
Naxes = self.scanner.n_axes
|
|
1092
|
+
scan_type = self.scanner.scan_type
|
|
1093
|
+
self.navigation_axes = self.scanner.get_nav_axes()
|
|
1094
|
+
self.status_sig.emit(["Update_Status", "Acquisition has started", 'log'])
|
|
1095
|
+
|
|
1096
|
+
self.timeout_scan_flag = False
|
|
1097
|
+
for ind_average in range(self.Naverage):
|
|
1098
|
+
self.ind_average = ind_average
|
|
1099
|
+
self.ind_scan = -1
|
|
1100
|
+
while True:
|
|
1101
|
+
self.ind_scan += 1
|
|
1102
|
+
if not self.isadaptive:
|
|
1103
|
+
if self.ind_scan >= len(self.scanner.positions):
|
|
1104
|
+
break
|
|
1105
|
+
positions = self.scanner.positions[self.ind_scan] # get positions
|
|
1106
|
+
else:
|
|
1107
|
+
pass
|
|
1108
|
+
#todo update for v4
|
|
1109
|
+
# positions = learner.ask(1)[0][-1] # next point to probe
|
|
1110
|
+
# if self.scanner.scan_type == 'Tabular': # translate normalized curvilinear position to real coordinates
|
|
1111
|
+
# self.curvilinear = positions
|
|
1112
|
+
# length = 0.
|
|
1113
|
+
# for v in self.scanner.vectors:
|
|
1114
|
+
# length += v.norm()
|
|
1115
|
+
# if length >= self.curvilinear:
|
|
1116
|
+
# vec = v
|
|
1117
|
+
# frac_curvilinear = (self.curvilinear - (length - v.norm())) / v.norm()
|
|
1118
|
+
# break
|
|
1119
|
+
#
|
|
1120
|
+
# position = (vec.vectorize() * frac_curvilinear).translate_to(vec.p1()).p2()
|
|
1121
|
+
# positions = [position.x(), position.y()]
|
|
1122
|
+
|
|
1123
|
+
self.status_sig.emit(["Update_scan_index", [self.ind_scan, ind_average]])
|
|
1124
|
+
|
|
1125
|
+
if self.stop_scan_flag or self.timeout_scan_flag:
|
|
1126
|
+
break
|
|
1127
|
+
|
|
1128
|
+
#move motors of modules and wait for move completion
|
|
1129
|
+
positions = self.modules_manager.order_positions(self.modules_manager.move_actuators(positions))
|
|
1130
|
+
|
|
1131
|
+
QThread.msleep(self.scan_settings.child('time_flow', 'wait_time_between').value())
|
|
1132
|
+
|
|
1133
|
+
#grab datas and wait for grab completion
|
|
1134
|
+
self.det_done(self.modules_manager.grab_datas(positions=positions), positions)
|
|
1135
|
+
|
|
1136
|
+
if self.isadaptive:
|
|
1137
|
+
#todo update for v4
|
|
1138
|
+
# det_channel = self.modules_manager.get_selected_probed_data()
|
|
1139
|
+
# det, channel = det_channel[0].split('/')
|
|
1140
|
+
# if self.scanner.scan_type == 'Tabular':
|
|
1141
|
+
# self.curvilinear_array.append(np.array([self.curvilinear]))
|
|
1142
|
+
# new_positions = self.curvilinear
|
|
1143
|
+
# elif self.scanner.scan_type == 'Scan1D':
|
|
1144
|
+
# new_positions = positions[0]
|
|
1145
|
+
# else:
|
|
1146
|
+
# new_positions = positions[:]
|
|
1147
|
+
# learner.tell(new_positions, self.modules_manager.det_done_datas[det]['data0D'][channel]['data'])
|
|
1148
|
+
pass
|
|
1149
|
+
|
|
1150
|
+
# daq_scan wait time
|
|
1151
|
+
QThread.msleep(self.scan_settings.child('time_flow', 'wait_time').value())
|
|
1152
|
+
|
|
1153
|
+
self.h5saver.h5_file.flush()
|
|
1154
|
+
self.modules_manager.connect_actuators(False)
|
|
1155
|
+
self.modules_manager.connect_detectors(False)
|
|
1156
|
+
|
|
1157
|
+
self.status_sig.emit(["Update_Status", "Acquisition has finished", 'log'])
|
|
1158
|
+
self.status_sig.emit(["Scan_done"])
|
|
1159
|
+
|
|
1160
|
+
except Exception as e:
|
|
1161
|
+
logger.exception(str(e))
|
|
1162
|
+
# self.status_sig.emit(["Update_Status", getLineInfo() + str(e), 'log'])
|
|
1163
|
+
|
|
1164
|
+
def det_done(self, det_done_datas: data_mod.DataToExport, positions=[]):
|
|
1165
|
+
"""
|
|
1166
|
+
|
|
1167
|
+
"""
|
|
1168
|
+
try:
|
|
1169
|
+
indexes = self.scanner.get_indexes_from_scan_index(self.ind_scan)
|
|
1170
|
+
if self.Naverage > 1:
|
|
1171
|
+
indexes = [self.ind_average] + list(indexes)
|
|
1172
|
+
indexes = tuple(indexes)
|
|
1173
|
+
if self.ind_scan == 0:
|
|
1174
|
+
nav_axes = self.scanner.get_nav_axes()
|
|
1175
|
+
if self.Naverage > 1:
|
|
1176
|
+
for nav_axis in nav_axes:
|
|
1177
|
+
nav_axis.index += 1
|
|
1178
|
+
nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1, self.Naverage),
|
|
1179
|
+
index=0))
|
|
1180
|
+
self.module_and_data_saver.add_nav_axes(nav_axes)
|
|
1181
|
+
|
|
1182
|
+
self.module_and_data_saver.add_data(indexes=indexes, distribution=self.scanner.distribution)
|
|
1183
|
+
|
|
1184
|
+
#todo related to adaptive (solution lies along the Enlargeable data saver)
|
|
1185
|
+
if self.isadaptive:
|
|
1186
|
+
for ind_ax, nav_axis in enumerate(self.navigation_axes):
|
|
1187
|
+
nav_axis.append(np.array(positions[ind_ax]))
|
|
1188
|
+
|
|
1189
|
+
self.det_done_flag = True
|
|
1190
|
+
|
|
1191
|
+
full_names: list = self.scan_settings['plot_options', 'plot_0d']['selected'][:]
|
|
1192
|
+
full_names.extend(self.scan_settings['plot_options', 'plot_1d']['selected'][:])
|
|
1193
|
+
data_temp = det_done_datas.get_data_from_full_names(full_names, deepcopy=False)
|
|
1194
|
+
|
|
1195
|
+
self.scan_data_tmp.emit(ScanDataTemp(self.ind_scan, indexes, data_temp))
|
|
1196
|
+
|
|
1197
|
+
except Exception as e:
|
|
1198
|
+
logger.exception(str(e))
|
|
1199
|
+
# self.status_sig.emit(["Update_Status", getLineInfo() + str(e), 'log'])
|
|
1200
|
+
|
|
1201
|
+
def timeout(self):
|
|
1202
|
+
"""
|
|
1203
|
+
Send the status signal *'Time out during acquisition'*.
|
|
1204
|
+
"""
|
|
1205
|
+
self.timeout_scan_flag = True
|
|
1206
|
+
self.status_sig.emit(["Update_Status", "Timeout during acquisition", 'log'])
|
|
1207
|
+
self.status_sig.emit(["Timeout"])
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
|
|
1211
|
+
def main_test(init_qt=True):
|
|
1212
|
+
from pymodaq.utils.data import DataToExport
|
|
1213
|
+
|
|
1214
|
+
LABEL = 'A Label'
|
|
1215
|
+
UNITS = 'units'
|
|
1216
|
+
OFFSET = -20.4
|
|
1217
|
+
SCALING = 0.22
|
|
1218
|
+
SIZE = 20
|
|
1219
|
+
DATA = OFFSET + SCALING * np.linspace(0, SIZE - 1, SIZE)
|
|
1220
|
+
|
|
1221
|
+
DATA0D = np.array([2.7])
|
|
1222
|
+
DATA1D = np.arange(0, 10)
|
|
1223
|
+
DATA2D = np.arange(0, 5 * 6).reshape((5, 6))
|
|
1224
|
+
DATAND = np.arange(0, 5 * 6 * 3).reshape((5, 6, 3))
|
|
1225
|
+
|
|
1226
|
+
def init_axis(data=None, index=0):
|
|
1227
|
+
if data is None:
|
|
1228
|
+
data = DATA
|
|
1229
|
+
return data_mod.Axis(label=LABEL, units=UNITS, data=data, index=index)
|
|
1230
|
+
|
|
1231
|
+
def init_data(data=None, Ndata=1, axes=[], name='myData') -> data_mod.DataWithAxes:
|
|
1232
|
+
if data is None:
|
|
1233
|
+
data = DATA2D
|
|
1234
|
+
return data_mod.DataWithAxes(name, data_mod.DataSource(0), data=[data for ind in range(Ndata)],
|
|
1235
|
+
axes=axes)
|
|
1236
|
+
|
|
1237
|
+
class ActuatorMock(QtCore.QObject):
|
|
1238
|
+
mod_name = 'act'
|
|
1239
|
+
move_done_signal = Signal(str, float)
|
|
1240
|
+
command_hardware = Signal(utils.ThreadCommand)
|
|
1241
|
+
|
|
1242
|
+
def __init__(self, ind):
|
|
1243
|
+
super().__init__()
|
|
1244
|
+
self.title = f'{self.mod_name}_{ind:02d}'
|
|
1245
|
+
self.units = f'unit_{ind:02d}'
|
|
1246
|
+
self.initialized_state = True
|
|
1247
|
+
self.module_and_data_saver = module_saving.ActuatorSaver(self)
|
|
1248
|
+
self.settings = Parameter.create(name='settings', type='str', value='mysettings')
|
|
1249
|
+
self.ui = None
|
|
1250
|
+
self.command_hardware.connect(self.move_done)
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
def move_done(self, command: utils.ThreadCommand):
|
|
1254
|
+
self.move_done_signal.emit(self.title, command.attribute[0])
|
|
1255
|
+
|
|
1256
|
+
class DetectorMock(QtCore.QObject):
|
|
1257
|
+
mod_name = 'det'
|
|
1258
|
+
grab_done_signal = Signal(DataToExport)
|
|
1259
|
+
command_hardware = Signal(utils.ThreadCommand)
|
|
1260
|
+
|
|
1261
|
+
def __init__(self, ind):
|
|
1262
|
+
super().__init__()
|
|
1263
|
+
self.title = f'{self.mod_name}_{ind:02d}'
|
|
1264
|
+
self.initialized_state = True
|
|
1265
|
+
self.module_and_data_saver = module_saving.DetectorSaver(self)
|
|
1266
|
+
self.settings = Parameter.create(name='settings', type='str', value='mysettings')
|
|
1267
|
+
self.ui = None
|
|
1268
|
+
self.command_hardware.connect(self.grab_done)
|
|
1269
|
+
|
|
1270
|
+
def grab_done(self):
|
|
1271
|
+
dat1 = init_data(data=DATA2D, Ndata=2, name=f'{self.title}/data2D')
|
|
1272
|
+
dat2 = init_data(data=DATA1D, Ndata=3, name=f'{self.title}/data1D')
|
|
1273
|
+
data = data_mod.DataToExport(name=f'{self.title}', data=[dat1, dat2])
|
|
1274
|
+
self.grab_done_signal.emit(data)
|
|
1275
|
+
|
|
1276
|
+
class DashBoardTest:
|
|
1277
|
+
def __init__(self):
|
|
1278
|
+
self.title = 'DashBoardTest'
|
|
1279
|
+
self.detector_modules = [DetectorMock(ind) for ind in range(2)]
|
|
1280
|
+
self.actuators_modules = [ActuatorMock(ind) for ind in range(3)]
|
|
1281
|
+
|
|
1282
|
+
if init_qt: # used for the test suite
|
|
1283
|
+
app = QtWidgets.QApplication(sys.argv)
|
|
1284
|
+
if config['style']['darkstyle']:
|
|
1285
|
+
import qdarkstyle
|
|
1286
|
+
app.setStyleSheet(qdarkstyle.load_stylesheet())
|
|
1287
|
+
|
|
1288
|
+
win = QtWidgets.QMainWindow()
|
|
1289
|
+
area = gutils.dock.DockArea()
|
|
1290
|
+
win.setCentralWidget(area)
|
|
1291
|
+
#win.resize(1000, 500)
|
|
1292
|
+
win.setWindowTitle('PyMoDAQ Dashboard')
|
|
1293
|
+
|
|
1294
|
+
dashboard = DashBoardTest()
|
|
1295
|
+
daq_scan = DAQScan(dockarea=area, dashboard=dashboard)
|
|
1296
|
+
win.show()
|
|
1297
|
+
|
|
1298
|
+
if init_qt:
|
|
1299
|
+
sys.exit(app.exec_())
|
|
1300
|
+
return dashboard, daq_scan, win
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
def main(init_qt=True):
|
|
1304
|
+
if init_qt: # used for the test suite
|
|
1305
|
+
app = QtWidgets.QApplication(sys.argv)
|
|
1306
|
+
if config['style']['darkstyle']:
|
|
1307
|
+
import qdarkstyle
|
|
1308
|
+
app.setStyleSheet(qdarkstyle.load_stylesheet())
|
|
1309
|
+
|
|
1310
|
+
from pymodaq.dashboard import DashBoard
|
|
1311
|
+
|
|
1312
|
+
win = QtWidgets.QMainWindow()
|
|
1313
|
+
area = gutils.dock.DockArea()
|
|
1314
|
+
win.setCentralWidget(area)
|
|
1315
|
+
win.resize(1000, 500)
|
|
1316
|
+
win.setWindowTitle('PyMoDAQ Dashboard')
|
|
1317
|
+
|
|
1318
|
+
dashboard = DashBoard(area)
|
|
1319
|
+
daq_scan = None
|
|
1320
|
+
file = Path(get_set_preset_path()).joinpath(f"{config('presets', 'default_preset_for_scan')}.xml")
|
|
1321
|
+
if file.exists():
|
|
1322
|
+
dashboard.set_preset_mode(file)
|
|
1323
|
+
daq_scan = dashboard.load_scan_module()
|
|
1324
|
+
else:
|
|
1325
|
+
msgBox = QtWidgets.QMessageBox()
|
|
1326
|
+
msgBox.setText(f"The default file specified in the configuration file does not exists!\n"
|
|
1327
|
+
f"{file}\n"
|
|
1328
|
+
f"Impossible to load the DAQScan Module")
|
|
1329
|
+
msgBox.setStandardButtons(msgBox.Ok)
|
|
1330
|
+
ret = msgBox.exec()
|
|
1331
|
+
|
|
1332
|
+
if init_qt:
|
|
1333
|
+
sys.exit(app.exec_())
|
|
1334
|
+
return dashboard, daq_scan, win
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
if __name__ == '__main__':
|
|
1338
|
+
main()
|
|
1339
|
+
#main_test()
|