tomwer 1.4.0rc0__py3-none-any.whl → 1.4.0rc2__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 (52) hide show
  1. orangecontrib/tomwer/tutorials/test_cor.ows +3 -3
  2. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +6 -14
  3. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +4 -2
  4. tomwer/app/axis.py +0 -3
  5. tomwer/app/multipag.py +11 -3
  6. tomwer/core/process/reconstruction/axis/axis.py +27 -736
  7. tomwer/core/process/reconstruction/axis/mode.py +86 -24
  8. tomwer/core/process/reconstruction/axis/params.py +127 -138
  9. tomwer/core/process/reconstruction/axis/side.py +8 -0
  10. tomwer/core/process/reconstruction/nabu/nabuscores.py +17 -20
  11. tomwer/core/process/reconstruction/nabu/nabuslices.py +5 -1
  12. tomwer/core/process/reconstruction/saaxis/saaxis.py +4 -4
  13. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +4 -4
  14. tomwer/core/process/reconstruction/tests/test_axis.py +1 -1
  15. tomwer/core/process/reconstruction/tests/test_utils.py +4 -4
  16. tomwer/core/process/reconstruction/utils/cor.py +8 -4
  17. tomwer/core/process/tests/test_nabu.py +1 -3
  18. tomwer/core/scan/nxtomoscan.py +2 -0
  19. tomwer/core/scan/scanbase.py +4 -4
  20. tomwer/core/scan/tests/test_process_registration.py +0 -18
  21. tomwer/gui/fonts.py +5 -0
  22. tomwer/gui/reconstruction/axis/AxisMainWindow.py +20 -9
  23. tomwer/gui/reconstruction/axis/AxisOptionsWidget.py +239 -79
  24. tomwer/gui/reconstruction/axis/AxisSettingsWidget.py +38 -17
  25. tomwer/gui/reconstruction/axis/AxisWidget.py +16 -8
  26. tomwer/gui/reconstruction/axis/CalculationWidget.py +40 -200
  27. tomwer/gui/reconstruction/axis/ControlWidget.py +10 -2
  28. tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +394 -0
  29. tomwer/gui/reconstruction/axis/EstimatedCorComboBox.py +118 -0
  30. tomwer/gui/reconstruction/axis/InputWidget.py +11 -155
  31. tomwer/gui/reconstruction/saaxis/corrangeselector.py +19 -10
  32. tomwer/gui/reconstruction/scores/scoreplot.py +5 -2
  33. tomwer/gui/reconstruction/tests/test_nabu.py +8 -0
  34. tomwer/gui/stitching/z_stitching/fineestimation.py +1 -1
  35. tomwer/gui/tests/test_axis_gui.py +31 -15
  36. tomwer/synctools/stacks/reconstruction/axis.py +5 -23
  37. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
  38. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  39. tomwer/synctools/stacks/reconstruction/normalization.py +1 -1
  40. tomwer/synctools/stacks/reconstruction/saaxis.py +1 -1
  41. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +1 -1
  42. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_axis.py +0 -16
  43. tomwer/tests/test_ewoks/test_single_node_execution.py +1 -1
  44. tomwer/tests/test_ewoks/test_workflows.py +1 -1
  45. tomwer/version.py +1 -1
  46. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/METADATA +2 -2
  47. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/RECORD +51 -48
  48. tomwer/core/process/tests/test_axis.py +0 -231
  49. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/LICENSE +0 -0
  50. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/WHEEL +0 -0
  51. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/entry_points.txt +0 -0
  52. {tomwer-1.4.0rc0.dist-info → tomwer-1.4.0rc2.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,16 @@
1
1
  def relative_pos_to_absolute(relative_pos: float, det_width: int):
2
2
  """
3
- convert relative center of rotation to absolute
3
+ Convert relative center of rotation to absolute.
4
4
  """
5
- return relative_pos + (det_width - 1) / 2.0
5
+ # Compute the midpoint differently for even and odd widths
6
+ midpoint = det_width / 2.0
7
+ return relative_pos + midpoint
6
8
 
7
9
 
8
10
  def absolute_pos_to_relative(absolute_pos: float, det_width: int):
9
11
  """
10
- convert absolute center of rotation to relative
12
+ Convert absolute center of rotation to relative.
11
13
  """
12
- return absolute_pos - (det_width - 1) / 2.0
14
+ # Compute the midpoint differently for even and odd widths
15
+ midpoint = det_width / 2.0
16
+ return absolute_pos - midpoint
@@ -402,9 +402,7 @@ class TestNabuAndAxis(unittest.TestCase):
402
402
  nabu_rotation_axis_value = self.get_saved_rotation_axis(nabu_conf_files[0])
403
403
  self.assertEqual(
404
404
  nabu_rotation_axis_value,
405
- str(
406
- self.scan._axis_params.relative_cor_value + (self.scan.dim_1 - 1) / 2.0
407
- ),
405
+ str(self.scan._axis_params.relative_cor_value + (self.scan.dim_1 / 2.0)),
408
406
  )
409
407
 
410
408
 
@@ -18,6 +18,7 @@ from tomoscan.esrf.identifier.hdf5Identifier import (
18
18
  )
19
19
  from tomoscan.esrf.identifier.url_utils import UrlSettings, split_path, split_query
20
20
  from tomoscan.esrf.scan.nxtomoscan import NXtomoScan as _tsNXtomoScan
21
+ from tomoscan.utils.io import filter_esrf_mounting_points
21
22
  from nxtomo.nxobject.nxdetector import ImageKey
22
23
  from nxtomo.paths.nxtomo import get_paths as _get_nexus_paths
23
24
 
@@ -487,6 +488,7 @@ class NXtomoScan(_tsNXtomoScan, TomwerScanBase):
487
488
  bliss_raw_data_files[0] if len(bliss_raw_data_files) > 0 else None
488
489
  )
489
490
  if bliss_raw_data_file is not None:
491
+ bliss_raw_data_file = filter_esrf_mounting_points(bliss_raw_data_file)
490
492
  strips = bliss_raw_data_file.lstrip("/").split("/")
491
493
  if len(strips) > 2 and bliss_raw_data_file.startswith(
492
494
  ("/data/visitor", "/data/visitor")
@@ -279,7 +279,6 @@ class TomwerScanBase(TomwerObject):
279
279
  def get_opposite_projections(self, mode) -> tuple:
280
280
  """
281
281
  Return the two best opposite projections for the required mode.
282
-
283
282
  """
284
283
  radios_with_angle = self.projections_with_angle()
285
284
  angles = numpy.array(
@@ -322,9 +321,10 @@ class TomwerScanBase(TomwerObject):
322
321
  nearest_c1 = find_nearest(angles=angles, angle=couples[0])
323
322
  nearest_c2 = find_nearest(angles=angles, angle=couples[1])
324
323
  if nearest_c1 is not None and nearest_c2 is not None:
325
- radio_0 = AxisResource(radios_with_angle[nearest_c1])
326
- radio_1 = AxisResource(radios_with_angle[nearest_c2])
327
- return radio_0, radio_1
324
+ return (
325
+ AxisResource(radios_with_angle[nearest_c1], angle=nearest_c1),
326
+ AxisResource(radios_with_angle[nearest_c2], angle=nearest_c2),
327
+ )
328
328
  else:
329
329
  return None, None
330
330
 
@@ -40,24 +40,6 @@ class TestProcessRegistration(unittest.TestCase):
40
40
  def tearDown(self):
41
41
  shutil.rmtree(self.tmp_dir)
42
42
 
43
- def testGetCorValues(self):
44
- from tomwer.core.process.reconstruction.axis import AxisTask
45
-
46
- for i in range(20):
47
- results = {"center_of_rotation": i}
48
- Task._register_process(
49
- self.scan.process_file,
50
- process=AxisTask,
51
- entry=self.scan.entry,
52
- configuration=None,
53
- results=results,
54
- process_index=i,
55
- )
56
- cor_value = AxisTask.get_cor_frm_process_file(
57
- self.scan.process_file, entry=self.scan.entry
58
- )
59
- self.assertEqual(cor_value, 19)
60
-
61
43
  def testGetProcessNodes(self):
62
44
  """insure it return the last dark process based on the processing index"""
63
45
 
tomwer/gui/fonts.py ADDED
@@ -0,0 +1,5 @@
1
+ from silx.gui import qt
2
+
3
+ FONT_IMPORTANT = qt.QFont("Arial", 12)
4
+ FONT_MEDIUM = qt.QFont("Arial", 10)
5
+ FONT_SMALL = qt.QFont("Arial", 8)
@@ -10,6 +10,10 @@ from silx.gui import qt
10
10
  from tomwer.gui.utils.qt_utils import block_signals
11
11
 
12
12
  from tomwer.core.process.reconstruction.axis.mode import AxisMode
13
+ from tomwer.core.process.reconstruction.utils.cor import (
14
+ absolute_pos_to_relative,
15
+ relative_pos_to_absolute,
16
+ )
13
17
  from tomwer.core.scan.scanbase import TomwerScanBase
14
18
 
15
19
  from ...utils.scandescription import ScanNameLabelAndShape
@@ -124,17 +128,15 @@ class AxisMainWindow(qt.QMainWindow):
124
128
  )
125
129
 
126
130
  def setAutoUpdateEstimatedCor(self, value):
127
- self._axisWidget.setUpdateAutomaticallyEstimatedCor(value)
128
-
129
- def getAutoUpdateEstimatedCor(self):
130
- return self._axisWidget.updateAutomaticallyEstimatedCor()
131
+ self._axisWidget._settingsWidget._mainWidget.setUpdateXRotationAxisPixelPositionOnNewScan(
132
+ value
133
+ )
131
134
 
132
135
  def manual_uses_full_image(self, value):
133
136
  self._axisWidget.manual_uses_full_image(value)
134
137
 
135
138
  def _modeChanged(self):
136
139
  self.getAxisParams().mode = self.getMode()
137
- self.getAxisParams().use_sinogram = self.useSinogram()
138
140
 
139
141
  def _CORValueLocked(self, lock):
140
142
  if lock:
@@ -152,8 +154,8 @@ class AxisMainWindow(qt.QMainWindow):
152
154
  and relative_value is not None
153
155
  ):
