labmate 0.8.3__tar.gz → 0.9.0__tar.gz
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.
- {labmate-0.8.3/labmate.egg-info → labmate-0.9.0}/PKG-INFO +1 -1
- {labmate-0.8.3 → labmate-0.9.0}/labmate/__config__.py +1 -1
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/acquisition_data.py +47 -8
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/acquisition_loop.py +1 -3
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/acquisition_manager.py +3 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/analysis_data.py +28 -2
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition_notebook/acquisition_analysis_manager.py +69 -3
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition_notebook/display_widget.py +26 -31
- {labmate-0.8.3 → labmate-0.9.0}/labmate/display/__init__.py +10 -3
- {labmate-0.8.3 → labmate-0.9.0}/labmate/display/buttons.py +4 -0
- labmate-0.9.0/labmate/display/html_output.py +63 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/display/main.py +19 -3
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/__init__.py +2 -1
- labmate-0.9.0/labmate/utils/file_read.py +96 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/title_parsing.py +23 -6
- {labmate-0.8.3 → labmate-0.9.0/labmate.egg-info}/PKG-INFO +1 -1
- labmate-0.8.3/labmate/display/html_output.py +0 -15
- labmate-0.8.3/labmate/utils/file_read.py +0 -49
- {labmate-0.8.3 → labmate-0.9.0}/LICENCE +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/MANIFEST.in +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/README.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/about.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/acquisition_notebook.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/code/acquisition_loop.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/code/acquisition_manager.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/code/acquisition_notebook_manager.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/code/analysis_data.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/code/linting.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/code/parsing_config.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/acquisition_and_analysis_notebook.ipynb +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/aqm_simple_example.ipynb +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/files/cfg.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/files/dummy_config1.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/files/dummy_config2.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/files/dummy_config3.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/files/init_analyse.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/more/acquisition_and_analysis_notebook_with_magic.ipynb +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/more/h5nparray.ipynb +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/examples/more/loop_example.ipynb +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/h5nparray.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/index.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/0.4.0.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/0.5.0.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/0.6.0.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/0.6.1.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/0.7.0.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/0.8.0.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/releases/index.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/starting_guide/advanced_examples.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/starting_guide/fine_tune.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/starting_guide/first_steps.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/docs/starting_guide/install.md +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/__init__.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/__init__.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/analysis_loop.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/config_file.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/custom_lint.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition/logger_setup.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition_notebook/__init__.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/attrdict/__init__.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/attrdict/attrdict_class.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/display/links.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/display/platform_utils/__init__.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/display/platform_utils/windows_utils.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/parsing/__init__.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/parsing/brackets_score.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/parsing/parsed_value.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/parsing/saving.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/async_utils.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/autoreload.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/errors.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/lint.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate/utils/random_utils.py +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate.egg-info/SOURCES.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate.egg-info/dependency_links.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate.egg-info/requires.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/labmate.egg-info/top_level.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/requirements-docs.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/requirements.txt +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/setup.cfg +0 -0
- {labmate-0.8.3 → labmate-0.9.0}/setup.py +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Module that contains NotebookAcquisitionData class."""
|
|
2
|
+
|
|
2
3
|
from typing import Dict, List, Optional, Union
|
|
3
4
|
|
|
4
5
|
from dh5 import DH5
|
|
5
|
-
from ..utils.file_read import read_files
|
|
6
6
|
|
|
7
|
+
from ..utils.file_read import read_files
|
|
7
8
|
from .logger_setup import logger
|
|
8
9
|
|
|
9
10
|
|
|
@@ -14,6 +15,9 @@ class NotebookAcquisitionData(DH5):
|
|
|
14
15
|
`cell` is a str. It saves using `save_cell` function that will save it to `..._CELL.py` file
|
|
15
16
|
"""
|
|
16
17
|
|
|
18
|
+
_current_step: int
|
|
19
|
+
_cells: Dict[int, Optional[str]]
|
|
20
|
+
|
|
17
21
|
def __init__(
|
|
18
22
|
self,
|
|
19
23
|
filepath: str,
|
|
@@ -58,10 +62,11 @@ class NotebookAcquisitionData(DH5):
|
|
|
58
62
|
self._config = configs
|
|
59
63
|
self.save_configs()
|
|
60
64
|
|
|
61
|
-
self.
|
|
62
|
-
self.save_cell(cell=cell)
|
|
65
|
+
self._cells = {1: cell}
|
|
66
|
+
self.save_cell(cell=cell, suffix="1")
|
|
63
67
|
|
|
64
68
|
self.experiment_name = experiment_name
|
|
69
|
+
self.current_step = 1
|
|
65
70
|
|
|
66
71
|
self["useful"] = False
|
|
67
72
|
|
|
@@ -95,7 +100,12 @@ class NotebookAcquisitionData(DH5):
|
|
|
95
100
|
with open(filepath + "_" + name, "w", encoding="utf-8") as file:
|
|
96
101
|
file.write(value)
|
|
97
102
|
|
|
98
|
-
def save_cell(
|
|
103
|
+
def save_cell(
|
|
104
|
+
self,
|
|
105
|
+
cell: Optional[str] = None,
|
|
106
|
+
filepath: Optional[str] = None,
|
|
107
|
+
suffix: Optional[str] = None,
|
|
108
|
+
):
|
|
99
109
|
"""Save the cell code to the h5 file and possibly to a file.
|
|
100
110
|
|
|
101
111
|
If `save_files` during init was set to True, then it will create a '.py' file near
|
|
@@ -108,14 +118,15 @@ class NotebookAcquisitionData(DH5):
|
|
|
108
118
|
the desired location, i.e. it should end with the file prefix to which the suffix and
|
|
109
119
|
'py' extension will be added. Defaults to save filepath as h5 file.
|
|
110
120
|
"""
|
|
111
|
-
cell = cell or self._cell
|
|
112
121
|
if cell == "none":
|
|
113
122
|
return
|
|
114
123
|
if cell is None or cell == "":
|
|
115
124
|
logger.warning("Acquisition cell is not set. Nothing to save")
|
|
116
125
|
return
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
if suffix is not None:
|
|
127
|
+
self[f"acquisition_cell/{suffix}"] = cell
|
|
128
|
+
else:
|
|
129
|
+
self["acquisition_cell/0"] = cell
|
|
119
130
|
|
|
120
131
|
if not self._save_files:
|
|
121
132
|
return
|
|
@@ -124,13 +135,26 @@ class NotebookAcquisitionData(DH5):
|
|
|
124
135
|
with open(filepath + "_CELL.py", "w", encoding="utf-8") as file:
|
|
125
136
|
file.write(cell)
|
|
126
137
|
|
|
138
|
+
def save_cells(
|
|
139
|
+
self,
|
|
140
|
+
cells: Optional[Dict[int, Optional[str]]] = None,
|
|
141
|
+
filepath: Optional[str] = None,
|
|
142
|
+
):
|
|
143
|
+
"""Save all sells that are provided or pushed into self._cells array."""
|
|
144
|
+
cells = cells or self._cells
|
|
145
|
+
# if len(cells) == 1:
|
|
146
|
+
# self.save_cell(cells.popitem()[1], filepath)
|
|
147
|
+
# return
|
|
148
|
+
for i, cell in cells.items():
|
|
149
|
+
self.save_cell(cell, filepath, suffix=str(i))
|
|
150
|
+
|
|
127
151
|
def save_additional_info(self):
|
|
128
152
|
"""Save all additional information, i.e. cell code, configs. Put useful key to True."""
|
|
129
153
|
self["useful"] = True
|
|
130
154
|
|
|
131
155
|
if not self._save_files:
|
|
132
156
|
return
|
|
133
|
-
self.
|
|
157
|
+
self.save_cells()
|
|
134
158
|
self.save_configs()
|
|
135
159
|
|
|
136
160
|
def save_acquisition(self, **kwds) -> "NotebookAcquisitionData":
|
|
@@ -140,3 +164,18 @@ class NotebookAcquisitionData(DH5):
|
|
|
140
164
|
if self.save_on_edit is False:
|
|
141
165
|
self.save()
|
|
142
166
|
return self
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def current_step(self):
|
|
170
|
+
"""Return the current step of the acquisition."""
|
|
171
|
+
return self._current_step
|
|
172
|
+
|
|
173
|
+
@current_step.setter
|
|
174
|
+
def current_step(self, value: int):
|
|
175
|
+
self._current_step = value
|
|
176
|
+
|
|
177
|
+
def set_cell(self, cell: Optional[str], step: Optional[int] = None):
|
|
178
|
+
"""Set the cell code."""
|
|
179
|
+
if step is None:
|
|
180
|
+
step = self.current_step
|
|
181
|
+
self._cells[step] = cell
|
|
@@ -185,9 +185,7 @@ class AcquisitionLoop(DH5):
|
|
|
185
185
|
)
|
|
186
186
|
self[key][iteration] = value
|
|
187
187
|
else:
|
|
188
|
-
if
|
|
189
|
-
isinstance(value, (np.ndarray,)) and value.dtype == np.complex_
|
|
190
|
-
):
|
|
188
|
+
if np.iscomplexobj(value):
|
|
191
189
|
self[key] = SyncNp(np.zeros(key_shape, dtype=np.complex128))
|
|
192
190
|
else:
|
|
193
191
|
self[key] = SyncNp(np.zeros(key_shape))
|
|
@@ -132,6 +132,9 @@ class AcquisitionManager:
|
|
|
132
132
|
filename = [str(filename)]
|
|
133
133
|
|
|
134
134
|
self.config_files = list(filename)
|
|
135
|
+
self._config_files_names_to_path = {
|
|
136
|
+
os.path.basename(file): file for file in self.config_files
|
|
137
|
+
}
|
|
135
138
|
|
|
136
139
|
for config_file in self.config_files:
|
|
137
140
|
if not os.path.exists(config_file):
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""AnalysisData class."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import os
|
|
4
|
-
from typing import List, Literal, Optional, Protocol, Tuple, TypeVar, Union
|
|
5
|
+
from typing import List, Literal, Optional, Protocol, Tuple, TypedDict, TypeVar, Union
|
|
5
6
|
|
|
6
7
|
from dh5 import DH5
|
|
7
8
|
from dh5.path import Path
|
|
9
|
+
from matplotlib.backends.backend_pdf import PdfPages
|
|
8
10
|
|
|
9
11
|
from .. import utils
|
|
10
12
|
from .analysis_loop import AnalysisLoop
|
|
@@ -21,6 +23,13 @@ class FigureProtocol(Protocol):
|
|
|
21
23
|
"""Save the figure to a file."""
|
|
22
24
|
|
|
23
25
|
|
|
26
|
+
class PdfMetadataDict(TypedDict, total=False):
|
|
27
|
+
"""Metadata for the pdf file."""
|
|
28
|
+
|
|
29
|
+
Subject: Union[str, dict]
|
|
30
|
+
Keywords: Union[str, dict]
|
|
31
|
+
|
|
32
|
+
|
|
24
33
|
class AnalysisData(DH5):
|
|
25
34
|
"""A subclass of DH5 that provides additional functionality for analyzing data.
|
|
26
35
|
|
|
@@ -167,6 +176,7 @@ class AnalysisData(DH5):
|
|
|
167
176
|
name: Optional[Union[str, int]] = None,
|
|
168
177
|
extensions: Optional[str] = None,
|
|
169
178
|
tight_layout: bool = True,
|
|
179
|
+
metadata: Optional[PdfMetadataDict] = None,
|
|
170
180
|
**kwargs,
|
|
171
181
|
) -> _T:
|
|
172
182
|
"""Save the figure with the filename (...)_FIG_name.
|
|
@@ -201,7 +211,23 @@ class AnalysisData(DH5):
|
|
|
201
211
|
)
|
|
202
212
|
if tight_layout and hasattr(fig, "tight_layout"):
|
|
203
213
|
fig.tight_layout() # type: ignore
|
|
204
|
-
|
|
214
|
+
if metadata is None:
|
|
215
|
+
fig.savefig(full_fig_name, **kwargs)
|
|
216
|
+
else:
|
|
217
|
+
if not full_fig_name.endswith(".pdf"):
|
|
218
|
+
raise ValueError("Metadata can be added only to pdf files.")
|
|
219
|
+
pdf_fig = PdfPages(full_fig_name)
|
|
220
|
+
fig.savefig(pdf_fig, format="pdf", **kwargs) # type: ignore
|
|
221
|
+
metadata = metadata or {}
|
|
222
|
+
if not isinstance(metadata.get("Subject", ""), str):
|
|
223
|
+
metadata["Subject"] = json.dumps(metadata.get("Subject"))
|
|
224
|
+
|
|
225
|
+
if not isinstance(metadata.get("Keywords", ""), str):
|
|
226
|
+
metadata["Keywords"] = json.dumps(metadata.get("Keywords"))
|
|
227
|
+
|
|
228
|
+
pdf_metadata = pdf_fig.infodict()
|
|
229
|
+
pdf_metadata.update(metadata)
|
|
230
|
+
pdf_fig.close()
|
|
205
231
|
|
|
206
232
|
self._figure_saved = True
|
|
207
233
|
|
{labmate-0.8.3 → labmate-0.9.0}/labmate/acquisition_notebook/acquisition_analysis_manager.py
RENAMED
|
@@ -290,6 +290,7 @@ class AcquisitionAnalysisManager(AcquisitionManager):
|
|
|
290
290
|
cell: Optional[str] = None,
|
|
291
291
|
prerun: Optional[Union[_CallableWithNoArgs, List[_CallableWithNoArgs]]] = None,
|
|
292
292
|
save_on_edit: Optional[bool] = None,
|
|
293
|
+
step: int = 1,
|
|
293
294
|
) -> "AcquisitionAnalysisManager":
|
|
294
295
|
self._analysis_cell_str = None
|
|
295
296
|
self._analysis_data = None
|
|
@@ -297,12 +298,25 @@ class AcquisitionAnalysisManager(AcquisitionManager):
|
|
|
297
298
|
self._acquisition_started = time.time()
|
|
298
299
|
|
|
299
300
|
cell = cell or get_current_cell(self.shell)
|
|
300
|
-
|
|
301
|
-
|
|
301
|
+
if step != 1 and self._current_acquisition is not None:
|
|
302
|
+
if self._current_acquisition.experiment_name != name:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Current acquisition ('{self.current_experiment_name}') "
|
|
305
|
+
f"isn't the one expected ('{name}') for this acquisition. "
|
|
306
|
+
f"Possible solutions: run acquisition '{name}' with step 1; "
|
|
307
|
+
f"or change current acquisition name to '{self.current_experiment_name}'"
|
|
308
|
+
)
|
|
309
|
+
self._current_acquisition.current_step = step
|
|
310
|
+
self._current_acquisition.set_cell(cell, step=step)
|
|
311
|
+
self._current_acquisition.save_cell(cell, suffix=str(step))
|
|
312
|
+
else:
|
|
313
|
+
self.new_acquisition(name=name, cell=cell, save_on_edit=save_on_edit)
|
|
302
314
|
|
|
303
315
|
logger.info(self.current_filepath.basename)
|
|
304
316
|
|
|
305
|
-
|
|
317
|
+
if step == 1:
|
|
318
|
+
utils.run_functions(self._acquisition_cell_prerun_hook)
|
|
319
|
+
|
|
306
320
|
utils.run_functions(prerun)
|
|
307
321
|
|
|
308
322
|
return self
|
|
@@ -528,10 +542,62 @@ class AcquisitionAnalysisManager(AcquisitionManager):
|
|
|
528
542
|
)
|
|
529
543
|
continue
|
|
530
544
|
file, line_no = res
|
|
545
|
+
file = self._config_files_names_to_path.get(file, file)
|
|
531
546
|
link = display.links.create_link(param_text, file, line_no, after_text)
|
|
532
547
|
links += link + "<br/>"
|
|
533
548
|
return display.display_html(links)
|
|
534
549
|
|
|
550
|
+
def display_cfg_link(
|
|
551
|
+
self,
|
|
552
|
+
parameters: Dict[str, Any],
|
|
553
|
+
):
|
|
554
|
+
from labmate.display import html_output
|
|
555
|
+
|
|
556
|
+
links = []
|
|
557
|
+
for param, value in parameters.items():
|
|
558
|
+
param_eq = f"{param.strip()} = "
|
|
559
|
+
res = self.find_param_in_config(param_eq)
|
|
560
|
+
if res is None:
|
|
561
|
+
logger.warning(
|
|
562
|
+
"Parameter '%s' cannot be found in default config files.", param
|
|
563
|
+
)
|
|
564
|
+
continue
|
|
565
|
+
file, line_no = res
|
|
566
|
+
file = self._config_files_names_to_path.get(file, file)
|
|
567
|
+
|
|
568
|
+
def update_value(param, value):
|
|
569
|
+
self.update_config_params_on_disk({param: value})
|
|
570
|
+
|
|
571
|
+
buttons = [
|
|
572
|
+
display.buttons.create_button(update_value, param, value, name="Update")
|
|
573
|
+
]
|
|
574
|
+
|
|
575
|
+
link = html_output.create_link_row(
|
|
576
|
+
link_text=f"{param} = ",
|
|
577
|
+
link_url=f"{file}:{line_no}",
|
|
578
|
+
text=str(value),
|
|
579
|
+
buttons=buttons, # type: ignore
|
|
580
|
+
)
|
|
581
|
+
links.append(link)
|
|
582
|
+
return display.display_widgets_vertically(links, class_="labmate-params")
|
|
583
|
+
|
|
584
|
+
def update_config_params_on_disk(self, params: Dict[str, Any]):
|
|
585
|
+
# params_per_files = {}
|
|
586
|
+
# for param, value in params.items():
|
|
587
|
+
# res = self.find_param_in_data_config(param)
|
|
588
|
+
# if res is None:
|
|
589
|
+
# raise ValueError(
|
|
590
|
+
# f"Parameter '{param}' cannot be found in default config files."
|
|
591
|
+
# )
|
|
592
|
+
# file, _ = res
|
|
593
|
+
# params_per_files.setdefault(file, {})[param] = value
|
|
594
|
+
|
|
595
|
+
for file in self.config_files:
|
|
596
|
+
file = self._config_files_names_to_path.get(file, file)
|
|
597
|
+
utils.file_read.update_file_variable(file, params)
|
|
598
|
+
|
|
599
|
+
return self
|
|
600
|
+
|
|
535
601
|
def connect_default_widget(
|
|
536
602
|
self,
|
|
537
603
|
objs: Union[
|
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
"""Module with widgets that can be displayed with AcquisitionAnalysisManager."""
|
|
2
|
-
|
|
3
1
|
import os
|
|
4
2
|
from typing import TYPE_CHECKING, List, Optional, Protocol, TypeVar
|
|
5
3
|
|
|
6
4
|
from .. import display as lm_display
|
|
7
5
|
from ..display import platform_utils
|
|
8
6
|
|
|
9
|
-
# import abc
|
|
10
|
-
|
|
11
|
-
|
|
12
7
|
if TYPE_CHECKING:
|
|
13
8
|
from labmate.acquisition_notebook import AcquisitionAnalysisManager
|
|
14
9
|
|
|
15
10
|
_T = TypeVar("_T")
|
|
16
11
|
|
|
17
12
|
|
|
13
|
+
def _get_filepath(aqm: "AcquisitionAnalysisManager") -> Optional[str]:
|
|
14
|
+
filepath = aqm.current_analysis or aqm.current_acquisition
|
|
15
|
+
return filepath.filepath if filepath else None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _create_file_link(aqm: "AcquisitionAnalysisManager", level_up) -> str:
|
|
19
|
+
filepath = _get_filepath(aqm)
|
|
20
|
+
if filepath is None:
|
|
21
|
+
return ""
|
|
22
|
+
link_name = os.path.basename(filepath)
|
|
23
|
+
link = "/".join(
|
|
24
|
+
os.path.abspath(filepath).replace("\\", "/").split("/")[-level_up:]
|
|
25
|
+
).replace(" ", "%20")
|
|
26
|
+
link = f"[{link_name}](//kyrylo-gr.github.io/h5viewer/open?url={link})"
|
|
27
|
+
return link
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def display_widgets(objs: List["WidgetProtocol"], *args, **kwargs):
|
|
31
|
+
"""Create (with *args, **kwargs) and display a list of widgets."""
|
|
32
|
+
widgets = [obj.create(*args, **kwargs) for obj in objs]
|
|
33
|
+
lm_display.display_widgets(widgets) # type: ignore
|
|
34
|
+
|
|
35
|
+
|
|
18
36
|
class WidgetProtocol(Protocol):
|
|
19
37
|
"""Protocol for widgets that can be displayed with AcquisitionAnalysisManager.
|
|
20
38
|
|
|
@@ -73,7 +91,7 @@ class BaseWidget:
|
|
|
73
91
|
raise NotImplementedError("This method is not implemented for the base class.")
|
|
74
92
|
|
|
75
93
|
|
|
76
|
-
class
|
|
94
|
+
class CopyFileURLPathButton(BaseWidget):
|
|
77
95
|
"""Create button to copy file path to clipboard.
|
|
78
96
|
|
|
79
97
|
Examples:
|
|
@@ -180,32 +198,9 @@ class OpenFinderButton(BaseWidget):
|
|
|
180
198
|
path = path.replace("/", "\\")
|
|
181
199
|
subprocess.run(["explorer", "/select,", path], shell=True)
|
|
182
200
|
elif sys.platform == "darwin":
|
|
183
|
-
subprocess.Popen(["open", "-R", path])
|
|
201
|
+
subprocess.Popen(["open", "-R", os.path.dirname(path)])
|
|
184
202
|
else:
|
|
185
|
-
subprocess.Popen(["nautilus", "--select", path])
|
|
203
|
+
subprocess.Popen(["nautilus", "--select", os.path.dirname(path)])
|
|
186
204
|
|
|
187
205
|
self.widget = lm_display.buttons.create_button(open_finder, name="Open finder")
|
|
188
206
|
return self.widget
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def _get_filepath(aqm: "AcquisitionAnalysisManager") -> Optional[str]:
|
|
192
|
-
filepath = aqm.current_analysis or aqm.current_acquisition
|
|
193
|
-
return filepath.filepath if filepath else None
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def _create_file_link(aqm: "AcquisitionAnalysisManager", level_up) -> str:
|
|
197
|
-
filepath = _get_filepath(aqm)
|
|
198
|
-
if filepath is None:
|
|
199
|
-
return ""
|
|
200
|
-
link_name = os.path.basename(filepath)
|
|
201
|
-
link = "/".join(
|
|
202
|
-
os.path.abspath(filepath).replace("\\", "/").split("/")[-level_up:]
|
|
203
|
-
).replace(" ", "%20")
|
|
204
|
-
link = f"[{link_name}](//kyrylo-gr.github.io/h5viewer/open?url={link})"
|
|
205
|
-
return link
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def display_widgets(objs: List["WidgetProtocol"], *args, **kwargs):
|
|
209
|
-
"""Create (with *args, **kwargs) and display a list of widgets."""
|
|
210
|
-
widgets = [obj.create(*args, **kwargs) for obj in objs]
|
|
211
|
-
lm_display.display_widgets(widgets) # type: ignore
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
import importlib
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
|
-
from .main import
|
|
5
|
+
from .main import (
|
|
6
|
+
display,
|
|
7
|
+
display_html,
|
|
8
|
+
display_widgets,
|
|
9
|
+
display_widgets_vertically,
|
|
10
|
+
logger,
|
|
11
|
+
)
|
|
6
12
|
|
|
7
|
-
__all__ = ["links", "buttons", "logger"]
|
|
13
|
+
__all__ = ["links", "buttons", "logger", "html_output"]
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
class _LazyModule:
|
|
@@ -28,7 +34,8 @@ class _LazyModule:
|
|
|
28
34
|
|
|
29
35
|
links = _LazyModule("links")
|
|
30
36
|
buttons = _LazyModule("buttons")
|
|
37
|
+
html_output = _LazyModule("html_output")
|
|
31
38
|
|
|
32
39
|
|
|
33
40
|
if TYPE_CHECKING:
|
|
34
|
-
from . import buttons, links
|
|
41
|
+
from . import buttons, html_output, links
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""This submodule contains functions that create Button widgets."""
|
|
2
|
+
|
|
2
3
|
from typing import TypeVar
|
|
4
|
+
|
|
3
5
|
from .main import display, widgets
|
|
4
6
|
|
|
5
7
|
# from functools import wraps
|
|
@@ -69,6 +71,8 @@ def create_button(func, *args, name=None, **kwargs) -> "DisplayingButton":
|
|
|
69
71
|
def on_button_click(_):
|
|
70
72
|
func(*args, **kwargs)
|
|
71
73
|
|
|
74
|
+
button.style.button_color = "transparent" # type: ignore
|
|
75
|
+
|
|
72
76
|
button.on_click(on_button_click)
|
|
73
77
|
return button
|
|
74
78
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""This submodule contains functions that create different html."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from .main import widgets
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def display_warning(text: str):
|
|
9
|
+
"""Display div warning block with `text`.
|
|
10
|
+
|
|
11
|
+
If IPython is not installed, log into logger from display submodule at warning level.
|
|
12
|
+
"""
|
|
13
|
+
from .main import display_html
|
|
14
|
+
|
|
15
|
+
html = f"""<div style="
|
|
16
|
+
background-color:#ec7413; padding: .5em; text-align:center"
|
|
17
|
+
>{text}</div>"""
|
|
18
|
+
|
|
19
|
+
display_html(str(html))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_link_row(
|
|
23
|
+
link_text: str,
|
|
24
|
+
link_url: str,
|
|
25
|
+
text: str,
|
|
26
|
+
buttons: Optional[List[widgets.Button]] = None,
|
|
27
|
+
):
|
|
28
|
+
|
|
29
|
+
# Create the HTML link with custom styling
|
|
30
|
+
link_widget = widgets.HTML(
|
|
31
|
+
value=f'<a href="{link_url}" target="_blank" onclick="return false;">{link_text}</a>'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Create the text with custom styling
|
|
35
|
+
text_widget = widgets.HTML(
|
|
36
|
+
value=f'<span style="padding: 0 10px;">{text}</span>',
|
|
37
|
+
layout=widgets.Layout(background_color="transparent"),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
custom_css = """
|
|
41
|
+
<style>
|
|
42
|
+
.cell-output-ipywidget-background:has(.labmate-params) {
|
|
43
|
+
background: transparent !important;
|
|
44
|
+
}
|
|
45
|
+
.labmate-params, .labmate-params * {
|
|
46
|
+
color: inherit !important;
|
|
47
|
+
}
|
|
48
|
+
.labmate-params a {
|
|
49
|
+
text-decoration: underline;
|
|
50
|
+
color: blue;
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
</style>
|
|
55
|
+
"""
|
|
56
|
+
buttons = buttons or []
|
|
57
|
+
|
|
58
|
+
hbox = widgets.HBox(
|
|
59
|
+
[widgets.HTML(custom_css), link_widget, text_widget, *buttons],
|
|
60
|
+
layout=widgets.Layout(background_color="transparent"),
|
|
61
|
+
)
|
|
62
|
+
hbox.add_class("labmate-params")
|
|
63
|
+
return hbox
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""This submodule contains functions that help to display content in IPython."""
|
|
2
|
+
|
|
2
3
|
import logging
|
|
3
4
|
import sys
|
|
4
5
|
from typing import Callable, List
|
|
5
6
|
|
|
6
|
-
|
|
7
7
|
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.WARNING)
|
|
8
8
|
logger = logging.getLogger(__name__)
|
|
9
9
|
handler = logging.StreamHandler()
|
|
@@ -18,9 +18,9 @@ try:
|
|
|
18
18
|
if "pytest" in sys.modules:
|
|
19
19
|
raise ImportError
|
|
20
20
|
|
|
21
|
-
from IPython.core.display import HTML # type: ignore
|
|
22
|
-
from IPython.core import display # type: ignore
|
|
23
21
|
import ipywidgets as widgets # pylint: disable=W0611 # type: ignore
|
|
22
|
+
from IPython.core import display # type: ignore
|
|
23
|
+
from IPython.core.display import HTML # type: ignore
|
|
24
24
|
|
|
25
25
|
display = display.display_functions.display
|
|
26
26
|
|
|
@@ -49,9 +49,17 @@ except ImportError:
|
|
|
49
49
|
def __init__(self, lst: list) -> None: # noqa: D107
|
|
50
50
|
pass
|
|
51
51
|
|
|
52
|
+
class VBox: # noqa: D106
|
|
53
|
+
def __init__(self, lst: list) -> None: # noqa: D107
|
|
54
|
+
pass
|
|
55
|
+
|
|
52
56
|
class CoreWidget: # noqa: D106
|
|
53
57
|
pass
|
|
54
58
|
|
|
59
|
+
class Layout:
|
|
60
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
61
|
+
del args, kwargs
|
|
62
|
+
|
|
55
63
|
# pylint: enable=C0115, C0103, R0903
|
|
56
64
|
|
|
57
65
|
|
|
@@ -64,3 +72,11 @@ def display_widgets(objs: List[widgets.CoreWidget]):
|
|
|
64
72
|
"""Display the given list of widgets in a HBox ."""
|
|
65
73
|
button_row = widgets.HBox(objs)
|
|
66
74
|
display(button_row)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def display_widgets_vertically(objs: List[widgets.CoreWidget], class_: str = ""):
|
|
78
|
+
"""Display the given list of widgets in a VBox ."""
|
|
79
|
+
button_row = widgets.VBox(objs)
|
|
80
|
+
if class_:
|
|
81
|
+
button_row.add_class(class_) # type: ignore
|
|
82
|
+
display(button_row)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Different method to read files."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from ..parsing.brackets_score import BracketsScore
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def read_file(file: str, /) -> str:
|
|
11
|
+
"""Read the contents of a file and returns it as a string.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
file (str): The path to the file to be read.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
str: The contents of the file as a string.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
ValueError: If the file does not exist or is not a file.
|
|
21
|
+
"""
|
|
22
|
+
if not os.path.isfile(file):
|
|
23
|
+
raise ValueError(
|
|
24
|
+
"Cannot read a file if it doesn't exist or it's not a file."
|
|
25
|
+
f"Path: {os.path.abspath(file)}"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
with open(file, "r", encoding="utf-8") as file_opened:
|
|
29
|
+
return file_opened.read()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def read_files(files: List[str], /) -> Dict[str, str]:
|
|
33
|
+
"""Read the contents of the given files and returns them as a dictionary.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
files: A list of file paths to read.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A dictionary where the keys are the file names and the values are the contents of the files.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: If some of the files have the same name, which would cause a key collision in the dictionary.
|
|
43
|
+
"""
|
|
44
|
+
configs: Dict[str, str] = {}
|
|
45
|
+
for config_file in files:
|
|
46
|
+
config_file_name = os.path.basename(config_file)
|
|
47
|
+
if config_file_name in configs:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
"Some of the files have the same name. So it cannot be pushed into dictionary to"
|
|
50
|
+
" preserve unique key"
|
|
51
|
+
)
|
|
52
|
+
configs[config_file_name] = read_file(config_file)
|
|
53
|
+
return configs
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def update_file_variable(file, params: Dict[str, Any]):
|
|
57
|
+
"""
|
|
58
|
+
Update the variables in a file with the given parameters.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
file (str): The path to the file to update.
|
|
62
|
+
params (Dict[str, Any]): The parameters to update the file with.
|
|
63
|
+
"""
|
|
64
|
+
with open(file, "r", encoding="utf-8") as file_opened:
|
|
65
|
+
lines = file_opened.readlines()
|
|
66
|
+
brackets = BracketsScore()
|
|
67
|
+
current_param: Optional[str] = None
|
|
68
|
+
# print(lines)
|
|
69
|
+
for line in lines:
|
|
70
|
+
# print(line)
|
|
71
|
+
if len(line) == 0:
|
|
72
|
+
continue
|
|
73
|
+
if brackets.is_zero() and "=" in line:
|
|
74
|
+
param = line.split("=")[0].strip()
|
|
75
|
+
# print("param", param)
|
|
76
|
+
if param in params:
|
|
77
|
+
current_param = param
|
|
78
|
+
start_line = lines.index(line)
|
|
79
|
+
brackets.update_from_str(line)
|
|
80
|
+
if brackets.is_zero() and current_param is not None:
|
|
81
|
+
end_line = lines.index(line)
|
|
82
|
+
end_comment = line.split("#")[-1].strip() if "#" in line else None
|
|
83
|
+
del lines[start_line : end_line + 1]
|
|
84
|
+
value_str = json.JSONEncoder().encode(params[current_param])
|
|
85
|
+
lines.insert(
|
|
86
|
+
start_line,
|
|
87
|
+
(
|
|
88
|
+
f"{current_param} = {value_str}"
|
|
89
|
+
+ (f" # {end_comment}" if end_comment is not None else "")
|
|
90
|
+
+ "\n"
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
current_param = None
|
|
94
|
+
|
|
95
|
+
with open(file, "w", encoding="utf-8") as file_opened:
|
|
96
|
+
file_opened.writelines(lines)
|
|
@@ -17,7 +17,11 @@ def parse_get_format(key: str) -> Tuple[str, Optional[str], Optional[str]]:
|
|
|
17
17
|
args = key.split("__")
|
|
18
18
|
if len(args) >= 3:
|
|
19
19
|
return args[0], args[1], args[2]
|
|
20
|
-
elif
|
|
20
|
+
elif (
|
|
21
|
+
len(args) == 2
|
|
22
|
+
and len(args[1]) > 0
|
|
23
|
+
and (args[1][0].isdigit() or args[1][0] in (".", "_"))
|
|
24
|
+
):
|
|
21
25
|
return args[0], None, args[1]
|
|
22
26
|
elif len(args) == 2:
|
|
23
27
|
return args[0], args[1], None
|
|
@@ -32,6 +36,23 @@ class ValueForPrint(NamedTuple):
|
|
|
32
36
|
units: Optional[str] = None
|
|
33
37
|
format: Optional[str] = None
|
|
34
38
|
|
|
39
|
+
def format_value(self, format_spec: Optional[str] = None) -> str:
|
|
40
|
+
format_spec = format_spec or self.format
|
|
41
|
+
if not format_spec:
|
|
42
|
+
return str(self.value)
|
|
43
|
+
if format_spec.endswith("p"):
|
|
44
|
+
format_spec = format_spec[:-1] + "e"
|
|
45
|
+
value_str = format(self.value, format_spec)
|
|
46
|
+
number, power = value_str.split("e")
|
|
47
|
+
number = number.rstrip("0_").rstrip(".") if "." in number else number
|
|
48
|
+
power = (
|
|
49
|
+
(power[0].lstrip("+0") + power[1:].lstrip("+0"))
|
|
50
|
+
if len(power) > 1
|
|
51
|
+
else power
|
|
52
|
+
)
|
|
53
|
+
return f"{number}e{power}"
|
|
54
|
+
return format(self.value, format_spec)
|
|
55
|
+
|
|
35
56
|
|
|
36
57
|
def format_title(values: List[ValueForPrint], max_length: Optional[int] = None) -> str:
|
|
37
58
|
"""Create title out of a list of valuesForPrint.
|
|
@@ -47,11 +68,7 @@ def format_title(values: List[ValueForPrint], max_length: Optional[int] = None)
|
|
|
47
68
|
last_line_len = 0
|
|
48
69
|
for value in values:
|
|
49
70
|
units = f" ({value.units})" if value.units is not None else ""
|
|
50
|
-
value_str = (
|
|
51
|
-
value.value
|
|
52
|
-
if value.format is None
|
|
53
|
-
else value.value.__format__(f".{value.format}")
|
|
54
|
-
)
|
|
71
|
+
value_str = value.format_value()
|
|
55
72
|
new_txt = f"{value.key} = {value_str}{units}"
|
|
56
73
|
if not max_length or (
|
|
57
74
|
(last_line_len + len(new_txt) < max_length) or last_line_len == 0
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"""This submodule contains functions that create different html."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def display_warning(text: str):
|
|
5
|
-
"""Display div warning block with `text`.
|
|
6
|
-
|
|
7
|
-
If IPython is not installed, log into logger from display submodule at warning level.
|
|
8
|
-
"""
|
|
9
|
-
from .main import display_html
|
|
10
|
-
|
|
11
|
-
html = f"""<div style="
|
|
12
|
-
background-color:#ec7413; padding: .5em; text-align:center"
|
|
13
|
-
>{text}</div>"""
|
|
14
|
-
|
|
15
|
-
display_html(str(html))
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""Different method to read files."""
|
|
2
|
-
import os
|
|
3
|
-
from typing import Dict, List
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def read_file(file: str, /) -> str:
|
|
7
|
-
"""Read the contents of a file and returns it as a string.
|
|
8
|
-
|
|
9
|
-
Args:
|
|
10
|
-
file (str): The path to the file to be read.
|
|
11
|
-
|
|
12
|
-
Returns:
|
|
13
|
-
str: The contents of the file as a string.
|
|
14
|
-
|
|
15
|
-
Raises:
|
|
16
|
-
ValueError: If the file does not exist or is not a file.
|
|
17
|
-
"""
|
|
18
|
-
if not os.path.isfile(file):
|
|
19
|
-
raise ValueError(
|
|
20
|
-
"Cannot read a file if it doesn't exist or it's not a file."
|
|
21
|
-
f"Path: {os.path.abspath(file)}"
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
with open(file, "r", encoding="utf-8") as file_opened:
|
|
25
|
-
return file_opened.read()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def read_files(files: List[str], /) -> Dict[str, str]:
|
|
29
|
-
"""Read the contents of the given files and returns them as a dictionary.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
files: A list of file paths to read.
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
A dictionary where the keys are the file names and the values are the contents of the files.
|
|
36
|
-
|
|
37
|
-
Raises:
|
|
38
|
-
ValueError: If some of the files have the same name, which would cause a key collision in the dictionary.
|
|
39
|
-
"""
|
|
40
|
-
configs: Dict[str, str] = {}
|
|
41
|
-
for config_file in files:
|
|
42
|
-
config_file_name = os.path.basename(config_file)
|
|
43
|
-
if config_file_name in configs:
|
|
44
|
-
raise ValueError(
|
|
45
|
-
"Some of the files have the same name. So it cannot be pushed into dictionary to"
|
|
46
|
-
" preserve unique key"
|
|
47
|
-
)
|
|
48
|
-
configs[config_file_name] = read_file(config_file)
|
|
49
|
-
return configs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|