tomwer 1.4.18__py3-none-any.whl → 1.5.0rc0__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.
Files changed (86) hide show
  1. orangecontrib/tomwer/tutorials/simple_volume_local_reconstruction.ows +11 -8
  2. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +12 -9
  3. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +11 -0
  4. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +1 -1
  5. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +21 -10
  6. tomwer/app/reducedarkflat.py +2 -2
  7. tomwer/core/process/edit/imagekeyeditor.py +4 -6
  8. tomwer/core/process/edit/nxtomoeditor.py +58 -20
  9. tomwer/core/process/output.py +6 -5
  10. tomwer/core/process/reconstruction/axis/anglemode.py +2 -2
  11. tomwer/core/process/reconstruction/axis/axis.py +1 -0
  12. tomwer/core/process/reconstruction/darkref/darkrefs.py +2 -2
  13. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +1 -1
  14. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  15. tomwer/core/process/reconstruction/nabu/castvolume.py +4 -1
  16. tomwer/core/process/reconstruction/nabu/helical.py +3 -1
  17. tomwer/core/process/reconstruction/nabu/nabuscores.py +1 -1
  18. tomwer/core/process/reconstruction/nabu/nabuslices.py +4 -4
  19. tomwer/core/process/reconstruction/nabu/plane.py +2 -2
  20. tomwer/core/process/reconstruction/nabu/test/test_castvolume.py +2 -0
  21. tomwer/core/process/reconstruction/nabu/utils.py +15 -14
  22. tomwer/core/process/reconstruction/normalization/params.py +1 -1
  23. tomwer/core/process/reconstruction/output.py +2 -2
  24. tomwer/core/process/reconstruction/tests/test_axis.py +1 -1
  25. tomwer/core/process/stitching/metadataholder.py +5 -5
  26. tomwer/core/process/stitching/nabustitcher.py +1 -4
  27. tomwer/core/scan/edfscan.py +3 -3
  28. tomwer/core/scan/nxtomoscan.py +3 -3
  29. tomwer/core/scan/scanbase.py +2 -2
  30. tomwer/core/tomwer_object.py +1 -1
  31. tomwer/core/utils/nxtomoutils.py +2 -2
  32. tomwer/core/utils/spec.py +6 -3
  33. tomwer/gui/cluster/slurm.py +9 -0
  34. tomwer/gui/control/datadiscovery.py +1 -1
  35. tomwer/gui/control/reducedarkflatselector.py +4 -4
  36. tomwer/gui/edit/imagekeyeditor.py +7 -9
  37. tomwer/gui/edit/nxtomoeditor.py +420 -112
  38. tomwer/gui/edit/tests/test_nx_editor.py +154 -82
  39. tomwer/gui/reconstruction/axis/CalculationWidget.py +1 -1
  40. tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +12 -8
  41. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -2
  42. tomwer/gui/reconstruction/nabu/castvolume.py +16 -1
  43. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +1 -1
  44. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +1 -1
  45. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +2 -2
  46. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +2 -4
  47. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +71 -49
  48. tomwer/gui/reconstruction/nabu/nabuflow.py +3 -13
  49. tomwer/gui/reconstruction/nabu/slices.py +6 -6
  50. tomwer/gui/reconstruction/nabu/test/test_cast_volume.py +19 -0
  51. tomwer/gui/reconstruction/normalization/intensity.py +2 -2
  52. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +71 -67
  53. tomwer/gui/reconstruction/scores/scoreplot.py +15 -7
  54. tomwer/gui/reconstruction/tests/test_saaxis.py +18 -16
  55. tomwer/gui/stitching/SingleAxisStitchingWidget.py +8 -8
  56. tomwer/gui/stitching/StitchingOptionsWidget.py +1 -1
  57. tomwer/gui/stitching/alignment.py +8 -8
  58. tomwer/gui/stitching/config/axisparams.py +2 -2
  59. tomwer/gui/stitching/config/output.py +1 -1
  60. tomwer/gui/stitching/config/stitchingstrategies.py +4 -6
  61. tomwer/gui/stitching/config/tomoobjdetails.py +21 -13
  62. tomwer/gui/stitching/normalization.py +6 -6
  63. tomwer/gui/stitching/tests/test_ZStitchingWindow.py +8 -1
  64. tomwer/gui/stitching/tests/test_preview.py +10 -7
  65. tomwer/gui/stitching/tests/utils.py +27 -18
  66. tomwer/gui/stitching/z_stitching/fineestimation.py +7 -9
  67. tomwer/gui/stitching/z_stitching/tests/test_raw_estimation.py +18 -7
  68. tomwer/gui/stitching/z_stitching/tests/test_stitching_window.py +7 -2
  69. tomwer/gui/utils/buttons.py +53 -35
  70. tomwer/gui/utils/flow.py +1 -1
  71. tomwer/gui/utils/unitsystem.py +44 -33
  72. tomwer/gui/visualization/diffviewer/diffviewer.py +4 -4
  73. tomwer/gui/visualization/diffviewer/shiftwidget.py +4 -6
  74. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +25 -13
  75. tomwer/model/dataset.py +0 -0
  76. tomwer/tasks/reconstruction/cleardarkflat.py +42 -0
  77. tomwer/tests/app/test_stitching.py +1 -1
  78. tomwer/tests/orangecontrib/tomwer/widgets/edit/tests/test_nxtomo_editor.py +32 -20
  79. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_widget.py +1 -1
  80. tomwer/version.py +3 -3
  81. {tomwer-1.4.18.dist-info → tomwer-1.5.0rc0.dist-info}/METADATA +8 -8
  82. {tomwer-1.4.18.dist-info → tomwer-1.5.0rc0.dist-info}/RECORD +86 -84
  83. {tomwer-1.4.18.dist-info → tomwer-1.5.0rc0.dist-info}/WHEEL +1 -1
  84. {tomwer-1.4.18.dist-info → tomwer-1.5.0rc0.dist-info}/entry_points.txt +0 -0
  85. {tomwer-1.4.18.dist-info → tomwer-1.5.0rc0.dist-info}/licenses/LICENSE +0 -0
  86. {tomwer-1.4.18.dist-info → tomwer-1.5.0rc0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import pint
3
4
  from nabu.stitching.config import StitchingType
4
5
  from silx.gui import qt
5
6
  from silx.gui.utils import blockSignals
@@ -11,6 +12,8 @@ from tomwer.gui.utils.qt_utils import block_signals
11
12
  from tomwer.gui.stitching.metadataholder import QStitchingMetadata
12
13
  from tomwer.gui.utils.unitsystem import MetricEntry, PixelEntry
13
14
 
15
+ _ureg = pint.get_application_registry()
16
+
14
17
 
15
18
  class _AxisPositionGroup(qt.QGroupBox):
16
19
  sigUpdate = qt.Signal()
@@ -26,13 +29,18 @@ class _AxisPositionGroup(qt.QGroupBox):
26
29
 
27
30
  # pixel size
28
31
  self._pixelSizeRaw = MetricEntry(
29
- value="unknown", name="pixel size", parent=self, default_unit="mm"
32
+ value="unknown",
33
+ name="pixel size",
34
+ parent=self,
35
+ default_unit=_ureg.millimeter,
30
36
  )
31
37
  self._pixelSizeRaw.setReadOnly(True)
32
38
  self.layout().addWidget(self._pixelSizeRaw, 0, 0, 1, 2)
33
39
  self._overridePixelSizeCB = qt.QCheckBox("overwrite", self)
34
40
  self.layout().addWidget(self._overridePixelSizeCB, 0, 2, 1, 1)
35
- self._pixelSizeOverride = MetricEntry(name="", parent=self, default_unit="mm")
41
+ self._pixelSizeOverride = MetricEntry(
42
+ name="", parent=self, default_unit=_ureg.millimeter
43
+ )
36
44
  self.layout().addWidget(self._pixelSizeOverride, 0, 3, 1, 2)
37
45
 
38
46
  # raw position (in meter or millimeter)
@@ -40,14 +48,14 @@ class _AxisPositionGroup(qt.QGroupBox):
40
48
  value="unknown",
41
49
  name=f"axis {axis} position (metric unit)",
42
50
  parent=self,
43
- default_unit="mm",
51
+ default_unit=_ureg.millimeter,
44
52
  )
