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.

Files changed (233) hide show
  1. pymodaq/__init__.py +13 -6
  2. pymodaq/control_modules/__init__.py +0 -7
  3. pymodaq/control_modules/daq_move.py +965 -2
  4. pymodaq/control_modules/daq_move_ui.py +319 -0
  5. pymodaq/control_modules/daq_viewer.py +1573 -3
  6. pymodaq/control_modules/daq_viewer_ui.py +393 -0
  7. pymodaq/control_modules/mocks.py +51 -0
  8. pymodaq/control_modules/move_utility_classes.py +709 -8
  9. pymodaq/control_modules/utils.py +256 -0
  10. pymodaq/control_modules/viewer_utility_classes.py +663 -6
  11. pymodaq/daq_utils.py +89 -0
  12. pymodaq/dashboard.py +91 -72
  13. pymodaq/examples/custom_app.py +12 -11
  14. pymodaq/examples/custom_viewer.py +10 -10
  15. pymodaq/examples/function_plotter.py +16 -13
  16. pymodaq/examples/nonlinearscanner.py +8 -6
  17. pymodaq/examples/parameter_ex.py +7 -7
  18. pymodaq/examples/preset_MockCamera.xml +1 -0
  19. pymodaq/extensions/__init__.py +16 -0
  20. pymodaq/extensions/console.py +76 -0
  21. pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
  22. pymodaq/extensions/daq_scan.py +1339 -0
  23. pymodaq/extensions/daq_scan_ui.py +240 -0
  24. pymodaq/extensions/h5browser.py +23 -0
  25. pymodaq/{pid → extensions/pid}/__init__.py +4 -2
  26. pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
  27. pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
  28. pymodaq/{pid → extensions/pid}/utils.py +52 -6
  29. pymodaq/extensions/utils.py +40 -0
  30. pymodaq/post_treatment/__init__.py +6 -0
  31. pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
  32. pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
  33. pymodaq/post_treatment/load_and_plot.py +219 -0
  34. pymodaq/post_treatment/process_to_scalar.py +263 -0
  35. pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
  36. pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
  37. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
  38. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
  39. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
  40. pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
  41. pymodaq/resources/VERSION +1 -1
  42. pymodaq/resources/config_template.toml +32 -13
  43. pymodaq/resources/preset_default.xml +1 -1
  44. pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
  45. pymodaq/utils/__init__.py +0 -29
  46. pymodaq/utils/abstract/__init__.py +48 -0
  47. pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
  48. pymodaq/utils/array_manipulation.py +379 -8
  49. pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
  50. pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
  51. pymodaq/utils/config.py +448 -0
  52. pymodaq/utils/conftests.py +5 -0
  53. pymodaq/utils/daq_utils.py +828 -8
  54. pymodaq/utils/data.py +1873 -7
  55. pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
  56. pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
  57. pymodaq/{daq_utils → utils}/enums.py +12 -7
  58. pymodaq/utils/exceptions.py +37 -0
  59. pymodaq/utils/factory.py +82 -0
  60. pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
  61. pymodaq/utils/gui_utils/custom_app.py +129 -0
  62. pymodaq/utils/gui_utils/file_io.py +66 -0
  63. pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
  64. pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
  65. pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
  66. pymodaq/utils/gui_utils/widgets/label.py +24 -0
  67. pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
  68. pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
  69. pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
  70. pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
  71. pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
  72. pymodaq/utils/h5modules/__init__.py +1 -0
  73. pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
  74. pymodaq/utils/h5modules/browsing.py +683 -0
  75. pymodaq/utils/h5modules/data_saving.py +839 -0
  76. pymodaq/utils/h5modules/h5logging.py +110 -0
  77. pymodaq/utils/h5modules/module_saving.py +350 -0
  78. pymodaq/utils/h5modules/saving.py +914 -0
  79. pymodaq/utils/h5modules/utils.py +85 -0
  80. pymodaq/utils/logger.py +64 -6
  81. pymodaq/utils/managers/action_manager.py +460 -0
  82. pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
  83. pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
  84. pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
  85. pymodaq/utils/managers/parameter_manager.py +110 -0
  86. pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
  87. pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
  88. pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
  89. pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
  90. pymodaq/utils/math_utils.py +546 -10
  91. pymodaq/{daq_utils → utils}/messenger.py +5 -1
  92. pymodaq/utils/parameter/__init__.py +2 -15
  93. pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
  94. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
  95. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
  96. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
  97. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
  98. pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
  99. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
  100. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
  101. pymodaq/utils/parameter/utils.py +206 -11
  102. pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
  103. pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
  104. pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
  105. pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
  106. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
  107. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
  108. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
  109. pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
  110. pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
  111. pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
  112. pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
  113. pymodaq/utils/plotting/navigator.py +355 -0
  114. pymodaq/utils/plotting/scan_selector.py +480 -0
  115. pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
  116. pymodaq/utils/plotting/utils/filter.py +538 -0
  117. pymodaq/utils/plotting/utils/lineout.py +224 -0
  118. pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
  119. pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
  120. pymodaq/utils/plotting/widgets.py +76 -0
  121. pymodaq/utils/scanner/__init__.py +10 -0
  122. pymodaq/utils/scanner/scan_factory.py +204 -0
  123. pymodaq/utils/scanner/scanner.py +271 -0
  124. pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
  125. pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
  126. pymodaq/utils/scanner/scanners/sequential.py +192 -0
  127. pymodaq/utils/scanner/scanners/tabular.py +294 -0
  128. pymodaq/utils/scanner/utils.py +83 -0
  129. pymodaq/utils/slicing.py +47 -0
  130. pymodaq/utils/svg/__init__.py +6 -0
  131. pymodaq/utils/svg/svg_renderer.py +20 -0
  132. pymodaq/utils/svg/svg_view.py +35 -0
  133. pymodaq/utils/svg/svg_viewer2D.py +51 -0
  134. pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
  135. pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
  136. pymodaq/utils/units.py +216 -0
  137. pymodaq-4.0.1.dist-info/METADATA +159 -0
  138. {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
  140. pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
  141. pymodaq/daq_move/daq_move_gui.py +0 -279
  142. pymodaq/daq_move/daq_move_gui.ui +0 -534
  143. pymodaq/daq_move/daq_move_main.py +0 -1042
  144. pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
  145. pymodaq/daq_move/utility_classes.py +0 -686
  146. pymodaq/daq_scan.py +0 -2160
  147. pymodaq/daq_utils/array_manipulation.py +0 -386
  148. pymodaq/daq_utils/config.py +0 -273
  149. pymodaq/daq_utils/conftests.py +0 -7
  150. pymodaq/daq_utils/custom_parameter_tree.py +0 -9
  151. pymodaq/daq_utils/daq_enums.py +0 -133
  152. pymodaq/daq_utils/daq_utils.py +0 -1402
  153. pymodaq/daq_utils/exceptions.py +0 -71
  154. pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
  155. pymodaq/daq_utils/gui_utils/file_io.py +0 -75
  156. pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
  157. pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
  158. pymodaq/daq_utils/h5exporters.py +0 -242
  159. pymodaq/daq_utils/h5modules.py +0 -1559
  160. pymodaq/daq_utils/h5utils.py +0 -241
  161. pymodaq/daq_utils/managers/action_manager.py +0 -236
  162. pymodaq/daq_utils/managers/parameter_manager.py +0 -57
  163. pymodaq/daq_utils/math_utils.py +0 -705
  164. pymodaq/daq_utils/parameter/__init__.py +0 -1
  165. pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
  166. pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
  167. pymodaq/daq_utils/parameter/utils.py +0 -136
  168. pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
  169. pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
  170. pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
  171. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
  172. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
  173. pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
  174. pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
  175. pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
  176. pymodaq/daq_utils/plotting/items/__init__.py +0 -0
  177. pymodaq/daq_utils/plotting/navigator.py +0 -500
  178. pymodaq/daq_utils/plotting/scan_selector.py +0 -289
  179. pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
  180. pymodaq/daq_utils/plotting/utils/filter.py +0 -236
  181. pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
  182. pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
  183. pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
  184. pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
  185. pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
  186. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
  187. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
  188. pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
  189. pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
  190. pymodaq/daq_utils/scanner.py +0 -1289
  191. pymodaq/daq_utils/tree_layout/__init__.py +0 -0
  192. pymodaq/daq_viewer/__init__.py +0 -0
  193. pymodaq/daq_viewer/daq_gui_settings.py +0 -237
  194. pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
  195. pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
  196. pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
  197. pymodaq/daq_viewer/utility_classes.py +0 -673
  198. pymodaq/examples/logger_image/__init__.py +0 -0
  199. pymodaq/examples/logger_image/logger_displayer.py +0 -121
  200. pymodaq/examples/logger_image/setup.svg +0 -3119
  201. pymodaq/examples/logger_image/setup_svg.py +0 -114
  202. pymodaq/h5browser.py +0 -39
  203. pymodaq/utils/scanner.py +0 -15
  204. pymodaq-3.6.13.dist-info/METADATA +0 -39
  205. pymodaq-3.6.13.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.13.dist-info/top_level.txt +0 -1
  207. /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
  208. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
  209. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
  210. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
  211. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
  212. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
  213. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
  214. /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
  215. /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
  216. /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
  217. /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
  218. /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
  219. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
  220. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
  221. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
  222. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
  223. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
  224. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
  225. /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
  226. /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
  227. /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
  228. /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
  229. /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
  230. /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
  231. /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
  232. /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
  233. {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -1,1402 +0,0 @@
1
- import os
2
- import sys
3
- from collections import OrderedDict
4
- from ctypes import CFUNCTYPE
5
-
6
- from pymodaq.daq_utils.config import get_set_config_path, get_set_preset_path, Config
7
- from pymodaq.daq_utils.messenger import deprecation_msg
8
- if 'win32' in sys.platform:
9
- from ctypes import WINFUNCTYPE
10
- import datetime
11
- import importlib
12
- import inspect
13
- import json
14
- import logging
15
- import functools
16
- import time
17
- from logging.handlers import TimedRotatingFileHandler
18
- from packaging import version as version_mod
19
- from pathlib import Path
20
- import pkgutil
21
- import traceback
22
- import warnings
23
- import numbers
24
-
25
- import numpy as np
26
- from qtpy import QtCore
27
- from qtpy.QtCore import QLocale
28
- from pymodaq.daq_utils.qvariant import QVariant
29
-
30
- python_version = f'{str(sys.version_info.major)}.{str(sys.version_info.minor)}'
31
- if version_mod.parse(python_version) >= version_mod.parse('3.8'): # from version 3.8 this feature is included in the
32
- # standard lib
33
- from importlib import metadata
34
- else:
35
- import importlib_metadata as metadata # pragma: no cover
36
-
37
- from pymodaq.daq_utils.exceptions import DataSourceError
38
-
39
-
40
- plot_colors = [(255, 255, 255), (255, 0, 0), (0, 255, 0), (0, 0, 255), (14, 207, 189), (207, 14, 166), (207, 204, 14)]
41
- config = Config()
42
-
43
- Cb = 1.602176e-19 # coulomb
44
- h = 6.626068e-34 # J.s
45
- c = 2.997924586e8 # m.s-1
46
-
47
-
48
- DATASOURCES = ('raw', 'roi')
49
- DATADIMS = ('Data0D', 'Data1D', 'Data2D', 'DataND')
50
-
51
-
52
- def load_config():
53
- deprecation_msg(f'Configuration file must now be imported from the pymodaq.daq_utils.messenger module')
54
- return Config()
55
-
56
-
57
- def is_64bits():
58
- return sys.maxsize > 2**32
59
-
60
-
61
- def timer(func):
62
- """Print the runtime of the decorated function"""
63
- @functools.wraps(func)
64
- def wrapper_timer(*args, **kwargs):
65
- start_time = time.perf_counter() # 1
66
- value = func(*args, **kwargs)
67
- end_time = time.perf_counter() # 2
68
- run_time = end_time - start_time # 3
69
- print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
70
- return value
71
- return wrapper_timer
72
-
73
-
74
- def set_logger(logger_name, add_handler=False, base_logger=False, add_to_console=False, log_level=None):
75
- """defines a logger of a given name and eventually add an handler to it
76
-
77
- Parameters
78
- ----------
79
- logger_name: (str) the name of the logger (usually it is the module name as returned by get_module_name
80
- add_handler (bool) if True adds a TimedRotatingFileHandler to the logger instance (should be True if logger set from
81
- main app
82
- base_logger: (bool) specify if this is the parent logger (usually where one defines the handler)
83
-
84
- Returns
85
- -------
86
- logger: (logging.logger) logger instance
87
- See Also
88
- --------
89
- get_module_name, logging.handlers.TimedRotatingFileHandler
90
- """
91
- if not base_logger:
92
- logger_name = f'pymodaq.{logger_name}'
93
-
94
- logger = logging.getLogger(logger_name)
95
- log_path = get_set_config_path('log')
96
- if add_handler:
97
- if log_level is None:
98
- log_level = config('general', 'debug_level')
99
- logger.setLevel(log_level)
100
- handler = TimedRotatingFileHandler(log_path.joinpath('pymodaq.log'), when='midnight')
101
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
102
- handler.setFormatter(formatter)
103
- logger.addHandler(handler)
104
-
105
- logging.captureWarnings(True)
106
- if log_level == 'DEBUG':
107
- warnings.filterwarnings('default', category=DeprecationWarning)
108
- else:
109
- warnings.filterwarnings('ignore', category=DeprecationWarning)
110
-
111
- warnings_logger = logging.getLogger("py.warnings")
112
- warnings_logger.addHandler(handler)
113
-
114
- if add_to_console:
115
- console_handler = logging.StreamHandler()
116
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
117
- console_handler.setFormatter(formatter)
118
- logger.addHandler(console_handler)
119
- return logger
120
-
121
-
122
- logger = set_logger('daq_utils')
123
-
124
-
125
- def get_version():
126
- with open(str(Path(__file__).parent.parent.joinpath('resources/VERSION')), 'r') as fvers:
127
- version = fvers.read().strip()
128
- return version
129
-
130
-
131
- def copy_preset(): # pragma: no cover
132
- path = get_set_preset_path().joinpath('preset_default.xml')
133
- if not path.exists(): # copy the preset_default from pymodaq folder and create one in pymodad's local folder
134
- with open(str(Path(__file__).parent.parent.joinpath('resources/preset_default.xml')), 'r') as file:
135
- path.write_text(file.read())
136
-
137
-
138
- def set_qt_backend():
139
- backend_present = True
140
- if config('qtbackend', 'backend').lower() not in [mod.lower() for mod in sys.modules]:
141
- backend_present = False
142
- logger.warning(f"The chosen Qt backend ({config('qtbackend', 'backend')}) has not been installed...\n"
143
- f"Trying another...")
144
- backends = config('qtbackend', 'backends')
145
- backends.pop(backends.index(config('qtbackend', 'backend')))
146
- for backend in backends:
147
- if backend.lower() in [mod.lower() for mod in sys.modules]:
148
- backend_present = True
149
- break
150
-
151
- if backend_present:
152
- os.environ['QT_API'] = config('qtbackend', 'backend')
153
- logger.info('************************')
154
- logger.info(f"{config('qtbackend', 'backend')} Qt backend loaded")
155
- logger.info('************************')
156
- else:
157
- msg = f"No Qt backend could be found in your system, please install either pyqt5/6 or pyside2/6." \
158
- f"pyqt5 is still preferred, while pyqt6 should mostly work."
159
- logger.critical(msg)
160
- print(msg.upper())
161
-
162
-
163
- class JsonConverter:
164
- def __init__(self):
165
- super().__init__()
166
-
167
- @classmethod
168
- def trusted_types(cls):
169
- return ['float', 'int', 'str', 'datetime', 'date', 'time', 'tuple', 'list', 'bool', 'bytes']
170
-
171
- @classmethod
172
- def istrusted(cls, type_name):
173
- return type_name in cls.trusted_types()
174
-
175
- @classmethod
176
- def object2json(cls, obj):
177
- dic = dict(module=type(obj).__module__, type=type(obj).__name__, data=repr(obj))
178
- return json.dumps(dic)
179
-
180
- @classmethod
181
- def json2object(cls, jsonstring):
182
- try:
183
- dic = json.loads(jsonstring)
184
- if isinstance(dic, dict):
185
- if dic['type'] in cls.trusted_types():
186
- return eval(dic['data'])
187
- else:
188
- return dic
189
- else: # pragma: no cover
190
- return dic
191
- except Exception:
192
- return jsonstring
193
-
194
-
195
- def decode_data(encoded_data):
196
- """
197
- Decode QbyteArrayData generated when drop items in table/tree/list view
198
- Parameters
199
- ----------
200
- encoded_data: QByteArray
201
- Encoded data of the mime data to be dropped
202
- Returns
203
- -------
204
- data: list
205
- list of dict whose key is the QtRole in the Model, and the value a QVariant
206
-
207
- """
208
- data = []
209
-
210
- ds = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
211
- while not ds.atEnd():
212
- row = ds.readInt32()
213
- col = ds.readInt32()
214
-
215
- map_items = ds.readInt32()
216
- item = {}
217
- for ind in range(map_items):
218
- key = ds.readInt32()
219
- #TODO check this is fine
220
- value = QVariant()
221
- #value = None
222
- ds >> value
223
- item[QtCore.Qt.ItemDataRole(key)] = value.value()
224
- data.append(item)
225
- return data
226
-
227
-
228
- # ###################################
229
- # # Units conversion
230
- def Enm2cmrel(E_nm, ref_wavelength=515):
231
- """Converts energy in nm to cm-1 relative to a ref wavelength
232
-
233
- Parameters
234
- ----------
235
- E_nm: float
236
- photon energy in wavelength (nm)
237
- ref_wavelength: float
238
- reference wavelength in nm from which calculate the photon relative energy
239
-
240
- Returns
241
- -------
242
- float
243
- photon energy in cm-1 relative to the ref wavelength
244
-
245
- Examples
246
- --------
247
- >>> Enm2cmrel(530, 515)
248
- 549.551199853453
249
- """
250
- return 1 / (ref_wavelength * 1e-7) - 1 / (E_nm * 1e-7)
251
-
252
-
253
- def Ecmrel2Enm(Ecmrel, ref_wavelength=515):
254
- """Converts energy from cm-1 relative to a ref wavelength to an energy in wavelength (nm)
255
-
256
- Parameters
257
- ----------
258
- Ecmrel: float
259
- photon energy in cm-1
260
- ref_wavelength: float
261
- reference wavelength in nm from which calculate the photon relative energy
262
-
263
- Returns
264
- -------
265
- float
266
- photon energy in nm
267
-
268
- Examples
269
- --------
270
- >>> Ecmrel2Enm(500, 515)
271
- 528.6117526302285
272
- """
273
- Ecm = 1 / (ref_wavelength * 1e-7) - Ecmrel
274
- return 1 / (Ecm * 1e-7)
275
-
276
-
277
- def eV2nm(E_eV):
278
- """Converts photon energy from electronvolt to wavelength in nm
279
-
280
- Parameters
281
- ----------
282
- E_eV: float
283
- Photon energy in eV
284
-
285
- Returns
286
- -------
287
- float
288
- photon energy in nm
289
-
290
- Examples
291
- --------
292
- >>> eV2nm(1.55)
293
- 799.898112990037
294
- """
295
- E_J = E_eV * Cb
296
- E_freq = E_J / h
297
- E_nm = c / E_freq * 1e9
298
- return E_nm
299
-
300
-
301
- def nm2eV(E_nm):
302
- """Converts photon energy from wavelength in nm to electronvolt
303
-
304
- Parameters
305
- ----------
306
- E_nm: float
307
- Photon energy in nm
308
-
309
- Returns
310
- -------
311
- float
312
- photon energy in eV
313
-
314
- Examples
315
- --------
316
- >>> nm2eV(800)
317
- 1.549802593918197
318
- """
319
- E_freq = c / E_nm * 1e9
320
- E_J = E_freq * h
321
- E_eV = E_J / Cb
322
- return E_eV
323
-
324
-
325
- def E_J2eV(E_J):
326
- E_eV = E_J / Cb
327
- return E_eV
328
-
329
-
330
- def eV2cm(E_eV):
331
- """Converts photon energy from electronvolt to absolute cm-1
332
-
333
- Parameters
334
- ----------
335
- E_eV: float
336
- Photon energy in eV
337
-
338
- Returns
339
- -------
340
- float
341
- photon energy in cm-1
342
-
343
- Examples
344
- --------
345
- >>> eV2cm(0.07)
346
- 564.5880342655984
347
- """
348
- E_nm = eV2nm(E_eV)
349
- E_cm = 1 / (E_nm * 1e-7)
350
- return E_cm
351
-
352
-
353
- def nm2cm(E_nm):
354
- """Converts photon energy from wavelength to absolute cm-1
355
-
356
- Parameters
357
- ----------
358
- E_nm: float
359
- Photon energy in nm
360
-
361
- Returns
362
- -------
363
- float
364
- photon energy in cm-1
365
-
366
- Examples
367
- --------
368
- >>> nm2cm(0.04)
369
- 0.000025
370
- """
371
- return 1 / (E_nm * 1e7)
372
-
373
-
374
- def cm2nm(E_cm):
375
- """Converts photon energy from absolute cm-1 to wavelength
376
-
377
- Parameters
378
- ----------
379
- E_cm: float
380
- photon energy in cm-1
381
-
382
- Returns
383
- -------
384
- float
385
- Photon energy in nm
386
-
387
- Examples
388
- --------
389
- >>> cm2nm(1e5)
390
- 100
391
- """
392
- return 1 / (E_cm * 1e-7)
393
-
394
-
395
- def eV2E_J(E_eV):
396
- E_J = E_eV * Cb
397
- return E_J
398
-
399
-
400
- def eV2radfs(E_eV):
401
- E_J = E_eV * Cb
402
- E_freq = E_J / h
403
- E_radfs = E_freq * 2 * np.pi / 1e15
404
- return E_radfs
405
-
406
-
407
- def l2w(x, speedlight=300):
408
- """Converts photon energy in rad/fs to nm (and vice-versa)
409
-
410
- Parameters
411
- ----------
412
- x: float
413
- photon energy in wavelength or rad/fs
414
- speedlight: float, optional
415
- the speed of light, by default 300 nm/fs
416
-
417
- Returns
418
- -------
419
- float
420
-
421
- Examples
422
- --------
423
- >>> l2w(800)
424
- 2.356194490192345
425
- >>> l2w(800,3e8)
426
- 2356194.490192345
427
- """
428
- y = 2 * np.pi * speedlight / x
429
- return y
430
-
431
-
432
- #############################
433
-
434
- def capitalize(string, Nfirst=1):
435
- """
436
- Returns same string but with first Nfirst letters upper
437
- Parameters
438
- ----------
439
- string: (str)
440
- Nfirst: (int)
441
- Returns
442
- -------
443
- str
444
- """
445
- return string[:Nfirst].upper() + string[Nfirst:]
446
-
447
-
448
- def uncapitalize(string, Nfirst=1):
449
- return string[:Nfirst].lower() + string[Nfirst:]
450
-
451
-
452
- def get_data_dimension(arr, scan_type='scan1D', remove_scan_dimension=False):
453
- dimension = len(arr.shape)
454
- if dimension == 1:
455
- if arr.size == 1:
456
- dimension = 0
457
-
458
- if remove_scan_dimension:
459
- if scan_type.lower() == 'scan1d':
460
- dimension -= 1
461
- elif scan_type.lower() == 'scan2d':
462
- dimension -= 2
463
- else:
464
- if dimension > 2:
465
- dimension = 'N'
466
- return arr.shape, f'{dimension}D', arr.size
467
-
468
-
469
- def scroll_log(scroll_val, min_val, max_val):
470
- """
471
- Convert a scroll value [0-100] to a log scale between min_val and max_val
472
- Parameters
473
- ----------
474
- scroll
475
- min_val
476
- max_val
477
- Returns
478
- -------
479
-
480
- """
481
- assert scroll_val >= 0
482
- assert scroll_val <= 100
483
- value = scroll_val * (np.log10(max_val) - np.log10(min_val)) / 100 + np.log10(min_val)
484
- return 10 ** value
485
-
486
-
487
- def scroll_linear(scroll_val, min_val, max_val):
488
- """
489
- Convert a scroll value [0-100] to a linear scale between min_val and max_val
490
- Parameters
491
- ----------
492
- scroll
493
- min_val
494
- max_val
495
- Returns
496
- -------
497
-
498
- """
499
- assert scroll_val >= 0
500
- assert scroll_val <= 100
501
- value = scroll_val * (max_val - min_val) / 100 + min_val
502
- return value
503
-
504
-
505
- def getLineInfo():
506
- """get information about where the Exception has been triggered"""
507
- tb = sys.exc_info()[2]
508
- res = ''
509
- for t in traceback.format_tb(tb):
510
- res += t
511
- return res
512
-
513
-
514
- class ThreadCommand(object):
515
- """ | Micro class managing the thread commands.
516
- |
517
- | A thread command is composed of a string name defining the command to execute and an attribute list splitable making arguments of the called function.
518
-
519
- =============== =============
520
- **Attributes** **Type**
521
- *command* string
522
- *attributes* generic list
523
- =============== =============
524
-
525
- """
526
-
527
- def __init__(self, command="", attributes=[]):
528
- self.command = command
529
- self.attributes = attributes
530
-
531
-
532
- class AxisBase(dict):
533
- """
534
- Utility class defining an axis for pymodaq's viewers, attributes can be accessed as dictionary keys or class
535
- type attributes
536
- """
537
-
538
- def __init__(self, label='', units='', **kwargs):
539
- """
540
-
541
- Parameters
542
- ----------
543
- data
544
- label
545
- units
546
- """
547
- if units is None:
548
- units = ''
549
- if label is None:
550
- label = ''
551
- if not isinstance(label, str):
552
- raise TypeError('label for the Axis class should be a string')
553
- self['label'] = label
554
- if not isinstance(units, str):
555
- raise TypeError('units for the Axis class should be a string')
556
- self['units'] = units
557
- self.update(kwargs)
558
-
559
- def __getattr__(self, item):
560
- if item in self:
561
- return self[item]
562
- else:
563
- raise AttributeError(f'{item} is not a valid attribute')
564
-
565
-
566
- class Axis(AxisBase):
567
- """
568
- Utility class defining an axis for pymodaq's viewers, attributes can be accessed as dictionary keys
569
- """
570
-
571
- def __init__(self, data=None, label='', units='', **kwargs):
572
- """
573
-
574
- Parameters
575
- ----------
576
- data
577
- label
578
- units
579
- """
580
- super().__init__(label=label, units=units, **kwargs)
581
- if data is None or isinstance(data, np.ndarray):
582
- self['data'] = data
583
- else:
584
- raise TypeError('data for the Axis class should be a ndarray')
585
- self.update(kwargs)
586
-
587
- def __mul__(self, other):
588
- if isinstance(other, numbers.Number):
589
- return Axis(data=self['data'] * other, label=self['label'], units=self['units'])
590
-
591
-
592
- class NavAxis(Axis):
593
- def __init__(self, data=None, label='', units='', nav_index=-1, **kwargs):
594
- super().__init__(data=data, label=label, units=units, **kwargs)
595
-
596
- if nav_index < 0:
597
- raise ValueError('nav_index should be a positive integer representing the index of this axis among all'
598
- 'navigation axes')
599
- self['nav_index'] = nav_index
600
-
601
-
602
- class ScaledAxis(AxisBase):
603
- def __init__(self, label='', units='', offset=0, scaling=1):
604
- super().__init__(label=label, units=units)
605
- if not (isinstance(offset, float) or isinstance(offset, int)):
606
- raise TypeError('offset for the ScalingAxis class should be a float (or int)')
607
- self['offset'] = offset
608
- if not (isinstance(scaling, float) or isinstance(scaling, int)):
609
- raise TypeError('scaling for the ScalingAxis class should be a non null float (or int)')
610
- if scaling == 0 or scaling == 0.:
611
- raise ValueError('scaling for the ScalingAxis class should be a non null float (or int)')
612
- self['scaling'] = scaling
613
-
614
-
615
- class ScalingOptions(dict):
616
- def __init__(self, scaled_xaxis: ScaledAxis, scaled_yaxis: ScaledAxis):
617
- assert isinstance(scaled_xaxis, ScaledAxis)
618
- assert isinstance(scaled_yaxis, ScaledAxis)
619
- self['scaled_xaxis'] = scaled_xaxis
620
- self['scaled_yaxis'] = scaled_yaxis
621
-
622
-
623
- class Data(OrderedDict):
624
- def __init__(self, name='', source='raw', distribution='uniform', x_axis=Axis(), y_axis=Axis(), **kwargs):
625
- """
626
- Generic class subclassing from OrderedDict defining data being exported from pymodaq's plugin or viewers,
627
- attributes can be accessed as dictionary keys. Should be subclassed from for real datas
628
- Parameters
629
- ----------
630
- source: (str) either 'raw' or 'roi...' if straight from a plugin or data processed within a viewer
631
- distribution: (str) either 'uniform' or 'spread'
632
- x_axis: (Axis) Axis class defining the corresponding axis (if any) (with data either linearly spaced or containing the
633
- x positions of the spread points)
634
- y_axis: (Axis) Axis class defining the corresponding axis (if any) (with data either linearly spaced or containing the
635
- x positions of the spread points)
636
- """
637
-
638
- if not isinstance(name, str):
639
- raise TypeError(f'name for the {self.__class__.__name__} class should be a string')
640
- self['name'] = name
641
- if not isinstance(source, str):
642
- raise TypeError(f'source for the {self.__class__.__name__} class should be a string')
643
- elif not ('raw' in source or 'roi' in source):
644
- raise ValueError(f'Invalid "source" for the {self.__class__.__name__} class')
645
- self['source'] = source
646
-
647
- if not isinstance(distribution, str):
648
- raise TypeError(f'distribution for the {self.__class__.__name__} class should be a string')
649
- elif distribution not in ('uniform', 'spread'):
650
- raise ValueError(f'Invalid "distribution" for the {self.__class__.__name__} class')
651
- self['distribution'] = distribution
652
-
653
- if not isinstance(x_axis, Axis):
654
- if isinstance(x_axis, np.ndarray):
655
- x_axis = Axis(data=x_axis)
656
- else:
657
- raise TypeError(f'x_axis for the {self.__class__.__name__} class should be a Axis class')
658
- self['x_axis'] = x_axis
659
- elif x_axis['data'] is not None:
660
- self['x_axis'] = x_axis
661
-
662
- if not isinstance(y_axis, Axis):
663
- if isinstance(y_axis, np.ndarray):
664
- y_axis = Axis(data=y_axis)
665
- else:
666
- raise TypeError(f'y_axis for the {self.__class__.__name__} class should be a Axis class')
667
- self['y_axis'] = y_axis
668
- elif y_axis['data'] is not None:
669
- self['y_axis'] = y_axis
670
-
671
- for k in kwargs:
672
- self[k] = kwargs[k]
673
-
674
- def __getattr__(self, name):
675
- if name in self:
676
- return self[name]
677
- else:
678
- raise AttributeError(f'{name} if not a key of {self}')
679
-
680
- def __repr__(self):
681
- return f'{self.__class__.__name__}: <name: {self.name}> - <distribution: {self.distribution}> - <source: {self.source}>'
682
-
683
-
684
- class DataFromPlugins(Data):
685
-
686
- def __init__(self, data=None, dim='', labels=[], nav_axes=[], nav_x_axis=Axis(), nav_y_axis=Axis(), **kwargs):
687
- """
688
- Parameters
689
- ----------
690
- dim: (str) data dimensionality (either Data0D, Data1D, Data2D or DataND)
691
- """
692
- super().__init__(**kwargs)
693
- self['labels'] = labels
694
- if len(nav_axes) != 0:
695
- self['nav_axes'] = nav_axes
696
- if nav_x_axis['data'] is not None:
697
- self['nav_x_axis'] = nav_x_axis
698
- if nav_y_axis['data'] is not None:
699
- self['nav_y_axis'] = nav_y_axis
700
-
701
- iscorrect = True
702
- if data is not None:
703
- if isinstance(data, list):
704
- for dat in data:
705
- if not isinstance(dat, np.ndarray):
706
- iscorrect = False
707
- else:
708
- iscorrect = False
709
-
710
- if iscorrect:
711
- self['data'] = data
712
- else:
713
- raise TypeError('data for the DataFromPlugins class should be None or a list of numpy arrays')
714
-
715
- if dim not in DATADIMS and data is not None:
716
- ndim = len(data[0].shape)
717
- if ndim == 1:
718
- if data[0].size == 1:
719
- dim = 'Data0D'
720
- else:
721
- dim = 'Data1D'
722
- elif ndim == 2:
723
- dim = 'Data2D'
724
- else:
725
- dim = 'DataND'
726
- self['dim'] = dim
727
-
728
- def __repr__(self):
729
- return f'{self.__class__.__name__}: <name: {self.name}> - <distribution: {self.distribution}>' \
730
- f' - <source: {self.source}> - <dim: {self.dim}>'
731
-
732
-
733
- class DataToExport(Data):
734
- def __init__(self, data=None, dim='', source='raw', **kwargs):
735
- """
736
- Utility class defining a data being exported from pymodaq's viewers, attributes can be accessed as dictionary keys
737
- Parameters
738
- ----------
739
- data: (ndarray or a scalar)
740
- dim: (str) data dimensionality (either Data0D, Data1D, Data2D or DataND)
741
- source: (str) either 'raw' for raw data or 'roi' for data extracted from a roi
742
- """
743
- super().__init__(source=source, **kwargs)
744
- if data is None or isinstance(data, np.ndarray) or isinstance(data, numbers.Number):
745
- self['data'] = data
746
- else:
747
- raise TypeError('data for the DataToExport class should be a scalar or a ndarray')
748
-
749
- if dim not in ('Data0D', 'Data1D', 'Data2D', 'DataND') or data is not None:
750
- if isinstance(data, np.ndarray):
751
- ndim = len(data.shape)
752
- if ndim == 1:
753
- if data.size == 1:
754
- dim = 'Data0D'
755
- else:
756
- dim = 'Data1D'
757
- elif ndim == 2:
758
- dim = 'Data2D'
759
- else:
760
- dim = 'DataND'
761
- else:
762
- dim = 'Data0D'
763
- self['dim'] = dim
764
- if source not in DATASOURCES:
765
- raise DataSourceError(f'Data source should be in {DATASOURCES}')
766
-
767
- def __repr__(self):
768
- return f'{self.__class__.__name__}: <name: {self.name}> - <distribution: {self.distribution}>' \
769
- f' - <source: {self.source}> - <dim: {self.dim}>'
770
-
771
-
772
- def ensure_ndarray(data):
773
- """
774
- Make sure data is returned as a numpy array
775
- Parameters
776
- ----------
777
- data
778
-
779
- Returns
780
- -------
781
- ndarray
782
- """
783
- if not isinstance(data, np.ndarray):
784
- if isinstance(data, list):
785
- data = np.array(data)
786
- else:
787
- data = np.array([data])
788
- return data
789
-
790
-
791
- def setLocale():
792
- """
793
- defines the Locale to use to convert numbers to strings representation using language/country conventions
794
- Default is English and US
795
- """
796
- language = getattr(QLocale, config('style', 'language'))
797
- country = getattr(QLocale, config('style', 'country'))
798
- QLocale.setDefault(QLocale(language, country))
799
-
800
-
801
- def recursive_find_files_extension(ini_path, ext, paths=[]):
802
- with os.scandir(ini_path) as it:
803
- for entry in it:
804
- if os.path.splitext(entry.name)[1][1:] == ext and entry.is_file():
805
- paths.append(entry.path)
806
- elif entry.is_dir():
807
- recursive_find_files_extension(entry.path, ext, paths)
808
- return paths
809
-
810
-
811
- def recursive_find_files(ini_path, exp='make_enum', paths=[],
812
- filters=['build']):
813
- for child in Path(ini_path).iterdir():
814
- if child.is_dir():
815
- recursive_find_files(child, exp, paths, filters)
816
- else:
817
- if exp in child.stem:
818
- if not any([filt in str(child) for filt in filters]):
819
- paths.append(child)
820
- return paths
821
-
822
-
823
- def recursive_find_expr_in_files(ini_path, exp='make_enum', paths=[],
824
- filters=['.git', '.idea', '__pycache__', 'build', 'egg', 'documentation', '.tox'],
825
- replace=False, replace_str=''):
826
-
827
- for child in Path(ini_path).iterdir():
828
- if not any(filt in str(child) for filt in filters):
829
- if child.is_dir():
830
- recursive_find_expr_in_files(child, exp, paths, filters, replace=replace, replace_str=replace_str)
831
- else:
832
- try:
833
- found = False
834
- with child.open('r') as f:
835
- replacement = ''
836
- for ind, line in enumerate(f):
837
- if exp in line:
838
- found = True
839
- paths.append([child, ind, line])
840
- if replace:
841
- replacement += line.replace(exp, replace_str)
842
- else:
843
- if replace:
844
- replacement += line
845
- if replace and found:
846
- with child.open('w') as f:
847
- f.write(replacement)
848
- except Exception:
849
- pass
850
- return paths
851
-
852
-
853
- def count_lines(ini_path, count=0, filters=['lextab', 'yacctab','pycache', 'pyc']):
854
- # if Path(ini_path).is_file():
855
- # with Path(ini_path).open('r') as f:
856
- # count += len(f.readlines())
857
- # return count
858
- for child in Path(ini_path).iterdir():
859
- if child.is_dir():
860
- count = count_lines(child, count)
861
- else:
862
- try:
863
- if not any([filt in child.name for filt in filters]):
864
- if '.py' in child.name:
865
- with child.open('r') as f:
866
- count += len(f.readlines())
867
- else:
868
- print(child.stem)
869
- except Exception:
870
- pass
871
- return count
872
-
873
- def remove_spaces(string):
874
- """
875
- return a string without any white spaces in it
876
- Parameters
877
- ----------
878
- string
879
-
880
- Returns
881
- -------
882
-
883
- """
884
- return ''.join(string.split())
885
-
886
-
887
- def rint(x):
888
- """
889
- almost same as numpy rint function but return an integer
890
- Parameters
891
- ----------
892
- x: (float or integer)
893
-
894
- Returns
895
- -------
896
- nearest integer
897
- """
898
- return int(np.rint(x))
899
-
900
-
901
- def elt_as_first_element(elt_list, match_word='Mock'):
902
- if not hasattr(elt_list, '__iter__'):
903
- raise TypeError('elt_list must be an iterable')
904
- if elt_list:
905
- ind_elt = 0
906
- for ind, elt in enumerate(elt_list):
907
- if not isinstance(elt, str):
908
- raise TypeError('elt_list must be a list of str')
909
- if match_word in elt:
910
- ind_elt = ind
911
- break
912
- plugin_match = elt_list[ind_elt]
913
- elt_list.remove(plugin_match)
914
- plugins = [plugin_match]
915
- plugins.extend(elt_list)
916
- else:
917
- plugins = []
918
- return plugins
919
-
920
-
921
- def elt_as_first_element_dicts(elt_list, match_word='Mock', key='name'):
922
- if not hasattr(elt_list, '__iter__'):
923
- raise TypeError('elt_list must be an iterable')
924
- if elt_list:
925
- ind_elt = 0
926
- for ind, elt in enumerate(elt_list):
927
- if not isinstance(elt, dict):
928
- raise TypeError('elt_list must be a list of dicts')
929
- if match_word in elt[key]:
930
- ind_elt = ind
931
- break
932
- plugin_match = elt_list[ind_elt]
933
- elt_list.remove(plugin_match)
934
- plugins = [plugin_match]
935
- plugins.extend(elt_list)
936
- else:
937
- plugins = []
938
- return plugins
939
-
940
-
941
- def get_extensions():
942
- """
943
- Get pymodaq extensions as a list
944
-
945
- Returns
946
- -------
947
- list: list of disct containting the name and module of the found extension
948
- """
949
- extension_import = []
950
- entry_points = metadata.entry_points()
951
- if 'pymodaq.extensions' in entry_points:
952
- discovered_extension = entry_points['pymodaq.extensions']
953
-
954
- for pkg in discovered_extension:
955
- try:
956
- module = importlib.import_module(pkg.value)
957
- if hasattr(module, 'NICE_NAME'):
958
- name = module.NICE_NAME
959
- else:
960
- name = pkg.value
961
- extension = {'name': name, 'module': module}
962
- extension_import.append(extension)
963
-
964
- except Exception as e: # pragma: no cover
965
- logger.warning(f'Impossible to import the {pkg.value} extension: {str(e)}')
966
-
967
- return extension_import
968
-
969
- def find_dict_if_matched_key_val(dict_tmp, key, value):
970
- """
971
- check if a key/value pair match in a given dictionnary
972
- Parameters
973
- ----------
974
- dict_tmp: (dict) the dictionnary to be tested
975
- key: (str) a key string to look for in dict_tmp
976
- value: (object) any python object
977
-
978
- Returns
979
- -------
980
- bool: True if the key/value pair has been found in dict_tmp
981
-
982
- """
983
- if key in dict_tmp:
984
- if dict_tmp[key] == value:
985
- return True
986
- return False
987
-
988
-
989
- def find_dict_in_list_from_key_val(dicts, key, value, return_index=False):
990
- """ lookup within a list of dicts. Look for the dict within the list which has the correct key, value pair
991
-
992
- Parameters
993
- ----------
994
- dicts: (list) list of dictionnaries
995
- key: (str) specific key to look for in each dict
996
- value: value to match
997
-
998
- Returns
999
- -------
1000
- dict: if found otherwise returns None
1001
- """
1002
- for ind, dict_tmp in enumerate(dicts):
1003
- if find_dict_if_matched_key_val(dict_tmp, key, value):
1004
- if return_index:
1005
- return dict_tmp, ind
1006
- else:
1007
- return dict_tmp
1008
- if return_index:
1009
- return None, -1
1010
- else:
1011
- return None
1012
-
1013
-
1014
- def get_models(model_name=None):
1015
- """
1016
- Get PID Models as a list to instantiate Control Actuators per degree of liberty in the model
1017
-
1018
- Returns
1019
- -------
1020
- list: list of disct containting the name and python module of the found models
1021
- """
1022
- from pymodaq.pid.utils import PIDModelGeneric
1023
- models_import = []
1024
- entry_points = metadata.entry_points()
1025
- if 'pymodaq.pid_models' in entry_points:
1026
- discovered_models = entry_points['pymodaq.pid_models']
1027
- for pkg in discovered_models:
1028
- try:
1029
- module = importlib.import_module(pkg.value)
1030
- module_name = pkg.value
1031
-
1032
- for mod in pkgutil.iter_modules([str(Path(module.__file__).parent.joinpath('models'))]):
1033
- try:
1034
- model_module = importlib.import_module(f'{module_name}.models.{mod.name}', module)
1035
- classes = inspect.getmembers(model_module, inspect.isclass)
1036
- for name, klass in classes:
1037
- if klass.__base__ is PIDModelGeneric:
1038
- models_import.append({'name': mod.name, 'module': model_module, 'class': klass})
1039
- break
1040
-
1041
- except Exception as e: # pragma: no cover
1042
- logger.warning(str(e))
1043
-
1044
- except Exception as e: # pragma: no cover
1045
- logger.warning(f'Impossible to import the {pkg.value} extension: {str(e)}')
1046
-
1047
- if model_name is None:
1048
- return models_import
1049
- else:
1050
- return find_dict_in_list_from_key_val(models_import, 'name', model_name)
1051
-
1052
- def get_plugins(plugin_type='daq_0Dviewer'): # pragma: no cover
1053
- """
1054
- Get plugins names as a list
1055
- Parameters
1056
- ----------
1057
- plugin_type: (str) plugin type either 'daq_0Dviewer', 'daq_1Dviewer', 'daq_2Dviewer', 'daq_NDviewer' or 'daq_move'
1058
- module: (module) parent module of the plugins
1059
-
1060
- Returns
1061
- -------
1062
-
1063
- """
1064
- plugins_import = []
1065
- discovered_plugins = metadata.entry_points()['pymodaq.plugins']
1066
-
1067
- for module in discovered_plugins:
1068
- try:
1069
- if plugin_type == 'daq_move':
1070
- submodule = importlib.import_module(f'{module.value}.daq_move_plugins', module.value)
1071
- else:
1072
- submodule = importlib.import_module(f'{module.value}.daq_viewer_plugins.plugins_{plugin_type[4:6]}',
1073
- module.value)
1074
- plugin_list = [{'name': mod[len(plugin_type) + 1:],
1075
- 'module': submodule} for mod in [mod[1] for
1076
- mod in pkgutil.iter_modules([submodule.path.parent])]
1077
- if plugin_type in mod]
1078
- # check if modules are importable
1079
-
1080
- for mod in plugin_list:
1081
- try:
1082
- if plugin_type == 'daq_move':
1083
- importlib.import_module(f'{submodule.__package__}.daq_move_{mod["name"]}')
1084
- else:
1085
- importlib.import_module(f'{submodule.__package__}.daq_{plugin_type[4:6]}viewer_{mod["name"]}')
1086
- plugins_import.append(mod)
1087
- except Exception as e: # pragma: no cover
1088
- pass
1089
- except Exception as e: # pragma: no cover
1090
- pass
1091
-
1092
- #add utility plugin for PID
1093
- if plugin_type == 'daq_move':
1094
- try:
1095
- submodule = importlib.import_module('pymodaq.pid')
1096
-
1097
- plugins_import.append({'name': 'PID', 'module': submodule})
1098
-
1099
- except Exception: # pragma: no cover
1100
- pass
1101
-
1102
- plugins_import = elt_as_first_element_dicts(plugins_import, match_word='Mock', key='name')
1103
- return plugins_import
1104
-
1105
-
1106
- def check_vals_in_iterable(iterable1, iterable2):
1107
- assert len(iterable1) == len(iterable2)
1108
- iterable1 = list(iterable1) # so the assertion below is valid for any kind of iterable, list, tuple, ndarray...
1109
- iterable2 = list(iterable2)
1110
- for val1, val2 in zip(iterable1, iterable2):
1111
- assert val1 == val2
1112
-
1113
-
1114
- def get_module_name(module__file__path):
1115
- """from the full path of a module extract its name"""
1116
- path = Path(module__file__path)
1117
- return path.stem
1118
-
1119
-
1120
- def caller_name(skip=2):
1121
- """Get a name of a caller in the format module.class.method
1122
-
1123
- `skip` specifies how many levels of stack to skip while getting caller
1124
- name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.
1125
-
1126
- An empty string is returned if skipped levels exceed stack height
1127
- """
1128
- stack = inspect.stack()
1129
- start = 0 + skip
1130
- if len(stack) < start + 1:
1131
- return ''
1132
- parentframe = stack[start][0]
1133
-
1134
- name = []
1135
- module = inspect.getmodule(parentframe)
1136
- # `modname` can be None when frame is executed directly in console
1137
- # TODO(techtonik): consider using __main__
1138
- if module:
1139
- name.append(module.__name__)
1140
- # detect classname
1141
- if 'self' in parentframe.f_locals:
1142
- # I don't know any way to detect call from the object method
1143
- # XXX: there seems to be no way to detect static method call - it will
1144
- # be just a function call
1145
- name.append(parentframe.f_locals['self'].__class__.__name__)
1146
- codename = parentframe.f_code.co_name
1147
- if codename != '<module>': # top level usually
1148
- name.append(codename) # function or a method
1149
- del parentframe
1150
- return ".".join(name)
1151
-
1152
-
1153
- def zeros_aligned(n, align, dtype=np.uint32):
1154
- """
1155
- Get aligned memory array wih alignment align.
1156
- Parameters
1157
- ----------
1158
- n: (int) length in dtype bytes of memory
1159
- align: (int) memory alignment
1160
- dtype: (numpy.dtype) type of the stored memory elements
1161
-
1162
- Returns
1163
- -------
1164
-
1165
- """
1166
- dtype = np.dtype(dtype)
1167
- nbytes = n * dtype.itemsize
1168
- buff = np.zeros(nbytes + align, dtype=np.uint8)
1169
- start_index = -buff.ctypes.data % align
1170
- return buff[start_index:start_index + nbytes].view(dtype)
1171
-
1172
-
1173
- def cfunc(name, dll, result, *args):
1174
- """build and apply a ctypes prototype complete with parameter flags
1175
-
1176
- Parameters
1177
- ----------
1178
- name: (str) function name in the dll
1179
- dll: (ctypes.windll) dll object
1180
- result : result is the type of the result (c_int,..., python function handle,...)
1181
- args: list of tuples with 3 or 4 elements each like (argname, argtype, in/out, default) where argname is the
1182
- name of the argument, argtype is the type, in/out is 1 for input and 2 for output, and default is an optional
1183
- default value.
1184
-
1185
- Returns
1186
- -------
1187
- python function
1188
- """
1189
- atypes = []
1190
- aflags = []
1191
- for arg in args:
1192
- atypes.append(arg[1])
1193
- aflags.append((arg[2], arg[0]) + arg[3:])
1194
- return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags))
1195
-
1196
-
1197
- def winfunc(name, dll, result, *args):
1198
- """build and apply a ctypes prototype complete with parameter flags
1199
- Parameters
1200
- ----------
1201
- name:(str) function name in the dll
1202
- dll: (ctypes.windll) dll object
1203
- result: result is the type of the result (c_int,..., python function handle,...)
1204
- args: list of tuples with 3 or 4 elements each like (argname, argtype, in/out, default) where argname is the
1205
- name of the argument, argtype is the type, in/out is 1 for input and 2 for output, and default is an optional
1206
- default value.
1207
-
1208
- Returns
1209
- -------
1210
- python function
1211
- """
1212
- atypes = []
1213
- aflags = []
1214
- for arg in args:
1215
- atypes.append(arg[1])
1216
- aflags.append((arg[2], arg[0]) + arg[3:])
1217
- return WINFUNCTYPE(result, *atypes)((name, dll), tuple(aflags))
1218
-
1219
-
1220
- def set_param_from_param(param_old, param_new):
1221
- """
1222
- Walk through parameters children and set values using new parameter values.
1223
- """
1224
- for child_old in param_old.children():
1225
- # try:
1226
- path = param_old.childPath(child_old)
1227
- child_new = param_new.child(*path)
1228
- param_type = child_old.type()
1229
-
1230
- if 'group' not in param_type: # covers 'group', custom 'groupmove'...
1231
- # try:
1232
- if 'list' in param_type: # check if the value is in the limits of the old params (limits are usually set at initialization)
1233
- if child_new.value() not in child_old.opts['limits']:
1234
- child_old.opts['limits'].append(child_new.value())
1235
-
1236
- child_old.setValue(child_new.value())
1237
- elif 'str' in param_type or 'browsepath' in param_type or 'text' in param_type:
1238
- if child_new.value() != "": # to make sure one doesnt overwrite something
1239
- child_old.setValue(child_new.value())
1240
- else:
1241
- child_old.setValue(child_new.value())
1242
- # except Exception as e:
1243
- # print(str(e))
1244
- else:
1245
- set_param_from_param(child_old, child_new)
1246
- # except Exception as e:
1247
- # print(str(e))
1248
-
1249
-
1250
- # ########################
1251
- # #File management
1252
-
1253
- def get_new_file_name(base_path=Path(config('data_saving', 'h5file', 'save_path')), base_name='tttr_data'):
1254
- if isinstance(base_path, str):
1255
- base_path = Path(base_path)
1256
-
1257
- today = datetime.datetime.now()
1258
-
1259
- date = today.strftime('%Y%m%d')
1260
- year = today.strftime('%Y')
1261
- year_dir = base_path.joinpath(year)
1262
- if not year_dir.is_dir():
1263
- year_dir.mkdir()
1264
- curr_dir = base_path.joinpath(year, date)
1265
- if not curr_dir.is_dir():
1266
- curr_dir.mkdir()
1267
-
1268
- files = []
1269
- for entry in curr_dir.iterdir():
1270
- if entry.name.startswith(base_name) and entry.is_file():
1271
- files.append(entry.stem)
1272
- files.sort()
1273
- if not files:
1274
- index = 0
1275
- else:
1276
- index = int(files[-1][-3:]) + 1
1277
-
1278
- file = f'{base_name}_{index:03d}'
1279
- return file, curr_dir
1280
-
1281
-
1282
- # ##############
1283
- # Math utilities
1284
-
1285
- # math utility functions, should now be imported from the math_utils module
1286
- import pymodaq.daq_utils.math_utils as mutils
1287
-
1288
- def my_moment(x, y):
1289
- deprecation_msg(f'my_moment function should now be imported from the {mutils.__name__} module', stacklevel=3)
1290
- return mutils.my_moment(x, y)
1291
-
1292
- def normalize(x):
1293
- deprecation_msg(f'normalize function should now be imported from the {mutils.__name__} module', stacklevel=3)
1294
- return mutils.normalize(x)
1295
-
1296
-
1297
- def odd_even(x):
1298
- deprecation_msg(f'odd_even function should now be imported from the {mutils.__name__} module', stacklevel=3)
1299
- return mutils.odd_even(x)
1300
-
1301
-
1302
- def greater2n(x):
1303
- deprecation_msg(f'greater2n function should now be imported from the {mutils.__name__} module', stacklevel=3)
1304
- return mutils.greater2n(x)
1305
-
1306
-
1307
- def linspace_step(start, stop, step):
1308
- deprecation_msg(f'linspace_step function should now be imported from the {mutils.__name__} module', stacklevel=3)
1309
- return mutils.linspace_step(start, stop, step)
1310
-
1311
-
1312
- def linspace_step_N(start, step, Npts):
1313
- deprecation_msg(f'linspace_step_N function should now be imported from the {mutils.__name__} module', stacklevel=3)
1314
- return mutils.linspace_step_N(start, step, Npts)
1315
-
1316
-
1317
- def find_index(x, threshold):
1318
- deprecation_msg(f'find_index function should now be imported from the {mutils.__name__} module', stacklevel=3)
1319
- return mutils.find_index(x, threshold)
1320
-
1321
-
1322
- def find_common_index(x, y, x0, y0):
1323
- deprecation_msg(f'find_common_index function should now be imported from the {mutils.__name__} module', stacklevel=3)
1324
- return mutils.find_common_index(x, y, x0, y0)
1325
-
1326
-
1327
- def gauss1D(x, x0, dx, n=1):
1328
- deprecation_msg(f'gauss1D function should now be imported from the {mutils.__name__} module', stacklevel=3)
1329
- return mutils.gauss1D(x, x0, dx, n=n)
1330
-
1331
-
1332
- def gauss2D(x, x0, dx, y, y0, dy, n=1, angle=0):
1333
- deprecation_msg(f'gauss2D function should now be imported from the {mutils.__name__} module', stacklevel=3)
1334
- return mutils.gauss2D(x, x0, dx, y, y0, dy, n, angle)
1335
-
1336
- def ftAxis(Npts, omega_max):
1337
- deprecation_msg(f'ftAxis function should now be imported from the {mutils.__name__} module', stacklevel=3)
1338
- return mutils.ftAxis(Npts, omega_max)
1339
-
1340
-
1341
- def ftAxis_time(Npts, time_max):
1342
- deprecation_msg(f'ftAxis_time function should now be imported from the {mutils.__name__} module', stacklevel=3)
1343
- return mutils.ftAxis_time(Npts, time_max)
1344
-
1345
-
1346
- def ft(x, dim=-1):
1347
- deprecation_msg(f'ft function should now be imported from the {mutils.__name__} module', stacklevel=3)
1348
- return mutils.ft(x, dim)
1349
-
1350
-
1351
- def ift(x, dim=0):
1352
- deprecation_msg(f'ift function should now be imported from the {mutils.__name__} module', stacklevel=3)
1353
- return mutils.ift(x, dim)
1354
-
1355
-
1356
- def ft2(x, dim=(-2, -1)):
1357
- deprecation_msg(f'ft2 function should now be imported from the {mutils.__name__} module', stacklevel=3)
1358
- return mutils.ft2(x, dim)
1359
-
1360
-
1361
- def ift2(x, dim=(-2, -1)):
1362
- deprecation_msg(f'ift2 function should now be imported from the {mutils.__name__} module', stacklevel=3)
1363
- return mutils.ift2(x, dim)
1364
-
1365
-
1366
- if __name__ == '__main__':
1367
- #paths = recursive_find_expr_in_files('C:\\Users\\weber\\Labo\\Programmes Python\\PyMoDAQ_Git', 'visa')
1368
- # for p in paths:
1369
- # print(str(p))
1370
- # v = get_version()
1371
- # pass
1372
- #plugins = get_plugins() # pragma: no cover
1373
- #extensions = get_extension()
1374
- #models = get_models()
1375
- #count = count_lines('C:\\Users\\weber\\Labo\\Programmes Python\\PyMoDAQ_Git\\pymodaq\src')
1376
-
1377
-
1378
- # import license
1379
- # mit = license.find('MIT')
1380
- #
1381
- # paths = recursive_find_expr_in_files('C:\\Users\\weber\\Labo\\Programmes Python\\PyMoDAQ_Git',
1382
- # exp='https://github.com/CEMES-CNRS',
1383
- # paths=[],
1384
- # filters=['.git', '.idea', '__pycache__', 'build', 'egg',
1385
- # '.tox', 'daq_utils.py'],
1386
- # replace=False, replace_str="https://github.com/PyMoDAQ")
1387
- pass
1388
- # paths = recursive_find_files('C:\\Users\\weber\\Labo\\Programmes Python\\PyMoDAQ_Git',
1389
- # exp='VERSION', paths=[])
1390
- # import version
1391
- # for file in paths:
1392
- # with open(str(file), 'r') as f:
1393
- # v = version.Version(f.read())
1394
- # v.minor += 1
1395
- # v.patch = 0
1396
- # with open(str(file), 'w') as f:
1397
- # f.write(str(v))
1398
-
1399
- # for file in paths:
1400
- # with open(str(file), 'w') as f:
1401
- # f.write(mit.render(name='Sebastien Weber', email='sebastien.weber@cemes.fr'))
1402
-