datalab-platform 1.0.2__py3-none-any.whl → 1.0.3__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.
- datalab/__init__.py +1 -1
- datalab/adapters_metadata/common.py +2 -2
- datalab/config.py +86 -26
- datalab/control/baseproxy.py +70 -0
- datalab/control/proxy.py +33 -0
- datalab/control/remote.py +35 -0
- datalab/data/doc/DataLab_en.pdf +0 -0
- datalab/data/doc/DataLab_fr.pdf +0 -0
- datalab/data/icons/create/linear_chirp.svg +1 -1
- datalab/data/icons/create/logistic.svg +1 -1
- datalab/gui/actionhandler.py +13 -0
- datalab/gui/h5io.py +25 -0
- datalab/gui/macroeditor.py +19 -5
- datalab/gui/main.py +60 -5
- datalab/gui/objectview.py +18 -3
- datalab/gui/panel/base.py +24 -18
- datalab/gui/panel/macro.py +26 -0
- datalab/gui/plothandler.py +10 -1
- datalab/gui/processor/base.py +43 -10
- datalab/gui/processor/image.py +6 -2
- datalab/locale/fr/LC_MESSAGES/datalab.mo +0 -0
- datalab/locale/fr/LC_MESSAGES/datalab.po +3288 -0
- datalab/objectmodel.py +1 -1
- datalab/tests/features/common/auto_analysis_recompute_unit_test.py +81 -0
- datalab/tests/features/common/coordutils_unit_test.py +1 -1
- datalab/tests/features/common/result_deletion_unit_test.py +121 -1
- datalab/tests/features/common/update_tree_robustness_test.py +65 -0
- datalab/tests/features/control/remoteclient_unit.py +10 -0
- datalab/tests/features/hdf5/h5workspace_unit_test.py +133 -0
- datalab/tests/features/image/roigrid_unit_test.py +75 -0
- datalab/tests/features/macro/macroeditor_unit_test.py +2 -2
- datalab/widgets/imagebackground.py +13 -4
- datalab/widgets/instconfviewer.py +2 -2
- datalab/widgets/signalcursor.py +7 -2
- datalab/widgets/signaldeltax.py +4 -1
- {datalab_platform-1.0.2.dist-info → datalab_platform-1.0.3.dist-info}/METADATA +2 -2
- {datalab_platform-1.0.2.dist-info → datalab_platform-1.0.3.dist-info}/RECORD +41 -37
- {datalab_platform-1.0.2.dist-info → datalab_platform-1.0.3.dist-info}/WHEEL +0 -0
- {datalab_platform-1.0.2.dist-info → datalab_platform-1.0.3.dist-info}/entry_points.txt +0 -0
- {datalab_platform-1.0.2.dist-info → datalab_platform-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {datalab_platform-1.0.2.dist-info → datalab_platform-1.0.3.dist-info}/top_level.txt +0 -0
datalab/__init__.py
CHANGED
|
@@ -24,7 +24,7 @@ except RuntimeError:
|
|
|
24
24
|
# this module is imported more than once, e.g. when running tests)
|
|
25
25
|
pass
|
|
26
26
|
|
|
27
|
-
__version__ = "1.0.
|
|
27
|
+
__version__ = "1.0.3"
|
|
28
28
|
__docurl__ = __homeurl__ = "https://datalab-platform.com/"
|
|
29
29
|
__supporturl__ = "https://github.com/DataLab-Platform/DataLab/issues/new/choose"
|
|
30
30
|
|
|
@@ -88,7 +88,7 @@ class ResultData:
|
|
|
88
88
|
if "roi_index" in df.columns:
|
|
89
89
|
i_roi = int(df.iloc[i_row_res]["roi_index"])
|
|
90
90
|
roititle = ""
|
|
91
|
-
if i_roi >= 0:
|
|
91
|
+
if i_roi >= 0 and obj.roi is not None:
|
|
92
92
|
roititle = obj.roi.get_single_roi_title(i_roi)
|
|
93
93
|
ylabel += f"|{roititle}"
|
|
94
94
|
self.ylabels.append(ylabel)
|
|
@@ -365,7 +365,7 @@ def resultadapter_to_html(
|
|
|
365
365
|
num_cols = max_display_cols
|
|
366
366
|
|
|
367
367
|
# Calculate number of cells (rows × columns)
|
|
368
|
-
num_rows = len(
|
|
368
|
+
num_rows = len(df)
|
|
369
369
|
num_cells = num_rows * num_cols
|
|
370
370
|
|
|
371
371
|
# Check if truncation is needed BEFORE calling to_html()
|
datalab/config.py
CHANGED
|
@@ -487,6 +487,8 @@ initialize()
|
|
|
487
487
|
|
|
488
488
|
ROI_LINE_COLOR = "#5555ff"
|
|
489
489
|
ROI_SEL_LINE_COLOR = "#9393ff"
|
|
490
|
+
MARKER_LINE_COLOR = "#A11818"
|
|
491
|
+
MARKER_TEXT_COLOR = "#440909"
|
|
490
492
|
|
|
491
493
|
PLOTPY_DEFAULTS = {
|
|
492
494
|
"plot": {
|
|
@@ -508,11 +510,27 @@ PLOTPY_DEFAULTS = {
|
|
|
508
510
|
"selected_curve_symbol/alpha": 0.3,
|
|
509
511
|
"selected_curve_symbol/size": 5,
|
|
510
512
|
"marker/curve/text/textcolor": "black",
|
|
511
|
-
|
|
513
|
+
# Cross marker style (shown when pressing Alt key on plot)
|
|
514
|
+
"marker/cross/symbol/marker": "Cross",
|
|
515
|
+
"marker/cross/symbol/edgecolor": MAIN_FG_COLOR,
|
|
516
|
+
"marker/cross/symbol/facecolor": "#ff0000",
|
|
517
|
+
"marker/cross/symbol/alpha": 1.0,
|
|
518
|
+
"marker/cross/symbol/size": 8,
|
|
519
|
+
"marker/cross/text/font/family": "default",
|
|
520
|
+
"marker/cross/text/font/size": 8,
|
|
521
|
+
"marker/cross/text/font/bold": False,
|
|
522
|
+
"marker/cross/text/font/italic": False,
|
|
523
|
+
"marker/cross/text/textcolor": "#000000",
|
|
524
|
+
"marker/cross/text/background_color": "#ffffff",
|
|
512
525
|
"marker/cross/text/background_alpha": 0.7,
|
|
526
|
+
"marker/cross/line/style": "DashLine",
|
|
527
|
+
"marker/cross/line/color": MARKER_LINE_COLOR,
|
|
528
|
+
"marker/cross/line/width": 1.0,
|
|
529
|
+
"marker/cross/markerstyle": "Cross",
|
|
530
|
+
"marker/cross/spacing": 7,
|
|
513
531
|
# Cursor line and symbol style
|
|
514
532
|
"marker/cursor/line/style": "SolidLine",
|
|
515
|
-
"marker/cursor/line/color":
|
|
533
|
+
"marker/cursor/line/color": MARKER_LINE_COLOR,
|
|
516
534
|
"marker/cursor/line/width": 1.0,
|
|
517
535
|
"marker/cursor/symbol/marker": "NoSymbol",
|
|
518
536
|
"marker/cursor/symbol/size": 11,
|
|
@@ -520,41 +538,83 @@ PLOTPY_DEFAULTS = {
|
|
|
520
538
|
"marker/cursor/symbol/facecolor": "#ff9393",
|
|
521
539
|
"marker/cursor/symbol/alpha": 1.0,
|
|
522
540
|
"marker/cursor/sel_line/style": "SolidLine",
|
|
523
|
-
"marker/cursor/sel_line/color":
|
|
541
|
+
"marker/cursor/sel_line/color": MARKER_LINE_COLOR,
|
|
524
542
|
"marker/cursor/sel_line/width": 2.0,
|
|
525
543
|
"marker/cursor/sel_symbol/marker": "NoSymbol",
|
|
526
544
|
"marker/cursor/sel_symbol/size": 11,
|
|
527
545
|
"marker/cursor/sel_symbol/edgecolor": MAIN_BG_COLOR,
|
|
528
|
-
"marker/cursor/sel_symbol/facecolor":
|
|
546
|
+
"marker/cursor/sel_symbol/facecolor": MARKER_LINE_COLOR,
|
|
529
547
|
"marker/cursor/sel_symbol/alpha": 0.8,
|
|
530
548
|
"marker/cursor/text/font/size": 9,
|
|
531
549
|
"marker/cursor/text/font/family": "default",
|
|
532
550
|
"marker/cursor/text/font/bold": False,
|
|
533
551
|
"marker/cursor/text/font/italic": False,
|
|
534
|
-
"marker/cursor/text/textcolor":
|
|
552
|
+
"marker/cursor/text/textcolor": MARKER_TEXT_COLOR,
|
|
535
553
|
"marker/cursor/text/background_color": "#ffffff",
|
|
536
554
|
"marker/cursor/text/background_alpha": 0.7,
|
|
537
555
|
"marker/cursor/sel_text/font/size": 9,
|
|
538
556
|
"marker/cursor/sel_text/font/family": "default",
|
|
539
557
|
"marker/cursor/sel_text/font/bold": False,
|
|
540
558
|
"marker/cursor/sel_text/font/italic": False,
|
|
541
|
-
"marker/cursor/sel_text/textcolor":
|
|
559
|
+
"marker/cursor/sel_text/textcolor": MARKER_TEXT_COLOR,
|
|
542
560
|
"marker/cursor/sel_text/background_color": "#ffffff",
|
|
543
561
|
"marker/cursor/sel_text/background_alpha": 0.7,
|
|
562
|
+
# Default annotation text style for segments:
|
|
563
|
+
"shape/segment/line/style": "SolidLine",
|
|
564
|
+
"shape/segment/line/color": "#00ff55",
|
|
565
|
+
"shape/segment/line/width": 1.0,
|
|
566
|
+
"shape/segment/sel_line/style": "SolidLine",
|
|
567
|
+
"shape/segment/sel_line/color": "#00ff55",
|
|
568
|
+
"shape/segment/sel_line/width": 2.0,
|
|
569
|
+
"shape/segment/fill/style": "NoBrush",
|
|
570
|
+
"shape/segment/sel_fill/style": "NoBrush",
|
|
571
|
+
"shape/segment/symbol/marker": "XCross",
|
|
572
|
+
"shape/segment/symbol/size": 9,
|
|
573
|
+
"shape/segment/symbol/edgecolor": "#00ff55",
|
|
574
|
+
"shape/segment/symbol/facecolor": "#00ff55",
|
|
575
|
+
"shape/segment/symbol/alpha": 1.0,
|
|
576
|
+
"shape/segment/sel_symbol/marker": "XCross",
|
|
577
|
+
"shape/segment/sel_symbol/size": 12,
|
|
578
|
+
"shape/segment/sel_symbol/edgecolor": "#00ff55",
|
|
579
|
+
"shape/segment/sel_symbol/facecolor": "#00ff55",
|
|
580
|
+
"shape/segment/sel_symbol/alpha": 0.7,
|
|
581
|
+
# Default style for drag shapes: (global annotations style)
|
|
582
|
+
"shape/drag/line/style": "SolidLine",
|
|
583
|
+
"shape/drag/line/color": "#00ff55",
|
|
584
|
+
"shape/drag/line/width": 1.0,
|
|
585
|
+
"shape/drag/fill/style": "SolidPattern",
|
|
586
|
+
"shape/drag/fill/color": MAIN_BG_COLOR,
|
|
587
|
+
"shape/drag/fill/alpha": 0.1,
|
|
588
|
+
"shape/drag/symbol/marker": "Rect",
|
|
589
|
+
"shape/drag/symbol/size": 3,
|
|
590
|
+
"shape/drag/symbol/edgecolor": "#00ff55",
|
|
591
|
+
"shape/drag/symbol/facecolor": "#00ff55",
|
|
592
|
+
"shape/drag/symbol/alpha": 1.0,
|
|
593
|
+
"shape/drag/sel_line/style": "SolidLine",
|
|
594
|
+
"shape/drag/sel_line/color": "#00ff55",
|
|
595
|
+
"shape/drag/sel_line/width": 2.0,
|
|
596
|
+
"shape/drag/sel_fill/style": "SolidPattern",
|
|
597
|
+
"shape/drag/sel_fill/color": MAIN_BG_COLOR,
|
|
598
|
+
"shape/drag/sel_fill/alpha": 0.1,
|
|
599
|
+
"shape/drag/sel_symbol/marker": "Rect",
|
|
600
|
+
"shape/drag/sel_symbol/size": 7,
|
|
601
|
+
"shape/drag/sel_symbol/edgecolor": "#00ff55",
|
|
602
|
+
"shape/drag/sel_symbol/facecolor": "#00ff00",
|
|
603
|
+
"shape/drag/sel_symbol/alpha": 0.7,
|
|
544
604
|
},
|
|
545
605
|
"results": {
|
|
546
606
|
# Annotated shape style for result shapes:
|
|
547
607
|
# Signals:
|
|
548
608
|
"s/annotation/line/style": "SolidLine",
|
|
549
|
-
"s/annotation/line/color":
|
|
550
|
-
"s/annotation/line/width":
|
|
551
|
-
"s/annotation/fill/style": "
|
|
609
|
+
"s/annotation/line/color": "#00aa00",
|
|
610
|
+
"s/annotation/line/width": 2,
|
|
611
|
+
"s/annotation/fill/style": "NoBrush",
|
|
552
612
|
"s/annotation/fill/color": MAIN_BG_COLOR,
|
|
553
613
|
"s/annotation/fill/alpha": 0.1,
|
|
554
614
|
"s/annotation/symbol/marker": "XCross",
|
|
555
615
|
"s/annotation/symbol/size": 7,
|
|
556
|
-
"s/annotation/symbol/edgecolor":
|
|
557
|
-
"s/annotation/symbol/facecolor":
|
|
616
|
+
"s/annotation/symbol/edgecolor": "#00aa00",
|
|
617
|
+
"s/annotation/symbol/facecolor": "#00aa00",
|
|
558
618
|
"s/annotation/symbol/alpha": 1.0,
|
|
559
619
|
"s/annotation/sel_line/style": "DashLine",
|
|
560
620
|
"s/annotation/sel_line/color": "#00ff00",
|
|
@@ -593,65 +653,65 @@ PLOTPY_DEFAULTS = {
|
|
|
593
653
|
# Marker styles for results:
|
|
594
654
|
# Signals:
|
|
595
655
|
"s/marker/cursor/line/style": "DashLine",
|
|
596
|
-
"s/marker/cursor/line/color":
|
|
656
|
+
"s/marker/cursor/line/color": MARKER_LINE_COLOR,
|
|
597
657
|
"s/marker/cursor/line/width": 1.0,
|
|
598
658
|
"s/marker/cursor/symbol/marker": "Ellipse",
|
|
599
659
|
"s/marker/cursor/symbol/size": 11,
|
|
600
660
|
"s/marker/cursor/symbol/edgecolor": MAIN_BG_COLOR,
|
|
601
|
-
"s/marker/cursor/symbol/facecolor":
|
|
661
|
+
"s/marker/cursor/symbol/facecolor": MARKER_LINE_COLOR,
|
|
602
662
|
"s/marker/cursor/symbol/alpha": 0.7,
|
|
603
663
|
"s/marker/cursor/sel_line/style": "DashLine",
|
|
604
|
-
"s/marker/cursor/sel_line/color":
|
|
664
|
+
"s/marker/cursor/sel_line/color": MARKER_LINE_COLOR,
|
|
605
665
|
"s/marker/cursor/sel_line/width": 2.0,
|
|
606
666
|
"s/marker/cursor/sel_symbol/marker": "Ellipse",
|
|
607
667
|
"s/marker/cursor/sel_symbol/size": 11,
|
|
608
|
-
"s/marker/cursor/sel_symbol/edgecolor":
|
|
609
|
-
"s/marker/cursor/sel_symbol/facecolor":
|
|
668
|
+
"s/marker/cursor/sel_symbol/edgecolor": MARKER_LINE_COLOR,
|
|
669
|
+
"s/marker/cursor/sel_symbol/facecolor": MARKER_LINE_COLOR,
|
|
610
670
|
"s/marker/cursor/sel_symbol/alpha": 0.7,
|
|
611
671
|
"s/marker/cursor/text/font/size": 9,
|
|
612
672
|
"s/marker/cursor/text/font/family": "default",
|
|
613
673
|
"s/marker/cursor/text/font/bold": False,
|
|
614
674
|
"s/marker/cursor/text/font/italic": False,
|
|
615
|
-
"s/marker/cursor/text/textcolor":
|
|
675
|
+
"s/marker/cursor/text/textcolor": MARKER_TEXT_COLOR,
|
|
616
676
|
"s/marker/cursor/text/background_color": "#ffffff",
|
|
617
677
|
"s/marker/cursor/text/background_alpha": 0.7,
|
|
618
678
|
"s/marker/cursor/sel_text/font/size": 9,
|
|
619
679
|
"s/marker/cursor/sel_text/font/family": "default",
|
|
620
680
|
"s/marker/cursor/sel_text/font/bold": False,
|
|
621
681
|
"s/marker/cursor/sel_text/font/italic": False,
|
|
622
|
-
"s/marker/cursor/sel_text/textcolor":
|
|
682
|
+
"s/marker/cursor/sel_text/textcolor": MARKER_TEXT_COLOR,
|
|
623
683
|
"s/marker/cursor/sel_text/background_color": "#ffffff",
|
|
624
684
|
"s/marker/cursor/sel_text/background_alpha": 0.7,
|
|
625
685
|
"s/marker/cursor/markerstyle": "Cross",
|
|
626
686
|
# Images:
|
|
627
687
|
"i/marker/cursor/line/style": "DashLine",
|
|
628
|
-
"i/marker/cursor/line/color":
|
|
688
|
+
"i/marker/cursor/line/color": MARKER_LINE_COLOR,
|
|
629
689
|
"i/marker/cursor/line/width": 1.0,
|
|
630
690
|
"i/marker/cursor/symbol/marker": "Diamond",
|
|
631
691
|
"i/marker/cursor/symbol/size": 11,
|
|
632
|
-
"i/marker/cursor/symbol/edgecolor":
|
|
633
|
-
"i/marker/cursor/symbol/facecolor":
|
|
692
|
+
"i/marker/cursor/symbol/edgecolor": MARKER_LINE_COLOR,
|
|
693
|
+
"i/marker/cursor/symbol/facecolor": MARKER_LINE_COLOR,
|
|
634
694
|
"i/marker/cursor/symbol/alpha": 0.7,
|
|
635
695
|
"i/marker/cursor/sel_line/style": "DashLine",
|
|
636
|
-
"i/marker/cursor/sel_line/color":
|
|
696
|
+
"i/marker/cursor/sel_line/color": MARKER_LINE_COLOR,
|
|
637
697
|
"i/marker/cursor/sel_line/width": 2.0,
|
|
638
698
|
"i/marker/cursor/sel_symbol/marker": "Diamond",
|
|
639
699
|
"i/marker/cursor/sel_symbol/size": 11,
|
|
640
|
-
"i/marker/cursor/sel_symbol/edgecolor":
|
|
641
|
-
"i/marker/cursor/sel_symbol/facecolor":
|
|
700
|
+
"i/marker/cursor/sel_symbol/edgecolor": MARKER_LINE_COLOR,
|
|
701
|
+
"i/marker/cursor/sel_symbol/facecolor": MARKER_LINE_COLOR,
|
|
642
702
|
"i/marker/cursor/sel_symbol/alpha": 0.7,
|
|
643
703
|
"i/marker/cursor/text/font/size": 9,
|
|
644
704
|
"i/marker/cursor/text/font/family": "default",
|
|
645
705
|
"i/marker/cursor/text/font/bold": False,
|
|
646
706
|
"i/marker/cursor/text/font/italic": False,
|
|
647
|
-
"i/marker/cursor/text/textcolor":
|
|
707
|
+
"i/marker/cursor/text/textcolor": MARKER_TEXT_COLOR,
|
|
648
708
|
"i/marker/cursor/text/background_color": "#ffffff",
|
|
649
709
|
"i/marker/cursor/text/background_alpha": 0.7,
|
|
650
710
|
"i/marker/cursor/sel_text/font/size": 9,
|
|
651
711
|
"i/marker/cursor/sel_text/font/family": "default",
|
|
652
712
|
"i/marker/cursor/sel_text/font/bold": False,
|
|
653
713
|
"i/marker/cursor/sel_text/font/italic": False,
|
|
654
|
-
"i/marker/cursor/sel_text/textcolor":
|
|
714
|
+
"i/marker/cursor/sel_text/textcolor": MARKER_TEXT_COLOR,
|
|
655
715
|
"i/marker/cursor/sel_text/background_color": "#ffffff",
|
|
656
716
|
"i/marker/cursor/sel_text/background_alpha": 0.7,
|
|
657
717
|
"i/marker/cursor/markerstyle": "Cross",
|
datalab/control/baseproxy.py
CHANGED
|
@@ -198,6 +198,41 @@ class AbstractDLControl(abc.ABC):
|
|
|
198
198
|
reset_all: Reset all application data. Defaults to None.
|
|
199
199
|
"""
|
|
200
200
|
|
|
201
|
+
@abc.abstractmethod
|
|
202
|
+
def load_h5_workspace(self, h5files: list[str], reset_all: bool = False) -> None:
|
|
203
|
+
"""Load native DataLab HDF5 workspace files without any GUI elements.
|
|
204
|
+
|
|
205
|
+
This method can be safely called from scripts (e.g., internal console,
|
|
206
|
+
macros) as it does not create any Qt widgets, dialogs, or progress bars.
|
|
207
|
+
|
|
208
|
+
.. warning::
|
|
209
|
+
|
|
210
|
+
This method only supports native DataLab HDF5 files. For importing
|
|
211
|
+
arbitrary HDF5 files (non-native), use :meth:`open_h5_files` or
|
|
212
|
+
:meth:`import_h5_file` instead.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
h5files: List of native DataLab HDF5 filenames
|
|
216
|
+
reset_all: Reset all application data before importing. Defaults to False.
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
ValueError: If a file is not a valid native DataLab HDF5 file
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
@abc.abstractmethod
|
|
223
|
+
def save_h5_workspace(self, filename: str) -> None:
|
|
224
|
+
"""Save current workspace to a native DataLab HDF5 file without GUI elements.
|
|
225
|
+
|
|
226
|
+
This method can be safely called from scripts (e.g., internal console,
|
|
227
|
+
macros) as it does not create any Qt widgets, dialogs, or progress bars.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
filename: HDF5 filename to save to
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
IOError: If file cannot be saved
|
|
234
|
+
"""
|
|
235
|
+
|
|
201
236
|
@abc.abstractmethod
|
|
202
237
|
def load_from_files(self, filenames: list[str]) -> None:
|
|
203
238
|
"""Open objects from files in current panel (signals/images).
|
|
@@ -607,6 +642,41 @@ class BaseProxy(AbstractDLControl, metaclass=abc.ABCMeta):
|
|
|
607
642
|
"""
|
|
608
643
|
self._datalab.import_h5_file(filename, reset_all)
|
|
609
644
|
|
|
645
|
+
def load_h5_workspace(self, h5files: list[str], reset_all: bool = False) -> None:
|
|
646
|
+
"""Load native DataLab HDF5 workspace files without any GUI elements.
|
|
647
|
+
|
|
648
|
+
This method can be safely called from scripts (e.g., internal console,
|
|
649
|
+
macros) as it does not create any Qt widgets, dialogs, or progress bars.
|
|
650
|
+
|
|
651
|
+
.. warning::
|
|
652
|
+
|
|
653
|
+
This method only supports native DataLab HDF5 files. For importing
|
|
654
|
+
arbitrary HDF5 files (non-native), use :meth:`open_h5_files` or
|
|
655
|
+
:meth:`import_h5_file` instead.
|
|
656
|
+
|
|
657
|
+
Args:
|
|
658
|
+
h5files: List of native DataLab HDF5 filenames
|
|
659
|
+
reset_all: Reset all application data before importing. Defaults to False.
|
|
660
|
+
|
|
661
|
+
Raises:
|
|
662
|
+
ValueError: If a file is not a valid native DataLab HDF5 file
|
|
663
|
+
"""
|
|
664
|
+
self._datalab.load_h5_workspace(h5files, reset_all)
|
|
665
|
+
|
|
666
|
+
def save_h5_workspace(self, filename: str) -> None:
|
|
667
|
+
"""Save current workspace to a native DataLab HDF5 file without GUI elements.
|
|
668
|
+
|
|
669
|
+
This method can be safely called from scripts (e.g., internal console,
|
|
670
|
+
macros) as it does not create any Qt widgets, dialogs, or progress bars.
|
|
671
|
+
|
|
672
|
+
Args:
|
|
673
|
+
filename: HDF5 filename to save to
|
|
674
|
+
|
|
675
|
+
Raises:
|
|
676
|
+
IOError: If file cannot be saved
|
|
677
|
+
"""
|
|
678
|
+
self._datalab.save_h5_workspace(filename)
|
|
679
|
+
|
|
610
680
|
def load_from_files(self, filenames: list[str]) -> None:
|
|
611
681
|
"""Open objects from files in current panel (signals/images).
|
|
612
682
|
|
datalab/control/proxy.py
CHANGED
|
@@ -300,6 +300,39 @@ class LocalProxy(BaseProxy):
|
|
|
300
300
|
"""
|
|
301
301
|
self._datalab.add_annotations_from_items(items, refresh_plot, panel)
|
|
302
302
|
|
|
303
|
+
def load_h5_workspace(
|
|
304
|
+
self, h5files: list[str] | str, reset_all: bool = False
|
|
305
|
+
) -> None:
|
|
306
|
+
"""Load HDF5 workspace files without showing file dialog.
|
|
307
|
+
|
|
308
|
+
This method loads one or more DataLab native HDF5 files directly, bypassing
|
|
309
|
+
the file dialog. It is safe to call from the internal console or any context
|
|
310
|
+
where Qt dialogs would cause threading issues.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
h5files: Path(s) to HDF5 file(s). Can be a single path string or a list
|
|
314
|
+
of paths
|
|
315
|
+
reset_all: If True, reset workspace before loading. Defaults to False.
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
ValueError: if file is not a DataLab native HDF5 file
|
|
319
|
+
"""
|
|
320
|
+
if isinstance(h5files, str):
|
|
321
|
+
h5files = [h5files]
|
|
322
|
+
self._datalab.load_h5_workspace(h5files, reset_all)
|
|
323
|
+
|
|
324
|
+
def save_h5_workspace(self, filename: str) -> None:
|
|
325
|
+
"""Save workspace to HDF5 file without showing file dialog.
|
|
326
|
+
|
|
327
|
+
This method saves the current workspace to a DataLab native HDF5 file
|
|
328
|
+
directly, bypassing the file dialog. It is safe to call from the internal
|
|
329
|
+
console or any context where Qt dialogs would cause threading issues.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
filename: Path to the output HDF5 file
|
|
333
|
+
"""
|
|
334
|
+
self._datalab.save_h5_workspace(filename)
|
|
335
|
+
|
|
303
336
|
|
|
304
337
|
@contextmanager
|
|
305
338
|
def proxy_context(what: str) -> Generator[LocalProxy | RemoteProxy, None, None]:
|
datalab/control/remote.py
CHANGED
|
@@ -84,6 +84,8 @@ class RemoteServer(QC.QThread):
|
|
|
84
84
|
SIG_SAVE_TO_H5 = QC.Signal(str)
|
|
85
85
|
SIG_OPEN_H5 = QC.Signal(list, bool, bool)
|
|
86
86
|
SIG_IMPORT_H5 = QC.Signal(str, bool)
|
|
87
|
+
SIG_LOAD_H5_WORKSPACE = QC.Signal(list, bool)
|
|
88
|
+
SIG_SAVE_H5_WORKSPACE = QC.Signal(str)
|
|
87
89
|
SIG_CALC = QC.Signal(str, object)
|
|
88
90
|
SIG_RUN_MACRO = QC.Signal(str)
|
|
89
91
|
SIG_STOP_MACRO = QC.Signal(str)
|
|
@@ -114,6 +116,8 @@ class RemoteServer(QC.QThread):
|
|
|
114
116
|
self.SIG_SAVE_TO_H5.connect(win.save_to_h5_file)
|
|
115
117
|
self.SIG_OPEN_H5.connect(win.open_h5_files)
|
|
116
118
|
self.SIG_IMPORT_H5.connect(win.import_h5_file)
|
|
119
|
+
self.SIG_LOAD_H5_WORKSPACE.connect(win.load_h5_workspace)
|
|
120
|
+
self.SIG_SAVE_H5_WORKSPACE.connect(win.save_h5_workspace)
|
|
117
121
|
self.SIG_CALC.connect(win.calc)
|
|
118
122
|
self.SIG_RUN_MACRO.connect(win.run_macro)
|
|
119
123
|
self.SIG_STOP_MACRO.connect(win.stop_macro)
|
|
@@ -268,6 +272,37 @@ class RemoteServer(QC.QThread):
|
|
|
268
272
|
reset_all = False if reset_all is None else reset_all
|
|
269
273
|
self.SIG_IMPORT_H5.emit(filename, reset_all)
|
|
270
274
|
|
|
275
|
+
@remote_call
|
|
276
|
+
def load_h5_workspace(self, h5files: list[str], reset_all: bool = False) -> None:
|
|
277
|
+
"""Load native DataLab HDF5 workspace files without any GUI elements.
|
|
278
|
+
|
|
279
|
+
This method can be safely called from scripts as it does not create
|
|
280
|
+
any Qt widgets, dialogs, or progress bars.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
h5files: List of native DataLab HDF5 filenames
|
|
284
|
+
reset_all: Reset all application data before importing. Defaults to False.
|
|
285
|
+
|
|
286
|
+
Raises:
|
|
287
|
+
ValueError: If a file is not a valid native DataLab HDF5 file
|
|
288
|
+
"""
|
|
289
|
+
self.SIG_LOAD_H5_WORKSPACE.emit(h5files, reset_all)
|
|
290
|
+
|
|
291
|
+
@remote_call
|
|
292
|
+
def save_h5_workspace(self, filename: str) -> None:
|
|
293
|
+
"""Save current workspace to a native DataLab HDF5 file without GUI elements.
|
|
294
|
+
|
|
295
|
+
This method can be safely called from scripts as it does not create
|
|
296
|
+
any Qt widgets, dialogs, or progress bars.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
filename: HDF5 filename to save to
|
|
300
|
+
|
|
301
|
+
Raises:
|
|
302
|
+
IOError: If file cannot be saved
|
|
303
|
+
"""
|
|
304
|
+
self.SIG_SAVE_H5_WORKSPACE.emit(filename)
|
|
305
|
+
|
|
271
306
|
@remote_call
|
|
272
307
|
def load_from_files(self, filenames: list[str]) -> None:
|
|
273
308
|
"""Open objects from files in current panel (signals/images).
|
datalab/data/doc/DataLab_en.pdf
CHANGED
|
Binary file
|
datalab/data/doc/DataLab_fr.pdf
CHANGED
|
Binary file
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
xmlns="http://www.w3.org/2000/svg">
|
|
4
4
|
<rect style="fill:#696969;fill-opacity:1;stroke-width:0.547874" width="0.53186357" height="12.584304" x="6.7340684" y="0.70784807" />
|
|
5
5
|
<rect style="fill:#696969;fill-opacity:1;stroke-width:0.54642" width="0.53186357" height="12.5176" x="6.7340684" y="-13.258801" transform="rotate(90)" />
|
|
6
|
-
<path style="fill:none;stroke:#009966;stroke-width:0.35;stroke-linejoin:round" d="M 1,7 C 1.5,5.5 2,4.5 2.5,5.5 3,7 3.3,8.5 3.6,7.5 3.9,6 4.2,4 4.5,5 4.8,6.5 5,8.5 5.3,7.5 5.6,6 5.9,3.5 6.3,5 6.7,7 6.9,9.5 7.2,7.5 7.5,5 7.8,2 8.2,5 8.6,8 8.9,11 9.3,8 9.7,5 10.1,2 10.6,5.5 11.1,9 11.6,12 12.2,8.5 12.8,5 13,2" />
|
|
6
|
+
<path style="fill:none;stroke:#009966;stroke-width:0.35;stroke-linejoin:round" d="M 1,7 C 1.5,5.5 2,4.5 2.5,5.5 C 3,7 3.3,8.5 3.6,7.5 C 3.9,6 4.2,4 4.5,5 C 4.8,6.5 5,8.5 5.3,7.5 C 5.6,6 5.9,3.5 6.3,5 C 6.7,7 6.9,9.5 7.2,7.5 C 7.5,5 7.8,2 8.2,5 C 8.6,8 8.9,11 9.3,8 C 9.7,5 10.1,2 10.6,5.5 C 11.1,9 11.6,12 12.2,8.5 C 12.8,5 13,3.5 13,2" />
|
|
7
7
|
</svg>
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
xmlns="http://www.w3.org/2000/svg">
|
|
4
4
|
<rect style="fill:#696969;fill-opacity:1;stroke-width:0.547874" width="0.53186357" height="12.584304" x="6.7340684" y="0.70784807" />
|
|
5
5
|
<rect style="fill:#696969;fill-opacity:1;stroke-width:0.54642" width="0.53186357" height="12.5176" x="6.7340684" y="-13.258801" transform="rotate(90)" />
|
|
6
|
-
<path style="fill:none;stroke:#9900cc;stroke-width:0.35;stroke-linejoin:round" d="M 1.5,12 C 2,12 2.5,12 3,11.9 3.5,11.8 4,11.6 4.5,11.2 5,10.8 5.5,10.2 6,9.5 6.5,8.5 7,7.2 7.5,5.5 8,4 8.5,3 9,2.5 9.5,2.2 10,2.1 10.5,2.05 11,2.02 11.5,2.01 12,2 12.5,2" />
|
|
6
|
+
<path style="fill:none;stroke:#9900cc;stroke-width:0.35;stroke-linejoin:round" d="M 1.5,12 C 2,12 2.5,12 3,11.9 C 3.5,11.8 4,11.6 4.5,11.2 C 5,10.8 5.5,10.2 6,9.5 C 6.5,8.5 7,7.2 7.5,5.5 C 8,4 8.5,3 9,2.5 C 9.5,2.2 10,2.1 10.5,2.05 C 11,2.02 11.5,2.01 12,2 L 12.5,2" />
|
|
7
7
|
</svg>
|
datalab/gui/actionhandler.py
CHANGED
|
@@ -50,6 +50,10 @@ from qtpy import QtWidgets as QW
|
|
|
50
50
|
from datalab.adapters_metadata import GeometryAdapter, TableAdapter, have_results
|
|
51
51
|
from datalab.config import Conf, _
|
|
52
52
|
from datalab.gui import newobject
|
|
53
|
+
from datalab.gui.processor.base import (
|
|
54
|
+
clear_analysis_parameters,
|
|
55
|
+
extract_analysis_parameters,
|
|
56
|
+
)
|
|
53
57
|
from datalab.widgets import fitdialog
|
|
54
58
|
|
|
55
59
|
if TYPE_CHECKING:
|
|
@@ -361,6 +365,15 @@ class BaseActionHandler(metaclass=abc.ABCMeta):
|
|
|
361
365
|
obj: Object containing the result
|
|
362
366
|
adapter: Adapter for the result to delete
|
|
363
367
|
"""
|
|
368
|
+
# Check if this result matches the stored analysis parameters
|
|
369
|
+
# If so, clear them to prevent auto-recompute from attempting to
|
|
370
|
+
# recompute the deleted analysis when ROI changes
|
|
371
|
+
analysis_params = extract_analysis_parameters(obj)
|
|
372
|
+
if (
|
|
373
|
+
analysis_params is not None
|
|
374
|
+
and analysis_params.func_name == adapter.func_name
|
|
375
|
+
):
|
|
376
|
+
clear_analysis_parameters(obj)
|
|
364
377
|
adapter.remove_from(obj)
|
|
365
378
|
# Update properties panel to reflect the removal
|
|
366
379
|
if obj is self.panel.objview.get_current_object():
|
datalab/gui/h5io.py
CHANGED
|
@@ -54,6 +54,31 @@ class H5InputOutput:
|
|
|
54
54
|
panel.serialize_to_hdf5(writer)
|
|
55
55
|
writer.close()
|
|
56
56
|
|
|
57
|
+
def open_file_headless(self, filename: str, reset_all: bool) -> bool:
|
|
58
|
+
"""Open native DataLab HDF5 file without any GUI elements.
|
|
59
|
+
|
|
60
|
+
This method can be safely called from any thread (e.g., the console thread)
|
|
61
|
+
as it does not create any Qt widgets or dialogs.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
filename: HDF5 filename
|
|
65
|
+
reset_all: Reset all application data before importing
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
True if file was successfully opened as a native DataLab file,
|
|
69
|
+
False if the file format is not compatible (KeyError was raised)
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
reader = NativeH5Reader(filename)
|
|
73
|
+
if reset_all:
|
|
74
|
+
self.mainwindow.reset_all()
|
|
75
|
+
for panel in self.mainwindow.panels:
|
|
76
|
+
panel.deserialize_from_hdf5(reader, reset_all)
|
|
77
|
+
reader.close()
|
|
78
|
+
return True
|
|
79
|
+
except KeyError:
|
|
80
|
+
return False
|
|
81
|
+
|
|
57
82
|
def open_file(self, filename: str, import_all: bool, reset_all: bool) -> None:
|
|
58
83
|
"""Open HDF5 file"""
|
|
59
84
|
progress = None
|
datalab/gui/macroeditor.py
CHANGED
|
@@ -186,8 +186,20 @@ print("All done!")
|
|
|
186
186
|
"""
|
|
187
187
|
global UNTITLED_NB # pylint: disable=global-statement
|
|
188
188
|
UNTITLED_NB += 1
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
return f"macro_{UNTITLED_NB:02d}"
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def set_untitled_number(number: int) -> None:
|
|
193
|
+
"""Set the untitled number counter
|
|
194
|
+
|
|
195
|
+
This is useful when loading macros from HDF5 or files to ensure
|
|
196
|
+
that the next untitled macro has a unique name.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
number: New untitled number
|
|
200
|
+
"""
|
|
201
|
+
global UNTITLED_NB # pylint: disable=global-statement
|
|
202
|
+
UNTITLED_NB = number
|
|
191
203
|
|
|
192
204
|
def modification_changed(self, state: bool) -> None:
|
|
193
205
|
"""Method called when macro's editor modification state changed
|
|
@@ -208,8 +220,10 @@ print("All done!")
|
|
|
208
220
|
Returns:
|
|
209
221
|
Locale str
|
|
210
222
|
"""
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
# Python 3 outputs UTF-8 by default, so we need to decode as UTF-8
|
|
224
|
+
# instead of using the system locale codec (which might be cp1252 on Windows)
|
|
225
|
+
utf8_codec = QC.QTextCodec.codecForName(b"UTF-8")
|
|
226
|
+
return utf8_codec.toUnicode(bytearr.data())
|
|
213
227
|
|
|
214
228
|
def get_stdout(self) -> str:
|
|
215
229
|
"""Return standard output str
|
|
@@ -258,7 +272,7 @@ print("All done!")
|
|
|
258
272
|
def run(self) -> None:
|
|
259
273
|
"""Run macro"""
|
|
260
274
|
self.process = QC.QProcess()
|
|
261
|
-
code = self.get_code()
|
|
275
|
+
code = self.get_code()
|
|
262
276
|
datalab_path = osp.abspath(osp.join(osp.dirname(datalab.__file__), os.pardir))
|
|
263
277
|
# Reconfigure stdout/stderr to use UTF-8 encoding to avoid UnicodeEncodeError
|
|
264
278
|
# on Windows with locales that don't support all Unicode characters
|
datalab/gui/main.py
CHANGED
|
@@ -1360,6 +1360,10 @@ class DLMainWindow(QW.QMainWindow, AbstractDLControl, metaclass=DLMainWindowMeta
|
|
|
1360
1360
|
title += f" [{datalab.__version__}]"
|
|
1361
1361
|
self.setWindowTitle(title)
|
|
1362
1362
|
|
|
1363
|
+
def is_modified(self) -> bool:
|
|
1364
|
+
"""Return True if mainwindow is modified"""
|
|
1365
|
+
return self.__is_modified
|
|
1366
|
+
|
|
1363
1367
|
def __add_dockwidget(self, child, title: str) -> QW.QDockWidget:
|
|
1364
1368
|
"""Add QDockWidget and toggleViewAction"""
|
|
1365
1369
|
dockwidget, location = child.create_dockwidget(title)
|
|
@@ -1559,9 +1563,7 @@ class DLMainWindow(QW.QMainWindow, AbstractDLControl, metaclass=DLMainWindowMeta
|
|
|
1559
1563
|
if not filename:
|
|
1560
1564
|
return
|
|
1561
1565
|
with qth.qt_try_loadsave_file(self, filename, "save"):
|
|
1562
|
-
|
|
1563
|
-
self.h5inputoutput.save_file(filename)
|
|
1564
|
-
self.set_modified(False)
|
|
1566
|
+
self.save_h5_workspace(filename)
|
|
1565
1567
|
|
|
1566
1568
|
@remote_controlled
|
|
1567
1569
|
def open_h5_files(
|
|
@@ -1667,6 +1669,59 @@ class DLMainWindow(QW.QMainWindow, AbstractDLControl, metaclass=DLMainWindowMeta
|
|
|
1667
1669
|
self.__check_h5file(filename, "load")
|
|
1668
1670
|
self.h5inputoutput.import_files(filenames, False, reset_all)
|
|
1669
1671
|
|
|
1672
|
+
@remote_controlled
|
|
1673
|
+
def load_h5_workspace(self, h5files: list[str], reset_all: bool = False) -> None:
|
|
1674
|
+
"""Load native DataLab HDF5 workspace files without any GUI elements.
|
|
1675
|
+
|
|
1676
|
+
This method can be safely called from the internal console as it does not
|
|
1677
|
+
create any Qt widgets, dialogs, or progress bars. It is designed for
|
|
1678
|
+
programmatic use when loading DataLab workspace files.
|
|
1679
|
+
|
|
1680
|
+
.. warning::
|
|
1681
|
+
|
|
1682
|
+
This method only supports native DataLab HDF5 files. For importing
|
|
1683
|
+
arbitrary HDF5 files (non-native), use the GUI menu or macros with
|
|
1684
|
+
:class:`datalab.control.proxy.RemoteProxy`.
|
|
1685
|
+
|
|
1686
|
+
Args:
|
|
1687
|
+
h5files: List of native DataLab HDF5 filenames
|
|
1688
|
+
reset_all: Reset all application data before importing. Defaults to False.
|
|
1689
|
+
|
|
1690
|
+
Raises:
|
|
1691
|
+
ValueError: If a file is not a valid native DataLab HDF5 file
|
|
1692
|
+
"""
|
|
1693
|
+
for idx, filename in enumerate(h5files):
|
|
1694
|
+
filename = self.__check_h5file(filename, "load")
|
|
1695
|
+
success = self.h5inputoutput.open_file_headless(
|
|
1696
|
+
filename, reset_all=(reset_all and idx == 0)
|
|
1697
|
+
)
|
|
1698
|
+
if not success:
|
|
1699
|
+
raise ValueError(
|
|
1700
|
+
f"File '{filename}' is not a native DataLab HDF5 file. "
|
|
1701
|
+
f"Use the GUI menu or a macro with RemoteProxy to import "
|
|
1702
|
+
f"arbitrary HDF5 files."
|
|
1703
|
+
)
|
|
1704
|
+
# Refresh panel trees after loading
|
|
1705
|
+
self.repopulate_panel_trees()
|
|
1706
|
+
|
|
1707
|
+
@remote_controlled
|
|
1708
|
+
def save_h5_workspace(self, filename: str) -> None:
|
|
1709
|
+
"""Save current workspace to a native DataLab HDF5 file without GUI elements.
|
|
1710
|
+
|
|
1711
|
+
This method can be safely called from the internal console as it does not
|
|
1712
|
+
create any Qt widgets, dialogs, or progress bars. It is designed for
|
|
1713
|
+
programmatic use when saving DataLab workspace files.
|
|
1714
|
+
|
|
1715
|
+
Args:
|
|
1716
|
+
filename: HDF5 filename to save to
|
|
1717
|
+
|
|
1718
|
+
Raises:
|
|
1719
|
+
IOError: If file cannot be saved
|
|
1720
|
+
"""
|
|
1721
|
+
filename = self.__check_h5file(filename, "save")
|
|
1722
|
+
self.h5inputoutput.save_file(filename)
|
|
1723
|
+
self.set_modified(False)
|
|
1724
|
+
|
|
1670
1725
|
@remote_controlled
|
|
1671
1726
|
def import_h5_file(self, filename: str, reset_all: bool | None = None) -> None:
|
|
1672
1727
|
"""Import HDF5 file into DataLab
|
|
@@ -2027,7 +2082,7 @@ class DLMainWindow(QW.QMainWindow, AbstractDLControl, metaclass=DLMainWindowMeta
|
|
|
2027
2082
|
Returns:
|
|
2028
2083
|
True if closed properly, False otherwise
|
|
2029
2084
|
"""
|
|
2030
|
-
if not env.execenv.unattended and self.
|
|
2085
|
+
if not env.execenv.unattended and self.is_modified():
|
|
2031
2086
|
answer = QW.QMessageBox.warning(
|
|
2032
2087
|
self,
|
|
2033
2088
|
_("Quit"),
|
|
@@ -2039,7 +2094,7 @@ class DLMainWindow(QW.QMainWindow, AbstractDLControl, metaclass=DLMainWindowMeta
|
|
|
2039
2094
|
)
|
|
2040
2095
|
if answer == QW.QMessageBox.Yes:
|
|
2041
2096
|
self.save_to_h5_file()
|
|
2042
|
-
if self.
|
|
2097
|
+
if self.is_modified():
|
|
2043
2098
|
return False
|
|
2044
2099
|
elif answer == QW.QMessageBox.Cancel:
|
|
2045
2100
|
return False
|