45
53
  self._rawPositionMetric.setReadOnly(True)
46
54
  self.layout().addWidget(self._rawPositionMetric, 1, 0, 1, 2)
47
55
  self._overrideMetricPositionCB = qt.QCheckBox("overwrite", self)
48
56
  self.layout().addWidget(self._overrideMetricPositionCB, 1, 2, 1, 1)
49
57
  self._metricPositionOverride = MetricEntry(
50
- name="", parent=self, default_unit="mm"
58
+ name="", parent=self, default_unit=_ureg.millimeter
51
59
  )
52
60
  self.layout().addWidget(self._metricPositionOverride, 1, 3, 1, 2)
53
61
 
@@ -110,9 +118,10 @@ class _AxisPositionGroup(qt.QGroupBox):
110
118
  self._stitching_metadata.setPixelOrVoxelSize(
111
119
  value=self.getOverrridePixelSize(), axis=self.axis
112
120
  )
113
- self._stitching_metadata.setMetricPos(
114
- self.getOverrrideMetricPosition(), axis=self.axis
115
- )
121
+ metric_quantity = self.getOverrrideMetricPosition()
122
+ if isinstance(metric_quantity, pint.Quantity):
123
+ metric_quantity = metric_quantity.to_base_units().magnitude
124
+ self._stitching_metadata.setMetricPos(metric_quantity, axis=self.axis)
116
125
  self._stitching_metadata.setPxPos(
117
126
  self.getOverrridePxPosition(), axis=self.axis
118
127
  )
