tomwer 1.0.3__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. orangecontrib/tomwer/tutorials/EBS_tomo_listener.ows +39 -0
  2. orangecontrib/tomwer/tutorials/cast_volume.ows +34 -0
  3. orangecontrib/tomwer/tutorials/simple_slice_reconstruction.ows +39 -0
  4. orangecontrib/tomwer/tutorials/simple_volume_local_reconstruction.ows +49 -0
  5. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +59 -0
  6. orangecontrib/tomwer/tutorials/using_saaxis_to_find_cor.ows +44 -0
  7. orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +1 -1
  8. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +14 -4
  9. orangecontrib/tomwer/widgets/cluster/__init__.py +1 -1
  10. orangecontrib/tomwer/widgets/control/DataListOW.py +12 -5
  11. orangecontrib/tomwer/widgets/control/DataListenerOW.py +18 -9
  12. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +13 -6
  13. orangecontrib/tomwer/widgets/control/DataTransfertOW.py +3 -5
  14. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +8 -4
  15. orangecontrib/tomwer/widgets/control/DataWatcherOW.py +4 -6
  16. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +49 -62
  17. orangecontrib/tomwer/widgets/control/FilterOW.py +2 -4
  18. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +93 -0
  19. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +135 -129
  20. orangecontrib/tomwer/widgets/control/NotifierOW.py +34 -9
  21. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +3 -5
  22. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +19 -13
  23. orangecontrib/tomwer/widgets/control/VolumeSelector.py +12 -4
  24. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +11 -7
  25. orangecontrib/tomwer/widgets/control/icons/notification.svg +4 -4
  26. orangecontrib/tomwer/widgets/control/icons/nxtomomill.png +0 -0
  27. orangecontrib/tomwer/widgets/control/icons/nxtomomill.svg +8 -5
  28. orangecontrib/tomwer/widgets/control/icons/tomoobjserie.png +0 -0
  29. orangecontrib/tomwer/widgets/control/icons/tomoobjserie.svg +73 -78
  30. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +16 -4
  31. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +100 -0
  32. orangecontrib/tomwer/widgets/edit/icons/image_key_editor.png +0 -0
  33. orangecontrib/tomwer/widgets/edit/icons/image_key_upgrader.png +0 -0
  34. orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.png +0 -0
  35. orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.svg +123 -0
  36. orangecontrib/tomwer/widgets/edit/test/test_dark_flat_patch.py +21 -1
  37. orangecontrib/tomwer/widgets/edit/test/test_image_key_editor.py +1 -1
  38. orangecontrib/tomwer/widgets/edit/test/test_image_key_upgrader.py +1 -1
  39. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +25 -0
  40. orangecontrib/tomwer/widgets/other/PythonScriptOW.py +19 -11
  41. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +20 -14
  42. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +24 -10
  43. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +26 -21
  44. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +29 -12
  45. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +44 -17
  46. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +28 -20
  47. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +24 -18
  48. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +6 -6
  49. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +4 -2
  50. orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.png +0 -0
  51. orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.svg +11 -8
  52. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +10 -4
  53. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  54. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +69 -0
  55. orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +2 -4
  56. orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.png +0 -0
  57. orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.svg +105 -0
  58. tomwer/__main__.py +10 -5
  59. tomwer/app/canvas_launcher/config.py +10 -10
  60. tomwer/app/canvas_launcher/mainwindow.py +68 -6
  61. tomwer/app/canvas_launcher/widgetsscheme.py +1 -3
  62. tomwer/app/darkref.py +16 -12
  63. tomwer/app/imagekeyeditor.py +2 -2
  64. tomwer/app/imagekeyupgrader.py +104 -0
  65. tomwer/app/intensitynormalization.py +0 -1
  66. tomwer/app/nxtomoeditor.py +103 -0
  67. tomwer/app/rsync.py +1 -1
  68. tomwer/core/cluster/cluster.py +1 -1
  69. tomwer/core/futureobject.py +1 -0
  70. tomwer/core/process/control/datalistener/datalistener.py +7 -1
  71. tomwer/core/process/control/datalistener/rpcserver.py +3 -4
  72. tomwer/core/process/control/datawatcher/datawatcher.py +18 -18
  73. tomwer/core/process/control/datawatcher/datawatcherobserver.py +5 -8
  74. tomwer/core/process/control/datawatcher/datawatcherprocess.py +2 -3
  75. tomwer/core/process/control/datawatcher/edfdwprocess.py +2 -2
  76. tomwer/core/process/control/nxtomomill.py +33 -58
  77. tomwer/core/process/control/scanlist.py +2 -1
  78. tomwer/core/process/control/scanselector.py +7 -0
  79. tomwer/core/process/control/scantransfer.py +2 -2
  80. tomwer/core/process/control/scanvalidator.py +6 -5
  81. tomwer/core/process/control/singletomoobj.py +2 -1
  82. tomwer/core/process/control/timer.py +2 -1
  83. tomwer/core/process/control/tomoobjserie.py +8 -2
  84. tomwer/core/process/control/volumeselector.py +2 -1
  85. tomwer/core/process/control/volumesymlink.py +2 -1
  86. tomwer/core/process/edit/darkflatpatch.py +2 -1
  87. tomwer/core/process/edit/imagekeyeditor.py +4 -3
  88. tomwer/core/process/reconstruction/axis/axis.py +29 -32
  89. tomwer/core/process/reconstruction/axis/mode.py +3 -2
  90. tomwer/core/process/reconstruction/axis/params.py +35 -16
  91. tomwer/core/process/reconstruction/darkref/darkrefs.py +90 -707
  92. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +44 -16
  93. tomwer/core/process/reconstruction/darkref/params.py +62 -67
  94. tomwer/core/process/reconstruction/lamino/tofu.py +1 -2
  95. tomwer/core/process/reconstruction/nabu/castvolume.py +21 -26
  96. tomwer/core/process/reconstruction/nabu/nabucommon.py +36 -38
  97. tomwer/core/process/reconstruction/nabu/nabuscores.py +28 -13
  98. tomwer/core/process/reconstruction/nabu/nabuslices.py +41 -14
  99. tomwer/core/process/reconstruction/nabu/nabuvolume.py +21 -12
  100. tomwer/core/process/reconstruction/nabu/utils.py +32 -3
  101. tomwer/core/process/reconstruction/normalization/normalization.py +9 -8
  102. tomwer/core/process/reconstruction/saaxis/saaxis.py +46 -20
  103. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +38 -12
  104. tomwer/core/process/reconstruction/test/__init__.py +0 -39
  105. tomwer/core/process/reconstruction/test/test_axis_params.py +25 -3
  106. tomwer/core/process/reconstruction/test/test_darkref_copy.py +117 -1
  107. tomwer/core/process/script/python.py +16 -12
  108. tomwer/core/process/task.py +3 -7
  109. tomwer/core/process/test/test_axis.py +1 -1
  110. tomwer/core/process/test/test_dark_and_flat.py +41 -111
  111. tomwer/core/process/test/test_data_listener.py +0 -29
  112. tomwer/core/process/test/test_data_transfer.py +10 -14
  113. tomwer/core/process/test/test_nabu.py +1 -1
  114. tomwer/core/process/test/test_normalization.py +1 -1
  115. tomwer/core/process/visualization/liveslice.py +6 -0
  116. tomwer/core/scan/blissscan.py +37 -2
  117. tomwer/core/scan/edfscan.py +19 -8
  118. tomwer/core/scan/hdf5scan.py +10 -4
  119. tomwer/core/scan/scanbase.py +35 -29
  120. tomwer/core/scan/scanfactory.py +3 -17
  121. tomwer/core/scan/test/test_h5.py +1 -1
  122. tomwer/core/scan/test/test_process_registration.py +0 -11
  123. tomwer/core/scan/test/test_scan.py +32 -30
  124. tomwer/core/settings.py +2 -2
  125. tomwer/core/test/test_utils.py +1 -1
  126. tomwer/core/tomwer_object.py +19 -0
  127. tomwer/core/utils/__init__.py +0 -45
  128. tomwer/core/utils/char.py +2 -0
  129. tomwer/core/utils/gpu.py +5 -5
  130. tomwer/core/utils/nxtomoutils.py +2 -2
  131. tomwer/core/utils/scanutils.py +50 -0
  132. tomwer/core/utils/volumeutils.py +13 -0
  133. tomwer/core/volume/edfvolume.py +4 -0
  134. tomwer/core/volume/hdf5volume.py +4 -0
  135. tomwer/core/volume/jp2kvolume.py +4 -0
  136. tomwer/core/volume/rawvolume.py +22 -5
  137. tomwer/core/volume/tiffvolume.py +4 -0
  138. tomwer/core/volume/volumebase.py +19 -12
  139. tomwer/core/volume/volumefactory.py +20 -1
  140. tomwer/gui/cluster/slurm.py +1 -1
  141. tomwer/gui/cluster/supervisor.py +0 -2
  142. tomwer/gui/cluster/test/test_cluster.py +2 -2
  143. tomwer/gui/control/datalist.py +109 -36
  144. tomwer/gui/control/datatransfert.py +1 -1
  145. tomwer/gui/control/datawatcher/configuration.py +0 -2
  146. tomwer/gui/control/datawatcher/datawatcher.py +23 -13
  147. tomwer/gui/control/datawatcher/datawatcherobserver.py +1 -1
  148. tomwer/gui/control/observations.py +0 -3
  149. tomwer/gui/control/selectorwidgetbase.py +42 -12
  150. tomwer/gui/control/serie/seriecreator.py +967 -0
  151. tomwer/{web/__init__.py → gui/control/serie/seriewaiter.py} +5 -7
  152. tomwer/gui/control/singletomoobj.py +15 -4
  153. tomwer/gui/control/test/test_datalist.py +1 -1
  154. tomwer/gui/control/test/test_datalistener.py +1 -1
  155. tomwer/gui/control/test/test_inputwidget.py +1 -1
  156. tomwer/gui/control/test/test_process_manager.py +1 -13
  157. tomwer/gui/control/test/test_scanselector.py +1 -1
  158. tomwer/gui/control/test/test_scanvalidator.py +1 -1
  159. tomwer/gui/control/test/test_single_tomo_obj.py +1 -1
  160. tomwer/gui/control/test/test_volume_dialog.py +19 -7
  161. tomwer/gui/control/test/test_volumeselector.py +4 -4
  162. tomwer/gui/debugtools/datasetgenerator.py +1 -9
  163. tomwer/gui/edit/dkrfpatch.py +2 -3
  164. tomwer/gui/edit/imagekeyeditor.py +12 -11
  165. tomwer/gui/edit/nxtomoeditor.py +475 -0
  166. tomwer/gui/edit/test/test_dkrf_patch.py +2 -14
  167. tomwer/gui/edit/test/test_image_key_editor.py +2 -2
  168. tomwer/gui/edit/test/test_nx_editor.py +155 -0
  169. tomwer/gui/icons.py +0 -1
  170. tomwer/gui/qfolderdialog.py +11 -0
  171. tomwer/gui/reconstruction/axis/CompareImages.py +27 -29
  172. tomwer/gui/reconstruction/axis/axis.py +2 -0
  173. tomwer/gui/reconstruction/axis/radioaxis.py +70 -14
  174. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +7 -9
  175. tomwer/gui/reconstruction/darkref/darkrefwidget.py +22 -24
  176. tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
  177. tomwer/gui/reconstruction/lamino/tofu/tofu.py +3 -3
  178. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +4 -4
  179. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +10 -5
  180. tomwer/gui/reconstruction/nabu/castvolume.py +103 -24
  181. tomwer/gui/reconstruction/nabu/check.py +1 -1
  182. tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +352 -0
  183. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +0 -9
  184. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +1 -1
  185. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +18 -19
  186. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +30 -7
  187. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +26 -15
  188. tomwer/gui/reconstruction/nabu/slices.py +10 -4
  189. tomwer/gui/reconstruction/nabu/slurm.py +1 -1
  190. tomwer/gui/reconstruction/nabu/volume.py +13 -7
  191. tomwer/gui/reconstruction/normalization/intensity.py +1 -5
  192. tomwer/gui/reconstruction/saaxis/corrangeselector.py +10 -37
  193. tomwer/gui/reconstruction/saaxis/saaxis.py +11 -7
  194. tomwer/gui/reconstruction/saaxis/sliceselector.py +11 -26
  195. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +13 -8
  196. tomwer/gui/reconstruction/scores/scoreplot.py +67 -62
  197. tomwer/gui/reconstruction/test/test_axis.py +2 -2
  198. tomwer/gui/reconstruction/test/test_lamino.py +2 -2
  199. tomwer/gui/reconstruction/test/test_nabu.py +14 -1
  200. tomwer/gui/reconstruction/test/test_saaxis.py +8 -17
  201. tomwer/gui/reconstruction/test/test_sadeltabeta.py +7 -13
  202. tomwer/gui/stackplot.py +11 -28
  203. tomwer/gui/test/test_axis_gui.py +4 -4
  204. tomwer/gui/test/test_qfolder_dialog.py +12 -0
  205. tomwer/gui/utils/inputwidget.py +42 -22
  206. tomwer/gui/utils/lineselector/lineselector.py +13 -21
  207. tomwer/gui/utils/scandescription.py +2 -4
  208. tomwer/gui/utils/slider.py +1 -102
  209. tomwer/gui/utils/unitsystem.py +48 -11
  210. tomwer/gui/visualization/dataviewer.py +24 -17
  211. tomwer/gui/visualization/diffviewer/diffviewer.py +2 -11
  212. tomwer/gui/visualization/nxtomometadata.py +21 -0
  213. tomwer/gui/visualization/scanoverview.py +0 -1
  214. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +72 -0
  215. tomwer/gui/visualization/test/test_stacks.py +1 -1
  216. tomwer/gui/visualization/tomoobjoverview.py +49 -0
  217. tomwer/gui/visualization/volumeoverview.py +64 -0
  218. tomwer/gui/visualization/volumeviewer.py +1 -1
  219. tomwer/io/utils/utils.py +2 -2
  220. tomwer/resources/gui/icons/multi-document-save.png +0 -0
  221. tomwer/resources/gui/icons/multi-document-save.svg +101 -0
  222. tomwer/resources/gui/illustrations/ctf_z1.png +0 -0
  223. tomwer/resources/gui/illustrations/ctf_z1.svg +471 -0
  224. tomwer/synctools/axis.py +0 -1
  225. tomwer/synctools/darkref.py +0 -1
  226. tomwer/synctools/datalistener.py +5 -1
  227. tomwer/synctools/imageloaderthread.py +2 -2
  228. tomwer/synctools/saaxis.py +0 -1
  229. tomwer/synctools/sadeltabeta.py +0 -1
  230. tomwer/synctools/stacks/edit/imagekeyeditor.py +1 -1
  231. tomwer/synctools/stacks/processingstack.py +2 -2
  232. tomwer/synctools/stacks/reconstruction/castvolume.py +1 -0
  233. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
  234. tomwer/synctools/stacks/reconstruction/lamino.py +1 -3
  235. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +0 -2
  236. tomwer/synctools/test/test_darkRefs.py +32 -149
  237. tomwer/synctools/test/test_foldertransfer.py +1 -1
  238. tomwer/synctools/test/test_scanstages.py +2 -2
  239. tomwer/tests/conftest.py +51 -0
  240. tomwer/{test → tests}/test_scripts.py +1 -1
  241. tomwer/tests/test_utils.py +10 -0
  242. tomwer/{test → tests}/utils/utilstest.py +0 -11
  243. tomwer/version.py +3 -3
  244. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/METADATA +14 -16
  245. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/RECORD +255 -235
  246. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/WHEEL +1 -1
  247. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/entry_points.txt +6 -0
  248. orangecontrib/tomwer/setup.py +0 -45
  249. orangecontrib/tomwer/widgets/setup.py +0 -49
  250. tomwer/app/process.py +0 -153
  251. tomwer/core/process/reconstruction/nabu/slurm.py +0 -36
  252. tomwer/core/process/reconstruction/utils/nabu_slice_exec.py +0 -10
  253. tomwer/core/utils/laminoutils.py +0 -80
  254. tomwer/gui/utils/lineselector/lineselection.py +0 -76
  255. tomwer/setup.py +0 -52
  256. tomwer/slurm/executor.py +0 -36
  257. tomwer/slurm/job.py +0 -349
  258. tomwer/slurm/utils.py +0 -44
  259. tomwer/web/client.py +0 -43
  260. tomwer/web/config.py +0 -36
  261. tomwer/web/test/test_graylog_connection.py +0 -59
  262. {tomwer/slurm → orangecontrib/tomwer/tutorials}/__init__.py +0 -0
  263. /tomwer/{test → gui/control/serie}/__init__.py +0 -0
  264. /tomwer/{web/test → tests}/__init__.py +0 -0
  265. /tomwer/{test → tests}/utils/__init__.py +0 -0
  266. /tomwer-1.0.3-py3.8-nspkg.pth → /tomwer-1.1.0-py3.9-nspkg.pth +0 -0
  267. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/LICENSE +0 -0
  268. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/namespace_packages.txt +0 -0
  269. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/top_level.txt +0 -0
