pymodaq 3.6.12__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.12.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.12.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 -671
  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.12.dist-info/METADATA +0 -39
  205. pymodaq-3.6.12.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.12.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.12.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,85 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 19/01/2023
4
+
5
+ @author: Sebastien Weber and N Tappy
6
+ """
7
+ # Standard imports
8
+ from collections import OrderedDict
9
+ from typing import List, Dict
10
+
11
+ # 3rd party imports
12
+ import numpy as np
13
+
14
+
15
+ def find_scan_node(scan_node):
16
+ """
17
+ utility function to find the parent node of "scan" type, meaning some of its children (DAQ_scan case)
18
+ or co-nodes (daq_logger case) are navigation axes
19
+ Parameters
20
+ ----------
21
+ scan_node: (pytables node)
22
+ data node from where this function look for its navigation axes if any
23
+ Returns
24
+ -------
25
+ node: the parent node of 'scan' type
26
+ list: the data nodes of type 'navigation_axis' corresponding to the initial data node
27
+
28
+
29
+ """
30
+ try:
31
+ while True:
32
+ if scan_node.attrs['type'] == 'scan':
33
+ break
34
+ else:
35
+ scan_node = scan_node.parent_node
36
+ children = list(scan_node.children().values()) # for data saved using daq_scan
37
+ children.extend([scan_node.parent_node.children()[child] for child in
38
+ scan_node.parent_node.children_name()]) # for data saved using the daq_logger
39
+ nav_children = []
40
+ for child in children:
41
+ if 'type' in child.attrs.attrs_name:
42
+ if child.attrs['type'] == 'navigation_axis':
43
+ nav_children.append(child)
44
+ return scan_node, nav_children
45
+ except Exception:
46
+ return None, []
47
+
48
+
49
+ def get_h5_attributes(self, node_path):
50
+ """
51
+ """
52
+ node = self.get_node(node_path)
53
+ attrs_names = node.attrs.attrs_name
54
+ attr_dict = OrderedDict([])
55
+ for attr in attrs_names:
56
+ # if attr!='settings':
57
+ attr_dict[attr] = node.attrs[attr]
58
+
59
+ settings = None
60
+ scan_settings = None
61
+ if 'settings' in attrs_names:
62
+ if node.attrs['settings'] != '':
63
+ settings = node.attrs['settings']
64
+
65
+ if 'scan_settings' in attrs_names:
66
+ if node.attrs['scan_settings'] != '':
67
+ scan_settings = node.attrs['scan_settings']
68
+ pixmaps = []
69
+ for attr in attrs_names:
70
+ if 'pixmap' in attr:
71
+ pixmaps.append(node.attrs[attr])
72
+
73
+ return attr_dict, settings, scan_settings, pixmaps
74
+
75
+
76
+ def get_h5_data_from_node():
77
+ pass
78
+
79
+
80
+ def extract_axis():
81
+ pass
82
+
83
+
84
+ def verify_axis_data_uniformity():
85
+ pass
pymodaq/utils/logger.py CHANGED
@@ -4,11 +4,69 @@ Created the 27/10/2022
4
4
 
5
5
  @author: Sebastien Weber
6
6
  """
7
- from pymodaq.daq_utils import daq_utils
7
+ import logging
8
+ import warnings
9
+ from logging.handlers import TimedRotatingFileHandler
10
+ from pathlib import Path
8
11
 
12
+ from pymodaq.utils.config import get_set_config_dir, Config
9
13
 