@@ -133,7 +142,6 @@ class _AxisPositionGroup(qt.QGroupBox):
133
142
  position_m = self.get_final_metric_position()
134
143
  if position_m is None:
135
144
  return None
136
-
137
145
  position_px = int(position_m / pixel_size_m)
138
146
  self._rawPxPosition.setValue(position_px)
139
147
 
@@ -189,9 +197,9 @@ class _AxisPositionGroup(qt.QGroupBox):
189
197
  with blockSignals(*self._editingWidgets):
190
198
  # handle pixel size
191
199
  if self.axis == 0:
192
- pixel_size = tomo_obj.y_pixel_size
200
+ pixel_size = tomo_obj.sample_y_pixel_size
193
201
  else:
194
- pixel_size = tomo_obj.x_pixel_size
202
+ pixel_size = tomo_obj.sample_x_pixel_size
195
203
  if pixel_size is not None:
196
204
  self.setRawPixelSize(pixel_size)
197
205
  override_pixel_size = stitching_metadata._pixel_or_voxel_size[self.axis]
@@ -257,7 +265,7 @@ class _AxisPositionGroup(qt.QGroupBox):
257
265
  self._overridePxPosition.setValue(position_px)
258
266
  self._overridePxPosition.valueChanged.emit()
259
267
 
260
- def getOverrrideMetricPosition(self):
268
+ def getOverrrideMetricPosition(self) -> pint.Quantity:
261
269
  if self._overrideMetricPositionCB.isChecked():
262
270
  return self._metricPositionOverride.getValue()
263
271
  else:
@@ -274,7 +282,7 @@ class _AxisPositionGroup(qt.QGroupBox):
274
282
  def getRawMetricPosition(self):
275
283
  return self._rawPositionMetric.getValue()
276
284
 
277
- def setRawMetricPosition(self, position_m, displayed_unit="mm"):
285
+ def setRawMetricPosition(self, position_m, displayed_unit=_ureg.millimeter):
278
286
  self._rawPositionMetric.setValue(
279
287
  value_m=position_m, displayed_unit=displayed_unit
280
288
  )
@@ -351,7 +359,7 @@ class TomoObjectPositionInfos(qt.QWidget):
351
359
  self._axis_2_pos.setStitchingMetadata(metadata)
352
360
 
353
361
  def updateStitchingType(self, stitching_type: StitchingType):
354
- stitching_type = StitchingType.from_value(stitching_type)
362
+ stitching_type = StitchingType(stitching_type)
355
363
  if stitching_type is StitchingType.Z_POSTPROC:
356
364
  pixel_or_voxel_label = "voxel size"
357
365
  elif stitching_type is StitchingType.Z_PREPROC:
@@ -23,13 +23,13 @@ class NormalizationBySampleGroupBox(qt.QGroupBox):
23
23
 
24
24
  # method
25
25
  self._methodCB = qt.QComboBox(self)
26
- self._methodCB.addItems(_SampleNormalizationMethod.values())
26
+ self._methodCB.addItems([item.value for item in _SampleNormalizationMethod])
27
27
  self._methodCB.setCurrentText(_SampleNormalizationMethod.MEDIAN.value)
28
28
  self.layout().addRow("method", self._methodCB)
29
29
 
30
30
  # side
31
31
  self._sideCB = qt.QComboBox(self)
32
- self._sideCB.addItems(_SampleSide.values())
32
+ self._sideCB.addItems([item.value for item in _SampleSide])
33
33
  self._sideCB.setCurrentText(_SampleSide.LEFT.value)
34
34
  self.layout().addRow("sampling side", self._sideCB)
35
35
 
@@ -57,17 +57,17 @@ class NormalizationBySampleGroupBox(qt.QGroupBox):
57
57
  self.sigConfigChanged.emit()
58
58
 
59
59
  def getMethod(self) -> _SampleNormalizationMethod:
60
- return _SampleNormalizationMethod.from_value(self._methodCB.currentText())
60
+ return _SampleNormalizationMethod(self._methodCB.currentText())
61
61
 
62
62
  def setMethod(self, method: _SampleNormalizationMethod | str):