@@ -37,13 +37,14 @@ from tomwer.core.process.reconstruction.nabu.castvolume import (
37
37
  from tomwer.core.process.reconstruction.nabu.nabucommon import NabuOutputFileFormat
38
38
  from tomwer.gui.qlefilesystem import QLFileSystem
39
39
  from tomwer.gui.reconstruction.nabu.nabuconfig.output import QNabuFileFormatComboBox
40
+ from silx.gui.utils import blockSignals
40
41
  import logging
42
+ from typing import Optional
41
43
 
42
44
  _logger = logging.getLogger(__name__)
43
45
 
44
46
 
45
47
  class CastVolumeWidget(qt.QWidget):
46
-
47
48
  sigConfigChanged = qt.Signal()
48
49
  """Signal emit when the configuration changed"""
49
50
 
@@ -59,28 +60,53 @@ class CastVolumeWidget(qt.QWidget):
59
60
  self._outputDataTypeCB = qt.QComboBox(self)
60
61
  for data_type in ("uint8", "uint16", "float32", "float64"):
61
62
  self._outputDataTypeCB.addItem(data_type)
62
- self.layout().addWidget(self._outputDataTypeCB, 0, 2, 1, 2)
63
+ self.layout().addWidget(self._outputDataTypeCB, 0, 1, 1, 3)
63
64
  # output file format
64
65
  self._outputFileformatLabel = qt.QLabel("output file format", self)
65
66
  self.layout().addWidget(self._outputFileformatLabel, 1, 0, 1, 1)
66
67
  self._outputFileformatCB = QNabuFileFormatComboBox(self)
67
- self.layout().addWidget(self._outputFileformatCB, 1, 2, 1, 1)
68
- # rescale percentiles
68
+ self.layout().addWidget(self._outputFileformatCB, 1, 1, 1, 3)
69
+
70
+ # let the user provide min and max manually
71
+ self._minMaxLabel = qt.QLabel("min max values")
72
+ self.layout().addWidget(self._minMaxLabel, 2, 0, 1, 1)
73
+ self._minMaxAuto = qt.QCheckBox("auto with rescale from percentiles")
74
+ self._minMaxAuto.setChecked(True)
75
+ self._minMaxAuto.setToolTip(
76
+ "If set to auto will try to get min/max pixel values from nabu histogram else will compute it. Otherwise will values provided by the user"
77
+ )
78
+ self.layout().addWidget(self._minMaxAuto, 2, 1, 1, 1)
79
+
80
+ self._minPixValue = qt.QLineEdit("", self)
81
+ self._minPixValue.setPlaceholderText("min")
82
+ self._maxPixValue = qt.QLineEdit("", self)
83
+ self._maxPixValue.setPlaceholderText("max")
84
+ validator = qt.QDoubleValidator(self)
85
+ validator.setNotation(qt.QDoubleValidator.ScientificNotation)
86
+ self._minPixValue.setValidator(validator)
87
+ self._maxPixValue.setValidator(validator)
88
+ self.layout().addWidget(self._minPixValue, 4, 2, 1, 1)
89
+ self.layout().addWidget(self._maxPixValue, 4, 3, 1, 1)
90
+ self._minPixValue.setVisible(False)
91
+ self._maxPixValue.setVisible(False)
92
+ # or from percentiles
69
93
  self._percentilesLabel = qt.QLabel("rescale percentiles")
70
- self.layout().addWidget(self._percentilesLabel, 2, 0, 1, 1)
94
+ self.layout().addWidget(self._percentilesLabel, 3, 1, 1, 1)
71
95
  self._lowPercentileQSB = qt.QSpinBox(self)
72
96
  self._lowPercentileQSB.setRange(0, 100)
73
97
  self._lowPercentileQSB.setPrefix("min:")
98
+ self._lowPercentileQSB.setSuffix("%")
74
99
  self._lowPercentileQSB.setValue(RESCALE_MIN_PERCENTILE)
75
- self.layout().addWidget(self._lowPercentileQSB, 2, 1, 1, 1)
100
+ self.layout().addWidget(self._lowPercentileQSB, 3, 2, 1, 1)
76
101
  self._highPercentileQSB = qt.QSpinBox(self)
77
102
  self._highPercentileQSB.setRange(0, 100)
78
103
  self._highPercentileQSB.setPrefix("max:")
104
+ self._highPercentileQSB.setSuffix("%")
79
105
  self._highPercentileQSB.setValue(RESCALE_MAX_PERCENTILE)
80
- self.layout().addWidget(self._highPercentileQSB, 2, 2, 1, 1)
106
+ self.layout().addWidget(self._highPercentileQSB, 3, 3, 1, 1)
81
107
  # save dir
82
108
  self._saveDirLabel = qt.QLabel("output directory", self)
83
- self.layout().addWidget(self._saveDirLabel, 3, 0, 1, 1)
109
+ self.layout().addWidget(self._saveDirLabel, 5, 0, 1, 1)
84
110
  self._useDefaultSaveDirQCB = qt.QCheckBox("default", self)
85
111
  self._useDefaultSaveDirQCB.setToolTip(
86
112
  f"Default directory is: {DEFAULT_OUTPUT_DIR}"
@@ -89,7 +115,7 @@ class CastVolumeWidget(qt.QWidget):
89
115
  qt.QSizePolicy.Minimum, qt.QSizePolicy.Minimum
90
116
  )
91
117
  self._useDefaultSaveDirQCB.setChecked(True)
92
- self.layout().addWidget(self._useDefaultSaveDirQCB, 3, 1, 1, 1)
118
+ self.layout().addWidget(self._useDefaultSaveDirQCB, 5, 1, 1, 1)
93
119
  self._saveDirQLE = QLFileSystem(text=DEFAULT_OUTPUT_DIR, parent=self)
94
120
  # force text because this is not a valid path
95
121
  self._saveDirQLE.setText(DEFAULT_OUTPUT_DIR)
@@ -100,11 +126,11 @@ class CastVolumeWidget(qt.QWidget):
100
126
  """
101
127
  )
102
128
  self._saveDirQLE.setVisible(False)
103
- self.layout().addWidget(self._saveDirQLE, 3, 2, 1, 1)
129
+ self.layout().addWidget(self._saveDirQLE, 5, 2, 1, 2)
104
130
  # overwrite
105
131
  self._overwriteCB = qt.QCheckBox("overwrite", self)
106
132
  self._overwriteCB.setChecked(True)
107
- self.layout().addWidget(self._overwriteCB, 4, 0, 1, 1)
133
+ self.layout().addWidget(self._overwriteCB, 6, 0, 1, 1)
108
134
  # spacer
109
135
  self._spacer = qt.QWidget(self)
110
136
  self._spacer.setSizePolicy(
@@ -119,6 +145,8 @@ class CastVolumeWidget(qt.QWidget):
119
145
  self._useDefaultSaveDirQCB.toggled.connect(self._configChanged)
120
146
  self._overwriteCB.toggled.connect(self._configChanged)
121
147
  self._useDefaultSaveDirQCB.toggled.connect(self._updateOutputDirVis)
148
+ self._minMaxAuto.toggled.connect(self._configChanged)
149
+ self._minMaxAuto.toggled.connect(self._updatePixMinMaxVis)
122
150
 
123
151
  def getOutputDataType(self) -> str:
124
152
  return self._outputDataTypeCB.currentText()
@@ -155,21 +183,59 @@ class CastVolumeWidget(qt.QWidget):
155
183
  def setOverwrite(self, remove) -> None:
156
184
  self._overwriteCB.setChecked(remove)
157
185
 
186
+ def getDataMin(self) -> Optional[float]:
187
+ if (
188
+ self._minMaxAuto.isChecked()
189
+ or self._minPixValue.text().replace(" ", "") == ""
190
+ ):
191
+ return None
192
+ else:
193
+ return float(self._minPixValue.text())
194
+
195
+ def setDataMin(self, value: Optional[float]) -> None:
196
+ with blockSignals(self._minMaxAuto):
197
+ self._minMaxAuto.setChecked(value is None)
198
+ if value is not None:
199
+ with blockSignals(self._minPixValue):
200
+ self._minPixValue.setText(str(value))
201
+
202
+ def getDataMax(self) -> Optional[float]:
203
+ if (
204
+ self._minMaxAuto.isChecked()
205
+ or self._maxPixValue.text().replace(" ", "") == ""
206
+ ):
207
+ return None
208
+ else:
209
+ return float(self._maxPixValue.text())
210
+
211
+ def setDataMax(self, value: Optional[float]) -> None:
212
+ with blockSignals(self._minMaxAuto):
213
+ self._minMaxAuto.setChecked(value is None)
214
+ if value is not None:
215
+ with blockSignals(self._maxPixValue):
216
+ self._maxPixValue.setText(str(value))
217
+
158
218
  def getConfiguration(self) -> dict:
219
+ rescale_min_percentile, rescale_max_percentile = self.getRescalePercentiles()
159
220
  return {
160
221
  "output_data_type": self.getOutputDataType(),
161
222
  "output_type": self.getOutputFileFormat().value,
162
223
  "output_dir": self.getOutputDir(),
163
224
  "overwrite": self.getOverwrite(),
164
- "rescale_percentiles": self.getRescalePercentiles(),
225
+ "rescale_min_percentile": rescale_min_percentile,
226
+ "rescale_max_percentile": rescale_max_percentile,
227
+ "data_min": self.getDataMin(),
228
+ "data_max": self.getDataMax(),
165
229
  }
166
230
 
167
231
  def getRescalePercentiles(self) -> tuple:
168
232
  return self._lowPercentileQSB.value(), self._highPercentileQSB.value()
169
233
 
170
234
  def setRescalePercentiles(self, low, high) -> tuple:
171
- self._lowPercentileQSB.setValue(low)
172
- self._highPercentileQSB.setValue(high)
235
+ if low is not None:
236
+ self._lowPercentileQSB.setValue(low)
237
+ if high is not None:
238
+ self._highPercentileQSB.setValue(high)
173
239
 
174
240
  def setConfiguration(self, config: dict) -> None:
175
241
  output_data_type = config.get("output_data_type", None)
@@ -184,19 +250,32 @@ class CastVolumeWidget(qt.QWidget):
184
250
  overwrite = config.get("overwrite", None)
185
251
  if overwrite is not None:
186
252
  self.setOverwrite(overwrite)
187
- rescale_percentiles = config.get("rescale_percentiles", None)
188
- if rescale_percentiles is not None:
189
- if not len(rescale_percentiles) == 2:
190
- _logger.error(
191
- f"percentiles is expected to contain two values not {len(rescale_percentiles)}"
192
- )
193
- else:
194
- self.setRescalePercentiles(
195
- rescale_percentiles[0], rescale_percentiles[1]
196
- )
253
+ if "rescale_percentiles" in config:
254
+ rescale_min_percentile, rescale_max_percentile = config[
255
+ "rescale_percentiles"
256
+ ]
257
+ _logger.warning(
258
+ "'rescale_percentiles' is deprecated. Please use 'rescale_min_percentile' and 'rescale_max_percentile' instead"
259
+ )
260
+ else:
261
+ rescale_min_percentile = config.get("rescale_min_percentile", None)
262
+ rescale_max_percentile = config.get("rescale_max_percentile", None)
263
+
264
+ self.setRescalePercentiles(rescale_min_percentile, rescale_max_percentile)
265
+ if "data_min" in config:
266
+ self.setDataMin(config["data_min"])
267
+ if "data_max" in config:
268
+ self.setDataMax(config["data_max"])
197
269
 
198
270
  def _configChanged(self, *args, **kwargs):
199
271
  self.sigConfigChanged.emit()
200
272
 
201
273
  def _updateOutputDirVis(self, *args, **kwargs):
202
274
  self._saveDirQLE.setVisible(not self._useDefaultSaveDirQCB.isChecked())
275
+
276
+ def _updatePixMinMaxVis(self, *args, **kwargs):
277
+ self._minPixValue.setVisible(not self._minMaxAuto.isChecked())
278
+ self._maxPixValue.setVisible(not self._minMaxAuto.isChecked())
279
+ self._percentilesLabel.setVisible(self._minMaxAuto.isChecked())
280
+ self._lowPercentileQSB.setVisible(self._minMaxAuto.isChecked())
281
+ self._highPercentileQSB.setVisible(self._minMaxAuto.isChecked())
@@ -30,7 +30,7 @@ __date__ = "02/12/2021"
30
30
 
31
31
  from tomwer.core.scan.hdf5scan import HDF5TomoScan
32
32
  from tomwer.core.utils.nxtomoutils import get_n_series
33
- from tomoscan.esrf.hdf5scan import ImageKey
33
+ from tomoscan.esrf.scan.hdf5scan import ImageKey
34
34
  from silx.gui import qt
35
35
 
36
36
 
@@ -0,0 +1,352 @@
1
+ from silx.gui import qt
2
+ from typing import Optional, Union
3
+ from tomwer.core.process.reconstruction.nabu.utils import _NabuStages
4
+ from tomwer.gui.reconstruction.nabu.nabuconfig import base
5
+ from tomwer.gui.utils.illustrations import _IllustrationWidget
6
+ from silx.gui.utils import blockSignals
7
+ from silx.utils.enum import Enum as _Enum
8
+ from .reconstruction import AnglesFileWidget as Filewidget
9
+
10
+
11
+ def nabu_param_ligne_to_dict(str_ligne: str) -> dict:
12
+ ddict = {}
13
+ if "=" in str_ligne:
14
+ for elmt in str_ligne.split(";"):
15
+ kwv = elmt.lstrip(" ").rstrip(" ")
16
+ key, value = kwv.split("=")
17
+ ddict[key] = value
18
+ return ddict
19
+
20
+
21
+ class _DoubleScientific(qt.QLineEdit):
22
+ """
23
+ A simple QLineEdit with a Doublevalidator with Scientific Notation
24
+ """
25
+
26
+ def __init__(self, parent=None, *args, **kwargs):
27
+ super().__init__(parent, *args, **kwargs)
28
+ validator = qt.QDoubleValidator(parent=self)
29
+ validator.setNotation(qt.QDoubleValidator.ScientificNotation)
30
+ self.setValidator(validator)
31
+
32
+ def value(self) -> float:
33
+ if self.text() == "":
34
+ return 0.0
35
+ else:
36
+ return float(self.text())
37
+
38
+ def setValue(self, value):
39
+ self.setText(str(value))
40
+
41
+
42
+ class OptionalDouble(qt.QWidget):
43
+ """
44
+ A spin box with a Check box to handle 'None' case of the nabu CTF options
45
+ """
46
+
47
+ sigChanged = qt.Signal()
48
+ """emit when the combobox of the double spin box value changed"""
49
+
50
+ def __init__(self, parent) -> None:
51
+ super().__init__(parent)
52
+ self.setLayout(qt.QHBoxLayout())
53
+ self._cb = qt.QCheckBox("", self)
54
+ self.layout().addWidget(self._cb)
55
+ self._dsb = _DoubleScientific(self)
56
+ self.layout().addWidget(self._dsb)
57
+
58
+ # connect signal ? slot
59
+ self._cb.toggled.connect(self._updateVisibility)
60
+ self._cb.toggled.connect(self._changed)
61
+ self._dsb.textChanged.connect(self._changed)
62
+
63
+ # set up
64
+ self._updateVisibility()
65
+
66
+ def _changed(self, *args, **kwargs):
67
+ self.sigChanged.emit()
68
+
69
+ def value(self) -> Optional[float]:
70
+ if self._cb.isChecked():
71
+ return self._dsb.value()
72
+ else:
73
+ return None
74
+
75
+ def setValue(self, value: Optional[float]):
76
+ if value in (None, "None"):
77
+ self._cb.setChecked(False)
78
+ else:
79
+ self._cb.setChecked(True)
80
+ self._dsb.setValue(float(value))
81
+
82
+ def setText(self, text: str):
83
+ self._cb.setText(text)
84
+
85
+ def _updateVisibility(self, *args, **kwargs):
86
+ self._dsb.setVisible(self._cb.isChecked())
87
+
88
+
89
+ class _BeamShape(_Enum):
90
+ PARALLEL = "parallel"
91
+ CONE = "cone"
92
+
93
+
94
+ class ConeBeamOpts(qt.QGroupBox):
95
+ """Specific settings for cone beam geometry"""
96
+
97
+ sigConfChanged = qt.Signal()
98
+ """emit when ctf parameters changed"""
99
+
100
+ def __init__(self, parent=None, title="cone settings"):
101
+ super().__init__(parent, title=title)
102
+ self.setLayout(qt.QGridLayout())
103
+ # z1_v
104
+ self._z1_v = _DoubleScientific(self)
105
+ self._z1_v.setPlaceholderText("z1_v in meter")
106
+ self.layout().addWidget(qt.QLabel("z1_v", self), 0, 0, 1, 1)
107
+ self.layout().addWidget(self._z1_v, 0, 1, 1, 1)
108
+ # z1_h
109
+ self._z1_h = _DoubleScientific(self)
110
+ self._z1_h.setPlaceholderText("z1_h in meter")
111
+ self.layout().addWidget(qt.QLabel("z1_h", self), 1, 0, 1, 1)
112
+ self.layout().addWidget(self._z1_h, 1, 1, 1, 1)
113
+
114
+ # illustration to define z1, z2
115
+ self._ctfIllustration = _IllustrationWidget(parent=self, img="ctf_z1")
116
+ self._ctfIllustration.setMinimumSize(qt.QSize(200, 90))
117
+ self._ctfIllustration.setSizePolicy(
118
+ qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding
119
+ )
120
+
121
+ self._ctfIllustration.setContentsMargins(0, 0, 0, 0)
122
+ self._ctfIllustration.layout().setSpacing(0)
123
+ self.layout().addWidget(self._ctfIllustration, 0, 2, 2, 2)
124
+
125
+ # connect signal / slot
126
+ self._z1_v.textChanged.connect(self._confChanged)
127
+ self._z1_h.textChanged.connect(self._confChanged)
128
+
129
+ def _confChanged(self, *args, **kwargs):
130
+ self.sigConfChanged.emit()
131
+
132
+ def getGeometry(self) -> dict:
133
+ return {
134
+ "z1_v": self._z1_v.value(),
135
+ "z1_h": self._z1_h.value(),
136
+ }
137
+
138
+ def setGeometry(self, config: dict) -> None:
139
+ with blockSignals(self):
140
+ if "z1_v" in config:
141
+ self._z1_v.setValue(float(config["z1_v"]))
142
+ if "z1_h" in config:
143
+ self._z1_h.setValue(float(config["z1_h"]))
144
+
145
+
146
+ class CTFGeometry(qt.QGroupBox):
147
+ """
148
+ widget for the ctf_geometry parameter of nabu
149
+ """
150
+
151
+ sigConfChanged = qt.Signal()
152
+ """emit when ctf parameters changed"""
153
+
154
+ def __init__(self, parent=None, title="ctf-geometry") -> None:
155
+ super().__init__(parent, title=title)
156
+ self.setLayout(qt.QFormLayout())
157
+ self._beamShapeCB = qt.QComboBox(self)
158
+ for shape in _BeamShape.values():
159
+ self._beamShapeCB.addItem(shape)
160
+ self.layout().addRow("shape", self._beamShapeCB)
161
+
162
+ # cone beam settings
163
+ self._coneBeamSettings = ConeBeamOpts(self)
164
+ self.layout().addRow(self._coneBeamSettings)
165
+
166
+ # detector pixel size
167
+ self._detectorPixelSize = OptionalDouble(self)
168
+ self._detectorPixelSize.setText("overwrite")
169
+ self._detectorPixelSize._dsb.setPlaceholderText("pixel size in meter")
170
+ self.layout().addRow("detector pixel size", self._detectorPixelSize)
171
+ # magnification
172
+ self._magnification = qt.QCheckBox("magnification", self)
173
+ self.layout().addRow(self._magnification)
174
+
175
+ # set up
176
+ self._magnification.setChecked(True)
177
+ self._updateView()
178
+
179
+ # connect signal / slot
180
+ self._coneBeamSettings.sigConfChanged.connect(self._confChanged)
181
+ self._detectorPixelSize.sigChanged.connect(self._confChanged)
182
+ self._magnification.toggled.connect(self._confChanged)
183
+ self._beamShapeCB.currentIndexChanged.connect(self._updateView)
184
+ self._beamShapeCB.currentIndexChanged.connect(self._confChanged)
185
+
186
+ def getBeamShape(self):
187
+ return _BeamShape.from_value(self._beamShapeCB.currentText())
188
+
189
+ def setBeamShape(self, shape: Union[str, _BeamShape]):
190
+ shape = _BeamShape.from_value(shape)
191
+ self._beamShapeCB.setCurrentText(shape.value)
192
+
193
+ def _updateView(self, *args, **kwargs):
194
+ self._coneBeamSettings.setVisible(self.getBeamShape() == _BeamShape.CONE)
195
+
196
+ def _confChanged(self, *args, **kwargs):
197
+ self.sigConfChanged.emit()
198
+
199
+ def getConfiguration(self) -> dict:
200
+ magnification = "True" if self._magnification.isChecked() else "False"
201
+ beam_shape = self.getBeamShape()
202
+ if beam_shape is _BeamShape.PARALLEL:
203
+ z1_v = None
204
+ z1_h = None
205
+ elif beam_shape is _BeamShape.CONE:
206
+ cone_params = self._coneBeamSettings.getGeometry()
207
+ z1_v = cone_params["z1_v"]
208
+ z1_h = cone_params["z1_h"]
209
+ else:
210
+ raise NotImplementedError(f"Geometry {beam_shape.value} is not handled")
211
+
212
+ geometry = f" z1_v={z1_v}; z1_h={z1_h}; detec_pixel_size={self._detectorPixelSize.value()}; magnification={magnification}"
213
+
214
+ return {"ctf_geometry": geometry, "beam_shape": beam_shape.value}
215
+
216
+ def setConfiguration(self, ddict: dict) -> None:
217
+ params = nabu_param_ligne_to_dict(ddict.get("ctf_geometry", ""))
218
+ with blockSignals(self):
219
+ for key, value in params.items():
220
+ if key == "detec_pixel_size":
221
+ self._detectorPixelSize.setValue(value)
222
+ elif key == "magnification":
223
+ self._magnification.setChecked(value.lower() == "true")
224
+
225
+ beam_shape = ddict.get("beam_shape", None)
226
+ if beam_shape is not None:
227
+ if _BeamShape.from_value(beam_shape) is _BeamShape.CONE:
228
+ self._coneBeamSettings.setGeometry(params)
229
+ self.setBeamShape(beam_shape)
230
+
231
+
232
+ class CTFAdvancedParams(qt.QGroupBox):
233
+ """
234
+ widget for the ctf_advanced_params parameter of nabu
235
+ """
236
+
237
+ sigConfChanged = qt.Signal()
238
+ """emit when ctf parameters changed"""
239
+
240
+ def __init__(self, parent=None, title="ctf-advanced-params") -> None:
241
+ super().__init__(parent, title=title)
242
+ self.setLayout(qt.QFormLayout())
243
+ # length_scale
244
+ self._length_scale = _DoubleScientific(self)
245
+ self.layout().addRow("length_scale", self._length_scale)
246
+ # lim1
247
+ self._lim1 = _DoubleScientific(self)
248
+ self._lim1Label = qt.QLabel("lim1")
249
+ self.layout().addRow(self._lim1Label, self._lim1)
250
+ # lim2
251
+ self._lim2 = _DoubleScientific(self)
252
+ self._lim2Label = qt.QLabel("lim2")
253
+ self.layout().addRow(self._lim2Label, self._lim2)
254
+ for w in (self._lim1, self._lim1Label, self._lim2, self._lim2Label):
255
+ w.setToolTip(
256
+ "parameter of the low-pass filter. Used in equation: 'lim = lim1 * r + lim2 * (1 - r)' where r is some low-pass filter (image dimensionality)"
257
+ )
258
+
259
+ # normalize_by_mean
260
+ self._normalize_by_mean = qt.QCheckBox("normalize by mean", self)
261
+ self.layout().addRow(self._normalize_by_mean)
262
+
263
+ # set up
264
+ self._length_scale.setText("1e-5")
265
+ self._lim1.setText("1e-5")
266
+ self._lim2.setText("0.2")
267
+ self._normalize_by_mean.setChecked(True)
268
+
269
+ # connect sginal / slot
270
+ self._length_scale.textChanged.connect(self._confChanged)
271
+ self._lim1.textChanged.connect(self._confChanged)
272
+ self._lim2.textChanged.connect(self._confChanged)
273
+ self._normalize_by_mean.toggled.connect(self._confChanged)
274
+
275
+ def getLengthScale(self) -> str:
276
+ if self._length_scale.text() == "":
277
+ return "0.0"
278
+ else:
279
+ return self._length_scale.text()
280
+
281
+ def _confChanged(self, *args, **kwargs):
282
+ self.sigConfChanged.emit()
283
+
284
+ def getConfiguration(self) -> dict:
285
+ normalize_by_mean = "True" if self._normalize_by_mean.isChecked() else "False"
286
+ ctf_advanced_params = f" length_scale={self._length_scale.value()}; lim1={self._lim1.value()}; lim2={self._lim2.value()}; normalize_by_mean={normalize_by_mean}"
287
+ return {
288
+ "ctf_advanced_params": ctf_advanced_params,
289
+ }
290
+
291
+ def setConfiguration(self, ddict: dict) -> None:
292
+ params = nabu_param_ligne_to_dict(ddict.get("ctf_advanced_params", {}))
293
+ with blockSignals(self):
294
+ for key, value in params.items():
295
+ if key == "length_scale":
296
+ self._length_scale.setText(value)
297
+ elif key == "lim1":
298
+ self._lim1.setText(value)
299
+ elif key == "lim2":
300
+ self._lim2.setText(value)
301
+ elif key == "normalize_by_mean":
302
+ self._normalize_by_mean.setChecked(value.lower() == "true")
303
+
304
+
305
+ class CTFConfig(qt.QGroupBox, base._NabuStageConfigBase):
306
+ """
307
+ Widget dedicated for the CTF parameters
308
+ """
309
+
310
+ sigConfChanged = qt.Signal(str)
311
+ """emit when ctf parameters changed"""
312
+
313
+ def __init__(self, parent=None, title="ctf parameters"):
314
+ qt.QGroupBox.__init__(self, parent, title=title, stage=_NabuStages.PHASE)
315
+ base._NabuStageConfigBase.__init__(self, stage=_NabuStages.PHASE)
316
+
317
+ self.setLayout(qt.QFormLayout())
318
+
319
+ # geometry
320
+ self._geometryWidget = CTFGeometry(parent=self, title="geometry")
321
+ self.layout().addRow(self._geometryWidget)
322
+ self.registerWidget(self._geometryWidget, "optional")
323
+ # translation file
324
+ self._translationFile = Filewidget(self)
325
+ self.layout().addRow("translation file", self._translationFile)
326
+ self.registerWidget(self._translationFile, "optional")
327
+ # advanced parameters
328
+ self._advancedParamsWidget = CTFAdvancedParams(
329
+ parent=self, title="advanced parameters"
330
+ )
331
+ self.layout().addRow(self._advancedParamsWidget)
332
+ self.registerWidget(self._advancedParamsWidget, "advanced")
333
+
334
+ # connect signal / slot
335
+ self._geometryWidget.sigConfChanged.connect(self._confChanged)
336
+ self._advancedParamsWidget.sigConfChanged.connect(self._confChanged)
337
+ self._translationFile._qle.textEdited.connect(self._confChanged)
338
+
339
+ def _confChanged(self, *args, **kwargs):
340
+ self.sigConfChanged.emit("phase")
341
+
342
+ def getConfiguration(self) -> dict:
343
+ ddict = self._geometryWidget.getConfiguration()
344
+ ddict.update(self._advancedParamsWidget.getConfiguration())
345
+ ddict["ctf_translations_file"] = self._translationFile.getFile()
346
+ return ddict
347
+
348
+ def setConfiguration(self, ddict):
349
+ self._geometryWidget.setConfiguration(ddict=ddict)
350
+ self._advancedParamsWidget.setConfiguration(ddict=ddict)
351
+ if "ctf_translations_file" in ddict:
352
+ self._translationFile.setFile(ddict["ctf_translations_file"])
@@ -293,12 +293,3 @@ class NabuConfigurationTab(qt.QTabWidget):
293
293
  self._reconstructionWidget.setConfiguration(config["reconstruction"])
294
294
  if "tomwer_slices" in config:
295
295
  self._reconstructionWidget.setSlices(config["tomwer_slices"])
296
-
297
-
298
- if __name__ == "__main__":
299
- app = qt.QApplication([])
300
- widget = NabuConfiguration(None, None)
301
- widget.show()
302
- print(widget.getConfiguration())
303
- print(widget.getSlices())
304
- app.exec_()
@@ -119,7 +119,7 @@ class NabuOutputLocationWidget(qt.QWidget):
119
119
  self._outputDirQLE.setVisible(not hide)
120
120
  self._selectOutputPB.setVisible(not hide)
121
121
 
122
- def _selectOutput(self):
122
+ def _selectOutput(self): # pragma: no cover
123
123
  defaultDirectory = self._outputDirQLE.text()
124
124
  if os.path.isdir(defaultDirectory):
125
125
  defaultDirectory = get_default_directory()