154
156
  try:
155
- absolute_value = (
156
- relative_value + (self._axis_params.frame_width - 1) / 2.0
157
+ absolute_value = relative_pos_to_absolute(
158
+ relative_pos=relative_value, det_width=self._axis_params.frame_width
157
159
  )
158
160
  except TypeError:
159
161
  absolute_value = None
@@ -163,8 +165,8 @@ class AxisMainWindow(qt.QMainWindow):
163
165
  and absolute_value is not None
164
166
  ):
165
167
  try:
166
- relative_value = (
167
- absolute_value - (self._axis_params.frame_width - 1) / 2.0
168
+ relative_value = absolute_pos_to_relative(
169
+ absolute_pos=absolute_value, det_width=self._axis_params.frame_width
168
170
  )
169
171
  except TypeError:
170
172
  relative_value = None
@@ -262,3 +264,12 @@ class AxisMainWindow(qt.QMainWindow):
262
264
 
263
265
  def setEstimatedCor(self, value: float):
264
266
  self._axisWidget.setEstimatedCor(value=value)
267
+
268
+ def getAutoUpdateEstimatedCor(self):
269
+ return self._axisWidget.updateXRotationAxisPixelPositionOnNewScan()
270
+
271
+ def isYAxisInverted(self) -> bool:
272
+ return self._axisWidget.isYAxisInverted()
273
+
274
+ def setYAxisInverted(self, checked: bool):
275
+ return self._axisWidget.setYAxisInverted(checked=checked)
@@ -2,9 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  from silx.gui import qt
4
4
 
5
+ from tomwer.core.process.reconstruction.axis.mode import AxisMode, AXIS_MODE_METADATAS
5
6
  from tomwer.synctools.axis import QAxisRP
6
7
  from tomwer.core.process.reconstruction.axis.params import AxisCalculationInput
7
8
  from tomwer.gui.utils.qt_utils import block_signals
9
+ from tomwer.gui.utils.scrollarea import QComboBoxIgnoreWheel, QSpinBoxIgnoreWheel
10
+ from tomwer.core.process.reconstruction.axis.params import (
11
+ DEFAULT_CMP_N_SUBSAMPLING_Y,
12
+ DEFAULT_CMP_OVERSAMPLING,
13
+ DEFAULT_CMP_TAKE_LOG,
14
+ DEFAULT_CMP_THETA,
15
+ )
8
16
 
9
17
 
10
18
  class AxisOptionsWidget(qt.QWidget):
@@ -14,12 +22,50 @@ class AxisOptionsWidget(qt.QWidget):
14
22
  Used as a tab of the AxisSettingsTabWidget
15
23
  """
16
24
 
25
+ sigChanged = qt.Signal()
26
+ """Emit when the options changed"""
27
+
17
28
  def __init__(self, parent, axis_params):
18
29
  qt.QWidget.__init__(self, parent=parent)
19
30
  assert isinstance(axis_params, QAxisRP)
20
31
  self._axis_params = axis_params
21
32
  self.setLayout(qt.QVBoxLayout())
22
33
 
34
+ # cor_options
35
+ self._corOptsWidget = qt.QWidget(self)
36
+ self._corOptsWidget.setLayout(qt.QFormLayout())
37
+ self._corOpts = qt.QLineEdit(self)
38
+ self._corOpts.setToolTip(
39
+ "Options for methods finding automatically the rotation axis position. 'side', 'near_pos' and 'near_width' are already provided by dedicated interface. The parameters are separated by commas and passed as 'name=value'. Mind the semicolon separator (;)."
40
+ )
41
+ self._corOpts.setPlaceholderText("low_pass=1; high_pass=20")
42
+ self._corOptsWidget.layout().addRow("cor advanced options", self._corOpts)
43
+ self.layout().addWidget(self._corOptsWidget)
44
+
45
+ # padding option
46
+ self._padding_widget = qt.QGroupBox("padding mode")
47
+ self._padding_widget.setCheckable(True)
48
+ self.layout().addWidget(self._padding_widget)
49
+ self._padding_widget.setLayout(qt.QHBoxLayout())
50
+
51
+ self._qbPaddingMode = QComboBoxIgnoreWheel(self._padding_widget)
52
+ for _mode in (
53
+ "constant",
54
+ "edge",
55
+ "linear_ramp",
56
+ "maximum",
57
+ "mean",
58
+ "median",
59
+ "minimum",
60
+ "reflect",
61
+ "symmetric",
62
+ "wrap",
63
+ ):
64
+ self._qbPaddingMode.addItem(_mode)
65
+ def_index = self._qbPaddingMode.findText("edge")
66
+ self._qbPaddingMode.setCurrentIndex(def_index)
67
+ self._padding_widget.layout().addWidget(self._qbPaddingMode)
68
+
23
69
  # define common options
24
70
  self._commonOpts = qt.QWidget(parent=self)
25
71
  self._commonOpts.setLayout(qt.QFormLayout())
@@ -30,39 +76,75 @@ class AxisOptionsWidget(qt.QWidget):
30
76
  self._qcbDataMode.addItem(data_mode.name(), data_mode)
31
77
  self._qcbDataMode.hide()
32
78
 
33
- # add scale option
34
- self._scaleOpt = qt.QCheckBox(parent=self)
35
- self._commonOpts.layout().addRow("scale the two images", self._scaleOpt)
36
79
  self.layout().addWidget(self._commonOpts)
37
80
 
38
- # add near options
39
- self._nearOpts = _AxisNearOptsWidget(parent=self, axis_params=self._axis_params)
40
- self.layout().addWidget(self._nearOpts)
81
+ # composite method advanced options
82
+ self._compositeOptsGroup = CompositeOptsGroup(
83
+ parent=self, axis_params=axis_params
84
+ )
85
+ self.layout().addWidget(self._compositeOptsGroup)
41
86
 
42
87
  # set up
43
88
  self.setCalculationInputType(self._axis_params.calculation_input_type)
89
+ self._compositeOptsGroup.setVisible(
90
+ self._axis_params.mode in (AxisMode.near, AxisMode.composite_coarse_to_fine)
91
+ )
44
92
 
45
93
  # connect signal / slot
46
- self._scaleOpt.toggled.connect(self._updateScaleOpt)
94
+ self._corOpts.editingFinished.connect(self._updateAdvancedCorOptions)
47
95
  self._qcbDataMode.currentIndexChanged.connect(self._updateInputType)
48
- self._axis_params.sigChanged.connect(self._updateMode)
96
+ self._axis_params.sigChanged.connect(self._axis_params_changed)
97
+ self._qbPaddingMode.currentIndexChanged.connect(self._paddingModeChanged)
98
+ self._padding_widget.toggled.connect(self._paddingModeChanged)
99
+ self._compositeOptsGroup.sigChanged.connect(self.sigChanged)
49
100
 
50
- def _updateMode(self):
101
+ def _axis_params_changed(self):
51
102
  with block_signals(self):
103
+ # update according to AxisCalculationInput
52
104
  index = self._qcbDataMode.findText(
53
105
  self._axis_params.calculation_input_type.name()
54
106
  )
55
107
  if index >= 0:
56
108
  self._qcbDataMode.setCurrentIndex(index)
57
-
58
- def _updateScaleOpt(self, *arg, **kwargs):
59
- self._axis_params.scale_img2_to_img1 = self.isImageScaled()
60
-
61
- def isImageScaled(self):
62
- return self._scaleOpt.isChecked()
109
+ # update advanced cor options visibility (not relevant if mode is manual or read)
110
+ axis_mode = self._axis_params.mode
111
+ self._corOptsWidget.setVisible(
112
+ axis_mode
113
+ not in (
114
+ AxisMode.manual,
115
+ AxisMode.read,
116
+ )
117
+ )
118
+ # update cor options value
119
+ self.setCorOptions(self._axis_params.extra_cor_options)
120
+ self.setPaddingMode(self._axis_params.padding_mode)
121
+ self._padding_widget.setVisible(
122
+ AXIS_MODE_METADATAS[axis_mode].allows_padding
123
+ )
63
124
 
64
125
  def _updateInputType(self, *arg, **kwargs):
65
126
  self._axis_params.calculation_input_type = self.getCalculationInputType()
127
+ self.sigChanged.emit()
128
+
129
+ def _paddingModeChanged(self, *args, **kwargs):
130
+ self._axis_params.padding_mode = self.getPaddingMode()
131
+ self.sigChanged.emit()
132
+
133
+ def getPaddingMode(self):
134
+ if self._padding_widget.isChecked():
135
+ return self._qbPaddingMode.currentText()
136
+ else:
137
+ return None
138
+
139
+ def setPaddingMode(self, mode):
140
+ index = self._qbPaddingMode.findText(mode)
141
+ if index >= 0:
142
+ self._qbPaddingMode.setCurrentIndex(index)
143
+ self._paddingModeChanged()
144
+
145
+ def _updateAdvancedCorOptions(self, *args, **kwargs):
146
+ self._axis_params.extra_cor_options = self.getCorOptions()
147
+ self.sigChanged.emit()
66
148
 
67
149
  def getCalculationInputType(self, *arg, **kwargs):
68
150
  return AxisCalculationInput.from_value(self._qcbDataMode.currentText())
@@ -73,81 +155,159 @@ class AxisOptionsWidget(qt.QWidget):
73
155
  self._qcbDataMode.setCurrentIndex(index_dm)
74
156
 
75
157
  def setAxisParams(self, axis):
76
- self._nearOpts.setAxisParams(axis=axis)
77
158
  self._axis_params = axis
78
159
  with block_signals(self):
79
- self._scaleOpt.setChecked(axis.scale_img2_to_img1)
80
160
  index = self._qcbDataMode.findText(axis.calculation_input_type.name())
81
161
  self._qcbDataMode.setCurrentIndex(index)
162
+ self._compositeOptsGroup.setAxisParams(axis)
82
163
 
164
+ def getCorOptions(self):
165
+ return self._corOpts.text()
83
166
 
84
- class _AxisNearOptsWidget(qt.QWidget):
85
- """GUI dedicated to the near options"""
86
-
87
- def __init__(self, parent, axis_params):
88
- qt.QWidget.__init__(self, parent=parent)
89
- assert isinstance(axis_params, QAxisRP)
90
- self._axis_params = axis_params
91
-
92
- self.setLayout(qt.QFormLayout())
93
-
94
- self._stdMaxOpt = qt.QCheckBox(parent=self)
95
- self.layout().addRow("look at max standard deviation", self._stdMaxOpt)
96
-
97
- self._nearWX = qt.QSpinBox(parent=self)
98
- self._nearWX.setMinimum(1)
99
- self._nearWX.setValue(5)
100
- self.layout().addRow("window size", self._nearWX)
101
-
102
- self._fineStepX = qt.QDoubleSpinBox(parent=self)
103
- self._fineStepX.setMinimum(0.05)
104
- self._fineStepX.setSingleStep(0.05)
105
- self._fineStepX.setMaximum(1.0)
106
- self.layout().addRow("fine step x", self._fineStepX)
107
-
108
- # connect signal / Slot
109
- self._stdMaxOpt.toggled.connect(self._lookForStxMaxChanged)
110
- self._nearWX.valueChanged.connect(self._windowSizeChanged)
111
- self._fineStepX.valueChanged.connect(self._fineStepXChanged)
112
-
113
- def _lookForStxMaxChanged(self, *args, **kwargs):
114
- self._axis_params.look_at_stdmax = self.isLookAtStdMax()
115
-
116
- def isLookAtStdMax(self) -> bool:
117
- """
167
+ def setCorOptions(self, opts: str | None):
168
+ with block_signals(self._axis_params):
169
+ self._corOpts.clear()
170
+ if opts:
171
+ self._corOpts.setText(opts)
172
+ self._updateAdvancedCorOptions()
118
173
 
119
- :return: is the option for looking at max standard deviation is
120
- activated
121
- """
122
- return self._stdMaxOpt.isChecked()
174
+ def setMode(self, mode: AxisMode):
175
+ composite_opts_visible = AxisMode.from_value(mode) in (
176
+ AxisMode.composite_coarse_to_fine,
177
+ AxisMode.near,
178
+ )
179
+ self._compositeOptsGroup.setVisible(composite_opts_visible)
123
180
 
124
- def _windowSizeChanged(self, *args, **kwargs):
125
- self._axis_params.near_wx = self.getWindowSize()
126
181
 
127
- def getWindowSize(self) -> int:
128
- """
182
+ class CompositeOptsGroup(qt.QGroupBox):
183
+ """Group box dedicated to the composite algorithms"""
129
184
 
130
- :return: window size for near search
131
- """
132
- return self._nearWX.value()
185
+ sigChanged = qt.Signal()
186
+ """Emit when the options changed"""
133
187
 
134
- def _fineStepXChanged(self, *args, **kwargs):
135
- self._axis_params.fine_step_x = self.getFineStepX()
136
-
137
- def getFineStepX(self) -> float:
138
- """
139
-
140
- :return: fine step x for near calculation
141
- """
142
- return self._fineStepX.value()
188
+ def __init__(self, title="composite options", parent=None, axis_params=None):
189
+ self._axis_params = axis_params
190
+ super().__init__(title, parent)
191
+ ## options for the composite mode
192
+ self.setLayout(qt.QFormLayout())
193
+ self.layout().setContentsMargins(0, 0, 0, 0)
194
+
195
+ self._thetaSB = QSpinBoxIgnoreWheel(self)
196
+ self._thetaSB.setRange(0, 360)
197
+ self._thetaSB.setValue(DEFAULT_CMP_THETA)
198
+ self._thetaSB.setToolTip("a radio will be picked each theta degrees")
199
+ self._thetaLabel = qt.QLabel("angle interval (in degree)", self)
200
+ self._thetaLabel.setToolTip(
201
+ "algorithm will take one projection each 'angle interval'. Also know as 'theta'"
202
+ )
203
+ self.layout().addRow(self._thetaLabel, self._thetaSB)
204
+
205
+ self._oversamplingSB = QSpinBoxIgnoreWheel(self)
206
+ self._oversamplingSB.setRange(1, 999999)
207
+ self._oversamplingSB.setValue(DEFAULT_CMP_OVERSAMPLING)
208
+ self._oversamplingSB.setToolTip("sinogram oversampling")
209
+ self.layout().addRow("oversampling", self._oversamplingSB)
210
+
211
+ self._nearWidthSB = QSpinBoxIgnoreWheel(self)
212
+ self._nearWidthSB.setRange(1, 999999)
213
+ self._nearWidthSB.setValue(0)
214
+ self._nearWidthSB.setToolTip("position to be used with near option")
215
+ self._nearWidthLabel = qt.QLabel("near width", self)
216
+ self._nearWidthLabel.setToolTip("position to be used with near option")
217
+ self.layout().addRow(self._nearWidthLabel, self._nearWidthSB)
218
+
219
+ self._subsamplingYSB = QSpinBoxIgnoreWheel(self)
220
+ self._subsamplingYSB.setRange(1, 999999)
221
+ self._subsamplingYSB.setValue(DEFAULT_CMP_N_SUBSAMPLING_Y)
222
+ self._subsamplingYSB.setToolTip("sinogram number of subsampling along y")
223
+ self.layout().addRow("n_subsampling_y", self._subsamplingYSB)
224
+
225
+ self._takeLogCB = qt.QCheckBox(self)
226
+ self._takeLogCB.setToolTip("Take logarithm")
227
+ self._takeLogCB.setChecked(DEFAULT_CMP_TAKE_LOG)
228
+ self._takeTheLogLabel = qt.QLabel("linearisation (-log(I/I0))")
229
+ self._takeTheLogLabel.setToolTip(
230
+ "take (-log(I/I0)) as input. Also know as 'take_log' option"
231
+ )
232
+ self.layout().addRow(self._takeTheLogLabel, self._takeLogCB)
143
233
 
144
- def setAxisParams(self, axis: QAxisRP):
145
- """
234
+ # connect signal / slot
235
+ self._thetaSB.valueChanged.connect(self._changed)
236
+ self._oversamplingSB.valueChanged.connect(self._changed)
237
+ self._subsamplingYSB.valueChanged.connect(self._changed)
238
+ self._nearWidthSB.valueChanged.connect(self._changed)
239
+ self._takeLogCB.toggled.connect(self._changed)
146
240
 
147
- :param axis: axis to edit
148
- """
241
+ def setAxisParams(self, axis_params):
149
242
  with block_signals(self):
150
- self._axis_params = axis
151
- self._stdMaxOpt.setChecked(axis.look_at_stdmax)
152
- self._nearWX.setValue(axis.near_wx)
153
- self._fineStepX.setValue(axis.fine_step_x)
243
+ self.setConfiguration(axis_params.composite_options)
244
+ self._axis_params = axis_params
245
+
246
+ def _changed(self):
247
+ if self._axis_params is not None:
248
+ self._axis_params.composite_options = self.getConfiguration()
249
+ self.sigChanged.emit()
250
+
251
+ def getTheta(self) -> int:
252
+ return self._thetaSB.value()
253
+
254
+ def setTheta(self, theta: int) -> None:
255
+ self._thetaSB.setValue(theta)
256
+
257
+ def getOversampling(self) -> int:
258
+ return self._oversamplingSB.value()
259
+
260
+ def setOversampling(self, oversampling: int) -> None:
261
+ self._oversamplingSB.setValue(oversampling)
262
+
263
+ def getNearWidth(self) -> int:
264
+ return self._nearWidthSB.value()
265
+
266
+ def setNearWidth(self, value) -> int:
267
+ return self._nearWidthSB.setValue(value)
268
+
269
+ def getSubSamplingY(self) -> int:
270
+ return self._subsamplingYSB.value()
271
+
272
+ def setSubSamplingY(self, subsampling: int) -> None:
273
+ self._subsamplingYSB.setValue(subsampling)
274
+
275
+ def getTakeLog(self) -> bool:
276
+ return self._takeLogCB.isChecked()
277
+
278
+ def setTakeLog(self, log: bool) -> None:
279
+ self._takeLogCB.setChecked(log)
280
+
281
+ def getConfiguration(self) -> dict:
282
+
283
+ return {
284
+ "theta": self.getTheta(),
285
+ "oversampling": self.getOversampling(),
286
+ "n_subsampling_y": self.getSubSamplingY(),
287
+ "take_log": self.getTakeLog(),
288
+ "near_width": self.getNearWidth(),
289
+ }
290
+
291
+ def setConfiguration(self, opts: dict) -> None:
292
+ if not isinstance(opts, dict):
293
+ raise TypeError("opts should be an instance of dict")
294
+ # theta
295
+ theta = opts.get("theta", None)
296
+ if theta is not None:
297
+ self.setTheta(theta=theta)
298
+ # oversampling
299
+ oversampling = opts.get("oversampling", None)
300
+ if oversampling is not None:
301
+ self.setOversampling(oversampling)
302
+ # n subsampling y
303
+ n_subsampling_y = opts.get("n_subsampling_y", None)
304
+ if n_subsampling_y is not None:
305
+ self.setSubSamplingY(n_subsampling_y)
306
+ # near_width
307
+ near_width = opts.get("near_width", None)
308
+ if near_width is not None:
309
+ self.setNearWidth(near_width)
310
+ # take log
311
+ take_log = opts.get("take_log", None)
312
+ if take_log is not None:
313
+ self.setTakeLog(take_log)