63
- method = _SampleNormalizationMethod.from_value(method)
63
+ method = _SampleNormalizationMethod(method)
64
64
  self._methodCB.setCurrentText(method.value)
65
65
 
66
66
  def getSide(self) -> _SampleSide:
67
- return _SampleSide.from_value(self._sideCB.currentText())
67
+ return _SampleSide(self._sideCB.currentText())
68
68
 
69
69
  def setSide(self, side: _SampleSide):
70
- side = _SampleSide.from_value(side)
70
+ side = _SampleSide(side)
71
71
  self._sideCB.setCurrentText(side.value)
72
72
 
73
73
  def getMargin(self) -> int:
@@ -1,4 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+
1
4
  import os
5
+ import pint
2
6
  import tempfile
3
7
 
4
8
  from tomoscan.series import Series
@@ -8,6 +12,9 @@ from tomwer.gui.stitching.tests.utils import create_scans_z_series
8
12
  from tomwer.tests.conftest import qtapp # noqa F401
9
13
 
10
14
 
15
+ _ureg = pint.get_application_registry()
16
+
17
+
11
18
  def test_ZStitchingWindow(
12
19
  qtapp, # noqa F811
13
20
  tmp_path,
@@ -34,7 +41,7 @@ def test_ZStitchingWindow(
34
41
  z_positions_m=axis_0_positions,
35
42
  x_positions_m=axis_2_positions,
36
43
  shifts=((0.0, 0.0), (-90.0, 0.0), (-180.0, 0.0)),
37
- pixel_size=pixel_size,
44
+ sample_pixel_size=pixel_size,
38
45
  raw_frame_width=280,
39
46
  final_frame_width=100,
40
47
  )
@@ -1,5 +1,7 @@
1
- import os
1
+ from __future__ import annotations
2
2
 
3
+ import os
4
+ import pint
3
5
  import numpy
4
6
  import pytest
5
7
  from nabu.stitching.config import PreProcessedZStitchingConfiguration
@@ -11,13 +13,15 @@ from silx.image.phantomgenerator import PhantomGenerator
11
13
  from tomwer.gui.stitching.stitching_preview import PreviewStitchingPlot
12
14
  from tomwer.gui.stitching.tests.utils import create_scans_z_series
13
15
 
16
+ _ureg = pint.get_application_registry()
17
+
14
18
 
15
19
  @pytest.mark.parametrize("flip_lr", (True, False))
16
20
  @pytest.mark.parametrize("flip_ud", (True, False))
17
21
  def test_preview(tmp_path, flip_lr, flip_ud, qtapp): # noqa F401
18
22
  axis_0_positions = (90, 0.0, -90.0)
19
23
  axis_2_positions = (0.0, 0.0, 0.0)
20
- pixel_size = 1.0
24
+ sample_pixel_size = 1.0
21
25
 
22
26
  output_file_path = os.path.join(str(tmp_path), "output", "stitched.nx")
23
27
  input_dir = os.path.join(tmp_path, "input")
@@ -27,7 +31,7 @@ def test_preview(tmp_path, flip_lr, flip_ud, qtapp): # noqa F401
27
31
  z_positions_m=axis_0_positions,
28
32
  x_positions_m=axis_2_positions,
29
33
  shifts=((0.0, 0.0), (-90.0, 0.0), (-180.0, 0.0)),
30
- pixel_size=pixel_size,
34
+ sample_pixel_size=sample_pixel_size,
31
35
  raw_frame_width=280,
32
36
  final_frame_width=100,
33
37
  flip_lr=flip_lr,
@@ -38,16 +42,15 @@ def test_preview(tmp_path, flip_lr, flip_ud, qtapp): # noqa F401
38
42
  output_data_path="entry_stitched",
39
43
  overwrite_results=True,
40
44
  slurm_config=None,
41
- axis_0_pos_mm=numpy.array(axis_0_positions) / 1000,
42
- axis_2_pos_mm=numpy.array(axis_2_positions) / 1000,
45
+ axis_0_pos_mm=numpy.array(axis_0_positions) * 1000,
46
+ axis_2_pos_mm=numpy.array(axis_2_positions) * 1000,
43
47
  axis_0_pos_px=None,
44
48
  axis_1_pos_px=None,
45
49
  axis_2_pos_px=None,
46
50
  input_scans=scans,
47
- pixel_size=pixel_size,
51
+ pixel_size=sample_pixel_size,
48
52
  )
49
53
  widget = PreviewStitchingPlot(axis=0)
50
-
51
54
  widget._backGroundAction.toggle()
52
55
 
53
56
  stitcher = PreProcessZStitcher(configuration=stitching_config)
@@ -1,4 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
4
+ import pint
2
5
 
3
6
  import numpy
