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.
- pymodaq/__init__.py +13 -6
- pymodaq/control_modules/__init__.py +0 -7
- pymodaq/control_modules/daq_move.py +965 -2
- pymodaq/control_modules/daq_move_ui.py +319 -0
- pymodaq/control_modules/daq_viewer.py +1573 -3
- pymodaq/control_modules/daq_viewer_ui.py +393 -0
- pymodaq/control_modules/mocks.py +51 -0
- pymodaq/control_modules/move_utility_classes.py +709 -8
- pymodaq/control_modules/utils.py +256 -0
- pymodaq/control_modules/viewer_utility_classes.py +663 -6
- pymodaq/daq_utils.py +89 -0
- pymodaq/dashboard.py +91 -72
- pymodaq/examples/custom_app.py +12 -11
- pymodaq/examples/custom_viewer.py +10 -10
- pymodaq/examples/function_plotter.py +16 -13
- pymodaq/examples/nonlinearscanner.py +8 -6
- pymodaq/examples/parameter_ex.py +7 -7
- pymodaq/examples/preset_MockCamera.xml +1 -0
- pymodaq/extensions/__init__.py +16 -0
- pymodaq/extensions/console.py +76 -0
- pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
- pymodaq/extensions/daq_scan.py +1339 -0
- pymodaq/extensions/daq_scan_ui.py +240 -0
- pymodaq/extensions/h5browser.py +23 -0
- pymodaq/{pid → extensions/pid}/__init__.py +4 -2
- pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
- pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
- pymodaq/{pid → extensions/pid}/utils.py +52 -6
- pymodaq/extensions/utils.py +40 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
- pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
- pymodaq/post_treatment/load_and_plot.py +219 -0
- pymodaq/post_treatment/process_to_scalar.py +263 -0
- pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
- pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
- pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
- pymodaq/resources/VERSION +1 -1
- pymodaq/resources/config_template.toml +32 -13
- pymodaq/resources/preset_default.xml +1 -1
- pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
- pymodaq/utils/__init__.py +0 -29
- pymodaq/utils/abstract/__init__.py +48 -0
- pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
- pymodaq/utils/array_manipulation.py +379 -8
- pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
- pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
- pymodaq/utils/config.py +448 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +828 -8
- pymodaq/utils/data.py +1873 -7
- pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
- pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
- pymodaq/{daq_utils → utils}/enums.py +12 -7
- pymodaq/utils/exceptions.py +37 -0
- pymodaq/utils/factory.py +82 -0
- pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
- pymodaq/utils/gui_utils/custom_app.py +129 -0
- pymodaq/utils/gui_utils/file_io.py +66 -0
- pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
- pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
- pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
- pymodaq/utils/gui_utils/widgets/label.py +24 -0
- pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
- pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
- pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
- pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
- pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
- pymodaq/utils/h5modules/__init__.py +1 -0
- pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
- pymodaq/utils/h5modules/browsing.py +683 -0
- pymodaq/utils/h5modules/data_saving.py +839 -0
- pymodaq/utils/h5modules/h5logging.py +110 -0
- pymodaq/utils/h5modules/module_saving.py +350 -0
- pymodaq/utils/h5modules/saving.py +914 -0
- pymodaq/utils/h5modules/utils.py +85 -0
- pymodaq/utils/logger.py +64 -6
- pymodaq/utils/managers/action_manager.py +460 -0
- pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
- pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
- pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
- pymodaq/utils/managers/parameter_manager.py +110 -0
- pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
- pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
- pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
- pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
- pymodaq/utils/math_utils.py +546 -10
- pymodaq/{daq_utils → utils}/messenger.py +5 -1
- pymodaq/utils/parameter/__init__.py +2 -15
- pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
- pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
- pymodaq/utils/parameter/utils.py +206 -11
- pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
- pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
- pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
- pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
- pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
- pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
- pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
- pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
- pymodaq/utils/plotting/navigator.py +355 -0
- pymodaq/utils/plotting/scan_selector.py +480 -0
- pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
- pymodaq/utils/plotting/utils/filter.py +538 -0
- pymodaq/utils/plotting/utils/lineout.py +224 -0
- pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
- pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
- pymodaq/utils/plotting/widgets.py +76 -0
- pymodaq/utils/scanner/__init__.py +10 -0
- pymodaq/utils/scanner/scan_factory.py +204 -0
- pymodaq/utils/scanner/scanner.py +271 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
- pymodaq/utils/scanner/scanners/sequential.py +192 -0
- pymodaq/utils/scanner/scanners/tabular.py +294 -0
- pymodaq/utils/scanner/utils.py +83 -0
- pymodaq/utils/slicing.py +47 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +51 -0
- pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
- pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
- pymodaq/utils/units.py +216 -0
- pymodaq-4.0.1.dist-info/METADATA +159 -0
- {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
- {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
- pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
- pymodaq/daq_move/daq_move_gui.py +0 -279
- pymodaq/daq_move/daq_move_gui.ui +0 -534
- pymodaq/daq_move/daq_move_main.py +0 -1042
- pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
- pymodaq/daq_move/utility_classes.py +0 -671
- pymodaq/daq_scan.py +0 -2160
- pymodaq/daq_utils/array_manipulation.py +0 -386
- pymodaq/daq_utils/config.py +0 -273
- pymodaq/daq_utils/conftests.py +0 -7
- pymodaq/daq_utils/custom_parameter_tree.py +0 -9
- pymodaq/daq_utils/daq_enums.py +0 -133
- pymodaq/daq_utils/daq_utils.py +0 -1402
- pymodaq/daq_utils/exceptions.py +0 -71
- pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
- pymodaq/daq_utils/gui_utils/file_io.py +0 -75
- pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
- pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
- pymodaq/daq_utils/h5exporters.py +0 -242
- pymodaq/daq_utils/h5modules.py +0 -1559
- pymodaq/daq_utils/h5utils.py +0 -241
- pymodaq/daq_utils/managers/action_manager.py +0 -236
- pymodaq/daq_utils/managers/parameter_manager.py +0 -57
- pymodaq/daq_utils/math_utils.py +0 -705
- pymodaq/daq_utils/parameter/__init__.py +0 -1
- pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
- pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
- pymodaq/daq_utils/parameter/utils.py +0 -136
- pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
- pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
- pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
- pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
- pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
- pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
- pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
- pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
- pymodaq/daq_utils/plotting/items/__init__.py +0 -0
- pymodaq/daq_utils/plotting/navigator.py +0 -500
- pymodaq/daq_utils/plotting/scan_selector.py +0 -289
- pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
- pymodaq/daq_utils/plotting/utils/filter.py +0 -236
- pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
- pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
- pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
- pymodaq/daq_utils/scanner.py +0 -1289
- pymodaq/daq_utils/tree_layout/__init__.py +0 -0
- pymodaq/daq_viewer/__init__.py +0 -0
- pymodaq/daq_viewer/daq_gui_settings.py +0 -237
- pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
- pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
- pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
- pymodaq/daq_viewer/utility_classes.py +0 -673
- pymodaq/examples/logger_image/__init__.py +0 -0
- pymodaq/examples/logger_image/logger_displayer.py +0 -121
- pymodaq/examples/logger_image/setup.svg +0 -3119
- pymodaq/examples/logger_image/setup_svg.py +0 -114
- pymodaq/h5browser.py +0 -39
- pymodaq/utils/scanner.py +0 -15
- pymodaq-3.6.12.dist-info/METADATA +0 -39
- pymodaq-3.6.12.dist-info/entry_points.txt +0 -8
- pymodaq-3.6.12.dist-info/top_level.txt +0 -1
- /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
- /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
- /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
- /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
- /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
- /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
- /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
- /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
- /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
- /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
- /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
- {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
pymodaq/utils/config.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
from abc import abstractproperty
|
|
2
|
+
|
|
3
|
+
from os import environ
|
|
4
|
+
import sys
|
|
5
|
+
import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
import toml
|
|
10
|
+
from qtpy.QtCore import QObject
|
|
11
|
+
from pyqtgraph.parametertree import Parameter, ParameterTree
|
|
12
|
+
from qtpy import QtWidgets, QtCore
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
USER = environ['USERNAME'] if sys.platform == 'win32' else environ['USER']
|
|
16
|
+
except:
|
|
17
|
+
USER = 'unknown_user'
|
|
18
|
+
|
|
19
|
+
CONFIG_BASE_PATH = Path(environ['PROGRAMDATA']) if sys.platform == 'win32' else \
|
|
20
|
+
Path('Library/Application Support') if sys.platform == 'darwin' else Path('/etc')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def replace_file_extension(filename: str, ext: str):
|
|
24
|
+
"""Replace the extension of a file by the specified one, without the dot"""
|
|
25
|
+
file_name = Path(filename).stem # remove eventual extensions
|
|
26
|
+
if ext[0] == '.':
|
|
27
|
+
ext = ext[1:]
|
|
28
|
+
file_name += '.' + ext
|
|
29
|
+
return file_name
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def getitem_recursive(dic, *args, ndepth=0):
|
|
33
|
+
"""Will scan recursively a dictionary in order to get the item defined by the iterable args
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
dic: dict
|
|
38
|
+
the dictionary to scan
|
|
39
|
+
args: an iterable of str
|
|
40
|
+
keys of the dict
|
|
41
|
+
ndepth: int
|
|
42
|
+
by default (0) get the last element defined by args. 1 would mean it get the parent dict, 2 the parent of the
|
|
43
|
+
parent...
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
object or dict
|
|
47
|
+
"""
|
|
48
|
+
args = list(args)
|
|
49
|
+
while len(args) > ndepth:
|
|
50
|
+
dic = dic[args.pop(0)]
|
|
51
|
+
return dic
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_set_path(a_base_path: Path, dir_name: str) -> Path:
|
|
55
|
+
path_to_get = a_base_path.joinpath(dir_name)
|
|
56
|
+
if not path_to_get.is_dir():
|
|
57
|
+
try:
|
|
58
|
+
path_to_get.mkdir()
|
|
59
|
+
except PermissionError as e:
|
|
60
|
+
print(f"Cannot create local config folder at this location: {path_to_get}"
|
|
61
|
+
f", try using admin rights. "
|
|
62
|
+
f"Changing the not permitted path to a user one: {Path.home().joinpath(dir_name)}.")
|
|
63
|
+
path_to_get = Path.home().joinpath(dir_name)
|
|
64
|
+
if not path_to_get.is_dir():
|
|
65
|
+
path_to_get.mkdir()
|
|
66
|
+
return path_to_get
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_set_local_dir(user=False) -> Path:
|
|
70
|
+
"""Defines, creates and returns a local folder where configuration files will be saved
|
|
71
|
+
|
|
72
|
+
Depending on the os the configurations files will be stored in CONFIG_BASE_PATH, then
|
|
73
|
+
each user will have another one created that could override the default and system-wide base folder
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
user: bool
|
|
78
|
+
if False get the system-wide folder, otherwise the user folder
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
Path: the local path
|
|
83
|
+
"""
|
|
84
|
+
if user:
|
|
85
|
+
local_path = get_set_path(Path.home(), '.pymodaq')
|
|
86
|
+
else:
|
|
87
|
+
local_path = get_set_path(CONFIG_BASE_PATH, '.pymodaq')
|
|
88
|
+
return local_path
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_config_file(config_file_name: str, user=False):
|
|
92
|
+
return get_set_local_dir(user).joinpath(replace_file_extension(config_file_name, 'toml'))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_set_config_dir(config_name='config', user=False):
|
|
96
|
+
"""Creates a folder in the local config directory to store specific configuration files
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
config_name: (str) name of the configuration folder
|
|
101
|
+
user: bool
|
|
102
|
+
if False get the system-wide folder, otherwise the user folder
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
Path
|
|
107
|
+
|
|
108
|
+
See Also
|
|
109
|
+
--------
|
|
110
|
+
get_set_local_dir
|
|
111
|
+
"""
|
|
112
|
+
return get_set_path(get_set_local_dir(user=user), config_name)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_set_log_path():
|
|
116
|
+
""" creates and return the config folder path for log files
|
|
117
|
+
"""
|
|
118
|
+
return get_set_config_dir('log')
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_set_preset_path():
|
|
122
|
+
""" creates and return the config folder path for managers files
|
|
123
|
+
"""
|
|
124
|
+
return get_set_config_dir('preset_configs')
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_set_batch_path():
|
|
128
|
+
""" creates and return the config folder path for managers files
|
|
129
|
+
"""
|
|
130
|
+
return get_set_config_dir('batch_configs')
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_set_pid_path():
|
|
134
|
+
""" creates and return the config folder path for PID files
|
|
135
|
+
"""
|
|
136
|
+
return get_set_config_dir('pid_configs')
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_set_layout_path():
|
|
140
|
+
""" creates and return the config folder path for layout files
|
|
141
|
+
"""
|
|
142
|
+
return get_set_config_dir('layout_configs')
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_set_remote_path():
|
|
146
|
+
""" creates and return the config folder path for remote (shortcuts or joystick) files
|
|
147
|
+
"""
|
|
148
|
+
return get_set_config_dir('remote_configs')
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_set_overshoot_path():
|
|
152
|
+
""" creates and return the config folder path for overshoot files
|
|
153
|
+
"""
|
|
154
|
+
return get_set_config_dir('overshoot_configs')
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_set_roi_path():
|
|
158
|
+
""" creates and return the config folder path for managers files
|
|
159
|
+
"""
|
|
160
|
+
return get_set_config_dir('roi_configs')
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def create_toml_from_dict(mydict: dict, dest_path: Path):
|
|
164
|
+
"""Create a Toml file at a given path from a dictionnary"""
|
|
165
|
+
dest_path.write_text(toml.dumps(mydict))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def check_config(config_base: dict, config_local: dict):
|
|
169
|
+
"""Compare two configuration dictionaries. Adding missing keys
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
config_base: dict
|
|
174
|
+
The base dictionaries with possible new keys
|
|
175
|
+
config_local: dict
|
|
176
|
+
a dict from a local config file potentially missing keys
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
bool: True if keys where missing else False
|
|
181
|
+
"""
|
|
182
|
+
status = False
|
|
183
|
+
for key in config_base:
|
|
184
|
+
if key in config_local:
|
|
185
|
+
if isinstance(config_base[key], dict):
|
|
186
|
+
status = status or check_config(config_base[key], config_local[key])
|
|
187
|
+
else:
|
|
188
|
+
config_local[key] = config_base[key]
|
|
189
|
+
status = True
|
|
190
|
+
return status
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def copy_template_config(config_file_name: str = 'config', source_path: Union[Path, str] = None,
|
|
194
|
+
dest_path: Union[Path, str] = None):
|
|
195
|
+
"""Get a toml file path and copy it
|
|
196
|
+
|
|
197
|
+
the destination is made of a given folder path (or the system-wide local path by default) and the config_file_name
|
|
198
|
+
appended by the suffix '.toml'
|
|
199
|
+
|
|
200
|
+
The source file (or pymodaq config template path by default) is read and dumped in this destination file
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
config_file_name: str
|
|
205
|
+
the name of the destination config file
|
|
206
|
+
source_path: Path or str
|
|
207
|
+
the path of the toml source to be copied
|
|
208
|
+
dest_path: Path or str
|
|
209
|
+
the destination path of the copied config
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
Path: the path of the copied file
|
|
214
|
+
"""
|
|
215
|
+
if dest_path is None:
|
|
216
|
+
dest_path = get_set_local_dir()
|
|
217
|
+
|
|
218
|
+
file_name = Path(config_file_name).stem # remove eventual extensions
|
|
219
|
+
file_name += '.toml'
|
|
220
|
+
dest_path_with_filename = dest_path.joinpath(file_name)
|
|
221
|
+
|
|
222
|
+
if source_path is None:
|
|
223
|
+
config_template_dict = toml.load(Path(__file__).parent.parent.joinpath('resources/config_template.toml'))
|
|
224
|
+
else:
|
|
225
|
+
config_template_dict = toml.load(Path(source_path))
|
|
226
|
+
|
|
227
|
+
create_toml_from_dict(config_template_dict, dest_path_with_filename)
|
|
228
|
+
return dest_path_with_filename
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def load_system_config_and_update_from_user(config_file_name: str):
|
|
232
|
+
"""load from a system-wide config file, update it from the user config file
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
config_file_name: str
|
|
237
|
+
The config file to be loaded
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
dict: contains the toml system-wide file update with the user file
|
|
241
|
+
"""
|
|
242
|
+
config_dict = dict([])
|
|
243
|
+
toml_base_path = get_config_file(config_file_name, user=False)
|
|
244
|
+
if toml_base_path.is_file():
|
|
245
|
+
config_dict = toml.load(toml_base_path)
|
|
246
|
+
toml_user_path = get_config_file(config_file_name, user=True)
|
|
247
|
+
if toml_user_path.is_file():
|
|
248
|
+
config_dict.update(toml.load(toml_user_path))
|
|
249
|
+
return config_dict
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ConfigError(Exception):
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class BaseConfig:
|
|
257
|
+
"""Base class to manage configuration files
|
|
258
|
+
|
|
259
|
+
Should be subclassed with proper class attributes for each configuration file you need with pymodaq
|
|
260
|
+
|
|
261
|
+
Attributes
|
|
262
|
+
----------
|
|
263
|
+
config_name: str
|
|
264
|
+
The name with which the configuration will be saved
|
|
265
|
+
config_template_path: Path
|
|
266
|
+
The Path of the template from which the config is constructed
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
config_template_path: Path = abstractproperty()
|
|
270
|
+
config_name: str = abstractproperty()
|
|
271
|
+
|
|
272
|
+
def __init__(self):
|
|
273
|
+
self._config = self.load_config(self.config_name, self.config_template_path)
|
|
274
|
+
|
|
275
|
+
def __call__(self, *args):
|
|
276
|
+
try:
|
|
277
|
+
ret = getitem_recursive(self._config, *args)
|
|
278
|
+
except KeyError as e:
|
|
279
|
+
raise ConfigError(f'the path {args} does not exist in your configuration toml file, check '
|
|
280
|
+
f'your pymodaq_local folder')
|
|
281
|
+
return ret
|
|
282
|
+
|
|
283
|
+
def to_dict(self):
|
|
284
|
+
return self._config
|
|
285
|
+
|
|
286
|
+
def __getitem__(self, item):
|
|
287
|
+
"""for backcompatibility when it was a dictionnary"""
|
|
288
|
+
if isinstance(item, tuple):
|
|
289
|
+
return getitem_recursive(self._config, *item)
|
|
290
|
+
else:
|
|
291
|
+
return self._config[item]
|
|
292
|
+
|
|
293
|
+
def __setitem__(self, key, value):
|
|
294
|
+
if isinstance(key, tuple):
|
|
295
|
+
dic = getitem_recursive(self._config, *key, ndepth=1)
|
|
296
|
+
dic[key[-1]] = value
|
|
297
|
+
else:
|
|
298
|
+
self._config[key] = value
|
|
299
|
+
|
|
300
|
+
def load_config(self, config_file_name, template_path: Path):
|
|
301
|
+
"""Load a configuration file from both system-wide and user file
|
|
302
|
+
|
|
303
|
+
check also if missing entries in the configuration file compared to the template"""
|
|
304
|
+
toml_base_path = get_config_file(config_file_name, user=False)
|
|
305
|
+
toml_user_path = get_config_file(config_file_name, user=True)
|
|
306
|
+
if toml_base_path.is_file():
|
|
307
|
+
config = toml.load(toml_base_path)
|
|
308
|
+
config_template = toml.load(template_path)
|
|
309
|
+
if check_config(config_template, config): # check if all fields from template are there
|
|
310
|
+
# (could have been modified by some commits)
|
|
311
|
+
create_toml_from_dict(toml.dumps(config), toml_base_path)
|
|
312
|
+
|
|
313
|
+
else:
|
|
314
|
+
copy_template_config(config_file_name, template_path, toml_base_path.parent)
|
|
315
|
+
|
|
316
|
+
if not toml_user_path.is_file():
|
|
317
|
+
# create the author from environment variable
|
|
318
|
+
config_dict = self.dict_to_add_to_user()
|
|
319
|
+
if config_dict is not None:
|
|
320
|
+
create_toml_from_dict(config_dict, toml_user_path)
|
|
321
|
+
|
|
322
|
+
config_dict = load_system_config_and_update_from_user(config_file_name)
|
|
323
|
+
return config_dict
|
|
324
|
+
|
|
325
|
+
def dict_to_add_to_user(self):
|
|
326
|
+
"""To subclass"""
|
|
327
|
+
return None
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def config_path(self):
|
|
331
|
+
"""Get the user config path"""
|
|
332
|
+
return get_config_file(self.config_name, user=True)
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def system_config_path(self):
|
|
336
|
+
"""Get the system_wide config path"""
|
|
337
|
+
return get_config_file(self.config_name, user=False)
|
|
338
|
+
|
|
339
|
+
def save(self):
|
|
340
|
+
"""Save the current Config object into the user toml file"""
|
|
341
|
+
self.config_path.write_text(toml.dumps(self.to_dict()))
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class Config(BaseConfig):
|
|
345
|
+
"""Main class to deal with configuration values for PyMoDAQ"""
|
|
346
|
+
config_template_path = Path(__file__).parent.parent.joinpath('resources/config_template.toml')
|
|
347
|
+
config_name = 'config_pymodaq'
|
|
348
|
+
|
|
349
|
+
def dict_to_add_to_user(self):
|
|
350
|
+
"""To subclass"""
|
|
351
|
+
return dict(user=dict(name=USER))
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class TreeFromToml(QObject):
|
|
355
|
+
def __init__(self, config: Config = None):
|
|
356
|
+
super().__init__()
|
|
357
|
+
if config is None:
|
|
358
|
+
config = Config()
|
|
359
|
+
self._config = config
|
|
360
|
+
params = [{'title': 'Config path', 'name': 'config_path', 'type': 'str', 'value': str(self._config.config_path),
|
|
361
|
+
'readonly': True}]
|
|
362
|
+
params.extend(self.dict_to_param(config.to_dict()))
|
|
363
|
+
|
|
364
|
+
self.settings = Parameter.create(title='settings', name='settings', type='group', children=params)
|
|
365
|
+
self.settings_tree = ParameterTree()
|
|
366
|
+
self.settings_tree.setParameters(self.settings, showTop=False)
|
|
367
|
+
|
|
368
|
+
def show_dialog(self):
|
|
369
|
+
|
|
370
|
+
self.dialog = QtWidgets.QDialog()
|
|
371
|
+
self.dialog.setWindowTitle('Please enter new configuration values!')
|
|
372
|
+
self.dialog.setLayout(QtWidgets.QVBoxLayout())
|
|
373
|
+
buttonBox = QtWidgets.QDialogButtonBox(parent=self.dialog)
|
|
374
|
+
|
|
375
|
+
buttonBox.addButton('Save', buttonBox.AcceptRole)
|
|
376
|
+
buttonBox.accepted.connect(self.dialog.accept)
|
|
377
|
+
buttonBox.addButton('Cancel', buttonBox.RejectRole)
|
|
378
|
+
buttonBox.rejected.connect(self.dialog.reject)
|
|
379
|
+
|
|
380
|
+
self.dialog.layout().addWidget(self.settings_tree)
|
|
381
|
+
self.dialog.layout().addWidget(buttonBox)
|
|
382
|
+
self.dialog.setWindowTitle('Configuration entries')
|
|
383
|
+
res = self.dialog.exec()
|
|
384
|
+
|
|
385
|
+
if res == self.dialog.Accepted:
|
|
386
|
+
with open(self._config.config_path, 'w') as f:
|
|
387
|
+
config_dict = self.param_to_dict(self.settings)
|
|
388
|
+
config_dict.pop('config_path')
|
|
389
|
+
create_toml_from_dict(config_dict, self._config.config_path)
|
|
390
|
+
|
|
391
|
+
@classmethod
|
|
392
|
+
def param_to_dict(cls, param: Parameter) -> dict:
|
|
393
|
+
config = dict()
|
|
394
|
+
for child in param.children():
|
|
395
|
+
if 'group' in child.opts['type']:
|
|
396
|
+
config[child.name()] = cls.param_to_dict(child)
|
|
397
|
+
else:
|
|
398
|
+
if child.opts['type'] == 'datetime':
|
|
399
|
+
config[child.name()] = datetime.fromtimestamp(
|
|
400
|
+
child.value().toSecsSinceEpoch()) # convert QDateTime to python datetime
|
|
401
|
+
elif child.opts['type'] == 'date':
|
|
402
|
+
qdt = QtCore.QDateTime()
|
|
403
|
+
qdt.setDate(child.value())
|
|
404
|
+
pdt = datetime.fromtimestamp(qdt.toSecsSinceEpoch())
|
|
405
|
+
config[child.name()] = pdt.date()
|
|
406
|
+
elif child.opts['type'] == 'list':
|
|
407
|
+
config[child.name()] = child.opts['limits']
|
|
408
|
+
else:
|
|
409
|
+
config[child.name()] = child.value()
|
|
410
|
+
return config
|
|
411
|
+
|
|
412
|
+
@classmethod
|
|
413
|
+
def dict_to_param(cls, config: dict) -> Parameter:
|
|
414
|
+
params = []
|
|
415
|
+
for key in config:
|
|
416
|
+
if isinstance(config[key], dict):
|
|
417
|
+
params.append({'title': f'{key.capitalize()}:', 'name': key, 'type': 'group',
|
|
418
|
+
'children': cls.dict_to_param(config[key]),
|
|
419
|
+
'expanded': 'user' in key.lower() or 'general' in key.lower()})
|
|
420
|
+
else:
|
|
421
|
+
param = {'title': f'{key.capitalize()}:', 'name': key, 'value': config[key]}
|
|
422
|
+
if isinstance(config[key], float):
|
|
423
|
+
param['type'] = 'float'
|
|
424
|
+
elif isinstance(config[key], bool): # placed before int because a bool is an instance of int
|
|
425
|
+
param['type'] = 'bool'
|
|
426
|
+
elif isinstance(config[key], int):
|
|
427
|
+
param['type'] = 'int'
|
|
428
|
+
elif isinstance(config[key], datetime.datetime):
|
|
429
|
+
param['type'] = 'datetime'
|
|
430
|
+
elif isinstance(config[key], datetime.date):
|
|
431
|
+
param['type'] = 'date'
|
|
432
|
+
elif isinstance(config[key], str):
|
|
433
|
+
param['type'] = 'str'
|
|
434
|
+
elif isinstance(config[key], list):
|
|
435
|
+
param['type'] = 'list'
|
|
436
|
+
param['limits'] = config[key]
|
|
437
|
+
param['value'] = config[key][0]
|
|
438
|
+
param['show_pb'] = True
|
|
439
|
+
params.append(param)
|
|
440
|
+
return params
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
if __name__ == '__main__':
|
|
444
|
+
|
|
445
|
+
config = Config()
|
|
446
|
+
config('style', 'darkstyle')
|
|
447
|
+
assert config('style', 'darkstyle') == config['style']['darkstyle']
|
|
448
|
+
|