tomwer 1.4.18__py3-none-any.whl → 1.5.0__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/simple_volume_local_reconstruction.ows +11 -8
- orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +12 -9
- orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +11 -0
- orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +1 -1
- orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +21 -10
- tomwer/app/axis.py +1 -1
- tomwer/app/reducedarkflat.py +2 -2
- tomwer/core/process/control/datalistener/rpcserver.py +2 -8
- tomwer/core/process/drac/binning.py +2 -2
- tomwer/core/process/drac/output.py +1 -1
- tomwer/core/process/edit/imagekeyeditor.py +4 -6
- tomwer/core/process/edit/nxtomoeditor.py +58 -20
- tomwer/core/process/output.py +6 -5
- tomwer/core/process/reconstruction/axis/anglemode.py +2 -2
- tomwer/core/process/reconstruction/axis/axis.py +1 -0
- tomwer/core/process/reconstruction/axis/mode.py +2 -2
- tomwer/core/process/reconstruction/axis/params.py +4 -4
- tomwer/core/process/reconstruction/axis/projectiontype.py +1 -1
- tomwer/core/process/reconstruction/axis/side.py +1 -1
- tomwer/core/process/reconstruction/darkref/darkrefs.py +2 -2
- tomwer/core/process/reconstruction/darkref/darkrefscopy.py +1 -1
- tomwer/core/process/reconstruction/darkref/params.py +2 -3
- tomwer/core/process/reconstruction/nabu/castvolume.py +4 -1
- tomwer/core/process/reconstruction/nabu/helical.py +3 -1
- tomwer/core/process/reconstruction/nabu/nabucommon.py +2 -2
- tomwer/core/process/reconstruction/nabu/nabuscores.py +1 -1
- tomwer/core/process/reconstruction/nabu/nabuslices.py +4 -4
- tomwer/core/process/reconstruction/nabu/plane.py +2 -2
- tomwer/core/process/reconstruction/nabu/target.py +1 -1
- tomwer/core/process/reconstruction/nabu/test/test_castvolume.py +2 -0
- tomwer/core/process/reconstruction/nabu/test/test_nabu_utils.py +9 -0
- tomwer/core/process/reconstruction/nabu/utils.py +18 -14
- tomwer/core/process/reconstruction/normalization/normalization.py +1 -1
- tomwer/core/process/reconstruction/normalization/params.py +4 -4
- tomwer/core/process/reconstruction/output.py +2 -2
- tomwer/core/process/reconstruction/saaxis/params.py +3 -3
- tomwer/core/process/reconstruction/saaxis/saaxis.py +5 -1
- tomwer/core/process/reconstruction/scores/params.py +2 -2
- tomwer/core/process/reconstruction/scores/scores.py +3 -3
- tomwer/core/process/reconstruction/tests/test_axis.py +1 -1
- tomwer/core/process/reconstruction/tests/test_saaxis.py +56 -66
- tomwer/core/process/stitching/metadataholder.py +5 -5
- tomwer/core/process/stitching/nabustitcher.py +1 -4
- tomwer/core/process/tests/test_normalization.py +2 -1
- tomwer/core/scan/edfscan.py +3 -3
- tomwer/core/scan/nxtomoscan.py +3 -3
- tomwer/core/scan/scanbase.py +3 -3
- tomwer/core/scan/scantype.py +1 -1
- tomwer/core/settings.py +1 -1
- tomwer/core/tomwer_object.py +1 -1
- tomwer/core/utils/nxtomoutils.py +2 -2
- tomwer/core/utils/spec.py +6 -3
- tomwer/gui/cluster/slurm.py +12 -3
- tomwer/gui/configuration/level.py +1 -1
- tomwer/gui/control/actions.py +1 -1
- tomwer/gui/control/datadiscovery.py +1 -1
- tomwer/gui/control/datalist.py +1 -1
- tomwer/gui/control/reducedarkflatselector.py +4 -4
- tomwer/gui/control/series/seriescreator.py +5 -5
- tomwer/gui/control/tomoobjdisplaymode.py +1 -1
- tomwer/gui/dataportal/gallery.py +6 -6
- tomwer/gui/edit/imagekeyeditor.py +7 -9
- tomwer/gui/edit/nxtomoeditor.py +420 -112
- tomwer/gui/edit/tests/test_nx_editor.py +155 -83
- tomwer/gui/reconstruction/axis/CalculationWidget.py +1 -1
- tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +12 -8
- tomwer/gui/reconstruction/axis/EstimatedCorComboBox.py +2 -2
- tomwer/gui/reconstruction/axis/InputWidget.py +3 -3
- tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -2
- tomwer/gui/reconstruction/nabu/castvolume.py +16 -1
- tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
- tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +6 -6
- tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +1 -1
- tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +5 -5
- tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +2 -4
- tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +78 -52
- tomwer/gui/reconstruction/nabu/nabuflow.py +3 -13
- tomwer/gui/reconstruction/nabu/slices.py +11 -11
- tomwer/gui/reconstruction/nabu/test/test_cast_volume.py +19 -0
- tomwer/gui/reconstruction/nabu/volume.py +1 -1
- tomwer/gui/reconstruction/normalization/intensity.py +8 -12
- tomwer/gui/reconstruction/saaxis/corrangeselector.py +2 -2
- tomwer/gui/reconstruction/saaxis/dimensionwidget.py +71 -67
- tomwer/gui/reconstruction/sacommon.py +1 -1
- tomwer/gui/reconstruction/scores/scoreplot.py +18 -10
- tomwer/gui/reconstruction/tests/test_saaxis.py +18 -16
- tomwer/gui/stitching/SingleAxisStitchingWidget.py +8 -8
- tomwer/gui/stitching/StitchingOptionsWidget.py +1 -1
- tomwer/gui/stitching/alignment.py +8 -8
- tomwer/gui/stitching/config/axisparams.py +2 -2
- tomwer/gui/stitching/config/output.py +1 -1
- tomwer/gui/stitching/config/positionoveraxis.py +1 -1
- tomwer/gui/stitching/config/stitchingstrategies.py +4 -6
- tomwer/gui/stitching/config/tomoobjdetails.py +21 -13
- tomwer/gui/stitching/normalization.py +6 -6
- tomwer/gui/stitching/tests/test_ZStitchingWindow.py +8 -1
- tomwer/gui/stitching/tests/test_preview.py +10 -7
- tomwer/gui/stitching/tests/utils.py +27 -18
- tomwer/gui/stitching/z_stitching/fineestimation.py +7 -9
- tomwer/gui/stitching/z_stitching/tests/test_raw_estimation.py +18 -7
- tomwer/gui/stitching/z_stitching/tests/test_stitching_window.py +7 -2
- tomwer/gui/utils/buttons.py +54 -36
- tomwer/gui/utils/flow.py +2 -2
- tomwer/gui/utils/loadingmode.py +1 -1
- tomwer/gui/utils/unitsystem.py +44 -33
- tomwer/gui/utils/vignettes.py +1 -1
- tomwer/gui/visualization/dataviewer.py +7 -7
- tomwer/gui/visualization/diffviewer/diffviewer.py +4 -4
- tomwer/gui/visualization/diffviewer/shiftwidget.py +4 -6
- tomwer/gui/visualization/reconstructionparameters.py +35 -23
- tomwer/gui/visualization/scanoverview.py +28 -11
- tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +25 -13
- tomwer/gui/visualization/test/test_reconstruction_parameters.py +2 -2
- tomwer/model/dataset.py +0 -0
- tomwer/resources/gui/icons/borders.png +0 -0
- tomwer/synctools/utils/scanstages.py +3 -3
- tomwer/tasks/reconstruction/cleardarkflat.py +42 -0
- tomwer/tests/app/test_stitching.py +1 -1
- tomwer/tests/orangecontrib/tomwer/widgets/edit/tests/test_nxtomo_editor.py +32 -20
- tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_widget.py +1 -1
- tomwer/version.py +2 -2
- {tomwer-1.4.18.dist-info → tomwer-1.5.0.dist-info}/METADATA +8 -8
- {tomwer-1.4.18.dist-info → tomwer-1.5.0.dist-info}/RECORD +127 -124
- {tomwer-1.4.18.dist-info → tomwer-1.5.0.dist-info}/WHEEL +1 -1
- {tomwer-1.4.18.dist-info → tomwer-1.5.0.dist-info}/entry_points.txt +0 -0
- {tomwer-1.4.18.dist-info → tomwer-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {tomwer-1.4.18.dist-info → tomwer-1.5.0.dist-info}/top_level.txt +0 -0
tomwer/gui/edit/nxtomoeditor.py
CHANGED
@@ -2,12 +2,13 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import logging
|
4
4
|
import weakref
|
5
|
-
|
5
|
+
import pint
|
6
6
|
import numpy
|
7
7
|
from silx.gui import qt
|
8
8
|
|
9
9
|
from nxtomo.nxobject.nxdetector import ImageKey, FOV
|
10
10
|
from nxtomo.utils.transformation import build_matrix
|
11
|
+
from nxtomo.paths.nxtomo import LATEST_VERSION as _LATEST_NXTOMO_VERSION
|
11
12
|
|
12
13
|
from tomwer.core.process.edit.nxtomoeditor import NXtomoEditorTask, NXtomoEditorKeys
|
13
14
|
from tomwer.core.scan.nxtomoscan import NXtomoScan
|
@@ -18,6 +19,8 @@ from tomwer.gui.edit.nxtomowarmer import NXtomoProxyWarmer
|
|
18
19
|
|
19
20
|
_logger = logging.getLogger(__name__)
|
20
21
|
|
22
|
+
_ureg = pint.get_application_registry()
|
23
|
+
|
21
24
|
|
22
25
|
class NXtomoEditorDialog(qt.QDialog):
|
23
26
|
"""
|
@@ -119,48 +122,58 @@ class NXtomoEditor(qt.QWidget):
|
|
119
122
|
self._detectorQTWI = qt.QTreeWidgetItem(self._instrumentQTWI)
|
120
123
|
self._detectorQTWI.setText(0, "detector")
|
121
124
|
## pixel size
|
122
|
-
self.
|
123
|
-
self.
|
124
|
-
self.
|
125
|
-
self.
|
126
|
-
self._tree.setItemWidget(
|
127
|
-
|
128
|
-
|
129
|
-
self.
|
130
|
-
self.
|
131
|
-
self.
|
132
|
-
|
133
|
-
self.
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
self.
|
138
|
-
self.
|
139
|
-
self.
|
140
|
-
self.
|
141
|
-
self.
|
142
|
-
|
143
|
-
|
144
|
-
|
125
|
+
self._xDetectorPixelSizeQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
126
|
+
self._xDetectorPixelSizeQTWI.setText(0, "x pixel size")
|
127
|
+
self._xDetectorPixelSizeMetricEntry = MetricEntry("", parent=self)
|
128
|
+
self._xDetectorPixelSizeMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
129
|
+
self._tree.setItemWidget(
|
130
|
+
self._xDetectorPixelSizeQTWI, 1, self._xDetectorPixelSizeMetricEntry
|
131
|
+
)
|
132
|
+
self._editableWidgets.append(self._xDetectorPixelSizeMetricEntry)
|
133
|
+
self._xDetectorPixelSizeLB = PadlockButton(self)
|
134
|
+
self._xDetectorPixelSizeLB.setMaximumSize(30, 30)
|
135
|
+
self._lockerPBs.append(self._xDetectorPixelSizeLB)
|
136
|
+
self._tree.setItemWidget(
|
137
|
+
self._xDetectorPixelSizeQTWI, 2, self._xDetectorPixelSizeLB
|
138
|
+
)
|
139
|
+
|
140
|
+
self._yDetectorPixelSizeQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
141
|
+
self._yDetectorPixelSizeQTWI.setText(0, "y pixel size")
|
142
|
+
self._yDetectorPixelSizeMetricEntry = MetricEntry("", parent=self)
|
143
|
+
self._yDetectorPixelSizeMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
144
|
+
self._tree.setItemWidget(
|
145
|
+
self._yDetectorPixelSizeQTWI, 1, self._yDetectorPixelSizeMetricEntry
|
146
|
+
)
|
147
|
+
self._editableWidgets.append(self._yDetectorPixelSizeMetricEntry)
|
148
|
+
self._yDetectorPixelSizeLB = PadlockButton(self)
|
149
|
+
self._yDetectorPixelSizeLB.setMaximumSize(30, 30)
|
150
|
+
self._lockerPBs.append(self._yDetectorPixelSizeLB)
|
151
|
+
self._tree.setItemWidget(
|
152
|
+
self._yDetectorPixelSizeQTWI, 2, self._yDetectorPixelSizeLB
|
153
|
+
)
|
154
|
+
|
155
|
+
## sample - distance
|
145
156
|
self._sampleDetectorDistanceQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
146
|
-
self._sampleDetectorDistanceQTWI.setText(0, "distance")
|
147
|
-
self.
|
148
|
-
self.
|
157
|
+
self._sampleDetectorDistanceQTWI.setText(0, "sample-detector distance")
|
158
|
+
self._sampleDetectorDistanceMetricEntry = MetricEntry("", parent=self)
|
159
|
+
self._sampleDetectorDistanceMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
160
|
+
self._tree.setItemWidget(
|
161
|
+
self._sampleDetectorDistanceQTWI, 1, self._sampleDetectorDistanceMetricEntry
|
162
|
+
)
|
163
|
+
self._editableWidgets.append(self._sampleDetectorDistanceMetricEntry)
|
164
|
+
self._sampleDetectorDistanceLB = PadlockButton(self)
|
165
|
+
self._sampleDetectorDistanceLB.setMaximumSize(30, 30)
|
166
|
+
self._lockerPBs.append(self._sampleDetectorDistanceLB)
|
149
167
|
self._tree.setItemWidget(
|
150
|
-
self._sampleDetectorDistanceQTWI,
|
168
|
+
self._sampleDetectorDistanceQTWI, 2, self._sampleDetectorDistanceLB
|
151
169
|
)
|
152
|
-
self._editableWidgets.append(self._distanceMetricEntry)
|
153
|
-
self._distanceLB = PadlockButton(self)
|
154
|
-
self._distanceLB.setMaximumSize(30, 30)
|
155
|
-
self._lockerPBs.append(self._distanceLB)
|
156
|
-
self._tree.setItemWidget(self._sampleDetectorDistanceQTWI, 2, self._distanceLB)
|
157
170
|
|
158
171
|
## field of view
|
159
172
|
self._fieldOfViewQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
160
173
|
self._fieldOfViewQTWI.setText(0, "field of view")
|
161
174
|
self._fieldOfViewCB = qt.QComboBox(self)
|
162
|
-
for
|
163
|
-
self._fieldOfViewCB.addItem(value)
|
175
|
+
for FOV_item in FOV:
|
176
|
+
self._fieldOfViewCB.addItem(FOV_item.value)
|
164
177
|
self._tree.setItemWidget(self._fieldOfViewQTWI, 1, self._fieldOfViewCB)
|
165
178
|
self._editableWidgets.append(self._fieldOfViewCB)
|
166
179
|
self._fieldOfViewLB = PadlockButton(self)
|
@@ -188,9 +201,77 @@ class NXtomoEditor(qt.QWidget):
|
|
188
201
|
self._yFlippedLB.setMaximumSize(30, 30)
|
189
202
|
self._lockerPBs.append(self._yFlippedLB)
|
190
203
|
self._tree.setItemWidget(self._yFlippedQTWI, 2, self._yFlippedLB)
|
204
|
+
# source
|
205
|
+
self._sourceQTWI = qt.QTreeWidgetItem(self._instrumentQTWI)
|
206
|
+
self._sourceQTWI.setText(0, "source")
|
207
|
+
## source - sample
|
208
|
+
self._sampleSourceDistanceQTWI = qt.QTreeWidgetItem(self._sourceQTWI)
|
209
|
+
self._sampleSourceDistanceQTWI.setText(0, "sample-source distance")
|
210
|
+
self._sampleSourceDistanceMetricEntry = MetricEntry("", parent=self)
|
211
|
+
self._sampleSourceDistanceMetricEntry.setToolTip(
|
212
|
+
"sample-source distance. Expected to be negative"
|
213
|
+
)
|
214
|
+
self._sampleSourceDistanceMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
215
|
+
self._tree.setItemWidget(
|
216
|
+
self._sampleSourceDistanceQTWI, 1, self._sampleSourceDistanceMetricEntry
|
217
|
+
)
|
218
|
+
self._editableWidgets.append(self._sampleSourceDistanceMetricEntry)
|
219
|
+
self._sampleSourceDistanceLB = PadlockButton(self)
|
220
|
+
self._sampleSourceDistanceLB.setMaximumSize(30, 30)
|
221
|
+
self._lockerPBs.append(self._sampleSourceDistanceLB)
|
222
|
+
self._tree.setItemWidget(
|
223
|
+
self._sampleSourceDistanceQTWI, 2, self._sampleSourceDistanceLB
|
224
|
+
)
|
191
225
|
# 2: sample
|
192
226
|
self._sampleQTWI = qt.QTreeWidgetItem(self._tree)
|
193
227
|
self._sampleQTWI.setText(0, "sample")
|
228
|
+
# ## pixel size
|
229
|
+
self._xSamplePixelSizeQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
|
230
|
+
self._xSamplePixelSizeQTWI.setText(0, "x pixel size")
|
231
|
+
self._xSamplePixelSizeMetricEntry = MetricEntry("", parent=self)
|
232
|
+
self._xSamplePixelSizeMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
233
|
+
self._tree.setItemWidget(
|
234
|
+
self._xSamplePixelSizeQTWI, 1, self._xSamplePixelSizeMetricEntry
|
235
|
+
)
|
236
|
+
self._editableWidgets.append(self._xSamplePixelSizeMetricEntry)
|
237
|
+
self._xSamplePixelSizeLB = PadlockButton(self)
|
238
|
+
self._xSamplePixelSizeLB.setMaximumSize(30, 30)
|
239
|
+
self._lockerPBs.append(self._xSamplePixelSizeLB)
|
240
|
+
self._tree.setItemWidget(
|
241
|
+
self._xSamplePixelSizeQTWI, 2, self._xSamplePixelSizeLB
|
242
|
+
)
|
243
|
+
|
244
|
+
self._ySamplePixelSizeQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
|
245
|
+
self._ySamplePixelSizeQTWI.setText(0, "y pixel size")
|
246
|
+
self._ySamplePixelSizeMetricEntry = MetricEntry("", parent=self)
|
247
|
+
self._ySamplePixelSizeMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
248
|
+
self._tree.setItemWidget(
|
249
|
+
self._ySamplePixelSizeQTWI, 1, self._ySamplePixelSizeMetricEntry
|
250
|
+
)
|
251
|
+
self._editableWidgets.append(self._ySamplePixelSizeMetricEntry)
|
252
|
+
self._ySamplePixelSizeLB = PadlockButton(self)
|
253
|
+
self._ySamplePixelSizeLB.setMaximumSize(30, 30)
|
254
|
+
self._lockerPBs.append(self._ySamplePixelSizeLB)
|
255
|
+
self._tree.setItemWidget(
|
256
|
+
self._ySamplePixelSizeQTWI, 2, self._ySamplePixelSizeLB
|
257
|
+
)
|
258
|
+
|
259
|
+
## propagation distance
|
260
|
+
self._propagationDistanceQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
|
261
|
+
self._propagationDistanceQTWI.setText(0, "propagation distance")
|
262
|
+
self._propagationDistanceMetricEntry = MetricEntry("", parent=self)
|
263
|
+
self._propagationDistanceMetricEntry.layout().setContentsMargins(2, 2, 2, 2)
|
264
|
+
self._tree.setItemWidget(
|
265
|
+
self._propagationDistanceQTWI, 1, self._propagationDistanceMetricEntry
|
266
|
+
)
|
267
|
+
self._editableWidgets.append(self._propagationDistanceMetricEntry)
|
268
|
+
self._propagationDistanceLB = PadlockButton(self)
|
269
|
+
self._propagationDistanceLB.setMaximumSize(30, 30)
|
270
|
+
self._lockerPBs.append(self._propagationDistanceLB)
|
271
|
+
self._tree.setItemWidget(
|
272
|
+
self._propagationDistanceQTWI, 2, self._propagationDistanceLB
|
273
|
+
)
|
274
|
+
|
194
275
|
## x translation
|
195
276
|
self._xTranslationQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
|
196
277
|
self._xTranslationQTWI.setText(0, "x translation")
|
@@ -215,12 +296,26 @@ class NXtomoEditor(qt.QWidget):
|
|
215
296
|
# connect signal / slot
|
216
297
|
self._energyEntry.editingFinished.connect(self._editingFinished)
|
217
298
|
self._energyLockerLB.toggled.connect(self._editingFinished)
|
218
|
-
self.
|
219
|
-
|
220
|
-
|
221
|
-
self.
|
222
|
-
self.
|
223
|
-
|
299
|
+
self._xDetectorPixelSizeMetricEntry.editingFinished.connect(
|
300
|
+
self._editingFinished
|
301
|
+
)
|
302
|
+
self._xDetectorPixelSizeLB.toggled.connect(self._editingFinished)
|
303
|
+
self._yDetectorPixelSizeMetricEntry.editingFinished.connect(
|
304
|
+
self._editingFinished
|
305
|
+
)
|
306
|
+
self._yDetectorPixelSizeLB.toggled.connect(self._editingFinished)
|
307
|
+
self._xSamplePixelSizeMetricEntry.editingFinished.connect(self._editingFinished)
|
308
|
+
self._xSamplePixelSizeLB.toggled.connect(self._editingFinished)
|
309
|
+
self._ySamplePixelSizeMetricEntry.editingFinished.connect(self._editingFinished)
|
310
|
+
self._ySamplePixelSizeLB.toggled.connect(self._editingFinished)
|
311
|
+
self._sampleDetectorDistanceMetricEntry.editingFinished.connect(
|
312
|
+
self._editingFinished
|
313
|
+
)
|
314
|
+
self._sampleDetectorDistanceLB.toggled.connect(self._editingFinished)
|
315
|
+
self._propagationDistanceMetricEntry.editingFinished.connect(
|
316
|
+
self._editingFinished
|
317
|
+
)
|
318
|
+
self._propagationDistanceLB.toggled.connect(self._editingFinished)
|
224
319
|
self._fieldOfViewCB.currentIndexChanged.connect(self._editingFinished)
|
225
320
|
self._fieldOfViewLB.toggled.connect(self._editingFinished)
|
226
321
|
self._xFlippedCB.toggled.connect(self._editingFinished)
|
@@ -229,37 +324,106 @@ class NXtomoEditor(qt.QWidget):
|
|
229
324
|
self._yFlippedLB.toggled.connect(self._editingFinished)
|
230
325
|
|
231
326
|
def update_tree(self) -> None:
|
232
|
-
|
327
|
+
scan = self.getScan()
|
328
|
+
if scan is not None:
|
233
329
|
self._updateInstrument()
|
234
330
|
self._updateSample()
|
331
|
+
self._updateSource()
|
235
332
|
self._tree.resizeColumnToContents(0)
|
236
333
|
|
334
|
+
# update items according to NXtomo nexus version
|
335
|
+
nexus_version = scan.nexus_version
|
336
|
+
if nexus_version is None:
|
337
|
+
nexus_version = _LATEST_NXTOMO_VERSION
|
338
|
+
# handle nxtomo 1.4 version
|
339
|
+
allow_source_sample_distance = bool(nexus_version >= 1.4)
|
340
|
+
self._sampleSourceDistanceMetricEntry.setEnabled(
|
341
|
+
allow_source_sample_distance
|
342
|
+
)
|
343
|
+
self._sampleSourceDistanceLB.setEnabled(allow_source_sample_distance)
|
344
|
+
|
345
|
+
# handle nxtomo 1.5 version
|
346
|
+
allow_sample_pixel_size_and_prop_distance = bool(nexus_version >= 1.5)
|
347
|
+
self._xSamplePixelSizeMetricEntry.setEnabled(
|
348
|
+
allow_sample_pixel_size_and_prop_distance
|
349
|
+
)
|
350
|
+
self._xSamplePixelSizeLB.setEnabled(
|
351
|
+
allow_sample_pixel_size_and_prop_distance
|
352
|
+
)
|
353
|
+
self._ySamplePixelSizeMetricEntry.setEnabled(
|
354
|
+
allow_sample_pixel_size_and_prop_distance
|
355
|
+
)
|
356
|
+
self._ySamplePixelSizeLB.setEnabled(
|
357
|
+
allow_sample_pixel_size_and_prop_distance
|
358
|
+
)
|
359
|
+
self._propagationDistanceMetricEntry.setEnabled(
|
360
|
+
allow_sample_pixel_size_and_prop_distance
|
361
|
+
)
|
362
|
+
self._propagationDistanceLB.setEnabled(
|
363
|
+
allow_sample_pixel_size_and_prop_distance
|
364
|
+
)
|
365
|
+
|
237
366
|
def _updateInstrument(self) -> None:
|
238
367
|
scan = self.getScan()
|
239
368
|
if scan is None:
|
240
369
|
return
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
370
|
+
update_dict = {
|
371
|
+
"energy": self._updateEnergy,
|
372
|
+
"detector pixel size": self._updateDetectorPixelSize,
|
373
|
+
"frame flips": self._updateFlipped,
|
374
|
+
"field of view": self._updateFieldOfView,
|
375
|
+
"sample-detector distance": self._updateSampleDetectorDistance,
|
376
|
+
}
|
377
|
+
if scan.nexus_version is None or scan.nexus_version >= 1.4:
|
378
|
+
update_dict.update(
|
379
|
+
{
|
380
|
+
"sample-source distance": self._updateSampleSourceDistance,
|
381
|
+
}
|
382
|
+
)
|
383
|
+
if scan.nexus_version is None or scan.nexus_version >= 1.5:
|
384
|
+
update_dict.update(
|
385
|
+
{
|
386
|
+
"propagation distance": self._updatePropagationDistance,
|
387
|
+
}
|
388
|
+
)
|
389
|
+
for name, fct in update_dict.items():
|
390
|
+
try:
|
391
|
+
fct(scan=scan)
|
392
|
+
except Exception as e:
|
393
|
+
_logger.error(f"Could not update {name}. Error is {e}")
|
253
394
|
|
254
395
|
def _updateSample(self) -> None:
|
255
396
|
scan = self.getScan()
|
256
397
|
if scan is None:
|
257
398
|
return
|
258
|
-
|
399
|
+
|
400
|
+
update_dict = {
|
401
|
+
"translations": self._updateTranslations,
|
402
|
+
}
|
403
|
+
if scan.nexus_version is None or scan.nexus_version >= 1.5:
|
404
|
+
update_dict.update(
|
405
|
+
{
|
406
|
+
"sample pixel size": self._updateSamplePixelSize,
|
407
|
+
"propagation distance": self._updatePropagationDistance,
|
408
|
+
}
|
409
|
+
)
|
410
|
+
for name, fct in update_dict.items():
|
411
|
+
try:
|
412
|
+
fct(scan=scan)
|
413
|
+
except Exception as e:
|
414
|
+
_logger.error(f"Fail to update {name}. Error is {e}")
|
415
|
+
|
416
|
+
def _updateSource(self) -> None:
|
417
|
+
scan = self.getScan()
|
418
|
+
if scan is None:
|
419
|
+
return
|
420
|
+
if scan.nexus_version is None or scan.nexus_version >= 1.4:
|
259
421
|
try:
|
260
|
-
self.
|
422
|
+
self._updateSampleSourceDistance(scan=scan)
|
261
423
|
except Exception as e:
|
262
|
-
_logger.error(
|
424
|
+
_logger.error(
|
425
|
+
f"Could not update sample - source distance. Error is {e}"
|
426
|
+
)
|
263
427
|
|
264
428
|
def _updateTranslations(self, scan: NXtomoScan) -> None:
|
265
429
|
assert isinstance(scan, NXtomoScan)
|
@@ -325,26 +489,52 @@ class NXtomoEditor(qt.QWidget):
|
|
325
489
|
if (not self._yFlippedLB.isLocked()) and flip_ud is not None:
|
326
490
|
self._yFlippedCB.setChecked(flip_ud)
|
327
491
|
|
328
|
-
def
|
329
|
-
if not self.
|
330
|
-
# if in ''
|
331
|
-
self.
|
492
|
+
def _updateSampleDetectorDistance(self, scan: NXtomoScan) -> None:
|
493
|
+
if not self._sampleDetectorDistanceLB.isLocked():
|
494
|
+
# if in 'auto' mode: we want to overwrite the NXtomo existing value by the one of the GUI
|
495
|
+
self._sampleDetectorDistanceMetricEntry.setValue(
|
496
|
+
scan.sample_detector_distance
|
497
|
+
)
|
498
|
+
|
499
|
+
def _updateSampleSourceDistance(self, scan: NXtomoScan) -> None:
|
500
|
+
if not self._sampleSourceDistanceLB.isLocked():
|
501
|
+
self._sampleSourceDistanceMetricEntry.setValue(scan.source_sample_distance)
|
502
|
+
|
503
|
+
def _updatePropagationDistance(self, scan: NXtomoScan) -> None:
|
504
|
+
if not self._propagationDistanceLB.isLocked():
|
505
|
+
# if in 'auto' mode: we want to overwrite the NXtomo existing value by the one of the GUI
|
506
|
+
self._propagationDistanceMetricEntry.setValue(scan.propagation_distance)
|
332
507
|
|
333
508
|
def _updateEnergy(self, scan: NXtomoScan) -> None:
|
334
509
|
assert isinstance(scan, NXtomoScan)
|
335
510
|
if not self._energyLockerLB.isLocked():
|
336
|
-
# if in ''
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
511
|
+
# if in 'auto' mode: we want to overwrite the NXtomo existing value by the one of the GUI
|
512
|
+
energy_in_kev: float | None = scan.energy
|
513
|
+
if energy_in_kev is not None:
|
514
|
+
# move from float to pint.Quantity
|
515
|
+
assert not isinstance(energy_in_kev, pint.Quantity)
|
516
|
+
self._energyEntry.setValue(energy_in_kev)
|
517
|
+
|
518
|
+
def _updateDetectorPixelSize(self, scan: NXtomoScan) -> None:
|
341
519
|
assert isinstance(scan, NXtomoScan)
|
342
|
-
if not self.
|
343
|
-
x_pixel_size = scan.
|
344
|
-
self.
|
345
|
-
if not self.
|
346
|
-
y_pixel_size = scan.
|
347
|
-
self.
|
520
|
+
if not self._xDetectorPixelSizeLB.isLocked():
|
521
|
+
x_pixel_size = scan.detector_x_pixel_size
|
522
|
+
self._xDetectorPixelSizeMetricEntry.setValue(x_pixel_size)
|
523
|
+
if not self._yDetectorPixelSizeLB.isLocked():
|
524
|
+
y_pixel_size = scan.detector_y_pixel_size
|
525
|
+
self._yDetectorPixelSizeMetricEntry.setValue(y_pixel_size)
|
526
|
+
|
527
|
+
def _updateSamplePixelSize(self, scan: NXtomoScan) -> None:
|
528
|
+
if not self._xSamplePixelSizeLB.isLocked():
|
529
|
+
# if in 'auto' mode: we want to overwrite the NXtomo existing value by the one of the GUI
|
530
|
+
self._xSamplePixelSizeMetricEntry.setValue(
|
531
|
+
scan.get_sample_pixel_size(which="x", fallback_to_det_pixel_size=False)
|
532
|
+
)
|
533
|
+
if not self._ySamplePixelSizeLB.isLocked():
|
534
|
+
# if in 'auto' mode: we want to overwrite the NXtomo existing value by the one of the GUI
|
535
|
+
self._ySamplePixelSizeMetricEntry.setValue(
|
536
|
+
scan.get_sample_pixel_size(which="y", fallback_to_det_pixel_size=False)
|
537
|
+
)
|
348
538
|
|
349
539
|
def _editingFinished(self, *args, **kwargs):
|
350
540
|
self.sigEditingFinished.emit()
|
@@ -384,24 +574,61 @@ class NXtomoEditor(qt.QWidget):
|
|
384
574
|
Return a dict with field full name as key
|
385
575
|
and a tuple as value (field_value, is_locked)
|
386
576
|
|
577
|
+
field_value is given in keV for energies else in 'base units'
|
578
|
+
|
387
579
|
limitation: for now sample position are not handled because this is a 'corner case' for now
|
388
580
|
"""
|
581
|
+
|
582
|
+
def to_base_units_magnitude_if_exists(value: None | pint.Quantity):
|
583
|
+
if value is None:
|
584
|
+
return None
|
585
|
+
else:
|
586
|
+
return value.to_base_units().magnitude
|
587
|
+
|
588
|
+
energy = self._energyEntry.getValue()
|
589
|
+
if energy is not None:
|
590
|
+
assert isinstance(energy, float)
|
591
|
+
|
389
592
|
return {
|
390
593
|
NXtomoEditorKeys.ENERGY: (
|
391
|
-
|
594
|
+
energy,
|
392
595
|
self._energyLockerLB.isLocked(),
|
393
596
|
),
|
394
|
-
NXtomoEditorKeys.
|
395
|
-
|
396
|
-
|
597
|
+
NXtomoEditorKeys.DETECTOR_X_PIXEL_SIZE: (
|
598
|
+
to_base_units_magnitude_if_exists(
|
599
|
+
self._xDetectorPixelSizeMetricEntry.getValue()
|
600
|
+
),
|
601
|
+
self._xDetectorPixelSizeLB.isLocked(),
|
397
602
|
),
|
398
|
-
NXtomoEditorKeys.
|
399
|
-
|
400
|
-
|
603
|
+
NXtomoEditorKeys.DETECTOR_Y_PIXEL_SIZE: (
|
604
|
+
to_base_units_magnitude_if_exists(
|
605
|
+
self._yDetectorPixelSizeMetricEntry.getValue()
|
606
|
+
),
|
607
|
+
self._yDetectorPixelSizeLB.isLocked(),
|
608
|
+
),
|
609
|
+
NXtomoEditorKeys.SAMPLE_X_PIXEL_SIZE: (
|
610
|
+
to_base_units_magnitude_if_exists(
|
611
|
+
self._xSamplePixelSizeMetricEntry.getValue()
|
612
|
+
),
|
613
|
+
self._xSamplePixelSizeLB.isLocked(),
|
614
|
+
),
|
615
|
+
NXtomoEditorKeys.SAMPLE_Y_PIXEL_SIZE: (
|
616
|
+
to_base_units_magnitude_if_exists(
|
617
|
+
self._ySamplePixelSizeMetricEntry.getValue()
|
618
|
+
),
|
619
|
+
self._ySamplePixelSizeLB.isLocked(),
|
401
620
|
),
|
402
621
|
NXtomoEditorKeys.SAMPLE_DETECTOR_DISTANCE: (
|
403
|
-
|
404
|
-
|
622
|
+
to_base_units_magnitude_if_exists(
|
623
|
+
self._sampleDetectorDistanceMetricEntry.getValue()
|
624
|
+
),
|
625
|
+
self._sampleDetectorDistanceLB.isLocked(),
|
626
|
+
),
|
627
|
+
NXtomoEditorKeys.SAMPLE_SOURCE_DISTANCE: (
|
628
|
+
to_base_units_magnitude_if_exists(
|
629
|
+
self._sampleSourceDistanceMetricEntry.getValue()
|
630
|
+
),
|
631
|
+
self._sampleSourceDistanceLB.isLocked(),
|
405
632
|
),
|
406
633
|
NXtomoEditorKeys.FIELD_OF_VIEW: (
|
407
634
|
self._fieldOfViewCB.currentText(),
|
@@ -415,55 +642,124 @@ class NXtomoEditor(qt.QWidget):
|
|
415
642
|
self._yFlippedCB.isChecked(),
|
416
643
|
self._yFlippedLB.isChecked(),
|
417
644
|
),
|
418
|
-
NXtomoEditorKeys.
|
419
|
-
|
645
|
+
NXtomoEditorKeys.PROPAGATION_DISTANCE: (
|
646
|
+
to_base_units_magnitude_if_exists(
|
647
|
+
self._propagationDistanceMetricEntry.getValue(),
|
648
|
+
),
|
649
|
+
self._propagationDistanceLB.isLocked(),
|
650
|
+
),
|
651
|
+
NXtomoEditorKeys.X_TRANSLATION: (
|
652
|
+
to_base_units_magnitude_if_exists(self._xTranslationQLE.getValue()),
|
653
|
+
),
|
654
|
+
NXtomoEditorKeys.Z_TRANSLATION: (
|
655
|
+
to_base_units_magnitude_if_exists(self._zTranslationQLE.getValue()),
|
656
|
+
),
|
420
657
|
}
|
421
658
|
|
422
659
|
def setConfiguration(self, config: dict) -> None:
|
423
|
-
|
660
|
+
"""
|
661
|
+
Load given configuration. We expect all quantities to be given:
|
662
|
+
* in keV for energies
|
663
|
+
* else in base units
|
664
|
+
"""
|
665
|
+
# energy
|
666
|
+
energy = config.get(NXtomoEditorKeys.ENERGY, None)
|
424
667
|
if energy is not None:
|
425
668
|
energy, energy_locked = energy
|
669
|
+
assert energy is None or isinstance(
|
670
|
+
energy, float
|
671
|
+
), "energy is expected to be dimensionless (float) already in keV"
|
426
672
|
self._energyEntry.setValue(energy)
|
427
673
|
self._energyLockerLB.setLock(energy_locked)
|
428
674
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
self.
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
self.
|
445
|
-
|
446
|
-
|
447
|
-
|
675
|
+
# detector pixel size
|
676
|
+
detector_x_pixel_size = config.get(NXtomoEditorKeys.DETECTOR_X_PIXEL_SIZE, None)
|
677
|
+
if detector_x_pixel_size is not None:
|
678
|
+
detector_x_pixel_size, detector_x_pixel_size_locked = detector_x_pixel_size
|
679
|
+
self._xDetectorPixelSizeMetricEntry.setValue(
|
680
|
+
detector_x_pixel_size, displayed_unit=_ureg.meter
|
681
|
+
)
|
682
|
+
self._xDetectorPixelSizeLB.setLock(detector_x_pixel_size_locked)
|
683
|
+
|
684
|
+
detector_y_pixel_size = config.get(NXtomoEditorKeys.DETECTOR_Y_PIXEL_SIZE, None)
|
685
|
+
if detector_y_pixel_size is not None:
|
686
|
+
detector_y_pixel_size, detector_y_pixel_size_locked = detector_y_pixel_size
|
687
|
+
self._yDetectorPixelSizeMetricEntry.setValue(
|
688
|
+
detector_y_pixel_size, displayed_unit=_ureg.meter
|
689
|
+
)
|
690
|
+
self._yDetectorPixelSizeLB.setLock(detector_y_pixel_size_locked)
|
691
|
+
|
692
|
+
# sample detector distance
|
693
|
+
sample_detector_distance = config.get(
|
694
|
+
NXtomoEditorKeys.SAMPLE_DETECTOR_DISTANCE, None
|
695
|
+
)
|
696
|
+
if sample_detector_distance is not None:
|
697
|
+
sample_detector_distance, distance_locked = sample_detector_distance
|
698
|
+
self._sampleDetectorDistanceMetricEntry.setValue(
|
699
|
+
sample_detector_distance, displayed_unit=_ureg.meter
|
700
|
+
)
|
701
|
+
self._sampleDetectorDistanceLB.setLock(distance_locked)
|
702
|
+
|
703
|
+
# field of view
|
704
|
+
field_of_view = config.get(NXtomoEditorKeys.FIELD_OF_VIEW, None)
|
448
705
|
if field_of_view is not None:
|
449
706
|
field_of_view, field_of_view_locked = field_of_view
|
450
|
-
|
707
|
+
field_of_view = FOV.from_value(field_of_view)
|
708
|
+
self._fieldOfViewCB.setCurrentText(field_of_view.value)
|
451
709
|
self._fieldOfViewLB.setLock(field_of_view_locked)
|
452
710
|
|
453
|
-
|
711
|
+
# detector flips
|
712
|
+
x_flipped = config.get(NXtomoEditorKeys.X_FLIPPED, None)
|
454
713
|
if x_flipped is not None:
|
455
714
|
x_flipped, x_flipped_locked = x_flipped
|
456
715
|
x_flipped = x_flipped in (True, "True", "true")
|
457
716
|
self._xFlippedCB.setChecked(x_flipped)
|
458
717
|
self._xFlippedLB.setLock(x_flipped_locked)
|
459
718
|
|
460
|
-
y_flipped = config.get(
|
719
|
+
y_flipped = config.get(NXtomoEditorKeys.Y_FLIPPED, None)
|
461
720
|
if y_flipped is not None:
|
462
721
|
y_flipped, y_flipped_locked = y_flipped
|
463
722
|
y_flipped = y_flipped in (True, "True", "true")
|
464
723
|
self._yFlippedCB.setChecked(y_flipped)
|
465
724
|
self._yFlippedLB.setLock(y_flipped_locked)
|
466
725
|
|
726
|
+
# sample source distance
|
727
|
+
sample_source_distance = config.get(
|
728
|
+
NXtomoEditorKeys.SAMPLE_SOURCE_DISTANCE, None
|
729
|
+
)
|
730
|
+
if sample_source_distance is not None:
|
731
|
+
sample_source_distance, sample_source_distance_locked = (
|
732
|
+
sample_source_distance
|
733
|
+
)
|
734
|
+
if sample_source_distance >= 0:
|
735
|
+
_logger.warning("the sample-source is expected to be negative")
|
736
|
+
self._sampleSourceDistanceMetricEntry.setValue(sample_source_distance)
|
737
|
+
self._sampleSourceDistanceLB.setLock(sample_source_distance_locked)
|
738
|
+
|
739
|
+
# sample pixel size
|
740
|
+
sample_x_pixel_size = config.get(NXtomoEditorKeys.SAMPLE_X_PIXEL_SIZE, None)
|
741
|
+
if sample_x_pixel_size is not None:
|
742
|
+
sample_x_pixel_size, x_sample_pixel_size_locked = sample_x_pixel_size
|
743
|
+
self._xSamplePixelSizeMetricEntry.setValue(
|
744
|
+
sample_x_pixel_size, displayed_unit=_ureg.meter
|
745
|
+
)
|
746
|
+
self._xSamplePixelSizeLB.setLock(x_sample_pixel_size_locked)
|
747
|
+
|
748
|
+
sample_y_pixel_size = config.get(NXtomoEditorKeys.SAMPLE_Y_PIXEL_SIZE, None)
|
749
|
+
if sample_y_pixel_size is not None:
|
750
|
+
sample_y_pixel_size, sample_y_pixel_size_locked = sample_y_pixel_size
|
751
|
+
self._ySamplePixelSizeMetricEntry.setValue(
|
752
|
+
sample_y_pixel_size, displayed_unit=_ureg.meter
|
753
|
+
)
|
754
|
+
self._ySamplePixelSizeLB.setLock(sample_y_pixel_size_locked)
|
755
|
+
|
756
|
+
# propagation distance
|
757
|
+
propagation_distance = config.get(NXtomoEditorKeys.PROPAGATION_DISTANCE, None)
|
758
|
+
if propagation_distance is not None:
|
759
|
+
propagation_distance, propagation_distance_locked = propagation_distance
|
760
|
+
self._propagationDistanceMetricEntry.setValue(propagation_distance)
|
761
|
+
self._propagationDistanceLB.setChecked(propagation_distance_locked)
|
762
|
+
|
467
763
|
def getConfigurationForTask(self) -> dict:
|
468
764
|
"""
|
469
765
|
default configuration is stored as field: (field_value, filed_is_locked) when the task expects field_key: field_value
|
@@ -498,7 +794,7 @@ class _TranslationMetricEntry(MetricEntry):
|
|
498
794
|
else:
|
499
795
|
return super().validate(a0, a1)
|
500
796
|
|
501
|
-
def __init__(self, name, default_unit=
|
797
|
+
def __init__(self, name, default_unit=_ureg.meter, parent=None):
|
502
798
|
super().__init__(name, default_unit=default_unit, parent=parent)
|
503
799
|
self._qlePixelSize.setValidator(self.TranslationValidator(self))
|
504
800
|
|
@@ -517,18 +813,30 @@ class _TranslationMetricEntry(MetricEntry):
|
|
517
813
|
|
518
814
|
|
519
815
|
class EnergyEntry(qt.QLineEdit):
|
816
|
+
"""
|
817
|
+
QLineEdit enabling to handle an energy in keV
|
818
|
+
"""
|
819
|
+
|
520
820
|
def __init__(self, *args, **kwargs):
|
521
821
|
super().__init__(*args, **kwargs)
|
522
822
|
self.setValidator(MetricEntry.DoubleValidator())
|
523
823
|
|
524
|
-
def setValue(self,
|
525
|
-
if
|
526
|
-
|
824
|
+
def setValue(self, value_kev: float | None):
|
825
|
+
if not isinstance(value_kev, (float, type(None))):
|
826
|
+
raise TypeError(
|
827
|
+
f"a0 is expected to be {None} or an instance of {float}. Got {type(value_kev)}"
|
828
|
+
)
|
829
|
+
|
830
|
+
if value_kev is None:
|
831
|
+
value_kev = "unknown"
|
527
832
|
else:
|
528
|
-
|
529
|
-
super().setText(
|
833
|
+
value_kev = str(value_kev)
|
834
|
+
super().setText(value_kev)
|
530
835
|
|
531
836
|
def getValue(self) -> float | None:
|
837
|
+
"""
|
838
|
+
Return energy in keV or None
|
839
|
+
"""
|
532
840
|
txt = self.text().replace(" ", "")
|
533
841
|
if txt in ("unknown", ""):
|
534
842
|
return None
|