4
7
  from nxtomo.application.nxtomo import NXtomo
@@ -9,15 +12,17 @@ from nxtomo.nxobject.nxdetector import ImageKey
9
12
  from tomwer.core.scan.nxtomoscan import NXtomoScan
10
13
  from nxtomo.utils.transformation import DetYFlipTransformation, DetZFlipTransformation
11
14
 
15
+ _ureg = pint.get_application_registry()
16
+
12
17
 
13
18
  def create_scans_z_series(
14
19
  output_dir,
15
- z_positions_m,
16
- x_positions_m=None,
17
- pixel_size=10e-6,
18
- n_proj=20,
19
- raw_frame_width=100,
20
- final_frame_width=100,
20
+ z_positions_m: tuple[float | None] | None,
21
+ x_positions_m: tuple[float | None] | None = None,
22
+ sample_pixel_size: float | tuple[float, float] = 10.0e6,
23
+ n_proj: int = 20,
24
+ raw_frame_width: int = 100,
25
+ final_frame_width: int = 100,
21
26
  shifts=None,
22
27
  z_rois=None,
23
28
  flip_lr: bool = False,
@@ -44,22 +49,26 @@ def create_scans_z_series(
44
49
  ):
45
50
  nx_tomo = NXtomo()
46
51
  if z_pos is not None:
47
- nx_tomo.sample.z_translation = [z_pos] * n_proj
52
+ nx_tomo.sample.z_translation = ([z_pos] * n_proj) * _ureg.meter
48
53
  if x_pos is not None:
49
- nx_tomo.sample.x_translation = [x_pos] * n_proj
50
- nx_tomo.sample.rotation_angle = numpy.linspace(
51
- 0, 180, num=n_proj, endpoint=False
54
+ nx_tomo.sample.x_translation = ([x_pos] * n_proj) * _ureg.meter
55
+ nx_tomo.sample.rotation_angle = (
56
+ numpy.linspace(0, 180, num=n_proj, endpoint=False) * _ureg.degree
52
57
  )
53
58
  nx_tomo.instrument.detector.image_key_control = [ImageKey.PROJECTION] * n_proj
54
- if pixel_size is not None:
55
- if isinstance(pixel_size, (tuple, list)):
56
- y_pix_size, x_pix_size = pixel_size
59
+ # sample pixel size
60
+ if sample_pixel_size is not None:
61
+ if isinstance(sample_pixel_size, (tuple, list)):
62
+ sample_y_pix_size, sample_x_pix_size = sample_pixel_size
57
63
  else:
58
- y_pix_size, x_pix_size = pixel_size, pixel_size
59
- nx_tomo.instrument.detector.x_pixel_size = x_pix_size
60
- nx_tomo.instrument.detector.y_pixel_size = y_pix_size
61
- nx_tomo.instrument.detector.distance = 2.3
62
- nx_tomo.energy = 19.2
64
+ sample_y_pix_size, sample_x_pix_size = (
65
+ sample_pixel_size,
66
+ sample_pixel_size,
67
+ )
68
+ nx_tomo.sample.x_pixel_size = sample_x_pix_size * _ureg.meter
69
+ nx_tomo.sample.y_pixel_size = sample_y_pix_size * _ureg.meter
70
+ nx_tomo.instrument.detector.distance = 2.3 * _ureg.meter
71
+ nx_tomo.energy = 19.2 * _ureg.keV
63
72
  nx_tomo.instrument.detector.transformations.add_transformation(
64
73
  DetYFlipTransformation(flip=flip_ud)
65
74
  )
@@ -25,8 +25,8 @@ class Axis_N_Params(qt.QGroupBox):
25
25
 
26
26
  # img registration method
27
27
  self._imageRegMethodCB = qt.QComboBox(self)
28
- for method in _NabuShiftAlgorithm.values():
29
- self._imageRegMethodCB.addItem(method)
28
+ for method in _NabuShiftAlgorithm:
29
+ self._imageRegMethodCB.addItem(method.value)
30
30
  self._imageRegMethodCB.setToolTip(
31
31
  "method to use in order to affine positions provided by the user"
32
32
  )
@@ -36,12 +36,10 @@ class Axis_N_Params(qt.QGroupBox):
36
36
  self._imageRegMethodCB.setCurrentText(_NabuShiftAlgorithm.NONE.value)
37
37
 
38
38
  def getCurrentMethod(self):
39
- return _NabuShiftAlgorithm.from_value(self._imageRegMethodCB.currentText())
39
+ return _NabuShiftAlgorithm(self._imageRegMethodCB.currentText())
40
40
 
41
41
  def setCurrentMethod(self, method):
42
- self._imageRegMethodCB.setCurrentText(
43
- _NabuShiftAlgorithm.from_value(method).value
44
- )
42
+ self._imageRegMethodCB.setCurrentText(_NabuShiftAlgorithm(method).value)
45
43
 
46
44
  def getOptsLine(self) -> str:
47
45
  current_method = self.getCurrentMethod()
@@ -86,8 +84,8 @@ class AutoRefineWidget(qt.QWidget):
86
84
 
87
85
  # stitching strategy
88
86
  self._stitchingStrategyCG = qt.QComboBox(parent=self)
89
- for strategy in OverlapStitchingStrategy.values():
90
- self._stitchingStrategyCG.addItem(strategy)
87
+ for strategy in OverlapStitchingStrategy:
88
+ self._stitchingStrategyCG.addItem(strategy.value)
91
89
  self._globalOpts.layout().addRow(
92
90
  "stitching strategy", self._stitchingStrategyCG
93
91
  )
@@ -135,7 +133,7 @@ class AutoRefineWidget(qt.QWidget):
135
133
  raise NotImplementedError
136
134
 
137
135
  def updateStitchingType(self, mode: StitchingType):
138
- mode = StitchingType.from_value(mode)
136
+ mode = StitchingType(mode)
139
137
  self._preProcGroup.setVisible(mode is StitchingType.Z_PREPROC)
140
138
  self._postProcGroup.setVisible(mode is StitchingType.Z_POSTPROC)
141
139
 
@@ -1,6 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import shutil
3
5
  import tempfile
6
+ import pint
7
+ import numpy
4
8
 
5
9
  from silx.gui import qt
6
10
  from silx.gui.utils.testutils import TestCaseQt
@@ -11,6 +15,8 @@ from tomwer.gui.stitching.tests.utils import create_scans_z_series
11
15
  from tomwer.gui.stitching.config.tomoobjdetails import TomoObjectPositionInfos
12
16
  from tomwer.gui.stitching.axisorderedlist import AxisOrderedTomoObjsModel
13
17
 
18
+ _ureg = pint.get_application_registry()
19
+
14
20
 
15
21
  class TestTomoObjectPositionInfos(TestCaseQt):
16
22
  """
@@ -26,14 +32,19 @@ class TestTomoObjectPositionInfos(TestCaseQt):
26
32
  self._tmp_path,
27
33
  z_positions_m=(0.200, 0.205, 0.210),
28
34
  x_positions_m=(0.0, 0.0, None),
29
- pixel_size=(self._y_pixel_size, self._x_pixel_size),
35
+ sample_pixel_size=(self._y_pixel_size, self._x_pixel_size),
30
36
  raw_frame_width=100,
31
37
  )
32
38
  self.scan_0_metadata = QStitchingMetadata(tomo_obj=self.scans[0])
33
- assert self.scan_0_metadata.get_raw_position_m(axis=0) == 0.20
39
+ numpy.testing.assert_almost_equal(
40
+ self.scan_0_metadata.get_raw_position_m(axis=0), 0.20
41
+ )
42
+
34
43
  assert self.scan_0_metadata.get_raw_position_m(axis=2) == 0.0
35
44
  self.scan_2_metadata = QStitchingMetadata(tomo_obj=self.scans[2])
36
- assert self.scan_2_metadata.get_raw_position_m(axis=0) == 0.21
45
+ numpy.testing.assert_almost_equal(
46
+ self.scan_2_metadata.get_raw_position_m(axis=0), 0.21
47
+ )
37
48
  self._widget = TomoObjectPositionInfos()
38
49
 
39
50
  def tearDown(self):
@@ -59,7 +70,7 @@ class TestTomoObjectPositionInfos(TestCaseQt):
59
70
  # test editing some parameters from the GUI. Make sure GUI is updated as the underlying metadata object
60
71
  self._widget._axis_0_pos._overrideMetricPositionCB.setChecked(True)
61
72
  self._widget._axis_0_pos.setRawMetricPosition(
62
- position_m=0.4, displayed_unit="nm"
73
+ position_m=0.4, displayed_unit=_ureg.nanometer
63
74
  )
64
75
  while self.qapp.hasPendingEvents():
65
76
  self.qapp.processEvents()
@@ -85,19 +96,19 @@ class TestAxisOrderedTomoObjsModel(TestCaseQt):
85
96
  self.scans_with_pos_and_pixel_size = create_scans_z_series(
86
97
  os.path.join(self._tmp_path, "case1"),
87
98
  z_positions_m=(0.200, 0.205, 0.210),
88
- pixel_size=0.001,
99
+ sample_pixel_size=0.001,
89
100
  raw_frame_width=100,
90
101
  )
91
102
  self.scans_with_pixel_size = create_scans_z_series(
92
103
  os.path.join(self._tmp_path, "case2"),
93
104
  z_positions_m=(None, None, None),
94
- pixel_size=0.001,
105
+ sample_pixel_size=0.001,
95
106
  raw_frame_width=100,
96
107
  )
97
108
  self.scans_without_metadata = create_scans_z_series(
98
109
  os.path.join(self._tmp_path, "case3"),
99
110
  z_positions_m=(None, None, None),
100
- pixel_size=None,
111
+ sample_pixel_size=None,
101
112
  raw_frame_width=100,
102
113
  )
103
114
  self._widget = qt.QTableView()
@@ -1,6 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import shutil
3
5
  import tempfile
6
+ import pint
4
7
 
5
8
  from silx.gui import qt
6
9
  from silx.gui.utils.testutils import TestCaseQt
@@ -9,6 +12,9 @@ from tomwer.gui.stitching.tests.utils import create_scans_z_series
9
12
  from tomwer.gui.stitching.StitchingWindow import ZStitchingWindow
10
13
 
11
14
 
15
+ _ureg = pint.get_application_registry()
16
+
17
+
12
18
  class TestZStichingWindow(TestCaseQt):
13
19
  """
14
20
  Test high level z stitching definition
@@ -20,7 +26,7 @@ class TestZStichingWindow(TestCaseQt):
20
26
  self.scans = create_scans_z_series(
21
27
  os.path.join(self._tmp_path, "case1"),
22
28
  z_positions_m=(0.200, 0.205, 0.210),
23
- pixel_size=0.001,
29
+ sample_pixel_size=0.001,
24
30
  raw_frame_width=100,
25
31
  )
26
32
 
@@ -45,7 +51,6 @@ class TestZStichingWindow(TestCaseQt):
45
51
  self._widget.show()
46
52
  # fill the widget with scans
47
53
 
48
- scan_0, scan_1, scan_2 = self.scans
49
54
  for scan in self.scans:
50
55
  self._widget.addTomoObj(scan)
51
56
  # note: add the sigChanged to the z ordered list 'update z' slot
@@ -4,6 +4,7 @@ Button of general usage.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import pint
7
8
  import numpy
8
9
  import logging
9
10
  from silx.gui import qt
@@ -12,11 +13,11 @@ from silx.gui.plot.items.roi import LineROI
12
13
  from silx.gui.plot.PlotToolButtons import PlotToolButton
13
14
 
14
15
  from tomwer.gui import icons
15
- from pyunitsystem.metricsystem import MetricSystem
16
-
17
16
 
18
17
  _logger = logging.getLogger(__file__)
19
18
 
19
+ _ureg = pint.get_application_registry()
20
+
20
21
 
21
22
  class PadlockButton(qt.QPushButton):
22
23
  """Simple button to define a button with PadLock icons"""
@@ -87,40 +88,11 @@ class TabBrowsersButtons(qt.QWidget):
87
88
  class TapeMeasureToolButton(PlotToolButton):
88
89
  """Button to active measurement between two point of the plot"""
89
90
 
90
- class TapeMeasureROI(LineROI):
91
- def __init__(self, parent=None, pixel_size_m=None):
92
- super().__init__(parent)
93
- self._pixel_size_m = None
94
- self.setPixelSize(pixel_size_m)
95
-
96
- def setEndPoints(self, startPoint, endPoint):
97
- distance_px = numpy.linalg.norm(endPoint - startPoint)
98
- super().setEndPoints(startPoint=startPoint, endPoint=endPoint)
99
- if self._pixel_size_m is None:
100
- self._updateText(f"{distance_px :.1f}px")
101
- else:
102
- distance_m = distance_px * self._pixel_size_m
103
- value, unit = MetricSystem.cast_metric_to_best_unit(distance_m)
104
- self._updateText(f"{distance_px :.1f}px ({value:.2f}{unit})")
105
-
106
- def setPixelSize(self, pixel_size_m: tuple | float | None):
107
- if isinstance(pixel_size_m, (tuple, list)):
108
- assert (
109
- len(pixel_size_m) == 2
110
- ), "expects at most two pixel size values (x and y values)"
111
- if not numpy.isclose(pixel_size_m[0], pixel_size_m[1]):
112
- value, unit = MetricSystem.cast_metric_to_best_unit(pixel_size_m[0])
113
- _logger.warning(
114
- f"TapeMeasure is only handling square pixels for now. Will consider the pixel is {value:.2f}{unit}x{value:.2f}{unit}"
115
- )
116
- pixel_size_m = pixel_size_m[0]
117
- self._pixel_size_m = pixel_size_m
118
-
119
91
  def __init__(self, parent=None, plot=None, pixel_size_mm=None):
120
92
  super().__init__(parent=parent, plot=plot)
121
93
  self._roiManager = None
122
94
  self._lastRoiCreated = None
123
- self._pixel_sixel_m = pixel_size_mm
95
+ self._pixel_size_m = pixel_size_mm
124
96
  self.setIcon(icons.getQIcon("ruler"))
125
97
  self.setToolTip("measure distance between two pixels")
126
98
  self.toggled.connect(self._callback)
@@ -130,7 +102,7 @@ class TapeMeasureToolButton(PlotToolButton):
130
102
  return super().setPlot(plot)
131
103
 
132
104
  def setPixelSize(self, pixel_size_m: tuple | float | None):
133
- self._pixel_sixel_m = pixel_size_m
105
+ self._pixel_size_m = pixel_size_m
134
106
 
135
107
  def _callback(self, toggled):
136
108
  if not self._roiManager:
@@ -179,8 +151,54 @@ class TapeMeasureToolButton(PlotToolButton):
179
151
  def _registerCurrentROI(self, currentRoi):
180
152
  if self._lastRoiCreated is None:
181
153
  self._lastRoiCreated = currentRoi
182
- self._lastRoiCreated.setPixelSize(self._pixel_sixel_m)
154
+ self._lastRoiCreated.setPixelSize(self._pixel_size_m)
183
155
  elif currentRoi != self._lastRoiCreated and self._roiManager is not None:
184
156
  self._roiManager.removeRoi(self._lastRoiCreated)
185
157
  self._lastRoiCreated = currentRoi
186
- self._lastRoiCreated.setPixelSize(self._pixel_sixel_m)
158
+ self._lastRoiCreated.setPixelSize(self._pixel_size_m)
159
+
160
+
161
+ class TapeMeasureROI(LineROI):
162
+ """ROI dedicated to tape measure"""
163
+
164
+ def __init__(self, parent=None, pixel_size_m=None):
165
+ super().__init__(parent)
166
+ self._pixel_size_m = None
167
+ self.setPixelSize(pixel_size_m)
168
+
169
+ def setEndPoints(self, startPoint, endPoint):
170
+ distance_px = numpy.linalg.norm(endPoint - startPoint)
171
+ super().setEndPoints(startPoint=startPoint, endPoint=endPoint)
172
+ if self._pixel_size_m is None:
173
+ self._updateText(f"{distance_px :.1f}px")
174
+ else:
175
+ distance_m = distance_px * self._pixel_size_m
176
+ value = self.cast_metric_to_best_unit(distance_m)
177
+ self._updateText(f"{distance_px :.1f}px ({value:~})")
178
+
179
+ def setPixelSize(self, pixel_size_m: tuple | float | None):
180
+ if isinstance(pixel_size_m, (tuple, list)):
181
+ assert (
182
+ len(pixel_size_m) == 2
183
+ ), "expects at most two pixel size values (x and y values)"
184
+ if not numpy.isclose(pixel_size_m[0], pixel_size_m[1]):
185
+ value = self.cast_metric_to_best_unit(pixel_size_m[0])
186
+ _logger.warning(
187
+ f"TapeMeasure is only handling square pixels for now. Will consider the pixel is {value:~}{value:~}"
188
+ )
189
+ pixel_size_m = pixel_size_m[0]
190
+ self._pixel_size_m = pixel_size_m
191
+
192
+ @staticmethod
193
+ def cast_metric_to_best_unit(value: pint.Quantity) -> pint.Quantity:
194
+ value = value.to_base_units()
195
+ if value < (1.0 * _ureg.micrometer):
196
+ return value.to(_ureg.nanometer)
197
+ elif value < (0.1 * _ureg.millimeter): # prefer mm to um
198
+ return value.to(_ureg.micrometer)
199
+ elif value < (1.0 * _ureg.centimeter):
200
+ return value.to(_ureg.millimeter)
201
+ elif value < (1.0 * _ureg.meter):
202
+ return value.to(_ureg.centimeter)
203
+ else:
204
+ return value.to(_ureg.meter)
tomwer/gui/utils/flow.py CHANGED
@@ -178,7 +178,7 @@ class FlowCanvas(qt.QWidget):
178
178
  self, parent, direction: str | FlowDirection, show_lock_state=True
179
179
  ) -> None:
180
180
  qt.QWidget.__init__(self, parent=parent)
181
- self._direction = FlowDirection.from_value(direction)
181
+ self._direction = FlowDirection(direction)
182
182
  if self._direction is FlowDirection.HORIZONTAL:
183
183
  layout = qt.QHBoxLayout()
184
184
  elif self._direction is FlowDirection.VERTICAL: