pymodaq 4.1.5__py3-none-any.whl → 4.2.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 (80) hide show
  1. pymodaq/__init__.py +23 -4
  2. pymodaq/control_modules/daq_move.py +32 -73
  3. pymodaq/control_modules/daq_viewer.py +73 -98
  4. pymodaq/control_modules/daq_viewer_ui.py +2 -1
  5. pymodaq/control_modules/move_utility_classes.py +17 -7
  6. pymodaq/control_modules/utils.py +153 -5
  7. pymodaq/control_modules/viewer_utility_classes.py +31 -20
  8. pymodaq/dashboard.py +23 -5
  9. pymodaq/examples/tcp_client.py +97 -0
  10. pymodaq/extensions/__init__.py +4 -0
  11. pymodaq/extensions/bayesian/__init__.py +2 -0
  12. pymodaq/extensions/bayesian/bayesian_optimisation.py +673 -0
  13. pymodaq/extensions/bayesian/utils.py +403 -0
  14. pymodaq/extensions/daq_scan.py +4 -4
  15. pymodaq/extensions/daq_scan_ui.py +2 -1
  16. pymodaq/extensions/pid/pid_controller.py +12 -7
  17. pymodaq/extensions/pid/utils.py +9 -26
  18. pymodaq/extensions/utils.py +3 -0
  19. pymodaq/post_treatment/load_and_plot.py +42 -19
  20. pymodaq/resources/VERSION +1 -1
  21. pymodaq/resources/config_template.toml +9 -24
  22. pymodaq/resources/setup_plugin.py +1 -1
  23. pymodaq/utils/config.py +103 -5
  24. pymodaq/utils/daq_utils.py +35 -134
  25. pymodaq/utils/data.py +614 -95
  26. pymodaq/utils/enums.py +17 -1
  27. pymodaq/utils/factory.py +2 -2
  28. pymodaq/utils/gui_utils/custom_app.py +5 -2
  29. pymodaq/utils/gui_utils/dock.py +33 -4
  30. pymodaq/utils/gui_utils/utils.py +14 -1
  31. pymodaq/utils/h5modules/backends.py +9 -1
  32. pymodaq/utils/h5modules/data_saving.py +254 -57
  33. pymodaq/utils/h5modules/saving.py +1 -0
  34. pymodaq/utils/leco/__init__.py +25 -0
  35. pymodaq/utils/leco/daq_move_LECODirector.py +172 -0
  36. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +170 -0
  37. pymodaq/utils/leco/desktop.ini +2 -0
  38. pymodaq/utils/leco/director_utils.py +58 -0
  39. pymodaq/utils/leco/leco_director.py +88 -0
  40. pymodaq/utils/leco/pymodaq_listener.py +279 -0
  41. pymodaq/utils/leco/utils.py +41 -0
  42. pymodaq/utils/managers/action_manager.py +20 -6
  43. pymodaq/utils/managers/parameter_manager.py +6 -4
  44. pymodaq/utils/managers/roi_manager.py +63 -54
  45. pymodaq/utils/math_utils.py +1 -1
  46. pymodaq/utils/plotting/data_viewers/__init__.py +3 -1
  47. pymodaq/utils/plotting/data_viewers/base.py +286 -0
  48. pymodaq/utils/plotting/data_viewers/viewer.py +29 -202
  49. pymodaq/utils/plotting/data_viewers/viewer0D.py +94 -47
  50. pymodaq/utils/plotting/data_viewers/viewer1D.py +341 -174
  51. pymodaq/utils/plotting/data_viewers/viewer1Dbasic.py +1 -1
  52. pymodaq/utils/plotting/data_viewers/viewer2D.py +271 -181
  53. pymodaq/utils/plotting/data_viewers/viewerND.py +26 -22
  54. pymodaq/utils/plotting/items/crosshair.py +3 -3
  55. pymodaq/utils/plotting/items/image.py +2 -1
  56. pymodaq/utils/plotting/plotter/plotter.py +94 -0
  57. pymodaq/utils/plotting/plotter/plotters/__init__.py +0 -0
  58. pymodaq/utils/plotting/plotter/plotters/matplotlib_plotters.py +134 -0
  59. pymodaq/utils/plotting/plotter/plotters/qt_plotters.py +78 -0
  60. pymodaq/utils/plotting/utils/axes_viewer.py +1 -1
  61. pymodaq/utils/plotting/utils/filter.py +194 -147
  62. pymodaq/utils/plotting/utils/lineout.py +13 -11
  63. pymodaq/utils/plotting/utils/plot_utils.py +89 -12
  64. pymodaq/utils/scanner/__init__.py +0 -3
  65. pymodaq/utils/scanner/scan_config.py +1 -9
  66. pymodaq/utils/scanner/scan_factory.py +10 -36
  67. pymodaq/utils/scanner/scanner.py +3 -2
  68. pymodaq/utils/scanner/scanners/_1d_scanners.py +7 -5
  69. pymodaq/utils/scanner/scanners/_2d_scanners.py +36 -49
  70. pymodaq/utils/scanner/scanners/sequential.py +10 -4
  71. pymodaq/utils/scanner/scanners/tabular.py +10 -5
  72. pymodaq/utils/slicing.py +1 -1
  73. pymodaq/utils/tcp_ip/serializer.py +38 -5
  74. pymodaq/utils/tcp_ip/tcp_server_client.py +25 -17
  75. {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/METADATA +4 -2
  76. {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/RECORD +79 -63
  77. pymodaq/resources/config_scan_template.toml +0 -42
  78. {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/WHEEL +0 -0
  79. {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/entry_points.txt +0 -0
  80. {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -6,15 +6,20 @@ Created the 22/01/2023
6
6
  """
7
7
  import os
8
8
  import sys
9
- from typing import List, Union, Callable, Iterable
9
+ from typing import List, Union, Callable, Iterable, Dict
10
10
 
11
11
  from qtpy import QtWidgets, QtCore
12
12
 
13
13
  from pymodaq.utils.data import DataToExport, DataFromPlugins, DataDim, enum_checker
14
14
  from pymodaq.utils.h5modules.data_saving import DataLoader
15
15
  from pymodaq.utils.h5modules.saving import H5Saver
16
- from pymodaq.utils.plotting.data_viewers.viewer import ViewerBase, ViewersEnum, ViewerDispatcher
16
+ from pymodaq.utils.plotting.data_viewers.viewer import ViewerBase, ViewerDispatcher
17
+ from pymodaq.utils.plotting.data_viewers import ViewersEnum, Viewer1D, Viewer2D, ViewerND
17
18
  from pymodaq.utils.gui_utils import Dock, DockArea
19
+ from pymodaq.utils.logger import set_logger, get_module_name
20
+
21
+
22
+ logger = set_logger(get_module_name(__file__))
18
23
 
19
24
 
20
25
  class LoaderPlotter:
@@ -182,20 +187,25 @@ class LoaderPlotter:
182
187
 
183
188
  target_at = kwargs.pop('target_at') if 'target_at' in kwargs else None
184
189
  last_step = kwargs.pop('last_step') if 'last_step' in kwargs else False
190
+ crosshair_at = kwargs.pop('crosshair_at') if 'crosshair_at' in kwargs else None
185
191
 
186
192
  self.load_data(**kwargs)
187
- self.show_data(target_at=target_at)
193
+ self.show_data(target_at=target_at,
194
+ crosshair_at=crosshair_at)
188
195
  if (last_step and 'average_index' in kwargs and kwargs['average_index']
189
196
  is not None):
190
197
  kwargs['last_step'] = last_step
191
198
  self.load_data(**kwargs)
192
- self.show_data(target_at=target_at)
199
+ self.show_data(target_at=target_at,
200
+ crosshair_at=crosshair_at)
193
201
 
194
202
  def show_data(self, **kwargs):
195
203
  """Send data to their dedicated viewers
196
204
  """
197
- #self._init_show_data(self._data)
198
- self.set_data_to_viewers(self._data, **kwargs)
205
+ try:
206
+ self.set_data_to_viewers(self._data, **kwargs)
207
+ except Exception as e:
208
+ logger.warning(f'Could not show data: {str(e)}')
199
209
 
200
210
  def _init_show_data(self, data: DataToExport):
201
211
  """Processing before showing data
@@ -203,7 +213,9 @@ class LoaderPlotter:
203
213
  self._viewer_types = [ViewersEnum(data.dim.name) for data in data]
204
214
  self.prepare_viewers(self._viewer_types)
205
215
 
206
- def prepare_viewers(self, viewers_enum: List[ViewersEnum], viewers_name: List[str] = None):
216
+ def prepare_viewers(self, viewers_enum: List[Union[ViewersEnum, str]],
217
+ viewers_name: List[str] = None) -> List[ViewerBase]:
218
+
207
219
  if self._viewers is not None:
208
220
  while len(self._viewers) > 0:
209
221
  self._viewers.pop(list(self._viewers.keys())[0])
@@ -214,13 +226,16 @@ class LoaderPlotter:
214
226
  viewers_name = [f'DataPlot{ind:02d}' for ind in range(len(self._viewer_types))]
215
227
 
216
228
  if self.dispatcher.viewer_types != self._viewer_types:
217
- self.dispatcher.update_viewers(self._viewer_types)
229
+ self.dispatcher.update_viewers(self._viewer_types,
230
+ viewers_name=viewers_name)
218
231
 
219
232
  self._viewers = dict(zip(viewers_name, self.dispatcher.viewers))
220
233
  self._viewer_docks = dict(zip(viewers_name, self.dispatcher.viewer_docks))
234
+ return self.dispatcher.viewers
221
235
 
222
236
  def set_data_to_viewers(self, data: DataToExport, temp=False,
223
- target_at: Iterable[float] = None):
237
+ target_at: Iterable[float] = None,
238
+ crosshair_at: Iterable[float] = None):
224
239
  """Process data dimensionality and send appropriate data to their data viewers
225
240
 
226
241
  Parameters
@@ -230,6 +245,8 @@ class LoaderPlotter:
230
245
  if True notify the data viewers to display data as temporary (meaning not exporting processed data from roi)
231
246
  target_at: Iterable[float]
232
247
  if specified show and plot the roi_target of each viewer at the given position
248
+ crosshair_at: Iterable[float]
249
+ if specified show and plot the viewer crosshair of each viewer at the given position
233
250
  See Also
234
251
  --------
235
252
  ViewerBase, Viewer0D, Viewer1D, Viewer2D
@@ -246,21 +263,27 @@ class LoaderPlotter:
246
263
  viewer.show_data_temp(_data)
247
264
  else:
248
265
  viewer.show_data(_data)
266
+ if crosshair_at is not None:
267
+ if not viewer.is_action_checked('crosshair'):
268
+ viewer.get_action('crosshair').trigger()
269
+ viewer.double_clicked(*crosshair_at)
249
270
  if target_at is not None:
250
271
  viewer.show_roi_target(True)
251
- if _data.dim == 'Data1D':
272
+ if isinstance(viewer, Viewer1D):
252
273
  viewer.move_roi_target(target_at)
253
- elif _data.dim == 'Data2D' and _data.distribution == 'uniform':
274
+ elif isinstance(viewer, Viewer2D):
254
275
  _target_at = target_at.copy()
255
-
256
- size = [_data.get_axis_from_index(1)[0].scaling]
257
- if len(_target_at) == 1: # means concatenation of 1D data
258
- axis = _data.get_axis_from_index(0)[0]
259
- size.append(axis.scaling * axis.size)
260
- _target_at = list(_target_at) + [axis.offset]
276
+ if _data.distribution == 'uniform':
277
+ size = [_data.get_axis_from_index(1)[0].scaling]
278
+ if len(_target_at) == 1: # means concatenation of 1D data
279
+ axis = _data.get_axis_from_index(0)[0]
280
+ size.append(axis.scaling * axis.size)
281
+ _target_at = list(_target_at) + [axis.offset]
282
+ else:
283
+ size.append(_data.get_axis_from_index(0)[0].scaling)
284
+ viewer.move_roi_target(_target_at, size)
261
285
  else:
262
- size.append(_data.get_axis_from_index(0)[0].scaling)
263
- viewer.move_roi_target(_target_at, size)
286
+ viewer.move_roi_target(_target_at)
264
287
 
265
288
  def main(init_qt=True):
266
289
  if init_qt: # used for the test suite
pymodaq/resources/VERSION CHANGED
@@ -1 +1 @@
1
- version = '4.1.5'
1
+ version = '4.2.1'
@@ -41,6 +41,9 @@ viewer_in_thread = true
41
41
  timeout = 10000 # default duration in ms to wait for data to be acquirred
42
42
  allow_settings_edition = false
43
43
 
44
+ [plotting]
45
+ backend = 'matplotlib' # either 'matplotlib' or 'qt' or any other custom backend
46
+
44
47
  [network]
45
48
  [network.logging]
46
49
  [network.logging.user]
@@ -55,6 +58,11 @@ allow_settings_edition = false
55
58
  ip = "10.47.0.39"
56
59
  port = 6341
57
60
 
61
+ [network.leco-server]
62
+ run_coordinator_at_startup = false
63
+ host = "localhost"
64
+ port = 12300 # pyleco default Coordinator port
65
+
58
66
  [presets]
59
67
  default_preset_for_scan = "preset_default"
60
68
  default_preset_for_logger = "preset_default"
@@ -62,7 +70,7 @@ default_preset_for_pid = "beam_steering_mock"
62
70
 
63
71
  [actuator]
64
72
  epsilon_default = 1
65
- polling_interval_ms = 100 # ms Carefull when using TCP/IP connection as you can saturate the connection with too much polling
73
+ polling_interval_ms = 100 # ms Careful when using TCP/IP connection as you can saturate the connection with too much polling
66
74
  polling_timeout_s = 20 # s
67
75
  refresh_timeout_ms = 500 # ms
68
76
  timeout = 10000 # default duration in ms to wait for data to be acquirred
@@ -80,26 +88,3 @@ default_preset_for_pid = "beam_steering_mock"
80
88
  wait_time_between = 0
81
89
  timeout = 10000 # in millisecond
82
90
 
83
- [scan.scan1D]
84
- type = "Linear" # either "Linear", "Adaptive", "Linear back to start", "Random" see pymodaq.utils.scanner.py
85
- start = 1.0
86
- stop = 2.0
87
- step = 0.01
88
- [scan.scan2D]
89
- type = "Spiral" # either "Spiral", "Linear", "Adaptive", "Back&Forth", "Random" see pymodaq.utils.scanner.py
90
- [scan.scan2D.spiral]
91
- center1 = -5
92
- center2 = 5
93
- rmax1 = 10
94
- rmax2 = 5
95
- npts = 10
96
- [scan.scan2D.linear]
97
- start1 = -5
98
- start2 = -5
99
- stop1 = 5
100
- stop2 = 5
101
- step1 = 0.5
102
- step2 = 0.5
103
- [scan.tabular]
104
- type = "Linear" #either "Linear", "Adaptive" see pymodaq.utils.scanner.py
105
- curvilinear = 0.1
@@ -47,7 +47,7 @@ def setup(path: Path):
47
47
  entrypoints['pymodaq.instruments'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}'
48
48
  if config['features'].get('extensions', False):
49
49
  entrypoints['pymodaq.extensions'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}'
50
- if config['features'].get('pid_models', False):
50
+ if config['features'].get('pid_models', False): # deprecated use 'models'
51
51
  entrypoints['pymodaq.pid_models'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}'
52
52
  if config['features'].get('models', False):
53
53
  entrypoints['pymodaq.models'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}'
pymodaq/utils/config.py CHANGED
@@ -1,13 +1,18 @@
1
1
  from abc import abstractproperty
2
+ from collections.abc import Iterable
2
3
 
3
4
  from os import environ
4
5
  import sys
5
6
  import datetime
6
7
  from pathlib import Path
7
- from typing import Union, Dict, TypeVar, Any
8
+ from typing import Union, Dict, TypeVar, Any, List, TYPE_CHECKING
9
+ from typing import Iterable as IterableType
8
10
 
9
11
  import toml
10
12
 
13
+ if TYPE_CHECKING:
14
+ from pymodaq.utils.parameter import Parameter
15
+
11
16
 
12
17
  try:
13
18
  USER = environ['USERNAME'] if sys.platform == 'win32' else environ['USER']
@@ -80,6 +85,16 @@ def getitem_recursive(dic, *args, ndepth=0, create_if_missing=False):
80
85
  return dic
81
86
 
82
87
 
88
+ def recursive_iterable_flattening(aniterable: IterableType):
89
+ flatten_iter = []
90
+ for elt in aniterable:
91
+ if not isinstance(elt, str) and isinstance(elt, Iterable):
92
+ flatten_iter.extend(recursive_iterable_flattening(elt))
93
+ else:
94
+ flatten_iter.append(elt)
95
+ return flatten_iter
96
+
97
+
83
98
  def get_set_path(a_base_path: Path, dir_name: str) -> Path:
84
99
  path_to_get = a_base_path.joinpath(dir_name)
85
100
  if not path_to_get.is_dir():
@@ -249,7 +264,7 @@ def copy_template_config(config_file_name: str = 'config', source_path: Union[Pa
249
264
  dest_path_with_filename = dest_path.joinpath(file_name)
250
265
 
251
266
  if source_path is None:
252
- config_template_dict = toml.load(Path(__file__).parent.parent.joinpath('resources/config_template.toml'))
267
+ config_template_dict = {}
253
268
  else:
254
269
  config_template_dict = toml.load(Path(source_path))
255
270
 
@@ -322,9 +337,18 @@ class BaseConfig:
322
337
  else:
323
338
  return self._config[item]
324
339
 
340
+ # def __setitem__(self, key, value):
341
+ # if isinstance(key, tuple):
342
+ # dic = getitem_recursive(self._config, *key, ndepth=1, create_if_missing=False)
343
+ # dic[key[-1]] = value
344
+ # else:
345
+ # self._config[key] = value
346
+
325
347
  def __setitem__(self, key, value):
326
348
  if isinstance(key, tuple):
327
- dic = getitem_recursive(self._config, *key, ndepth=1, create_if_missing=False)
349
+ dic = getitem_recursive(self._config, *key, ndepth=1, create_if_missing=True)
350
+ if value is None: # means the setting is a group
351
+ value = {}
328
352
  dic[key[-1]] = value
329
353
  else:
330
354
  self._config[key] = value
@@ -337,7 +361,10 @@ class BaseConfig:
337
361
  toml_user_path = get_config_file(config_file_name, user=True)
338
362
  if toml_base_path.is_file():
339
363
  config = toml.load(toml_base_path)
340
- config_template = toml.load(template_path)
364
+ if template_path is not None:
365
+ config_template = toml.load(template_path)
366
+ else:
367
+ config_template = {}
341
368
  if check_config(config_template, config): # check if all fields from template are there
342
369
  # (could have been modified by some commits)
343
370
  create_toml_from_dict(config, toml_base_path)
@@ -383,8 +410,79 @@ class Config(BaseConfig):
383
410
  return dict(user=dict(name=USER))
384
411
 
385
412
 
413
+ class ConfigSaverLoader:
414
+ """ Allows to set Parameters values from previously saved one in a configuration file
415
+
416
+ This plays the role of a cache for these Parameters
417
+
418
+ Parameters
419
+ ----------
420
+ base_param: Parameter
421
+ The parent Parameter whose children should be cached in the config file
422
+ config: BaseConfig
423
+ The Config object that will cache the Parameter values
424
+ base_path: Iterable[str]
425
+ an iterable of string defining a "category"
426
+ """
427
+
428
+ def __init__(self, base_param: 'Parameter', config: BaseConfig,
429
+ base_path: IterableType[str] = None):
430
+ self.config = config
431
+ if base_path is None:
432
+ base_path = []
433
+ self._base_path: List[str] = list(recursive_iterable_flattening(base_path))
434
+ self._base_param = base_param
435
+
436
+ @property
437
+ def base_path(self):
438
+ """ Get/Set the iterable of string defining a particular configuration to be loaded/saved"""
439
+ return self._base_path
440
+
441
+ @base_path.setter
442
+ def base_path(self, path: IterableType[str]):
443
+ self._base_path = list(recursive_iterable_flattening(path))
444
+
445
+ @property
446
+ def base_param(self):
447
+ """ Get/Set the parent Parameter whose children should be saved in the config file"""
448
+ return self._base_param
449
+
450
+ @base_param.setter
451
+ def base_param(self, param: 'Parameter'):
452
+ self._base_param = param
453
+
454
+ def load_config(self, param: 'Parameter' = None):
455
+ from pymodaq.utils.parameter import utils as putils
456
+
457
+ if param is None:
458
+ param = self.base_param
459
+ base_path = self.base_path[:]
460
+ for child in putils.iter_children_params(param, []):
461
+ if len(child.children()) == 0: # means it is not a group parameter
462
+
463
+ path = base_path + putils.get_param_path(child)[1:]
464
+
465
+ try:
466
+ child.setValue(self.config(
467
+ *path)) # first try to load the config including the actuators name
468
+ except ConfigError as e:
469
+ pass
470
+ else:
471
+ self.load_config(child)
472
+
473
+ def save_config(self):
474
+ from pymodaq.utils.parameter import utils as putils
475
+
476
+ for param in putils.iter_children_params(self.base_param, []):
477
+ path_param = self.base_path[:]
478
+ path_param.extend(putils.get_param_path(param)[1:])
479
+ try:
480
+ self.config[tuple(path_param)] = param.value()
481
+ except Exception as e:
482
+ pass
483
+ self.config.save()
484
+
386
485
 
387
-
388
486
  if __name__ == '__main__':
389
487
 
390
488
  config = Config()
@@ -15,6 +15,8 @@ import pkgutil
15
15
  import traceback
16
16
  import platform
17
17
  from typing import Union, List
18
+ from typing import Iterable as IterableType
19
+ from collections.abc import Iterable
18
20
 
19
21
  import numpy as np
20
22
  from qtpy import QtCore
@@ -44,32 +46,6 @@ plot_colors = [(255, 255, 255), (255, 0, 0), (0, 255, 0), (0, 0, 255), (14, 207,
44
46
  config = Config()
45
47
 
46
48
 
47
- def __getattr__(name):
48
- if name in ['Axis', 'NavAxis', 'ScaledAxis', 'ScalingOptions', 'Data', 'DataTimeStamped', 'DataFromPlugins',
49
- 'DataToEmit', 'DataToExport']:
50
- data_mod = importlib.import_module('.data', 'pymodaq.utils')
51
- deprecation_msg('Loading Axis or Data and their derived classes from daq_utils is deprecated, import them from'
52
- ' pymodaq.utils.data module', 3)
53
- return getattr(data_mod, name)
54
- else:
55
- raise AttributeError
56
-
57
-
58
- def load_config():
59
- deprecation_msg(f'config methods must now be imported from the pymodaq.utils.messenger.cnfig module')
60
- return Config()
61
-
62
-
63
- def set_logger(*args, **kwargs):
64
- deprecation_msg(f'Logger methods must now be imported from the pymodaq.utils.logger module', 3)
65
- return logger_module.set_logger(*args, **kwargs)
66
-
67
-
68
- def get_module_name(*args, **kwargs):
69
- deprecation_msg(f'Logger methods must now be imported from the pymodaq.utils.logger module', 3)
70
- return logger_module.get_module_name(*args, **kwargs)
71
-
72
-
73
49
  def is_64bits():
74
50
  return sys.maxsize > 2**32
75
51
 
@@ -255,7 +231,7 @@ class ThreadCommand:
255
231
  raise TypeError(f'The command in a Threadcommand object should be a string, not a {type(command)}')
256
232
  self.command = command
257
233
  if attribute is None and attributes is not None:
258
- deprecation_msg('ThreadCommand signature changed, use attribute in place of attribute')
234
+ deprecation_msg('ThreadCommand signature changed, use attribute in place of attributes')
259
235
  self.attribute = attributes
260
236
  self.attributes = attributes
261
237
  self.attribute = attribute
@@ -548,7 +524,7 @@ def find_dict_in_list_from_key_val(dicts, key, value, return_index=False):
548
524
  return None
549
525
 
550
526
 
551
- def get_entrypoints(group='pymodaq.plugins'):
527
+ def get_entrypoints(group='pymodaq.plugins') -> List[metadata.EntryPoint]:
552
528
  """ Get the list of modules defined from a group entry point
553
529
 
554
530
  Because of evolution in the package, one or another of the forms below may be deprecated.
@@ -568,6 +544,8 @@ def get_entrypoints(group='pymodaq.plugins'):
568
544
  discovered_entrypoints = metadata.entry_points().get(group, [])
569
545
  if isinstance(discovered_entrypoints, tuple): # API for python > 3.8
570
546
  discovered_entrypoints = list(discovered_entrypoints)
547
+ if not isinstance(discovered_entrypoints, list):
548
+ discovered_entrypoints = list(discovered_entrypoints)
571
549
  return discovered_entrypoints
572
550
 
573
551
 
@@ -586,16 +564,18 @@ def get_instrument_plugins(): # pragma: no cover
586
564
  """
587
565
  plugins_import = []
588
566
  discovered_plugins = []
589
- discovered_plugins_all = get_entrypoints(group='pymodaq.plugins') # old naming of the instrument plugins
590
- discovered_plugins_all.extend(get_entrypoints(group='pymodaq.instruments')) # new naming convention
567
+ discovered_plugins_all = list(get_entrypoints(group='pymodaq.plugins')) # old naming of the instrument plugins
568
+ discovered_plugins_all.extend(list(get_entrypoints(group='pymodaq.instruments'))) # new naming convention
591
569
  for entry in discovered_plugins_all:
592
570
  if entry.name not in [ent.name for ent in discovered_plugins]:
593
571
  discovered_plugins.append(entry)
572
+ discovered_plugins = list(set(discovered_plugins))
594
573
  logger.debug(f'Found {len(discovered_plugins)} installed plugins, trying to import them')
595
574
  viewer_types = ['0D', '1D', '2D', 'ND']
575
+ plugin_list = []
596
576
  for entrypoint in discovered_plugins:
597
577
  #print(f'Looking for valid instrument plugins in package: {module.value}')
598
- plugin_list = []
578
+
599
579
  try:
600
580
  try:
601
581
  movemodule = importlib.import_module(f'{entrypoint.value}.daq_move_plugins', entrypoint.value)
@@ -606,9 +586,11 @@ def get_instrument_plugins(): # pragma: no cover
606
586
  for mod in [mod[1] for mod in pkgutil.iter_modules([str(movemodule.path.parent)])]
607
587
  if 'daq_move' in mod])
608
588
  if len(plugin_list) > 0:
609
- logger.info(f"Found Move Instrument: {plugin_list[-1]['name']}")
589
+ logger.debug(f"Found Move Instrument:"
590
+ f" {plugin_list[-1]['module'].__name__}/{plugin_list[-1]['name']}")
610
591
  except ModuleNotFoundError:
611
592
  pass
593
+
612
594
  viewer_modules = {}
613
595
  for vtype in viewer_types:
614
596
  try:
@@ -621,28 +603,31 @@ def get_instrument_plugins(): # pragma: no cover
621
603
  for mod in [mod[1] for mod in pkgutil.iter_modules([str(viewer_modules[vtype].path.parent)])]
622
604
  if f'daq_{vtype}viewer' in mod])
623
605
  if len(plugin_list) > 0:
624
- logger.info(f"Found Viewer Instrument: {plugin_list[-1]['name']}")
606
+ logger.debug(f"Found Viewer Instrument: "
607
+ f"{plugin_list[-1]['module'].__name__}/{plugin_list[-1]['name']}")
625
608
  except ModuleNotFoundError:
626
609
  pass
627
610
 
628
- # check if modules are importable
629
- for mod in plugin_list:
630
- try:
631
- plugin_type = mod['type']
632
- if plugin_type == 'daq_move':
633
- submodule = mod['module']
634
- importlib.import_module(f'{submodule.__package__}.daq_move_{mod["name"]}')
635
- else:
636
- submodule = mod['module']
637
- importlib.import_module(f'{submodule.__package__}.daq_{plugin_type[4:6]}viewer_{mod["name"]}')
638
- plugins_import.append(mod)
639
- except Exception as e: # pragma: no cover
640
- """If an error is generated at the import, then exclude this plugin"""
641
- logger.debug(f'Impossible to import Instrument plugin {mod["name"]}'
642
- f' from module: {mod["parent_module"].__package__}')
643
611
  except Exception as e: # pragma: no cover
644
612
  logger.debug(str(e))
645
-
613
+
614
+ for mod in plugin_list:
615
+ try:
616
+ plugin_type = mod['type']
617
+ if plugin_type == 'daq_move':
618
+ submodule = mod['module']
619
+ importlib.import_module(f'{submodule.__package__}.daq_move_{mod["name"]}')
620
+ else:
621
+ submodule = mod['module']
622
+ importlib.import_module(f'{submodule.__package__}.daq_{plugin_type[4:6]}viewer_{mod["name"]}')
623
+ plugins_import.append(mod)
624
+ logger.info(f"{mod['module'].__name__}/{mod['name']} available")
625
+ except Exception as e: # pragma: no cover
626
+ """If an error is generated at the import, then exclude this plugin"""
627
+ logger.debug(f'Impossible to import Instrument plugin {mod["name"]}'
628
+ f' from module: {mod["parent_module"].__package__}')
629
+
630
+
646
631
  # add utility plugin for PID
647
632
  try:
648
633
  pidmodule = importlib.import_module('pymodaq.extensions.pid')
@@ -658,6 +643,7 @@ def get_instrument_plugins(): # pragma: no cover
658
643
 
659
644
  return plugins_import
660
645
 
646
+
661
647
  def get_plugins(plugin_type='daq_0Dviewer'): # pragma: no cover
662
648
  """
663
649
  Get plugins names as a list
@@ -766,91 +752,6 @@ def get_new_file_name(base_path=Path(config('data_saving', 'h5file', 'save_path'
766
752
  return file, curr_dir
767
753
 
768
754
 
769
- # ##############
770
- # Math utilities
771
- # math utility functions, should now be imported from the math_utils module
772
- import pymodaq.utils.math_utils as mutils
773
-
774
- def my_moment(x, y):
775
- deprecation_msg(f'my_moment function should now be imported from the {mutils.__name__} module', stacklevel=3)
776
- return mutils.my_moment(x, y)
777
-
778
- def normalize(x):
779
- deprecation_msg(f'normalize function should now be imported from the {mutils.__name__} module', stacklevel=3)
780
- return mutils.normalize(x)
781
-
782
-
783
- def odd_even(x):
784
- deprecation_msg(f'odd_even function should now be imported from the {mutils.__name__} module', stacklevel=3)
785
- return mutils.odd_even(x)
786
-
787
-
788
- def greater2n(x):
789
- deprecation_msg(f'greater2n function should now be imported from the {mutils.__name__} module', stacklevel=3)
790
- return mutils.greater2n(x)
791
-
792
-
793
- def linspace_step(start, stop, step):
794
- deprecation_msg(f'linspace_step function should now be imported from the {mutils.__name__} module', stacklevel=3)
795
- return mutils.linspace_step(start, stop, step)
796
-
797
-
798
- def linspace_step_N(start, step, Npts):
799
- deprecation_msg(f'linspace_step_N function should now be imported from the {mutils.__name__} module', stacklevel=3)
800
- return mutils.linspace_step_N(start, step, Npts)
801
-
802
-
803
- def find_index(x, threshold):
804
- deprecation_msg(f'find_index function should now be imported from the {mutils.__name__} module', stacklevel=3)
805
- return mutils.find_index(x, threshold)
806
-
807
-
808
- def find_common_index(x, y, x0, y0):
809
- deprecation_msg(f'find_common_index function should now be imported from the {mutils.__name__} module', stacklevel=3)
810
- return mutils.find_common_index(x, y, x0, y0)
811
-
812
-
813
- def gauss1D(x, x0, dx, n=1):
814
- deprecation_msg(f'gauss1D function should now be imported from the {mutils.__name__} module', stacklevel=3)
815
- return mutils.gauss1D(x, x0, dx, n=n)
816
-
817
-
818
- def gauss2D(x, x0, dx, y, y0, dy, n=1, angle=0):
819
- deprecation_msg(f'gauss2D function should now be imported from the {mutils.__name__} module', stacklevel=3)
820
- return mutils.gauss2D(x, x0, dx, y, y0, dy, n, angle)
821
-
822
- def ftAxis(Npts, omega_max):
823
- deprecation_msg(f'ftAxis function should now be imported from the {mutils.__name__} module', stacklevel=3)
824
- return mutils.ftAxis(Npts, omega_max)
825
-
826
-
827
- def ftAxis_time(Npts, time_max):
828
- deprecation_msg(f'ftAxis_time function should now be imported from the {mutils.__name__} module', stacklevel=3)
829
- return mutils.ftAxis_time(Npts, time_max)
830
-
831
-
832
- def ft(x, dim=-1):
833
- deprecation_msg(f'ft function should now be imported from the {mutils.__name__} module', stacklevel=3)
834
- return mutils.ft(x, dim)
835
-
836
-
837
- def ift(x, dim=0):
838
- deprecation_msg(f'ift function should now be imported from the {mutils.__name__} module', stacklevel=3)
839
- return mutils.ift(x, dim)
840
-
841
-
842
- def ft2(x, dim=(-2, -1)):
843
- deprecation_msg(f'ft2 function should now be imported from the {mutils.__name__} module', stacklevel=3)
844
- return mutils.ft2(x, dim)
845
-
846
-
847
- def ift2(x, dim=(-2, -1)):
848
- deprecation_msg(f'ift2 function should now be imported from the {mutils.__name__} module', stacklevel=3)
849
- return mutils.ift2(x, dim)
850
-
851
-
852
-
853
-
854
755
  if __name__ == '__main__':
855
756
  #paths = recursive_find_expr_in_files('C:\\Users\\weber\\Labo\\Programmes Python\\PyMoDAQ_Git', 'visa')
856
757
  # for p in paths: