tomwer 1.3.0.dev2__py3-none-any.whl → 1.3.0rc10__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 (156) hide show
  1. orangecontrib/tomwer/widgets/__init__.py +11 -12
  2. orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
  3. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
  4. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
  5. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +10 -8
  6. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
  7. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +1 -1
  8. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -98
  9. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -129
  10. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +13 -12
  11. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +11 -9
  12. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +11 -9
  13. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +12 -15
  14. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
  15. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  16. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
  17. tomwer/__main__.py +0 -10
  18. tomwer/app/canvas_launcher/config.py +3 -3
  19. tomwer/app/canvas_launcher/environ.py +1 -0
  20. tomwer/app/intensitynormalization.py +12 -11
  21. tomwer/app/nabuapp.py +0 -11
  22. tomwer/app/zstitching.py +11 -1
  23. tomwer/core/process/control/datalistener/datalistener.py +15 -10
  24. tomwer/core/process/control/nxtomomill.py +1 -1
  25. tomwer/core/process/control/scantransfer.py +8 -32
  26. tomwer/core/process/edit/darkflatpatch.py +8 -9
  27. tomwer/core/process/edit/imagekeyeditor.py +20 -22
  28. tomwer/core/process/icat/screenshots.py +1 -0
  29. tomwer/core/process/reconstruction/axis/axis.py +263 -59
  30. tomwer/core/process/reconstruction/axis/mode.py +161 -50
  31. tomwer/core/process/reconstruction/axis/params.py +23 -20
  32. tomwer/core/process/reconstruction/darkref/darkrefs.py +12 -13
  33. tomwer/core/process/reconstruction/nabu/castvolume.py +3 -3
  34. tomwer/core/process/reconstruction/nabu/nabucommon.py +43 -19
  35. tomwer/core/process/reconstruction/nabu/nabuscores.py +34 -7
  36. tomwer/core/process/reconstruction/nabu/nabuslices.py +81 -26
  37. tomwer/core/process/reconstruction/nabu/nabuvolume.py +31 -26
  38. tomwer/core/process/reconstruction/nabu/plane.py +9 -0
  39. tomwer/core/process/reconstruction/nabu/utils.py +32 -9
  40. tomwer/core/process/reconstruction/saaxis/saaxis.py +4 -1
  41. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +9 -1
  42. tomwer/core/process/reconstruction/scores/params.py +3 -3
  43. tomwer/core/process/reconstruction/test/test_darkref_copy.py +4 -4
  44. tomwer/core/process/stitching/nabustitcher.py +11 -10
  45. tomwer/core/process/task.py +33 -27
  46. tomwer/core/process/test/test_axis.py +7 -6
  47. tomwer/core/process/test/test_data_transfer.py +3 -3
  48. tomwer/core/process/test/test_nabu.py +10 -2
  49. tomwer/core/process/test/test_normalization.py +2 -2
  50. tomwer/core/scan/blissscan.py +3 -3
  51. tomwer/core/scan/edfscan.py +9 -9
  52. tomwer/core/scan/nxtomoscan.py +11 -11
  53. tomwer/core/scan/scanbase.py +31 -24
  54. tomwer/core/scan/test/test_future_scan.py +1 -1
  55. tomwer/core/scan/test/test_h5.py +4 -4
  56. tomwer/core/scan/test/test_process_registration.py +2 -2
  57. tomwer/core/scan/test/test_scan.py +1 -75
  58. tomwer/core/settings.py +3 -3
  59. tomwer/core/test/test_utils.py +2 -2
  60. tomwer/core/volume/edfvolume.py +6 -6
  61. tomwer/core/volume/hdf5volume.py +6 -6
  62. tomwer/core/volume/jp2kvolume.py +6 -6
  63. tomwer/core/volume/rawvolume.py +6 -6
  64. tomwer/core/volume/tiffvolume.py +12 -12
  65. tomwer/gui/cluster/slurm.py +14 -9
  66. tomwer/gui/cluster/supervisor.py +12 -0
  67. tomwer/gui/cluster/test/test_cluster.py +1 -2
  68. tomwer/gui/cluster/test/test_supervisor.py +1 -1
  69. tomwer/gui/control/datalist.py +5 -0
  70. tomwer/gui/control/datawatcher/controlwidget.py +2 -4
  71. tomwer/gui/control/reducedarkflatselector.py +8 -8
  72. tomwer/gui/control/test/test_single_tomo_obj.py +1 -1
  73. tomwer/gui/edit/dkrfpatch.py +4 -4
  74. tomwer/gui/edit/nxtomowarmer.py +2 -2
  75. tomwer/gui/edit/test/test_dkrf_patch.py +6 -6
  76. tomwer/gui/imagefromfile.py +2 -2
  77. tomwer/gui/qfolderdialog.py +5 -0
  78. tomwer/gui/reconstruction/axis/CompareImages.py +94 -168
  79. tomwer/gui/reconstruction/axis/radioaxis.py +58 -182
  80. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
  81. tomwer/gui/reconstruction/nabu/castvolume.py +8 -1
  82. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +54 -21
  83. tomwer/gui/reconstruction/normalization/intensity.py +3 -25
  84. tomwer/gui/reconstruction/saaxis/corrangeselector.py +1 -1
  85. tomwer/gui/reconstruction/saaxis/saaxis.py +1 -11
  86. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +0 -10
  87. tomwer/gui/reconstruction/scores/scoreplot.py +1 -6
  88. tomwer/gui/reconstruction/test/test_axis.py +18 -4
  89. tomwer/gui/reconstruction/test/test_nabu.py +3 -0
  90. tomwer/gui/stitching/stitching.py +2 -2
  91. tomwer/gui/stitching/stitching_preview.py +7 -53
  92. tomwer/gui/stitching/stitching_raw.py +3 -3
  93. tomwer/gui/utils/inputwidget.py +12 -2
  94. tomwer/gui/utils/lineselector/lineselector.py +1 -1
  95. tomwer/gui/visualization/dataviewer.py +47 -17
  96. tomwer/gui/visualization/sinogramviewer.py +19 -26
  97. tomwer/gui/visualization/test/test_volumeviewer.py +64 -66
  98. tomwer/gui/visualization/volumeviewer.py +105 -105
  99. tomwer/io/utils/h5pyutils.py +7 -3
  100. tomwer/io/utils/utils.py +3 -3
  101. tomwer/resources/gui/icons/parameters.svg +1 -1
  102. tomwer/resources/gui/illustrations/no_rot.svg +1 -1
  103. tomwer/synctools/stacks/edit/darkflatpatch.py +17 -12
  104. tomwer/tests/test_scripts.py +0 -3
  105. tomwer/third_part/WaitingOverlay.py +110 -0
  106. tomwer/third_part/__init__.py +0 -0
  107. tomwer/version.py +2 -2
  108. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/METADATA +32 -31
  109. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/RECORD +115 -153
  110. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/WHEEL +1 -1
  111. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
  112. orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
  113. orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
  114. orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
  115. tomwer/app/lamino.py +0 -143
  116. tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
  117. tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
  118. tomwer/core/process/test/test_lamino.py +0 -76
  119. tomwer/core/test/test_lamino.py +0 -92
  120. tomwer/gui/reconstruction/lamino/__init__.py +0 -31
  121. tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
  122. tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
  123. tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
  124. tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
  125. tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
  126. tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
  127. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
  128. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -757
  129. tomwer/gui/reconstruction/test/test_lamino.py +0 -194
  130. tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
  131. tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
  132. tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
  133. tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
  134. tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
  135. tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
  136. tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
  137. tomwer/resources/gui/illustrations/manual_slice.png +0 -0
  138. tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
  139. tomwer/resources/gui/illustrations/psi_angle.png +0 -0
  140. tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
  141. tomwer/resources/gui/illustrations/rotation_center.png +0 -0
  142. tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
  143. tomwer/resources/gui/illustrations/slice_stack.png +0 -0
  144. tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
  145. tomwer/resources/gui/illustrations/xy_slice.png +0 -0
  146. tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
  147. tomwer/resources/gui/illustrations/xz_slice.png +0 -0
  148. tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
  149. tomwer/resources/gui/illustrations/yz_slice.png +0 -0
  150. tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
  151. tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
  152. /tomwer-1.3.0.dev2-py3.11-nspkg.pth → /tomwer-1.3.0rc10-py3.11-nspkg.pth +0 -0
  153. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/LICENSE +0 -0
  154. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/entry_points.txt +0 -0
  155. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/namespace_packages.txt +0 -0
  156. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/top_level.txt +0 -0
@@ -1,757 +0,0 @@
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__ = "01/06/2018"
29
-
30
-
31
- import logging
32
- import os
33
-
34
- from silx.gui import qt
35
-
36
- from tomwer.core.process.reconstruction.lamino.tofu import (
37
- LAMINO_ANGLE_TYPE,
38
- PSI_ANGLE_TYPE,
39
- ROTATION_CENTER_TYPE,
40
- SLICE_STACK_TYPE,
41
- )
42
- from tomwer.core.settings import get_lbsram_path
43
- from tomwer.core.utils.char import DELTA_CHAR
44
- from tomwer.gui.reconstruction.lamino.tofu import settings
45
- from tomwer.gui.utils.illustrations import _IllustrationWidget
46
-
47
- from .misc import PadlockButton, _AngleWidget, _RegionLE
48
- from .TofuOptionLoader import _getterSetter, _TofuOptionLoader
49
-
50
- _logger = logging.getLogger(__name__)
51
-
52
-
53
- class OutputTofuWidget(_TofuOptionLoader, qt.QWidget):
54
- """
55
- Main widgets for the tofu reconstruction
56
- """
57
-
58
- def __init__(self, parent):
59
- qt.QWidget.__init__(self, parent)
60
- self._scan_type = "slice stack"
61
- self.__scanID = None
62
- self.setLayout(qt.QGridLayout())
63
-
64
- self._controlWidget = qt.QWidget(parent=self)
65
- self._controlWidget.setLayout(qt.QVBoxLayout())
66
-
67
- self._volumeAngleGrp = VolumeAnglesWidget(parent=self)
68
- self._stepSizeAndRange = StepGroup(parent=self._controlWidget)
69
-
70
- self._planeDisplay = _IllustrationWidget(parent=self)
71
- self._planeDisplay.setMinimumSize(qt.QSize(250, 250))
72
- self._planeDisplay.setSizePolicy(
73
- qt.QSizePolicy.Preferred, qt.QSizePolicy.Preferred
74
- )
75
- self._controlWidget.layout().setContentsMargins(0, 0, 0, 0)
76
- self._controlWidget.layout().addWidget(self._volumeAngleGrp)
77
- self._controlWidget.layout().addWidget(self._stepSizeAndRange)
78
- self._region = RegionGB(parent=self)
79
- self._controlWidget.layout().addWidget(self._region)
80
-
81
- self._outputWidget = _OutputPathWidget(parent=self)
82
- self.layout().addWidget(self._outputWidget, 1, 0, 1, 2)
83
-
84
- self.layout().addWidget(self._controlWidget, 0, 0)
85
- self.layout().addWidget(self._planeDisplay, 0, 1)
86
-
87
- spacer = qt.QWidget(self)
88
- spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
89
- self.layout().addWidget(spacer, 2, 0)
90
-
91
- self._controlWidget.setFixedWidth(500)
92
-
93
- # signal/slot connect
94
- self._volumeAngleGrp.sigPlaneChanged.connect(self._updateOutput)
95
-
96
- # expose API
97
- self.getAngles = self._volumeAngleGrp._angles.get
98
- self.getOutput = self._outputWidget.getOutput
99
- self.setOutput = self._outputWidget.setOutput
100
- self.lockOutput = self._outputWidget.lock
101
- self.forceLbsram = self._outputWidget.forceLbsram
102
- self.setForceLbsram = self._outputWidget.setForceLbsram
103
- self.isOutputFolderLocked = self._outputWidget.isLocked
104
- self.removeTiff = self._outputWidget.removeIfExist
105
- self.setRemoveTiff = self._outputWidget.setRemoveIfExist
106
- self.setVolumeAngleX = self._volumeAngleGrp.setVolumeAngleX
107
- self.setVolumeAngleY = self._volumeAngleGrp.setVolumeAngleY
108
- self.setVolumeAngleZ = self._volumeAngleGrp.setVolumeAngleZ
109
- self.getVolumeAngleX = self._volumeAngleGrp.getVolumeAngleX
110
- self.getVolumeAngleY = self._volumeAngleGrp.getVolumeAngleY
111
- self.getVolumeAngleZ = self._volumeAngleGrp.getVolumeAngleZ
112
- self.setRegionSelType = self._stepSizeAndRange.setRegionSelType
113
- self.setRegion = self._stepSizeAndRange.setRegion
114
- self.getRegion = self._stepSizeAndRange.getRegion
115
- self.dryRun = self._outputWidget.dryRun
116
- self.setHighLimit = self._stepSizeAndRange.setHighLimit
117
- self.resetHighLimit = self._stepSizeAndRange.resetHighLimit
118
- self.getZ = self._stepSizeAndRange.getZ
119
- self.setZ = self._stepSizeAndRange.setZ
120
-
121
- options = {
122
- "x-region": _getterSetter(
123
- getter=self._region.getXRegion, setter=self._region.setXRegionFrmStr
124
- ),
125
- "y-region": _getterSetter(
126
- getter=self._region.getYRegion, setter=self._region.setYRegionFrmStr
127
- ),
128
- "volume-angle-x": _getterSetter(
129
- getter=self.getVolumeAngleX, setter=self.setVolumeAngleX
130
- ),
131
- "volume-angle-y": _getterSetter(
132
- getter=self.getVolumeAngleY, setter=self.setVolumeAngleY
133
- ),
134
- "volume-angle-z": _getterSetter(
135
- getter=self.getVolumeAngleZ, setter=self.setVolumeAngleZ
136
- ),
137
- "region": _getterSetter(getter=self.getRegion, setter=self.setRegion),
138
- "output": _getterSetter(getter=self.getOutput, setter=self.setOutput),
139
- "dry-run": _getterSetter(getter=self.dryRun, setter=self.clearOutput),
140
- "z": _getterSetter(getter=self.getZ, setter=self.setZ),
141
- "rm-tif": _getterSetter(getter=self.removeTiff, setter=self.setRemoveTiff),
142
- }
143
- _TofuOptionLoader.__init__(self, options=options)
144
-
145
- def clearOutput(self, *args, **kwargs):
146
- self._outputWidget.clear()
147
-
148
- def _setImg(self, plane):
149
- assert plane in ("XY", "XZ", "YZ", "manual")
150
- self._planeDisplay.setImage(plane + "_lamino")
151
-
152
- def loadFromScan(self, scanID):
153
- self.__scanID = scanID
154
- self._updateOutput()
155
-
156
- def _updateOutput(self):
157
- if self.__scanID is None:
158
- return
159
- try:
160
- if self.isOutputFolderLocked() is False:
161
- self.setOutput(os.path.join(self.__scanID, self._getNameExtension()))
162
- except Exception as error:
163
- _logger.error(error)
164
-
165
- def setPlane(self, plane):
166
- self._volumeAngleGrp._grpPlane.setPlane(plane=plane)
167
-
168
- def _getNameExtension(self):
169
- """Return the default name extension according to the scan type"""
170
- if self._scan_type == SLICE_STACK_TYPE:
171
- if self._volumeAngleGrp._grpPlane.getPlane() == "XY":
172
- return "xySlice"
173
- elif self._volumeAngleGrp._grpPlane.getPlane() == "XZ":
174
- return "xzSlice"
175
- elif self._volumeAngleGrp._grpPlane.getPlane() == "YZ":
176
- return "yzSlice"
177
- elif self._volumeAngleGrp._grpPlane.getPlane() == "manual":
178
- return "Slice"
179
- elif self._scan_type == ROTATION_CENTER_TYPE:
180
- return "xCenter"
181
- elif self._scan_type == LAMINO_ANGLE_TYPE:
182
- return "ctAngle"
183
- elif self._scan_type in (PSI_ANGLE_TYPE, "psi angle"):
184
- return "rotC"
185
- else:
186
- return "Slice"
187
-
188
- def _setScanType(self, scan_type):
189
- self._scan_type = scan_type
190
- illustration = scan_type
191
- if illustration == "slice stack":
192
- if self._volumeAngleGrp._grpPlane.getPlane() == "XY":
193
- illustration = "xy slice"
194
- elif self._volumeAngleGrp._grpPlane.getPlane() == "XZ":
195
- illustration = "xz slice"
196
- elif self._volumeAngleGrp._grpPlane.getPlane() == "YZ":
197
- illustration = "yz slice"
198
- elif self._volumeAngleGrp._grpPlane.getPlane() == "manual":
199
- illustration = "manual slice"
200
- self._planeDisplay.setImage(illustration)
201
-
202
- self._updateOutput()
203
-
204
-
205
- class PlaneGroup(qt.QGroupBox):
206
- sigPlaneChanged = qt.Signal(str)
207
- """Signal emitted when the plane selection changed"""
208
-
209
- def __init__(self, parent):
210
- qt.QGroupBox.__init__(self, parent=parent, title="Plane selection")
211
-
212
- self.setLayout(qt.QVBoxLayout())
213
- self._XYPlaneRB = qt.QRadioButton("XY", parent=self)
214
- self.layout().addWidget(self._XYPlaneRB)
215
- self._YZPlaneRB = qt.QRadioButton("YZ", parent=self)
216
- self.layout().addWidget(self._YZPlaneRB)
217
- self._XZPlaneRB = qt.QRadioButton("XZ", parent=self)
218
- self.layout().addWidget(self._XZPlaneRB)
219
- self._autoPlaneRB = qt.QRadioButton("manual", parent=self)
220
- self.layout().addWidget(self._autoPlaneRB)
221
-
222
- self._rBtns = (
223
- self._XYPlaneRB,
224
- self._XZPlaneRB,
225
- self._YZPlaneRB,
226
- self._autoPlaneRB,
227
- )
228
- self._XYPlaneRB.setChecked(True)
229
-
230
- for btn in self._rBtns:
231
- btn.toggled.connect(self.__planeChangedCllbck)
232
-
233
- self.setSizePolicy(qt.QSizePolicy.Preferred, qt.QSizePolicy.Preferred)
234
-
235
- def getPlane(self):
236
- if self._XYPlaneRB.isChecked():
237
- return "XY"
238
- if self._YZPlaneRB.isChecked():
239
- return "YZ"
240
- if self._XZPlaneRB.isChecked():
241
- return "XZ"
242
- if self._autoPlaneRB.isChecked():
243
- return "manual"
244
-
245
- def setPlane(self, plane):
246
- assert plane in ("XY", "XZ", "YZ", "manual")
247
- for w in self._rBtns:
248
- w.blockSignals(True)
249
- if plane == "XY":
250
- self._XYPlaneRB.setChecked(True)
251
- elif plane == "XZ":
252
- self._XZPlaneRB.setChecked(True)
253
- elif plane == "YZ":
254
- self._YZPlaneRB.setChecked(True)
255
- else:
256
- self._autoPlaneRB.setChecked(True)
257
- self.sigPlaneChanged.emit(plane)
258
- for w in self._rBtns:
259
- w.blockSignals(False)
260
-
261
- def __planeChangedCllbck(self):
262
- self.sigPlaneChanged.emit(self.getPlane())
263
-
264
- def setManualEdition(self):
265
- self._autoPlaneRB.setChecked(True)
266
-
267
-
268
- class VolumeAnglesWidget(qt.QWidget):
269
- def __init__(self, parent):
270
- qt.QWidget.__init__(self, parent)
271
- self.setLayout(qt.QHBoxLayout())
272
- self._grpPlane = PlaneGroup(parent=self)
273
- self.layout().addWidget(self._grpPlane)
274
- self._angles = _AnglesWidget(parent=self)
275
- self.layout().addWidget(self._angles)
276
-
277
- # API exposed
278
- self.sigPlaneChanged = self._grpPlane.sigPlaneChanged
279
- self.sigAnglesEdited = self._angles.sigAnglesEdited
280
-
281
- self.sigAnglesEdited.connect(self._grpPlane.setManualEdition)
282
- self.sigPlaneChanged.connect(self._resetAngleFor)
283
-
284
- self.setAngles = self._angles.set
285
- self.getAngles = self._angles.get
286
- self.setVolumeAngleX = self._angles.setVolumeAngleX
287
- self.setVolumeAngleY = self._angles.setVolumeAngleY
288
- self.setVolumeAngleZ = self._angles.setVolumeAngleZ
289
- self.getVolumeAngleX = self._angles.getVolumeAngleX
290
- self.getVolumeAngleY = self._angles.getVolumeAngleY
291
- self.getVolumeAngleZ = self._angles.getVolumeAngleZ
292
-
293
- def _resetAngleFor(self, plane):
294
- assert plane in ("XY", "XZ", "YZ", "manual")
295
- if plane == "manual":
296
- return
297
-
298
- if plane == "XY":
299
- self._angles.set(0, 0, 0)
300
- if plane == "XZ":
301
- self._angles.set(90, 0, 0)
302
- if plane == "YZ":
303
- self._angles.set(0, 90, 0)
304
-
305
-
306
- class _AnglesWidget(qt.QWidget):
307
- sigAnglesEdited = qt.Signal()
308
- """Signal emitted when an angle is edited"""
309
-
310
- def __init__(self, parent):
311
- qt.QWidget.__init__(self, parent)
312
- self.setLayout(qt.QVBoxLayout())
313
- self.layout().setContentsMargins(0, 0, 0, 0)
314
- self._spacerTop = qt.QWidget(parent=self)
315
- self._spacerTop.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
316
- self.layout().addWidget(self._spacerTop)
317
-
318
- self._volAngleX = _AngleWidget(parent=self, name="volume angle x")
319
- self.layout().addWidget(self._volAngleX)
320
- self._volAngleY = _AngleWidget(parent=self, name="volume angle y")
321
- self.layout().addWidget(self._volAngleY)
322
- self._volAngleZ = _AngleWidget(parent=self, name="volume angle z")
323
- self.layout().addWidget(self._volAngleZ)
324
- self._spacerBot = qt.QWidget(parent=self)
325
- self._spacerBot.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
326
- self.layout().addWidget(self._spacerBot)
327
-
328
- self._volAngleX.sigEdited.connect(self.sigAnglesEdited)
329
- self._volAngleY.sigEdited.connect(self.sigAnglesEdited)
330
- self._volAngleZ.sigEdited.connect(self.sigAnglesEdited)
331
-
332
- # aliases
333
- self.setVolumeAngleX = self._volAngleX.setAngle
334
- self.setVolumeAngleY = self._volAngleY.setAngle
335
- self.setVolumeAngleZ = self._volAngleZ.setAngle
336
- self.getVolumeAngleX = self._volAngleX.getAngle
337
- self.getVolumeAngleY = self._volAngleY.getAngle
338
- self.getVolumeAngleZ = self._volAngleZ.getAngle
339
-
340
- def set(self, xAngle, yAngle, zAngle):
341
- self.blockSignals(True)
342
- self.setVolumeAngleX(xAngle)
343
- self.setVolumeAngleY(yAngle)
344
- self.setVolumeAngleZ(zAngle)
345
- self.blockSignals(False)
346
-
347
- def get(self):
348
- """
349
-
350
- :return: tuple of (anglex, angley, anglez)
351
- """
352
- return (
353
- self._volAngleX.getAngle(),
354
- self._volAngleY.getAngle(),
355
- self._volAngleZ.getAngle(),
356
- )
357
-
358
-
359
- class StepGroup(qt.QGroupBox):
360
- sigSelectionChanged = qt.Signal()
361
- """Signal emitted when the selection changed"""
362
-
363
- _NB_SLICE_NAME = "number of slices"
364
- _RANGE_NAME = "range"
365
-
366
- _TITLE = "Cut selection"
367
-
368
- def __init__(self, parent):
369
- qt.QGroupBox.__init__(self, parent=parent, title=StepGroup._TITLE)
370
- self._xcenter = 0.0
371
- self.__mediumLimit = settings._N_SLICE_LIMITS_MEDIUM
372
- self.__highLimit = settings._N_SLICE_LIMITS_HIGH
373
-
374
- self._defaultStyleSheet = ""
375
- self._mediumLimitStyleSheet = (
376
- "QLineEdit {" + self._convertToStyleSheetColor(settings._COLOR_MEDIUM) + "}"
377
- )
378
- self._highLimitStyleSheet = (
379
- "QLineEdit {" + self._convertToStyleSheetColor(settings._COLOR_HIGH) + "}"
380
- )
381
-
382
- self.setLayout(qt.QGridLayout())
383
-
384
- # z
385
- self._z_label = qt.QLabel("z", parent=self)
386
- self.layout().addWidget(self._z_label, 0, 0)
387
- self._zSB = qt.QSpinBox(parent=self)
388
- self._zSB.setMinimum(-99999)
389
- self._zSB.setValue(0)
390
- self.layout().addWidget(self._zSB, 0, 1)
391
-
392
- # step size
393
- self._stepLabel = qt.QLabel("", parent=self)
394
- self.layout().addWidget(self._stepLabel, 1, 0)
395
-
396
- self._stepSize = qt.QLineEdit("1.0", parent=self)
397
- validator = qt.QDoubleValidator(parent=self._stepSize)
398
- validator.setBottom(0)
399
- self._stepSize.setValidator(validator)
400
- self.layout().addWidget(self._stepSize, 1, 1, 1, 6)
401
-
402
- style = qt.QApplication.style()
403
- icon = style.standardIcon(qt.QStyle.SP_MessageBoxWarning)
404
- self._warningLabel = qt.QLabel("", parent=self)
405
- self._warningLabel.setPixmap(icon.pixmap(30, state=qt.QIcon.On))
406
- self._warningLabel.setVisible(False)
407
- self._warningLabel.setToolTip(
408
- "Number of slice to reconstruct seems to high, might fail"
409
- )
410
-
411
- self.layout().addWidget(self._warningLabel, 1, 7, 1, 1)
412
-
413
- # selection mode
414
- self.layout().addWidget(qt.QLabel("selection mode:"), 2, 0)
415
- self._selModeTooltips = {
416
- self._NB_SLICE_NAME: "Will pick n slices from centered in the center, "
417
- "spaced of step size",
418
- self._RANGE_NAME: "Will pick slices from a range, each spaced of "
419
- "step size",
420
- }
421
- self._selectionMode = qt.QComboBox(parent=self)
422
- self._selectionMode.addItem(self._NB_SLICE_NAME)
423
- idx = self._selectionMode.findText(self._NB_SLICE_NAME)
424
- self._selectionMode.setItemData(
425
- idx, self._selModeTooltips[self._NB_SLICE_NAME], qt.Qt.ToolTipRole
426
- )
427
- self._selectionMode.addItem(self._RANGE_NAME)
428
- idx = self._selectionMode.findText(self._RANGE_NAME)
429
- self._selectionMode.setItemData(
430
- idx, self._selModeTooltips[self._RANGE_NAME], qt.Qt.ToolTipRole
431
- )
432
- self.layout().addWidget(self._selectionMode, 2, 1)
433
-
434
- self._nCutLE = qt.QLineEdit(str(-settings.SLICE_STACK_STEP_SIZE), self)
435
- validator = qt.QIntValidator(parent=self._nCutLE)
436
- validator.setBottom(1)
437
- self._nCutLE.setValidator(validator)
438
- self.layout().addWidget(self._nCutLE, 2, 2)
439
-
440
- self._fromLabel = qt.QLabel("from:", self)
441
- self.layout().addWidget(self._fromLabel, 2, 3)
442
- self._fromLE = qt.QLineEdit(str(-settings.SLICE_STACK_RANGE_HS), self)
443
- validator = qt.QDoubleValidator(parent=self._fromLE)
444
- self._fromLE.setValidator(validator)
445
- self.layout().addWidget(self._fromLE, 2, 4)
446
- self._toLabel = qt.QLabel("to:", self)
447
- self.layout().addWidget(self._toLabel, 2, 5)
448
- self._toLE = qt.QLineEdit(str(settings.SLICE_STACK_RANGE_HS), self)
449
- validator = qt.QDoubleValidator(parent=self._toLE)
450
- self._toLE.setValidator(validator)
451
- self.layout().addWidget(self._toLE, 2, 6)
452
-
453
- self.setRegionSelType(self._RANGE_NAME)
454
- self._selectionMode.currentIndexChanged[str].connect(self.setRegionSelType)
455
- self._setStepSizeType("(pixel)")
456
-
457
- # connect signals / SLOT
458
- for widget in (self._nCutLE, self._stepSize, self._toLE, self._fromLE):
459
- widget.textChanged.connect(self._updateLimitsColor)
460
-
461
- @staticmethod
462
- def _convertToStyleSheetColor(color):
463
- assert type(color) is tuple
464
- _color = []
465
- [_color.append(str(c)) for c in color]
466
- return "color: rgb(" + ",".join(_color) + ")"
467
-
468
- def _setStepSizeType(self, _type):
469
- assert type(_type) is str
470
- self._stepLabel.setText(" ".join(("Step size -", DELTA_CHAR, _type)))
471
-
472
- def setRegionSelType(self, selType):
473
- """
474
-
475
- :param str selType: should be in ('range', 'number of cut')
476
- """
477
- assert selType in (self._RANGE_NAME, self._NB_SLICE_NAME)
478
- index = self._selectionMode.findText(selType)
479
- assert index >= 0
480
- self._selectionMode.blockSignals(True)
481
- self._selectionMode.setCurrentIndex(index)
482
- self._selectionMode.setToolTip(self._selModeTooltips[selType])
483
- for w in (self._fromLE, self._toLE, self._toLabel, self._fromLabel):
484
- w.setVisible(selType == self._RANGE_NAME)
485
- self._nCutLE.setVisible(selType == self._NB_SLICE_NAME)
486
- self._selectionMode.blockSignals(False)
487
- self.sigSelectionChanged.emit()
488
-
489
- def getSelectionType(self):
490
- """Return active selected mode ('range' or 'number of cut')"""
491
- return self._selectionMode.currentText()
492
-
493
- def getStepSize(self):
494
- """Return step size in fofu ref (meter)"""
495
- return float(self._stepSize.text())
496
-
497
- def getNCut(self):
498
- if self.getSelectionType() != self._NB_SLICE_NAME:
499
- return None
500
- else:
501
- return int(self._nCutLE.text())
502
-
503
- def setNCut(self, ncut):
504
- self._nCutLE.setText(str(ncut))
505
-
506
- def getRegion(self):
507
- if self.getSelectionType() == self._NB_SLICE_NAME:
508
- start_from = self._xcenter
509
- if self._zSB.isVisible() is True:
510
- start_from = self.getZ() or 0.0
511
- nbCut = self.getNCut()
512
- if nbCut is None:
513
- raise ValueError("couldn't find nb cut")
514
- else:
515
- _from = (
516
- -nbCut / 2 * self.getStepSize() # pylint: disable=E1130
517
- + start_from
518
- )
519
- _to = nbCut / 2 * self.getStepSize() + start_from
520
- return _from, _to, self.getStepSize()
521
- else:
522
- start_from = 0.0
523
- if self._zSB.isVisible() is True:
524
- start_from = self.getZ() or 0.0
525
- # question: are from, to always integers ?
526
- return (
527
- float(self._fromLE.text()) + start_from,
528
- float(self._toLE.text()) + start_from,
529
- float(self.getStepSize()),
530
- )
531
-
532
- def setRegion(self, region):
533
- if type(region) is str:
534
- try:
535
- _from, _to, step_size = region.split(",")
536
- except Exception:
537
- _logger.warning(
538
- "Fail to setRegion range. given string "
539
- "does not fir the standard (from, to, stepSize)"
540
- )
541
- return
542
- else:
543
- assert type(region) is tuple
544
- _from, _to, step_size = region
545
- _from, _to, step_size = str(_from), str(_to), str(step_size)
546
-
547
- self._fromLE.setText(_from)
548
- self._toLE.setText(_to)
549
- self._stepSize.setText(step_size)
550
-
551
- def _updateLimitsColor(self, *argv, **kwargs):
552
- warning = False
553
- if self._isUpperHightLimit() is True:
554
- styleSheet = self._highLimitStyleSheet
555
- warning = True
556
- elif self._isUpperMediumLimit() is True:
557
- styleSheet = self._mediumLimitStyleSheet
558
- warning = True
559
- else:
560
- styleSheet = self._defaultStyleSheet
561
-
562
- for widget in (self._fromLE, self._toLE, self._nCutLE, self._stepSize):
563
- widget.blockSignals(True)
564
-
565
- self.setStyleSheet(styleSheet)
566
-
567
- for widget in (self._fromLE, self._toLE, self._nCutLE, self._stepSize):
568
- widget.blockSignals(False)
569
-
570
- self._warningLabel.setVisible(warning)
571
-
572
- def _isUpperMediumLimit(self):
573
- return self._getNSlices() > self.__mediumLimit
574
-
575
- def _isUpperHightLimit(self):
576
- return self._getNSlices() > self.__highLimit
577
-
578
- def _getNSlices(self):
579
- """
580
-
581
- :return: number of slice to be reconstructed
582
- :rtype: int
583
- """
584
- if self.getSelectionType() == self._NB_SLICE_NAME:
585
- return self.getNCut()
586
- else:
587
- # as this cast is made during edition, several value can fail on the
588
- # float conversion or division
589
- try:
590
- _from = float(self._fromLE.text())
591
- _to = float(self._toLE.text())
592
- _step = float(self.getStepSize())
593
- return int((_to - _from) / _step)
594
- except Exception:
595
- return 1.0
596
-
597
- def setHighLimit(self, val):
598
- if val == self.__highLimit:
599
- return
600
- if val is None:
601
- self.resetHighLimit()
602
- return
603
- if val <= 0.0:
604
- _logger.error("incoherent height limit: %s" % val)
605
- return
606
-
607
- self.__highLimit = val
608
- self._updateLimitsColor()
609
-
610
- def resetHighLimit(self):
611
- self.__highLimit = settings._N_SLICE_LIMITS_HIGH
612
- self._updateLimitsColor()
613
-
614
- def getZ(self):
615
- if self._zSB.isVisible():
616
- return self._zSB.value()
617
- else:
618
- return None
619
-
620
- def setZ(self, value):
621
- if value is None:
622
- return
623
- return self._zSB.setValue(int(value))
624
-
625
- def _setXCenter(self, xcenter):
626
- self._xcenter = xcenter
627
-
628
-
629
- class RegionGB(qt.QGroupBox):
630
- def __init__(self, parent):
631
- super().__init__("ROI", parent=parent)
632
- self.setLayout(qt.QVBoxLayout())
633
- self._xRegion = _RegionLE(name="x ROI", parent=self)
634
- self.layout().addWidget(self._xRegion)
635
- self._yRegion = _RegionLE(name="y ROI", parent=self)
636
- self.layout().addWidget(self._yRegion)
637
-
638
- # expose API
639
- self.getXRegion = self._xRegion.getRegion
640
- self.getYRegion = self._yRegion.getRegion
641
- self.setXRegion = self._xRegion.setRegion
642
- self.setYRegion = self._yRegion.setRegion
643
- self.setXRegionFrmStr = self._xRegion.setRegionFromStr
644
- self.setYRegionFrmStr = self._yRegion.setRegionFromStr
645
-
646
-
647
- class _OutputPathWidget(qt.QGroupBox):
648
- class _OptionsWidget(qt.QWidget):
649
- def __init__(self, parent):
650
- super().__init__(parent)
651
- self.setLayout(qt.QHBoxLayout())
652
- self.layout().setContentsMargins(0, 0, 0, 0)
653
-
654
- self.forceLbsram = qt.QCheckBox("force lbsram", parent=self)
655
- self.forceLbsram.setToolTip("Make sure reconstruction is saved in lbsram")
656
- self.layout().addWidget(self.forceLbsram)
657
- self.forceLbsram.setVisible(os.path.exists(get_lbsram_path()))
658
-
659
- self.removeExisting = qt.QCheckBox('rm "*.tif"', parent=self)
660
- self.removeExisting.setToolTip("Remove existing files if any")
661
- self.layout().addWidget(self.removeExisting)
662
- spacer = qt.QWidget(self)
663
- spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum)
664
- self.layout().addWidget(spacer)
665
-
666
- def __init__(self, parent):
667
- super().__init__("output folder", parent)
668
- self.setLayout(qt.QVBoxLayout())
669
- self.layout().setContentsMargins(4, 4, 4, 4)
670
-
671
- self._checkboxWidget = _OutputPathWidget._OptionsWidget(parent=self)
672
- self.layout().addWidget(self._checkboxWidget)
673
-
674
- self._qteFolderSelected = qt.QLineEdit("", parent=self)
675
- style = qt.QApplication.style()
676
- icon = style.standardIcon(qt.QStyle.SP_DialogOpenButton)
677
- self._qtbSelectFolder = qt.QPushButton(icon, "", parent=self)
678
- self._qteFolderSelected.setToolTip(
679
- "If not output folder is given, then" ' this will generate a "dry run"'
680
- )
681
-
682
- self._lockButton = PadlockButton(parent=self)
683
- self._lockButton.toggled.connect(self._dealWithLbsramOptAndLocker)
684
-
685
- leWidget = qt.QWidget(parent=self)
686
- leWidget.setLayout(qt.QHBoxLayout())
687
- leWidget.layout().setContentsMargins(0, 0, 0, 0)
688
- leWidget.layout().addWidget(self._qteFolderSelected)
689
- leWidget.layout().addWidget(self._qtbSelectFolder)
690
- leWidget.layout().addWidget(self._lockButton)
691
- self.layout().addWidget(leWidget)
692
-
693
- # connect signals/SLOT
694
- self._checkboxWidget.forceLbsram.toggled.connect(self._updateLbsramStatus)
695
- self._qtbSelectFolder.clicked.connect(self._setFolderPathFrmDiag)
696
- self._qteFolderSelected.editingFinished.connect(self._checkUpdateforLbsram)
697
-
698
- # add some speaking API
699
- self.clear = self._qteFolderSelected.clear
700
- self.isLocked = self._lockButton.isLocked
701
- self.forceLbsram = self._checkboxWidget.forceLbsram.isChecked
702
- self.setForceLbsram = self._checkboxWidget.forceLbsram.setChecked
703
- self.removeIfExist = self._checkboxWidget.removeExisting.isChecked
704
- self.setRemoveIfExist = self._checkboxWidget.removeExisting.setChecked
705
-
706
- def setOutput(self, text):
707
- if self.forceLbsram() is True and text.startswith(get_lbsram_path()) is False:
708
- text = os.path.join(get_lbsram_path(), text.lstrip(os.sep))
709
- self._qteFolderSelected.setText(text)
710
-
711
- def lock(self, lock=True):
712
- self._lockButton.setChecked(lock)
713
-
714
- def _setFolderPathFrmDiag(self): # pragma: no cover
715
- dialog = qt.QFileDialog(self)
716
- dialog.setFileMode(qt.QFileDialog.DirectoryOnly)
717
-
718
- if not dialog.exec_() or (len(dialog.selectedFiles()) < 1):
719
- dialog.close()
720
- return
721
- self._qteFolderSelected.setText(dialog.selectedFiles()[0])
722
-
723
- def dryRun(self):
724
- """Should we execute a dry-run and avoid storing reconstruction"""
725
- return self.getOutput() is None
726
-
727
- def getOutput(self):
728
- """Return output folder whre the reconstruction should be saved"""
729
- if self._qteFolderSelected.text() == "":
730
- return None
731
- else:
732
- return self.__getOutputWithLbsramExtension()
733
-
734
- def _dealWithLbsramOptAndLocker(self, toggle):
735
- self._checkboxWidget.forceLbsram.setDisabled(toggle)
736
- if toggle:
737
- self._checkboxWidget.forceLbsram.setChecked(False)
738
-
739
- def _updateLbsramStatus(self, toggle):
740
- if toggle is True:
741
- self._qteFolderSelected.setText(self.__getOutputWithLbsramExtension())
742
-
743
- def __getOutputWithLbsramExtension(self):
744
- _output = self._qteFolderSelected.text()
745
- if (
746
- self.forceLbsram() is True
747
- and _output.startswith(get_lbsram_path()) is False
748
- ):
749
- _output = os.path.join(get_lbsram_path(), _output.lstrip(os.sep))
750
- return _output
751
-
752
- def _checkUpdateforLbsram(self):
753
- """Check if the line edit value has to be changed if force lbsram is
754
- active and the line does not start with it"""
755
- if self.forceLbsram() is True:
756
- if not self._qteFolderSelected.text().startswith(get_lbsram_path()):
757
- self._qteFolderSelected.setText(self.__getOutputWithLbsramExtension())