tomwer 1.2.9__py3-none-any.whl → 1.3.0a0__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.
- orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
- orangecontrib/tomwer/widgets/__init__.py +1 -0
- orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
- orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
- orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
- orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +11 -5
- orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
- orangecontrib/tomwer/widgets/control/NXTomomillOW.py +31 -18
- orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +14 -7
- orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
- orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
- orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
- orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +4 -4
- orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
- orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
- orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +2 -0
- orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
- orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
- orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
- orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
- orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
- orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
- orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
- orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
- orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
- orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
- orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
- orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +9 -8
- orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +3 -3
- orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +179 -169
- orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
- orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
- orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +7 -13
- orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +7 -17
- orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +3 -4
- orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
- orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
- orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
- tomwer/__main__.py +11 -58
- tomwer/app/canvas.py +8 -0
- tomwer/app/canvas_launcher/config.py +13 -11
- tomwer/app/darkref.py +1 -1
- tomwer/app/darkrefpatch.py +1 -1
- tomwer/app/imagekeyeditor.py +5 -5
- tomwer/app/imagekeyupgrader.py +5 -5
- tomwer/app/intensitynormalization.py +2 -2
- tomwer/app/radiostack.py +2 -2
- tomwer/app/zstitching.py +74 -3
- tomwer/core/cluster/cluster.py +26 -0
- tomwer/core/log/logger.py +7 -5
- tomwer/core/process/conditions/filters.py +1 -1
- tomwer/core/process/control/datalistener/datalistener.py +3 -3
- tomwer/core/process/control/nxtomoconcatenate.py +13 -13
- tomwer/core/process/control/nxtomomill.py +83 -25
- tomwer/core/process/control/scantransfer.py +11 -10
- tomwer/core/process/control/scanvalidator.py +3 -2
- tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
- tomwer/core/process/control/test/test_email.py +4 -4
- tomwer/core/process/control/test/test_h52nx_process.py +59 -7
- tomwer/core/process/control/test/test_volume_link.py +64 -64
- tomwer/core/process/control/timer.py +1 -1
- tomwer/core/process/control/volumesymlink.py +200 -200
- tomwer/core/process/edit/darkflatpatch.py +6 -6
- tomwer/core/process/edit/imagekeyeditor.py +17 -18
- tomwer/core/process/icat/__init__.py +0 -0
- tomwer/core/process/icat/createscreenshots.py +100 -0
- tomwer/core/process/icat/gallery.py +377 -0
- tomwer/core/process/icat/icatbase.py +36 -0
- tomwer/core/process/icat/publish.py +228 -0
- tomwer/core/process/icat/screenshots.py +26 -0
- tomwer/core/process/output.py +52 -0
- tomwer/core/process/reconstruction/axis/axis.py +17 -10
- tomwer/core/process/reconstruction/axis/mode.py +4 -0
- tomwer/core/process/reconstruction/axis/params.py +9 -4
- tomwer/core/process/reconstruction/darkref/darkrefs.py +8 -6
- tomwer/core/process/reconstruction/darkref/darkrefscopy.py +1 -1
- tomwer/core/process/reconstruction/darkref/params.py +1 -1
- tomwer/core/process/reconstruction/lamino/tofu.py +4 -4
- tomwer/core/process/reconstruction/nabu/castvolume.py +1 -1
- tomwer/core/process/reconstruction/nabu/helical.py +9 -5
- tomwer/core/process/reconstruction/nabu/nabucommon.py +32 -62
- tomwer/core/process/reconstruction/nabu/nabuscores.py +387 -61
- tomwer/core/process/reconstruction/nabu/nabuslices.py +33 -21
- tomwer/core/process/reconstruction/nabu/nabuvolume.py +37 -14
- tomwer/core/process/reconstruction/nabu/settings.py +2 -2
- tomwer/core/process/reconstruction/nabu/utils.py +129 -24
- tomwer/core/process/reconstruction/output.py +108 -0
- tomwer/core/process/reconstruction/saaxis/saaxis.py +233 -263
- tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +140 -86
- tomwer/core/process/reconstruction/scores/params.py +4 -1
- tomwer/core/process/reconstruction/scores/scores.py +13 -0
- tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
- tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
- tomwer/core/process/reconstruction/test/test_darkref_copy.py +3 -3
- tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
- tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
- tomwer/core/process/stitching/nabustitcher.py +2 -2
- tomwer/core/process/test/test_axis.py +6 -6
- tomwer/core/process/test/test_dark_and_flat.py +10 -7
- tomwer/core/process/test/test_data_transfer.py +7 -6
- tomwer/core/process/test/test_nabu.py +4 -4
- tomwer/core/process/test/test_normalization.py +2 -2
- tomwer/core/scan/edfscan.py +4 -1
- tomwer/core/scan/hdf5scan.py +19 -500
- tomwer/core/scan/nxtomoscan.py +532 -0
- tomwer/core/scan/scanbase.py +42 -20
- tomwer/core/scan/scanfactory.py +13 -13
- tomwer/core/scan/test/test_future_scan.py +2 -2
- tomwer/core/scan/test/test_h5.py +12 -10
- tomwer/core/scan/test/test_process_registration.py +2 -2
- tomwer/core/scan/test/test_scan.py +4 -3
- tomwer/core/settings.py +20 -0
- tomwer/core/test/test_scanutils.py +8 -7
- tomwer/core/test/test_utils.py +33 -26
- tomwer/core/utils/__init__.py +0 -466
- tomwer/core/utils/deprecation.py +1 -1
- tomwer/core/utils/dictutils.py +14 -0
- tomwer/core/utils/lbsram.py +35 -0
- tomwer/core/utils/nxtomoutils.py +1 -1
- tomwer/core/utils/scanutils.py +6 -6
- tomwer/core/utils/spec.py +263 -0
- tomwer/core/volume/volumefactory.py +2 -2
- tomwer/gui/cluster/slurm.py +260 -60
- tomwer/gui/cluster/test/test_cluster.py +13 -0
- tomwer/gui/cluster/test/test_supervisor.py +2 -2
- tomwer/gui/configuration/__init__.py +0 -0
- tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
- tomwer/gui/configuration/level.py +22 -0
- tomwer/gui/control/actions.py +54 -0
- tomwer/gui/control/datalist.py +78 -16
- tomwer/gui/control/datalistener.py +4 -16
- tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
- tomwer/gui/control/history.py +2 -2
- tomwer/gui/control/observations.py +2 -2
- tomwer/gui/control/reducedarkflatselector.py +1 -1
- tomwer/gui/control/selectorwidgetbase.py +36 -9
- tomwer/gui/control/serie/seriecreator.py +5 -22
- tomwer/gui/control/test/test_email.py +1 -1
- tomwer/gui/control/test/test_scanvalidator.py +6 -5
- tomwer/gui/control/test/test_single_tomo_obj.py +2 -2
- tomwer/gui/control/tomoobjdisplaymode.py +8 -0
- tomwer/gui/debugtools/datasetgenerator.py +3 -3
- tomwer/gui/edit/dkrfpatch.py +16 -22
- tomwer/gui/edit/imagekeyeditor.py +8 -11
- tomwer/gui/edit/nxtomoeditor.py +111 -44
- tomwer/gui/edit/nxtomowarmer.py +4 -4
- tomwer/gui/edit/test/test_dkrf_patch.py +7 -7
- tomwer/gui/edit/test/test_image_key_editor.py +3 -3
- tomwer/gui/edit/test/test_nx_editor.py +40 -16
- tomwer/gui/icat/__init__.py +0 -0
- tomwer/gui/icat/createscreenshots.py +80 -0
- tomwer/gui/icat/gallery.py +214 -0
- tomwer/gui/icat/publish.py +187 -0
- tomwer/gui/reconstruction/axis/axis.py +171 -57
- tomwer/gui/reconstruction/axis/radioaxis.py +80 -95
- tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
- tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
- tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +3 -6
- tomwer/gui/reconstruction/nabu/castvolume.py +1 -1
- tomwer/gui/reconstruction/nabu/check.py +9 -9
- tomwer/gui/reconstruction/nabu/helical.py +29 -12
- tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
- tomwer/gui/reconstruction/nabu/nabuconfig/output.py +110 -33
- tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +9 -12
- tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +219 -29
- tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +3 -6
- tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
- tomwer/gui/reconstruction/nabu/slices.py +6 -7
- tomwer/gui/reconstruction/nabu/volume.py +22 -10
- tomwer/gui/reconstruction/normalization/intensity.py +15 -23
- tomwer/gui/reconstruction/saaxis/corrangeselector.py +7 -23
- tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
- tomwer/gui/reconstruction/saaxis/saaxis.py +7 -9
- tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +2 -1
- tomwer/gui/reconstruction/scores/control.py +2 -9
- tomwer/gui/reconstruction/scores/scoreplot.py +11 -5
- tomwer/gui/reconstruction/test/test_axis.py +23 -12
- tomwer/gui/reconstruction/test/test_lamino.py +8 -3
- tomwer/gui/reconstruction/test/test_nabu.py +28 -9
- tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
- tomwer/gui/reconstruction/test/test_sadeltabeta.py +2 -2
- tomwer/gui/settings.py +5 -28
- tomwer/gui/stackplot.py +2 -5
- tomwer/gui/stitching/action.py +49 -0
- tomwer/gui/stitching/config/axisparams.py +7 -24
- tomwer/gui/stitching/config/output.py +10 -8
- tomwer/gui/stitching/config/positionoveraxis.py +22 -23
- tomwer/gui/stitching/normalization.py +117 -0
- tomwer/gui/stitching/stitchandbackground.py +4 -6
- tomwer/gui/stitching/stitching.py +265 -43
- tomwer/gui/stitching/stitching_preview.py +62 -5
- tomwer/gui/stitching/stitching_raw.py +2 -5
- tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
- tomwer/gui/utils/buttons.py +112 -29
- tomwer/gui/utils/inputwidget.py +33 -25
- tomwer/gui/utils/scandescription.py +4 -0
- tomwer/gui/utils/step.py +144 -0
- tomwer/gui/utils/unitsystem.py +2 -5
- tomwer/gui/utils/vignettes.py +176 -15
- tomwer/gui/visualization/dataviewer.py +1 -4
- tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
- tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
- tomwer/gui/visualization/scanoverview.py +1 -1
- tomwer/gui/visualization/sinogramviewer.py +1 -10
- tomwer/gui/visualization/test/test_diffviewer.py +3 -3
- tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
- tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
- tomwer/gui/visualization/test/test_stacks.py +3 -3
- tomwer/gui/visualization/test/test_volumeviewer.py +2 -2
- tomwer/io/utils/raw_and_processed_data.py +84 -0
- tomwer/io/utils/tomoobj.py +4 -6
- tomwer/resources/gui/icons/ruler.png +0 -0
- tomwer/resources/gui/icons/ruler.svg +273 -0
- tomwer/resources/gui/icons/short_description.png +0 -0
- tomwer/resources/gui/icons/short_description.svg +58 -0
- tomwer/resources/gui/icons/url.png +0 -0
- tomwer/resources/gui/icons/url.svg +58 -0
- tomwer/synctools/stacks/edit/darkflatpatch.py +2 -2
- tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
- tomwer/synctools/stacks/reconstruction/axis.py +4 -4
- tomwer/synctools/stacks/reconstruction/castvolume.py +2 -2
- tomwer/synctools/stacks/reconstruction/dkrefcopy.py +4 -10
- tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
- tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
- tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
- tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
- tomwer/synctools/test/test_darkRefs.py +7 -58
- tomwer/synctools/test/test_foldertransfer.py +6 -6
- tomwer/synctools/utils/scanstages.py +6 -6
- tomwer/tests/conftest.py +34 -0
- tomwer/tests/datasets.py +13 -0
- tomwer/tests/test_scripts.py +92 -39
- tomwer/tests/utils.py +5 -0
- tomwer/version.py +3 -3
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/METADATA +39 -39
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/RECORD +248 -209
- tomwer/resources/gui/icons/esrf_1.svg +0 -307
- tomwer/resources/gui/icons/triangle.svg +0 -80
- tomwer/synctools/test/test_scanstages.py +0 -162
- tomwer/tests/utils/__init__.py +0 -247
- tomwer/tests/utils/utilstest.py +0 -220
- /tomwer/app/{saaxis.py → multicor.py} +0 -0
- /tomwer/app/{sadeltabeta.py → multipag.py} +0 -0
- /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
- /tomwer-1.2.9-py3.11-nspkg.pth → /tomwer-1.3.0a0-py3.11-nspkg.pth +0 -0
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/LICENSE +0 -0
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/WHEEL +0 -0
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/entry_points.txt +0 -0
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/namespace_packages.txt +0 -0
- {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/top_level.txt +0 -0
tomwer/gui/cluster/slurm.py
CHANGED
@@ -28,13 +28,23 @@ __license__ = "MIT"
|
|
28
28
|
__date__ = "11/10/2021"
|
29
29
|
|
30
30
|
|
31
|
+
import logging
|
31
32
|
from typing import Optional
|
33
|
+
from functools import lru_cache as cache
|
32
34
|
|
33
35
|
from silx.gui import qt
|
34
|
-
from sluurp.utils import get_partitions
|
36
|
+
from sluurp.utils import get_partitions, get_partition_gpus
|
35
37
|
|
36
38
|
from tomwer.core.settings import SlurmSettings, SlurmSettingsMode
|
37
39
|
from tomwer.gui.utils.qt_utils import block_signals
|
40
|
+
from tomwer.gui.configuration.action import (
|
41
|
+
BasicConfigurationAction,
|
42
|
+
ExpertConfigurationAction,
|
43
|
+
)
|
44
|
+
from tomwer.gui.configuration.level import ConfigurationLevel
|
45
|
+
from nxtomomill.io.utils import convert_str_to_tuple
|
46
|
+
|
47
|
+
_logger = logging.getLogger(__name__)
|
38
48
|
|
39
49
|
|
40
50
|
class SlurmSettingsDialog(qt.QDialog):
|
@@ -58,10 +68,7 @@ class SlurmSettingsDialog(qt.QDialog):
|
|
58
68
|
self._buttons.button(qt.QDialogButtonBox.Close).clicked.connect(self.close)
|
59
69
|
|
60
70
|
# connect signal /slot
|
61
|
-
self._mainWidget.sigConfigChanged.connect(self.
|
62
|
-
|
63
|
-
def _configChanged(self, *args, **kwargs):
|
64
|
-
self.sigConfigChanged.emit()
|
71
|
+
self._mainWidget.sigConfigChanged.connect(self.sigConfigChanged)
|
65
72
|
|
66
73
|
def isSlurmActive(self):
|
67
74
|
return self._mainWidget.isSlurmActive()
|
@@ -84,6 +91,29 @@ class SlurmSettingsWindow(qt.QMainWindow):
|
|
84
91
|
|
85
92
|
def __init__(self, parent: Optional[qt.QWidget] = None) -> None:
|
86
93
|
super().__init__(parent)
|
94
|
+
|
95
|
+
# define toolbar
|
96
|
+
toolbar = qt.QToolBar(self)
|
97
|
+
self.addToolBar(qt.Qt.TopToolBarArea, toolbar)
|
98
|
+
|
99
|
+
self.__configurationModesAction = qt.QAction(self)
|
100
|
+
self.__configurationModesAction.setCheckable(False)
|
101
|
+
menu = qt.QMenu(self)
|
102
|
+
self.__configurationModesAction.setMenu(menu)
|
103
|
+
toolbar.addAction(self.__configurationModesAction)
|
104
|
+
|
105
|
+
self.__configurationModesGroup = qt.QActionGroup(self)
|
106
|
+
self.__configurationModesGroup.setExclusive(True)
|
107
|
+
self.__configurationModesGroup.triggered.connect(self._userModeChanged)
|
108
|
+
|
109
|
+
self._basicConfigAction = BasicConfigurationAction(toolbar)
|
110
|
+
menu.addAction(self._basicConfigAction)
|
111
|
+
self.__configurationModesGroup.addAction(self._basicConfigAction)
|
112
|
+
self._expertConfiguration = ExpertConfigurationAction(toolbar)
|
113
|
+
menu.addAction(self._expertConfiguration)
|
114
|
+
self.__configurationModesGroup.addAction(self._expertConfiguration)
|
115
|
+
|
116
|
+
# define maini widget
|
87
117
|
self._mainWidget = qt.QWidget(self)
|
88
118
|
self._mainWidget.setLayout(qt.QFormLayout())
|
89
119
|
|
@@ -92,13 +122,15 @@ class SlurmSettingsWindow(qt.QMainWindow):
|
|
92
122
|
self._modeCombox.addItems(SlurmSettingsMode.values())
|
93
123
|
self._modeCombox.setCurrentText(SlurmSettingsMode.GENERIC.value)
|
94
124
|
|
95
|
-
self._settingsWidget = SlurmSettingsWidget(self)
|
125
|
+
self._settingsWidget = SlurmSettingsWidget(self, jobLimitation=None)
|
96
126
|
self._mainWidget.layout().addRow(self._settingsWidget)
|
97
127
|
|
98
128
|
self.setCentralWidget(self._mainWidget)
|
99
129
|
|
100
130
|
# set up
|
101
131
|
self._reloadPredefinedSettings()
|
132
|
+
self._basicConfigAction.setChecked(True)
|
133
|
+
self._userModeChanged(self._basicConfigAction)
|
102
134
|
|
103
135
|
# connect signal / slot
|
104
136
|
self._modeCombox.currentIndexChanged.connect(self._reloadPredefinedSettings)
|
@@ -106,6 +138,17 @@ class SlurmSettingsWindow(qt.QMainWindow):
|
|
106
138
|
# when the settings widget is edited them we automatically move to 'manual' settings. To notify visually the user
|
107
139
|
self._settingsWidget.sigConfigChanged.connect(self._switchToManual)
|
108
140
|
|
141
|
+
def _userModeChanged(self, action):
|
142
|
+
self.__configurationModesAction.setIcon(action.icon())
|
143
|
+
self.__configurationModesAction.setToolTip(action.tooltip())
|
144
|
+
if action is self._basicConfigAction:
|
145
|
+
level = ConfigurationLevel.OPTIONAL
|
146
|
+
elif action is self._expertConfiguration:
|
147
|
+
level = ConfigurationLevel.ADVANCED
|
148
|
+
else:
|
149
|
+
raise NotImplementedError
|
150
|
+
self._settingsWidget.setConfigurationLevel(level)
|
151
|
+
|
109
152
|
def _reloadPredefinedSettings(self, *args, **kkwargs):
|
110
153
|
"""
|
111
154
|
reload settings from some predefined configuration
|
@@ -185,7 +228,8 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
185
228
|
# n workers
|
186
229
|
self._nWorkers = qt.QSpinBox(self)
|
187
230
|
self._nWorkers.setRange(1, 100)
|
188
|
-
self.
|
231
|
+
self._nWorkersLabel = qt.QLabel("number of task", self)
|
232
|
+
self.layout().addRow(self._nWorkersLabel, self._nWorkers)
|
189
233
|
|
190
234
|
# ncores active
|
191
235
|
self._nCores = qt.QSpinBox(self)
|
@@ -218,17 +262,34 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
218
262
|
self._wallTimeLabel = qt.QLabel("wall time", self)
|
219
263
|
self.layout().addRow(self._wallTimeLabel, self._wallTimeQLE)
|
220
264
|
|
221
|
-
# python exe
|
265
|
+
# python exe / modules
|
266
|
+
self._preProcessingGroup = qt.QGroupBox("pre-processing", self)
|
267
|
+
self._preProcessingGroup.setLayout(qt.QFormLayout())
|
268
|
+
self._preProcessingButtonGroup = qt.QButtonGroup(self)
|
269
|
+
self._preProcessingButtonGroup.setExclusive(True)
|
270
|
+
self.layout().addRow(self._preProcessingGroup)
|
271
|
+
|
272
|
+
# python venv
|
222
273
|
self._pythonVenv = qt.QLineEdit("", self)
|
223
|
-
self.
|
224
|
-
|
225
|
-
)
|
274
|
+
self._sourceScriptCB = qt.QRadioButton("source script (python venv)", self)
|
275
|
+
self._preProcessingButtonGroup.addButton(self._sourceScriptCB)
|
276
|
+
self._preProcessingGroup.layout().addRow(self._sourceScriptCB, self._pythonVenv)
|
226
277
|
self._pythonVenv.setToolTip(
|
227
278
|
"""
|
228
279
|
Optional path to a bash script to source before executing the script.
|
229
280
|
"""
|
230
281
|
)
|
231
282
|
|
283
|
+
self._modulesQLE = qt.QLineEdit("tomotools,", self)
|
284
|
+
self._modulesCB = qt.QRadioButton("module to load", self)
|
285
|
+
self._preProcessingButtonGroup.addButton(self._modulesCB)
|
286
|
+
self._preProcessingGroup.layout().addRow(self._modulesCB, self._modulesQLE)
|
287
|
+
self._preProcessingGroup.setToolTip(
|
288
|
+
"""
|
289
|
+
Optional list of modules to load before executing the script.
|
290
|
+
"""
|
291
|
+
)
|
292
|
+
|
232
293
|
# job name
|
233
294
|
self._jobName = qt.QLineEdit("", self)
|
234
295
|
self._jobName.setToolTip(
|
@@ -257,6 +318,39 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
257
318
|
self._dashboardPortLabel = qt.QLabel("dashboard port", self)
|
258
319
|
self.layout().addRow(self._dashboardPortLabel, self._dashboardPort)
|
259
320
|
|
321
|
+
# sbatch advance parameters
|
322
|
+
self._sbatchAdvancedParameters = qt.QGroupBox("sbatch advanced settings", self)
|
323
|
+
self._sbatchAdvancedParameters.setLayout(qt.QFormLayout())
|
324
|
+
self.layout().addRow(self._sbatchAdvancedParameters)
|
325
|
+
|
326
|
+
## export parameter
|
327
|
+
self._exportValueCM = qt.QComboBox(self)
|
328
|
+
self._exportValueCM.addItems(("NONE", "ALL"))
|
329
|
+
self._exportValueCM.setItemData(
|
330
|
+
self._exportValueCM.findText("NONE"),
|
331
|
+
"""
|
332
|
+
Only SLURM_* variables from the user environment will be defined. User must use absolute path to the binary to be executed that will define the environment. User can not specify explicit environment variables with "NONE". However, Slurm will then implicitly attempt to load the user's environment on the node where the script is being executed, as if --get-user-env was specified. \nThis option is particularly important for jobs that are submitted on one cluster and execute on a different cluster (e.g. with different paths). To avoid steps inheriting environment export settings (e.g. "NONE") from sbatch command, the environment variable SLURM_EXPORT_ENV should be set to "ALL" in the job script.
|
333
|
+
""",
|
334
|
+
)
|
335
|
+
self._exportValueCM.setItemData(
|
336
|
+
self._exportValueCM.findText("ALL"),
|
337
|
+
"""
|
338
|
+
All of the user's environment will be loaded (either from the caller's environment or from a clean environment if --get-user-env is specified). \nCan fail when submitting cross-platform jobs and user has some module loaded
|
339
|
+
""",
|
340
|
+
)
|
341
|
+
self._exportValueCM.setCurrentText("NONE")
|
342
|
+
self._sbatchAdvancedParameters.layout().addRow("--export", self._exportValueCM)
|
343
|
+
|
344
|
+
## gpu card
|
345
|
+
self._gpuCardCB = qt.QComboBox(self)
|
346
|
+
self._gpuCardCB.setToolTip(
|
347
|
+
"Specify a GPU card to be used. Using the -C command from sbatch"
|
348
|
+
)
|
349
|
+
self._gpuCardCB.setEditable(
|
350
|
+
True
|
351
|
+
) # let the user the ability to provide a GPU that is not found for now (expecting he knows what he is doing)
|
352
|
+
self._sbatchAdvancedParameters.layout().addRow("-C (gpu card)", self._gpuCardCB)
|
353
|
+
|
260
354
|
# simplify gui
|
261
355
|
self._wallTimeLabel.hide()
|
262
356
|
self._wallTimeQLE.hide()
|
@@ -266,6 +360,9 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
266
360
|
self._job_nameQLabel.hide()
|
267
361
|
self._portLabel.hide() # for now we don't use the port. This can be done automatically
|
268
362
|
self._port.hide() # for now we don't use the port. This can be done automatically
|
363
|
+
# for now nworker == ntask is not used
|
364
|
+
self._nWorkers.setVisible(False)
|
365
|
+
self._nWorkersLabel.setVisible(False)
|
269
366
|
|
270
367
|
# set up the gui
|
271
368
|
self._nCores.setValue(SlurmSettings.N_CORES_PER_TASK)
|
@@ -276,6 +373,10 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
276
373
|
self._jobName.setText(SlurmSettings.PROJECT_NAME)
|
277
374
|
self._wallTimeQLE.setText(SlurmSettings.DEFAULT_WALLTIME)
|
278
375
|
self._pythonVenv.setText(SlurmSettings.PYTHON_VENV)
|
376
|
+
self._sourceScriptCB.setChecked(True)
|
377
|
+
self._preProcessingModeChanged()
|
378
|
+
self._partitionChanged()
|
379
|
+
self._nGpuChanged()
|
279
380
|
|
280
381
|
# connect signal / slot
|
281
382
|
self._nCores.valueChanged.connect(self._configurationChanged)
|
@@ -286,8 +387,20 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
286
387
|
self._jobName.editingFinished.connect(self._configurationChanged)
|
287
388
|
self._wallTimeQLE.editingFinished.connect(self._configurationChanged)
|
288
389
|
self._pythonVenv.editingFinished.connect(self._configurationChanged)
|
390
|
+
self._modulesQLE.editingFinished.connect(self._configurationChanged)
|
391
|
+
self._preProcessingButtonGroup.buttonClicked.connect(self._configurationChanged)
|
289
392
|
self._port.sigRangeChanged.connect(self._configurationChanged)
|
290
393
|
self._dashboardPort.valueChanged.connect(self._configurationChanged)
|
394
|
+
self._preProcessingButtonGroup.buttonClicked.connect(
|
395
|
+
self._preProcessingModeChanged
|
396
|
+
)
|
397
|
+
self._queue.currentTextChanged.connect(self._partitionChanged)
|
398
|
+
self._nGpu.valueChanged.connect(self._nGpuChanged)
|
399
|
+
self._gpuCardCB.currentTextChanged.connect(self._configurationChanged)
|
400
|
+
|
401
|
+
def _nGpuChanged(self, *args, **kwargs):
|
402
|
+
nGpu = self.getNGPU()
|
403
|
+
self._gpuCardCB.setEnabled(nGpu > 0)
|
291
404
|
|
292
405
|
def _configurationChanged(self, *args, **kwargs):
|
293
406
|
self.sigConfigChanged.emit()
|
@@ -341,60 +454,118 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
341
454
|
self._wallTimeQLE.setText(walltime)
|
342
455
|
|
343
456
|
def getPythonExe(self):
|
344
|
-
|
457
|
+
if self._sourceScriptCB.isChecked():
|
458
|
+
return self._pythonVenv.text()
|
459
|
+
else:
|
460
|
+
return None
|
345
461
|
|
346
462
|
def setPythonExe(self, python_venv):
|
347
463
|
self._pythonVenv.setText(python_venv)
|
464
|
+
if python_venv != "":
|
465
|
+
self._sourceScriptCB.setChecked(True)
|
466
|
+
|
467
|
+
def getModulesToLoad(self) -> tuple:
|
468
|
+
if self._modulesCB.isChecked():
|
469
|
+
return convert_str_to_tuple(self._modulesQLE.text())
|
470
|
+
else:
|
471
|
+
return tuple()
|
472
|
+
|
473
|
+
def setModulesToLoad(self, modules: tuple):
|
474
|
+
if not isinstance(modules, (tuple, list)):
|
475
|
+
raise TypeError(
|
476
|
+
f"modules is expected to be a tuple or a list. Get {type(modules)} instead"
|
477
|
+
)
|
478
|
+
self._modulesQLE.setText(str(modules))
|
479
|
+
if len(modules) > 0:
|
480
|
+
self._modulesCB.setChecked(True)
|
481
|
+
|
482
|
+
def getGpuCard(self) -> Optional[str]:
|
483
|
+
card = self._gpuCardCB.currentText()
|
484
|
+
if card == "any" or self._nGpu == 0:
|
485
|
+
return None
|
486
|
+
else:
|
487
|
+
return card
|
488
|
+
|
489
|
+
def getSBatchExtraParams(self):
|
490
|
+
return {
|
491
|
+
"export": self._exportValueCM.currentText(),
|
492
|
+
"gpu_card": self.getGpuCard(),
|
493
|
+
}
|
494
|
+
|
495
|
+
def setSBatchExtraParams(self, params: dict):
|
496
|
+
export_ = params.get("export", None)
|
497
|
+
if export_ is not None:
|
498
|
+
index = self._exportValueCM.findText(export_)
|
499
|
+
if index >= 0:
|
500
|
+
self._exportValueCM.setCurrentIndex(index)
|
501
|
+
gpu_card = params.get("gpu_card", None)
|
502
|
+
if gpu_card is not None:
|
503
|
+
index = self._gpuCardCB.findText("gpu_card")
|
504
|
+
if index >= 0:
|
505
|
+
self._gpuCardCB.setCurrentIndex(index)
|
506
|
+
else:
|
507
|
+
# policy when setting the extra params: if doesn't exists / found then won't set it.
|
508
|
+
# because they can be part of .ows, this parameter is hidden by default.
|
509
|
+
# so safer to use 'any' in the case it is unknown (debatable).
|
510
|
+
_logger.warning(f"unable to find gpu {gpu_card}. Won't set it")
|
348
511
|
|
349
512
|
def setConfiguration(self, config: dict) -> None:
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
513
|
+
with block_signals(self):
|
514
|
+
active_slurm = config.get("active_slurm", None)
|
515
|
+
if active_slurm is not None:
|
516
|
+
self._slurmCB.setChecked(active_slurm)
|
517
|
+
|
518
|
+
n_cores = config.get("cpu-per-task", None)
|
519
|
+
if n_cores is not None:
|
520
|
+
self.setNCores(n_cores)
|
521
|
+
|
522
|
+
n_workers = config.get("n_tasks", None)
|
523
|
+
if n_workers is not None:
|
524
|
+
self.setNWorkers(n_workers)
|
525
|
+
|
526
|
+
memory = config.get("memory", None)
|
527
|
+
if memory is not None:
|
528
|
+
if isinstance(memory, str):
|
529
|
+
memory = memory.replace(" ", "").lower().rstrip("gb")
|
530
|
+
self.setMemory(int(memory))
|
531
|
+
|
532
|
+
queue_ = config.get("partition", None)
|
533
|
+
if queue_ is not None:
|
534
|
+
queue_ = queue_.rstrip("'").rstrip('"')
|
535
|
+
queue_ = queue_.lstrip("'").lstrip('"')
|
536
|
+
self.setQueue(queue_)
|
537
|
+
|
538
|
+
n_gpu = config.get("n_gpus", None)
|
539
|
+
if n_gpu is not None:
|
540
|
+
self.setNGPU(int(n_gpu))
|
541
|
+
|
542
|
+
project_name = config.get("job_name", None)
|
543
|
+
if project_name is not None:
|
544
|
+
self.setProjectName(project_name)
|
545
|
+
|
546
|
+
wall_time = config.get("walltime", None)
|
547
|
+
if wall_time is not None:
|
548
|
+
self.setWallTime(wall_time)
|
549
|
+
|
550
|
+
python_venv = config.get("python_venv", None)
|
551
|
+
if python_venv is not None:
|
552
|
+
python_venv = python_venv.rstrip("'").rstrip('"')
|
553
|
+
python_venv = python_venv.lstrip("'").lstrip('"')
|
554
|
+
self.setPythonExe(python_venv)
|
555
|
+
|
556
|
+
modules = config.get("modules", None)
|
557
|
+
if modules is not None:
|
558
|
+
modules = convert_str_to_tuple(modules)
|
559
|
+
self.setModulesToLoad(modules)
|
560
|
+
|
561
|
+
sbatch_extra_params = config.get("sbatch_extra_params", {})
|
562
|
+
self.setSBatchExtraParams(sbatch_extra_params)
|
563
|
+
|
564
|
+
n_jobs = config.get("n_jobs", None)
|
565
|
+
if n_jobs is not None:
|
566
|
+
self.setNJobs(n_jobs)
|
567
|
+
self._preProcessingModeChanged() # make sure modules and python venv is enabled according to the activate mode
|
568
|
+
|
398
569
|
self.sigConfigChanged.emit()
|
399
570
|
|
400
571
|
def getConfiguration(self) -> dict:
|
@@ -408,6 +579,8 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
408
579
|
"job_name": self.getProjectName(),
|
409
580
|
"walltime": self.getWallTime(),
|
410
581
|
"python_venv": self.getPythonExe(),
|
582
|
+
"modules": self.getModulesToLoad(),
|
583
|
+
"sbatch_extra_params": self.getSBatchExtraParams(),
|
411
584
|
}
|
412
585
|
|
413
586
|
def getSlurmClusterConfiguration(self):
|
@@ -415,6 +588,33 @@ class SlurmSettingsWidget(qt.QWidget):
|
|
415
588
|
|
416
589
|
return SlurmClusterConfiguration().from_dict(self.getConfiguration())
|
417
590
|
|
591
|
+
def _preProcessingModeChanged(self):
|
592
|
+
self._modulesQLE.setEnabled(self._modulesCB.isChecked())
|
593
|
+
self._pythonVenv.setEnabled(self._sourceScriptCB.isChecked())
|
594
|
+
|
595
|
+
def setConfigurationLevel(self, level: ConfigurationLevel):
|
596
|
+
self._sbatchAdvancedParameters.setVisible(level >= ConfigurationLevel.ADVANCED)
|
597
|
+
|
598
|
+
def _partitionChanged(self, *args, **kwargs):
|
599
|
+
partition = self.getQueue()
|
600
|
+
gpus = self._getGpus(partition=partition)
|
601
|
+
self._gpuCardCB.clear()
|
602
|
+
self._gpuCardCB.addItems(gpus)
|
603
|
+
self._gpuCardCB.setCurrentText("any")
|
604
|
+
|
605
|
+
@cache(maxsize=None)
|
606
|
+
def _getGpus(self, partition) -> tuple:
|
607
|
+
try:
|
608
|
+
gpus = get_partition_gpus(partition)
|
609
|
+
except Exception as e:
|
610
|
+
_logger.error(f"Failed to detect GPU on {partition}. Error is {e}")
|
611
|
+
gpus = ("any",)
|
612
|
+
else:
|
613
|
+
gpus = list(gpus) + [
|
614
|
+
"any",
|
615
|
+
]
|
616
|
+
return gpus
|
617
|
+
|
418
618
|
|
419
619
|
class _PortRangeSelection(qt.QWidget):
|
420
620
|
sigRangeChanged = qt.Signal()
|
@@ -61,9 +61,14 @@ class TestSlurmWidget(TestCaseQt):
|
|
61
61
|
"partition": SlurmSettings.PARTITION,
|
62
62
|
"n_gpus": SlurmSettings.N_GPUS_PER_WORKER,
|
63
63
|
"job_name": "tomwer_{scan}_-_{process}_-_{info}",
|
64
|
+
"modules": tuple(),
|
64
65
|
"n_jobs": 1,
|
65
66
|
"python_venv": "/scisoft/tomotools/activate stable",
|
66
67
|
"walltime": "01:00:00",
|
68
|
+
"sbatch_extra_params": {
|
69
|
+
"export": "NONE",
|
70
|
+
"gpu_card": None,
|
71
|
+
},
|
67
72
|
}
|
68
73
|
assert dict_res == expected_dict, f"{dict_res} vs {expected_dict}"
|
69
74
|
|
@@ -75,6 +80,10 @@ class TestSlurmWidget(TestCaseQt):
|
|
75
80
|
"memory": 156,
|
76
81
|
"partition": "test-queue",
|
77
82
|
"n_gpus": 5,
|
83
|
+
"modules": "mymodule, mysecond/10.3",
|
84
|
+
"sbatch_extra_params": {
|
85
|
+
"export": "ALL",
|
86
|
+
},
|
78
87
|
}
|
79
88
|
)
|
80
89
|
|
@@ -83,6 +92,10 @@ class TestSlurmWidget(TestCaseQt):
|
|
83
92
|
assert self.slurmWidget.getMemory() == 156
|
84
93
|
assert self.slurmWidget.getQueue() == "test-queue"
|
85
94
|
assert self.slurmWidget.getNGPU() == 5
|
95
|
+
assert self.slurmWidget.getSBatchExtraParams() == {
|
96
|
+
"export": "ALL",
|
97
|
+
"gpu_card": None,
|
98
|
+
}
|
86
99
|
|
87
100
|
|
88
101
|
def test_SlurmSettingsWindow(qtapp): # noqa F811
|
@@ -36,7 +36,7 @@ from silx.gui import qt
|
|
36
36
|
from silx.gui.utils.testutils import TestCaseQt
|
37
37
|
|
38
38
|
from tomwer.core.futureobject import FutureTomwerObject
|
39
|
-
from tomwer.core.utils.scanutils import
|
39
|
+
from tomwer.core.utils.scanutils import MockNXtomo
|
40
40
|
from tomwer.gui.cluster.supervisor import FutureTomwerScanObserverWidget
|
41
41
|
|
42
42
|
|
@@ -52,7 +52,7 @@ class TestSupervisor(TestCaseQt):
|
|
52
52
|
self._future_tomo_objs = []
|
53
53
|
for i in range(5):
|
54
54
|
# create scan
|
55
|
-
scan =
|
55
|
+
scan = MockNXtomo(
|
56
56
|
scan_path=os.path.join(self.tempdir, f"scan_test{i}"),
|
57
57
|
n_proj=10,
|
58
58
|
n_ini_proj=10,
|
File without changes
|
@@ -1,35 +1,4 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
# /*##########################################################################
|
3
|
-
#
|
4
|
-
# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
|
5
|
-
#
|
6
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
-
# of this software and associated documentation files (the "Software"), to deal
|
8
|
-
# in the Software without restriction, including without limitation the rights
|
9
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
-
# copies of the Software, and to permit persons to whom the Software is
|
11
|
-
# furnished to do so, subject to the following conditions:
|
12
|
-
#
|
13
|
-
# The above copyright notice and this permission notice shall be included in
|
14
|
-
# all copies or substantial portions of the Software.
|
15
|
-
#
|
16
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
-
# THE SOFTWARE.
|
23
|
-
#
|
24
|
-
# ###########################################################################*/
|
25
|
-
|
26
|
-
__authors__ = ["H. Payno"]
|
27
|
-
__license__ = "MIT"
|
28
|
-
__date__ = "04/08/2020"
|
29
|
-
|
30
|
-
|
31
1
|
from silx.gui import qt
|
32
|
-
|
33
2
|
from tomwer.gui import icons as tomwer_icons
|
34
3
|
|
35
4
|
|
@@ -114,7 +83,7 @@ class FilterAction(qt.QAction):
|
|
114
83
|
qt.QAction.__init__(self, icon, "filter configuration", parent)
|
115
84
|
self.setToolTip(
|
116
85
|
"If activated will only display the configuration"
|
117
|
-
"for the active nabu
|
86
|
+
"for the active nabu step"
|
118
87
|
)
|
119
88
|
self.setCheckable(True)
|
120
89
|
self.setShortcut(qt.QKeySequence(qt.Qt.Key_F))
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from silx.utils.enum import Enum as _Enum
|
2
|
+
|
3
|
+
|
4
|
+
class ConfigurationLevel(_Enum):
|
5
|
+
REQUIRED = "required"
|
6
|
+
OPTIONAL = "optional"
|
7
|
+
ADVANCED = "advanced"
|
8
|
+
|
9
|
+
def _get_num_value(self) -> int:
|
10
|
+
if self is self.REQUIRED:
|
11
|
+
return 0
|
12
|
+
elif self is self.OPTIONAL:
|
13
|
+
return 1
|
14
|
+
elif self is self.ADVANCED:
|
15
|
+
return 2
|
16
|
+
|
17
|
+
def __le__(self, other):
|
18
|
+
if not isinstance(other, ConfigurationLevel):
|
19
|
+
raise TypeError(
|
20
|
+
f"other is expected to be an instance of ConfigurationLevel. {type(other)} provided"
|
21
|
+
)
|
22
|
+
return self._get_num_value() <= other._get_num_value()
|
tomwer/gui/control/actions.py
CHANGED
@@ -29,8 +29,10 @@ __date__ = "23/03/2021"
|
|
29
29
|
|
30
30
|
|
31
31
|
from silx.gui import qt
|
32
|
+
from functools import partial
|
32
33
|
|
33
34
|
from tomwer.gui import icons as tomwer_icons
|
35
|
+
from tomwer.gui.control.tomoobjdisplaymode import DisplayMode
|
34
36
|
|
35
37
|
|
36
38
|
class NXTomomillParamsAction(qt.QAction):
|
@@ -68,3 +70,55 @@ class CFGFileActiveLabel(qt.QLabel):
|
|
68
70
|
|
69
71
|
def setInactive(self):
|
70
72
|
self.setActive(active=False)
|
73
|
+
|
74
|
+
|
75
|
+
class TomoObjDisplayModeToolButton(qt.QToolButton):
|
76
|
+
"""
|
77
|
+
Button to change the way tomo object are displayed.
|
78
|
+
Either using the full url or only a 'short' description.
|
79
|
+
"""
|
80
|
+
|
81
|
+
sigDisplayModeChanged = qt.Signal(str)
|
82
|
+
|
83
|
+
_SHORT_DESC_TOOLTIP = "Use a short description of the tomo object. Two different scans can have the same short desciption"
|
84
|
+
_URL_TOOLTIP = (
|
85
|
+
"Use the full url to display the tomo object. Url is guaranted to be unique."
|
86
|
+
)
|
87
|
+
|
88
|
+
def __init__(self, parent=None) -> None:
|
89
|
+
super().__init__(parent)
|
90
|
+
|
91
|
+
self._shortDescIcon = tomwer_icons.getQIcon("short_description")
|
92
|
+
shortDescAction = qt.QAction(self._shortDescIcon, "short description", self)
|
93
|
+
shortDescAction.setToolTip(self._SHORT_DESC_TOOLTIP)
|
94
|
+
|
95
|
+
self._urlIcon = tomwer_icons.getQIcon("url")
|
96
|
+
urlDescAction = qt.QAction(self._urlIcon, "url", self)
|
97
|
+
urlDescAction.setToolTip(self._URL_TOOLTIP)
|
98
|
+
|
99
|
+
menu = qt.QMenu(self)
|
100
|
+
menu.addAction(shortDescAction)
|
101
|
+
menu.addAction(urlDescAction)
|
102
|
+
self.setMenu(menu)
|
103
|
+
self.setPopupMode(qt.QToolButton.InstantPopup)
|
104
|
+
|
105
|
+
# set up
|
106
|
+
self.setDisplayMode(DisplayMode.SHORT)
|
107
|
+
|
108
|
+
# connect signal / slot
|
109
|
+
shortDescAction.triggered.connect(
|
110
|
+
partial(self.setDisplayMode, DisplayMode.SHORT)
|
111
|
+
)
|
112
|
+
urlDescAction.triggered.connect(partial(self.setDisplayMode, DisplayMode.URL))
|
113
|
+
|
114
|
+
def setDisplayMode(self, mode: DisplayMode):
|
115
|
+
mode = DisplayMode.from_value(mode)
|
116
|
+
if mode is DisplayMode.SHORT:
|
117
|
+
self.setIcon(self._shortDescIcon)
|
118
|
+
self.setToolTip(self._SHORT_DESC_TOOLTIP)
|
119
|
+
elif mode is DisplayMode.URL:
|
120
|
+
self.setIcon(self._urlIcon)
|
121
|
+
self.setToolTip(self._URL_TOOLTIP)
|
122
|
+
else:
|
123
|
+
raise ValueError(f"display mode {mode} not handled")
|
124
|
+
self.sigDisplayModeChanged.emit(mode.value)
|