10
- def __getattr__(name):
11
- if hasattr(daq_utils, name):
12
- return getattr(daq_utils, name)
13
- else:
14
- raise AttributeError
14
+ config = Config()
15
+
16
+
17
+ def set_logger(logger_name, add_handler=False, base_logger=False, add_to_console=False, log_level=None,
18
+ logger_base_name='pymodaq') -> logging.Logger:
19
+ """defines a logger of a given name and eventually add an handler to it
20
+
21
+ Parameters
22
+ ----------
23
+ logger_name: (str) the name of the logger (usually it is the module name as returned by get_module_name
24
+ add_handler (bool) if True adds a TimedRotatingFileHandler to the logger instance (should be True if logger set from
25
+ main app
26
+ base_logger: (bool) specify if this is the parent logger (usually where one defines the handler)
27
+
28
+ Returns
29
+ -------
30
+ logger: (logging.Logger) logger instance
31
+ See Also
32
+ --------
33
+ get_module_name, logging.handlers.TimedRotatingFileHandler
34
+ """
35
+ if not base_logger:
36
+ logger_name = f'{logger_base_name}.{logger_name}'
37
+
38
+ logger = logging.getLogger(logger_name)
39
+ log_path = get_set_config_dir('log', user=True)
40
+ if log_level is None:
41
+ log_level = config('general', 'debug_level')
42
+ logger.setLevel(log_level)
43
+ if add_handler:
44
+ log_file_path = log_path.joinpath(f'{logger_base_name}.log')
45
+ if not log_file_path.is_file():
46
+ log_file_path.touch(mode=0o777)
47
+ handler = TimedRotatingFileHandler(log_file_path, when='midnight')
48
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
49
+ handler.setFormatter(formatter)
50
+ logger.addHandler(handler)
51
+
52
+ logging.captureWarnings(True)
53
+ # only catch DeprecationWarning in DEBUG level
54
+ if log_level == 'DEBUG':
55
+ warnings.filterwarnings('default', category=DeprecationWarning)
56
+ else:
57
+ warnings.filterwarnings('ignore', category=DeprecationWarning)
58
+ warnings_logger = logging.getLogger("py.warnings")
59
+ warnings_logger.addHandler(handler)
60
+
61
+ if add_to_console:
62
+ console_handler = logging.StreamHandler()
63
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
64
+ console_handler.setFormatter(formatter)
65
+ logger.addHandler(console_handler)
66
+ return logger
67
+
68
+
69
+ def get_module_name(module__file__path):
70
+ """from the full path of a module extract its name"""
71
+ path = Path(module__file__path)
72
+ return path.stem
@@ -0,0 +1,460 @@
1
+ import warnings
2
+
3
+ import pymodaq.utils.messenger
4
+ from multipledispatch import dispatch
5
+ from typing import Union, Callable, List
6
+
7
+ from qtpy import QtGui, QtWidgets, QtCore
8
+ from qtpy.QtWidgets import QAction
9
+
10
+ from pymodaq.resources.QtDesigner_Ressources import QtDesigner_ressources_rc
11
+ from pathlib import Path
12
+
13
+
14
+ class QAction(QAction):
15
+ """
16
+ QAction subclass to mimic signals as pushbuttons. Done to be sure of backcompatibility when I moved from
17
+ pushbuttons to QAction
18
+ """
19
+ def __init__(self, *args, **kwargs):
20
+ super().__init__(*args, **kwargs)
21
+
22
+ def click(self):
23
+ pymodaq.utils.messenger.deprecation_msg("click for PyMoDAQ's QAction is deprecated, use *trigger*",
24
+ stacklevel=3)
25
+ self.trigger()
26
+
27
+ @property
28
+ def clicked(self):
29
+ pymodaq.utils.messenger.deprecation_msg("clicked for PyMoDAQ's QAction is deprecated, use *trigger*",
30
+ stacklevel=3)
31
+ return self.triggered
32
+
33
+ def connect_to(self, slot):
34
+ self.triggered.connect(slot)
35
+
36
+
37
+ def addaction(name='', icon_name='', tip='', checkable=False, slot: Callable = None, toolbar: QtWidgets.QToolBar = None,
38
+ menu: QtWidgets.QMenu = None, visible=True, shortcut=None):
39
+ """Create a new action and add it eventually to a toolbar and a menu
40
+
41
+ Parameters
42
+ ----------
43
+ name: str
44
+ Displayed name if should be displayed (for instance in menus)
45
+ icon_name: str
46
+ png file name to produce the icon
47
+ tip: str
48
+ a tooltip to be displayed when hovering above the action
49
+ checkable: bool
50
+ set the checkable state of the action
51
+ slot: callable
52
+ Method or function that will be called when the action is triggered
53
+ toolbar: QToolBar
54
+ a toolbar where action should be added.
55
+ menu: QMenu
56
+ a menu where action should be added.
57
+ visible: bool
58
+ display or not the action in the toolbar/menu
59
+ """
60
+ if icon_name != '':
61
+ icon = QtGui.QIcon()
62
+ if Path(icon_name).is_file():
63
+ icon.addPixmap(QtGui.QPixmap(icon_name), QtGui.QIcon.Normal,
64
+ QtGui.QIcon.Off)
65
+ else:
66
+ icon.addPixmap(QtGui.QPixmap(f":/icons/Icon_Library/{icon_name}.png"), QtGui.QIcon.Normal,
67
+ QtGui.QIcon.Off)
68
+ action = QAction(icon, name, None)
69
+ else:
70
+ action = QAction(name)
71
+
72
+ if slot is not None:
73
+ action.connect_to(slot)
74
+ action.setCheckable(checkable)
75
+ action.setToolTip(tip)
76
+ if toolbar is not None:
77
+ toolbar.addAction(action)
78
+ if menu is not None:
79
+ menu.addAction(action)
80
+ if shortcut is not None:
81
+ action.setShortcut(shortcut)
82
+ action.setVisible(visible)
83
+ return action
84
+
85
+
86
+ def addwidget(klass: Union[str, QtWidgets.QWidget], *args, tip='', toolbar: QtWidgets.QToolBar = None, visible=True,
87
+ signal_str=None, slot: Callable=None, setters = {}, **kwargs):
88
+ """Create and eventually add a widget to a toolbar
89
+
90
+ Parameters
91
+ ----------
92
+ klass: str or QWidget
93
+ should be a custom widget class or the name of a standard widget of QWidgets
94
+ args: list
95
+ variable arguments passed as is to the widget constructor
96
+ tip: str
97
+ a tooltip to be displayed when hovering above the widget
98
+ toolbar: QToolBar
99
+ a toolbar where the widget should be added.
100
+ visible: bool
101
+ display or not the action in the toolbar/menu
102
+ signal_str: str
103
+ an attribute of type Signal of the widget
104
+ slot: Callable
105
+ a callable connected to the signal
106
+ kwargs: dict
107
+ variable named arguments used as is in the widget constructor
108
+ setters: dict
109
+ method/value pair of the widget (for instance setMaximumWidth)
110
+ Returns
111
+ -------
112
+ QtWidgets.QWidget
113
+ """
114
+ if isinstance(klass, str):
115
+ if hasattr(QtWidgets, klass):
116
+ widget: QtWidgets.QWidget = getattr(QtWidgets, klass)(*args)
117
+ else:
118
+ return None
119
+ else:
120
+ try:
121
+ widget = klass(*args, **kwargs)
122
+ except:
123
+ return None
124
+ widget.setVisible(visible)
125
+ widget.setToolTip(tip)
126
+ if toolbar is not None:
127
+ toolbar.addWidget(widget)
128
+ if isinstance(signal_str, str) and slot is not None:
129
+ if hasattr(widget, signal_str):
130
+ getattr(widget, signal_str).connect(slot)
131
+
132
+ for setter in setters:
133
+ if hasattr(widget, setter):
134
+ getattr(widget, setter)(setters[setter])
135
+
136
+ return widget
137
+
138
+
139
+ class ActionManager:
140
+ """MixIn Class to be used by all UserInterface to manage their QActions and the action they are connected to
141
+
142
+ Parameters
143
+ ----------
144
+ toolbar: QToolbar, optional
145
+ The toolbar to use as default
146
+ menu: QMenu, option
147
+ The menu to use as default
148
+ """
149
+ def __init__(self, toolbar=None, menu=None):
150
+ self._actions = dict([])
151
+ self._toolbar = toolbar
152
+ self._menu = menu
153
+
154
+ #self.setup_actions()
155
+
156
+ def setup_actions(self):
157
+ """Method where to create actions to be subclassed. Mandatory
158
+
159
+ Examples
160
+ --------
161
+ >>> self.add_action('Quit', 'close2', "Quit program")
162
+ >>> self.add_action('Grab', 'camera', "Grab from camera", checkable=True)
163
+ >>> self.add_action('Load', 'Open', "Load target file (.h5, .png, .jpg) or data from camera", checkable=False)
164
+ >>> self.add_action('Save', 'SaveAs', "Save current data", checkable=False)
165
+
166
+ See Also
167
+ --------
168
+ ActionManager.add_action
169
+ """
170
+ raise NotImplementedError(f'You have to define actions here in the following form:'
171
+ f'{self.setup_actions.__doc__}')
172
+
173
+ def add_action(self, short_name='', name='', icon_name='', tip='', checkable=False, toolbar=None, menu=None,
174
+ visible=True, shortcut=None, auto_toolbar=True, auto_menu=True):
175
+ """Create a new action and add it to toolbar and menu
176
+
177
+ Parameters
178
+ ----------
179
+ short_name: str
180
+ the name as referenced in the dict self.actions
181
+ name: str
182
+ Displayed name if should be displayed in
183
+ icon_name: str
184
+ png file name to produce the icon
185
+ tip: str
186
+ a tooltip to be displayed when hovering above the action
187
+ checkable: bool
188
+ set the checkable state of the action
189
+ toolbar: QToolBar
190
+ a toolbar where action should be added. Actions can also be added later see *affect_to*
191
+ menu: QMenu
192
+ a menu where action should be added. Actions can also be added later see *affect_to*
193
+ visible: bool
194
+ display or not the action in the toolbar/menu
195
+
196
+ See Also
197
+ --------
198
+ affect_to, pymodaq.resources.QtDesigner_Ressources.Icon_Library,
199
+ pymodaq.utils.managers.action_manager.add_action
200
+ """
201
+ if auto_toolbar:
202
+ if toolbar is None:
203
+ toolbar = self._toolbar
204
+ if auto_menu:
205
+ if menu is None:
206
+ menu = self._menu
207
+ self._actions[short_name] = addaction(name, icon_name, tip, checkable=checkable, toolbar=toolbar, menu=menu,
208
+ visible=visible, shortcut=shortcut)
209
+
210
+ def add_widget(self, short_name, klass: Union[str, QtWidgets.QWidget], *args, tip='',
211
+ toolbar: QtWidgets.QToolBar = None, visible=True, signal_str=None, slot: Callable=None, **kwargs):
212
+ """Create and add a widget to a toolbar
213
+
214
+ Parameters
215
+ ----------
216
+ short_name: str
217
+ the name as referenced in the dict self.actions
218
+ klass: str or QWidget
219
+ should be a custom widget class or the name of a standard widget of QWidgets
220
+ args: list
221
+ variable arguments passed as is to the widget constructor
222
+ tip: str
223
+ a tooltip to be displayed when hovering above the widget
224
+ toolbar: QToolBar
225
+ a toolbar where the widget should be added.
226
+ visible: bool
227
+ display or not the action in the toolbar/menu
228
+ signal_str: str
229
+ an attribute of type Signal of the widget
230
+ slot: Callable
231
+ a callable connected to the signal
232
+ kwargs: dict
233
+ variable named arguments passed as is to the widget constructor
234
+ Returns
235
+ -------
236
+ QtWidgets.QWidget
237
+ """
238
+ if toolbar is None:
239
+ toolbar = self._toolbar
240
+ widget = addwidget(klass, *args, tip=tip, toolbar=toolbar, visible=visible, signal_str=signal_str,
241
+ slot=slot, **kwargs)
242
+ if widget is not None:
243
+ self._actions[short_name] = widget
244
+ else:
245
+ warnings.warn(UserWarning(f'Impossible to add the widget {short_name} and type {klass} to the toolbar'))
246
+
247
+ def set_toolbar(self, toolbar):
248
+ """affect a toolbar to self
249
+
250
+ Parameters
251
+ ----------
252
+ toolbar:
253
+ QtWidgets.QToolBar
254
+ """
255
+ self._toolbar = toolbar
256
+
257
+ def set_menu(self, menu):
258
+ """affect a menu to self
259
+
260
+ Parameters
261
+ ----------
262
+ menu:
263
+ QtWidgets.QMenu
264
+ """
265
+ self._menu = menu
266
+
267
+ def set_action_text(self, action_name: str, text: str):
268
+ """Convenience method to set the displayed text on an action
269
+
270
+ Parameters
271
+ ----------
272
+ action_name: str
273
+ The action name as defined in setup_actions
274
+ text: str
275
+ The text to display
276
+ """
277
+ self.get_action(action_name).setText(text)
278
+
279
+ @property
280
+ def actions(self) -> List[QAction]:
281
+ return list(self._actions.values())
282
+
283
+ def get_action(self, name) -> QAction:
284
+ """Getter of a given action
285
+
286
+ Parameters
287
+ ----------
288
+ name: str
289
+ The action name as defined in setup_actions
290
+
291
+ Returns
292
+ -------
293
+ QAction
294
+ """
295
+ if self.has_action(name):
296
+ return self._actions[name]
297
+ else:
298
+ raise KeyError(f'The action with name: {name} is not referenced'
299
+ f' in the view actions: {self._actions.keys()}')
300
+
301
+ def has_action(self, action_name) -> bool:
302
+ """Check if an action has been defined
303
+ Parameters
304
+ ----------
305
+ action_name: str
306
+ The action name as defined in setup_actions
307
+
308
+ Returns
309
+ -------
310
+ bool: True if the action exists, False otherwise
311
+ """
312
+ return action_name in self._actions
313
+
314
+ @property
315
+ def toolbar(self):
316
+ """Get the default toolbar"""
317
+ return self._toolbar
318
+
319
+ @property
320
+ def menu(self):
321
+ """Get the default menu"""
322
+ return self._menu
323
+
324
+ def affect_to(self, action_name, obj: Union[QtWidgets.QToolBar, QtWidgets.QMenu]):
325
+ """Affect action to an object either a toolbar or a menu
326
+
327
+ Parameters
328
+ ----------
329
+ action_name: str
330
+ The action name as defined in setup_actions
331
+ obj: QToolbar or QMenu
332
+ The object where to add the action
333
+ """
334
+ if isinstance(obj, QtWidgets.QToolBar) or isinstance(obj, QtWidgets.QMenu):
335
+ obj.addAction(self._actions[action_name])
336
+
337
+ def connect_action(self, name, slot, connect=True, signal_name=''):
338
+ """Connect (or disconnect) the action referenced by name to the given slot
339
+
340
+ Parameters
341
+ ----------
342
+ name: str
343
+ key of the action as referenced in the self._actions dict
344
+ slot: method
345
+ a method/function
346
+ connect: bool
347
+ if True connect the trigger signal of the action to the defined slot else disconnect it
348
+ signal_name: str
349
+ try to use it as a signal (for widgets added...) otherwise use the *triggered* signal
350
+ """
351
+ signal = 'triggered'
352
+ if name in self._actions:
353
+ if hasattr(self._actions[name], signal_name):
354
+ signal = signal_name
355
+ if connect:
356
+ getattr(self._actions[name], signal).connect(slot)
357
+ else:
358
+ try:
359
+ getattr(self._actions[name], signal).disconnect()
360
+ except (TypeError,) as e:
361
+ pass # the action was not connected
362
+ else:
363
+ raise KeyError(f'The action with name: {name} is not referenced'
364
+ f' in the view actions: {self._actions.keys()}')
365
+
366
+ @dispatch(str)
367
+ def is_action_visible(self, action_name: str):
368
+ """Check the visibility of a given action or the list of an action"""
369
+ if action_name in self._actions:
370
+ return self._actions[action_name].isVisible()
371
+ else:
372
+ raise KeyError(f'The action with name: {action_name} is not referenced'
373
+ f' in the actions list: {self._actions}')
374
+
375
+ @dispatch(list)
376
+ def is_action_visible(self, actions_name: list):
377
+ """Check the visibility of a given action or the list of an action"""
378
+ isvisible = False
379
+ for action_name in actions_name:
380
+ isvisible = isvisible and self.is_action_visible(action_name)
381
+ return isvisible
382
+
383
+ @dispatch(str)
384
+ def is_action_checked(self, action_name: str):
385
+ """Get the CheckState of a given action or a list of actions"""
386
+ if action_name in self._actions:
387
+ return self._actions[action_name].isChecked()
388
+ else:
389
+ raise KeyError(f'The action with name: {action_name} is not referenced'
390
+ f' in the actions list: {self._actions}')
391
+
392
+ @dispatch(list)
393
+ def is_action_checked(self, actions_name: list):
394
+ """Get the CheckState of a given action or a list of actions"""
395
+ ischecked = False
396
+ for action_name in actions_name:
397
+ ischecked = ischecked and self.is_action_checked(action_name)
398
+ return ischecked
399
+
400
+ @dispatch(str, bool)
401
+ def set_action_visible(self, action_name: str, visible=True):
402
+ """Set the visibility of a given action or a list of an action"""
403
+ if action_name in self._actions:
404
+ self._actions[action_name].setVisible(visible)
405
+ else:
406
+ raise KeyError(f'The action with name: {action_name} is not referenced'
407
+ f' in the actions list: {self._actions}')
408
+
409
+ @dispatch(list, bool)
410
+ def set_action_visible(self, actions_name: list, visible=True):
411
+ """Set the visibility of a given action or a list of an action"""
412
+ for action_name in actions_name:
413
+ self.set_action_visible(action_name, visible)
414
+
415
+ @dispatch(str, bool)
416
+ def set_action_checked(self, action_name: str, checked=True):
417
+ """Set the CheckedState of a given action or a list of actions"""
418
+ if action_name in self._actions:
419
+ self._actions[action_name].setChecked(checked)
420
+ else:
421
+ raise KeyError(f'The action with name: {action_name} is not referenced'
422
+ f' in the actions list: {self._actions}')
423
+
424
+ @dispatch(list, bool)
425
+ def set_action_checked(self, actions_name: list, checked=True):
426
+ """Set the CheckedState of a given action or a list of actions"""
427
+ for action_name in actions_name:
428
+ self.set_action_checked(action_name, checked)
429
+
430
+ @dispatch(str, bool)
431
+ def set_action_enabled(self, action_name: str, enabled=True):
432
+ """Set the EnabledState of a given action or a list of actions"""
433
+ if action_name in self._actions:
434
+ self._actions[action_name].setEnabled(enabled)
435
+ else:
436
+ raise KeyError(f'The action with name: {action_name} is not referenced'
437
+ f' in the actions list: {self._actions}')
438
+
439
+ @dispatch(list, bool)
440
+ def set_action_enabled(self, actions_name: list, enabled=True):
441
+ """Set the EnabledState of a given action or a list of actions"""
442
+ for action_name in actions_name:
443
+ self.set_action_enabled(action_name, enabled)
444
+
445
+ @dispatch(str)
446
+ def is_action_enabled(self, action_name: str):
447
+ """Get the EnabledState of a given action or a list of actions"""
448
+ if action_name in self._actions:
449
+ return self._actions[action_name].isEnabled()
450
+ else:
451
+ raise KeyError(f'The action with name: {action_name} is not referenced'
452
+ f' in the actions list: {self._actions}')
453
+
454
+ @dispatch(list)
455
+ def is_action_checked(self, actions_name: list):
456
+ """Get the EnabledState of a given action or a list of actions"""
457
+ is_enabled = False
458
+ for action_name in actions_name:
459
+ is_enabled = is_enabled and self.is_action_enabled(action_name)
460
+ return is_enabled