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
@@ -0,0 +1,475 @@
1
+ import weakref
2
+ import logging
3
+ import numpy
4
+ import h5py
5
+ from silx.gui import qt
6
+ from typing import Optional
7
+ from tomwer.core.scan.hdf5scan import HDF5TomoScan
8
+ from tomwer.gui.utils.unitsystem import MetricEntry
9
+ from tomwer.gui.utils.scandescription import ScanNameLabelAndShape
10
+ from tomoscan.esrf.scan.hdf5scan import ImageKey
11
+ from tomoscan.io import HDF5File
12
+ from tomoscan.nexus.paths.nxtomo import get_paths as get_nexus_paths
13
+ from silx.io.utils import h5py_read_dataset
14
+ from tomoscan.scanbase import _FOV
15
+
16
+
17
+ _logger = logging.getLogger(__name__)
18
+
19
+
20
+ class NXtomoEditorDialog(qt.QDialog):
21
+ def __init__(self, parent=None) -> None:
22
+ super().__init__(parent)
23
+ self.setLayout(qt.QVBoxLayout())
24
+
25
+ self.mainWidget = NXtomoEditor(parent=self)
26
+ self.layout().addWidget(self.mainWidget)
27
+
28
+ types = qt.QDialogButtonBox.Ok
29
+ self._buttons = qt.QDialogButtonBox(parent=self)
30
+ self._buttons.setStandardButtons(types)
31
+ self._buttons.button(qt.QDialogButtonBox.Ok).setText("validate")
32
+ self.layout().addWidget(self._buttons)
33
+
34
+ # expose API
35
+ def setScan(self, scan):
36
+ self.mainWidget.setScan(scan)
37
+
38
+ def overwriteNXtomo(self):
39
+ self.mainWidget.overwriteNXtomo()
40
+
41
+
42
+ class NXtomoEditor(qt.QWidget):
43
+ """
44
+ class to edit parameter of a NXtomo.
45
+ The preliminary goal is to let the user define pixel / voxel position and x and z positions
46
+ in order to simplify stitching down the line
47
+
48
+ As energy and scan range was also often requested this part is also editable (user bonus ^^)
49
+ """
50
+
51
+ def __init__(self, parent=None):
52
+ super().__init__(parent)
53
+ self._editableWidgets = []
54
+ self._scan = None
55
+ self.setLayout(qt.QVBoxLayout())
56
+ self._scanInfoQLE = ScanNameLabelAndShape(parent=self)
57
+ self.layout().addWidget(self._scanInfoQLE)
58
+
59
+ # nxtomo tree
60
+ self._tree = qt.QTreeWidget(self)
61
+ self._tree.setColumnCount(2)
62
+ self._tree.setHeaderLabels(("entry", "value"))
63
+ self.layout().addWidget(self._tree)
64
+
65
+ # 1: instrument
66
+ self._instrumentQTWI = qt.QTreeWidgetItem(self._tree)
67
+ self._instrumentQTWI.setText(0, "instrument")
68
+ # handle energy
69
+ self._beamQTWI = qt.QTreeWidgetItem(self._instrumentQTWI)
70
+ self._beamQTWI.setText(0, "beam")
71
+ self._energyQTWI = qt.QTreeWidgetItem(self._beamQTWI)
72
+ self._energyQTWI.setText(0, "energy (keV)")
73
+ self._energyQLE = EnergyEntry("", self)
74
+ self._energyQLE.setPlaceholderText("energy in kev")
75
+ self._tree.setItemWidget(self._energyQTWI, 1, self._energyQLE)
76
+ self._editableWidgets.append(self._energyQLE)
77
+
78
+ # 1.1 detector
79
+ self._detectorQTWI = qt.QTreeWidgetItem(self._instrumentQTWI)
80
+ self._detectorQTWI.setText(0, "detector")
81
+ ## pixel size
82
+ self._xPixelSizeQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
83
+ self._xPixelSizeQTWI.setText(0, "x pixel size")
84
+ self._xPixelSizeMetricEntry = MetricEntry("", parent=self)
85
+ self._tree.setItemWidget(self._xPixelSizeQTWI, 1, self._xPixelSizeMetricEntry)
86
+ self._editableWidgets.append(self._xPixelSizeMetricEntry)
87
+
88
+ self._yPixelSizeQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
89
+ self._yPixelSizeQTWI.setText(0, "y pixel size")
90
+ self._yPixelSizeMetricEntry = MetricEntry("", parent=self)
91
+ self._tree.setItemWidget(self._yPixelSizeQTWI, 1, self._yPixelSizeMetricEntry)
92
+ self._editableWidgets.append(self._yPixelSizeMetricEntry)
93
+
94
+ ## distance
95
+ self._sampleDetectorDistanceQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
96
+ self._sampleDetectorDistanceQTWI.setText(0, "distance")
97
+ self._distanceMetricEntry = MetricEntry("", parent=self)
98
+ self._tree.setItemWidget(
99
+ self._sampleDetectorDistanceQTWI, 1, self._distanceMetricEntry
100
+ )
101
+ self._editableWidgets.append(self._distanceMetricEntry)
102
+ ## field of view
103
+ self._fieldOfViewQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
104
+ self._fieldOfViewQTWI.setText(0, "field of view")
105
+ self._fieldOfViewCB = qt.QComboBox(self)
106
+ for value in _FOV.values():
107
+ self._fieldOfViewCB.addItem(value)
108
+ self._tree.setItemWidget(self._fieldOfViewQTWI, 1, self._fieldOfViewCB)
109
+ self._editableWidgets.append(self._fieldOfViewCB)
110
+ ## x flipped
111
+ self._xFlippedQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
112
+ self._xFlippedQTWI.setText(0, "x flipped")
113
+ self._xFlippedCB = qt.QCheckBox("", self)
114
+ self._tree.setItemWidget(self._xFlippedQTWI, 1, self._xFlippedCB)
115
+ self._editableWidgets.append(self._xFlippedCB)
116
+
117
+ ## y flipped
118
+ self._yFlippedQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
119
+ self._yFlippedQTWI.setText(0, "y flipped")
120
+ self._yFlippedCB = qt.QCheckBox("", self)
121
+ self._tree.setItemWidget(self._yFlippedQTWI, 1, self._yFlippedCB)
122
+ self._editableWidgets.append(self._yFlippedCB)
123
+
124
+ # 2: sample
125
+ self._sampleQTWI = qt.QTreeWidgetItem(self._tree)
126
+ self._sampleQTWI.setText(0, "sample")
127
+ ## x translation
128
+ self._xTranslationQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
129
+ self._xTranslationQTWI.setText(0, "x translation")
130
+ self._xTranslationQLE = _TranslationMetricEntry(name="", parent=self)
131
+ self._tree.setItemWidget(self._xTranslationQTWI, 1, self._xTranslationQLE)
132
+ self._editableWidgets.append(self._xTranslationQLE)
133
+
134
+ ## z translation
135
+ self._zTranslationQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
136
+ self._zTranslationQTWI.setText(0, "z translation")
137
+ self._zTranslationQLE = _TranslationMetricEntry(name="", parent=self)
138
+ self._tree.setItemWidget(self._zTranslationQTWI, 1, self._zTranslationQLE)
139
+ self._editableWidgets.append(self._zTranslationQLE)
140
+
141
+ # set up
142
+ self._instrumentQTWI.setExpanded(True)
143
+ self._sampleQTWI.setExpanded(True)
144
+ self._beamQTWI.setExpanded(True)
145
+ self._detectorQTWI.setExpanded(True)
146
+
147
+ def getEditableWidgets(self):
148
+ return self._editableWidgets
149
+
150
+ def setScan(self, scan):
151
+ if scan is None:
152
+ self._scan = scan
153
+ elif not isinstance(scan, HDF5TomoScan):
154
+ raise TypeError(
155
+ f"{scan} is expected to be an instance of {HDF5TomoScan}. Not {type(scan)}"
156
+ )
157
+ else:
158
+ self._scan = weakref.ref(scan)
159
+ self._scanInfoQLE.setScan(scan)
160
+ # scan will only be read and not kept
161
+ self.update_tree()
162
+
163
+ def getScan(self):
164
+ if self._scan is None or self._scan() is None:
165
+ return None
166
+ else:
167
+ return self._scan()
168
+
169
+ def update_tree(self):
170
+ if self.getScan() is not None:
171
+ for fct in (
172
+ self._updateInstrument,
173
+ self._updateSample,
174
+ ):
175
+ try:
176
+ fct()
177
+ except Exception as e:
178
+ _logger.info(e)
179
+ self._tree.resizeColumnToContents(0)
180
+
181
+ def _updateInstrument(self):
182
+ scan = self.getScan()
183
+ if scan is None:
184
+ return
185
+ else:
186
+ self._updateEnergy(scan=scan)
187
+ self._updatePixelSize(scan=scan)
188
+ self._updateFlipped(scan=scan)
189
+ self._updateFieldOfView(scan=scan)
190
+ self._updateDistance(scan=scan)
191
+
192
+ def _updateSample(self):
193
+ scan = self.getScan()
194
+ if scan is None:
195
+ return
196
+ else:
197
+ self._updateTranslations(scan=scan)
198
+
199
+ def _updateTranslations(self, scan: HDF5TomoScan):
200
+ assert isinstance(scan, HDF5TomoScan)
201
+
202
+ # note: for now and in order to allow edition we expect to have at most a unique value. Will fail for helicoidal
203
+ def reduce(values):
204
+ if values is None:
205
+ return None
206
+ values = numpy.array(values)
207
+ values = numpy.unique(
208
+ values[scan.image_key_control == ImageKey.PROJECTION.value]
209
+ )
210
+ if values.size == 1:
211
+ return values[0]
212
+ elif values.size == 0:
213
+ return None
214
+ else:
215
+ return f"{values[0]} ... {values[-1]}"
216
+
217
+ x_translation = reduce(scan.x_translation)
218
+ z_translation = reduce(scan.z_translation)
219
+ self._xTranslationQLE.setValue(x_translation)
220
+ self._zTranslationQLE.setValue(z_translation)
221
+
222
+ def _updateFieldOfView(self, scan):
223
+ idx = self._fieldOfViewCB.findText(_FOV.from_value(scan.field_of_view).value)
224
+ if idx > 0:
225
+ self._fieldOfViewCB.setCurrentIndex(idx)
226
+
227
+ def _updateFlipped(self, scan):
228
+ if scan.x_flipped is not None:
229
+ self._xFlippedCB.setChecked(scan.x_flipped)
230
+ if scan.y_flipped is not None:
231
+ self._yFlippedCB.setChecked(scan.y_flipped)
232
+
233
+ def _updateDistance(self, scan):
234
+ self._distanceMetricEntry.setValue(scan.distance)
235
+
236
+ def _updateEnergy(self, scan):
237
+ assert isinstance(scan, HDF5TomoScan)
238
+ energy = scan.energy
239
+ self._energyQLE.setValue(energy)
240
+
241
+ def _updateScanRange(self, scan):
242
+ assert isinstance(scan, HDF5TomoScan)
243
+ scan_range = scan.scan_range
244
+ self._scanRangeQLE.setText(1, str(scan_range))
245
+
246
+ def _updatePixelSize(self, scan):
247
+ assert isinstance(scan, HDF5TomoScan)
248
+ x_pixel_size = scan.x_pixel_size
249
+ y_pixel_size = scan.y_pixel_size
250
+ self._xPixelSizeMetricEntry.setValue(x_pixel_size)
251
+ self._yPixelSizeMetricEntry.setValue(y_pixel_size)
252
+
253
+ def clear(self):
254
+ self._tree.clear()
255
+
256
+ def overwriteNXtomo(self):
257
+ """overwrite data on disk"""
258
+ scan = self.getScan()
259
+ if scan is None:
260
+ _logger.warning("no scan found to be saved")
261
+ return
262
+ nexus_paths = get_nexus_paths(scan.nexus_version)
263
+ assert isinstance(scan, HDF5TomoScan)
264
+ with HDF5File(scan.master_file, mode="a") as h5f:
265
+ entry = h5f[scan.entry]
266
+ # overwrite energy
267
+ energy = self._energyQLE.getValue()
268
+ self.__write_to_file(
269
+ entry=entry,
270
+ path=nexus_paths.ENERGY_PATH,
271
+ value=energy,
272
+ name="energy",
273
+ expected_type=float,
274
+ units="kev",
275
+ )
276
+ # overwrite x pixel size
277
+ self.__write_to_file(
278
+ entry=entry,
279
+ path=nexus_paths.X_PIXEL_SIZE_PATH,
280
+ value=self._xPixelSizeMetricEntry.getValue(),
281
+ name="x pixel size",
282
+ expected_type=float,
283
+ units="m",
284
+ )
285
+ # overwrite y pixel size
286
+ self.__write_to_file(
287
+ entry=entry,
288
+ path=nexus_paths.Y_PIXEL_SIZE_PATH,
289
+ value=self._yPixelSizeMetricEntry.getValue(),
290
+ name="y pixel size",
291
+ expected_type=float,
292
+ units="m",
293
+ )
294
+ n_frames = len(scan.image_key_control)
295
+
296
+ # overwrite x translation
297
+ self.__write_to_file(
298
+ entry=entry,
299
+ path=nexus_paths.X_TRANS_PATH,
300
+ value=self._xTranslationQLE.getValue(),
301
+ name="x translation",
302
+ expected_type=float,
303
+ n_value=n_frames,
304
+ units="m",
305
+ )
306
+ # overwrite z translation
307
+ self.__write_to_file(
308
+ entry=entry,
309
+ path=nexus_paths.Z_TRANS_PATH,
310
+ value=self._zTranslationQLE.getValue(),
311
+ name="z translation",
312
+ expected_type=float,
313
+ n_value=n_frames,
314
+ units="m",
315
+ )
316
+ # overwrite sample detector distance
317
+ self.__write_to_file(
318
+ entry=entry,
319
+ path=nexus_paths.DISTANCE_PATH,
320
+ value=self._distanceMetricEntry.getValue(),
321
+ name="z translation",
322
+ expected_type=float,
323
+ units="m",
324
+ )
325
+ # overwrite FOV
326
+ self.__write_to_file(
327
+ entry=entry,
328
+ path=nexus_paths.FOV_PATH,
329
+ value=self._fieldOfViewCB.currentText(),
330
+ name="field of view",
331
+ expected_type=str,
332
+ )
333
+ # overwrite x flipped
334
+ self.__write_to_file(
335
+ entry=entry,
336
+ path="/".join(
337
+ (
338
+ nexus_paths.INSTRUMENT_PATH,
339
+ nexus_paths.nx_instrument_paths.DETECTOR_PATH,
340
+ nexus_paths.nx_detector_paths.X_FLIPPED,
341
+ )
342
+ ),
343
+ value=self._xFlippedCB.isChecked(),
344
+ name="x flipped",
345
+ expected_type=bool,
346
+ )
347
+ # overwrite y flipped
348
+ self.__write_to_file(
349
+ entry=entry,
350
+ path="/".join(
351
+ (
352
+ nexus_paths.INSTRUMENT_PATH,
353
+ nexus_paths.nx_instrument_paths.DETECTOR_PATH,
354
+ nexus_paths.nx_detector_paths.Y_FLIPPED,
355
+ )
356
+ ),
357
+ value=self._yFlippedCB.isChecked(),
358
+ name="y flipped",
359
+ expected_type=bool,
360
+ )
361
+ # clear caches to make sure all modifications will be considered
362
+ scan.clear_caches()
363
+ scan.clear_frames_caches()
364
+
365
+ @staticmethod
366
+ def _newValueIsExistingValue(dataset: h5py.Dataset, new_value, units):
367
+ """
368
+ return true if the given value is same as the one stored
369
+ """
370
+ current_value = h5py_read_dataset(dataset)
371
+ attrs = dataset.attrs
372
+ current_unit = attrs.get("units", attrs.get("unit", None))
373
+ if units != current_unit:
374
+ # if the unit is not the same, eithen if the value is the same we will overwrite it
375
+ return False
376
+ else:
377
+ if isinstance(new_value, numpy.ndarray) and isinstance(
378
+ current_value, numpy.ndarray
379
+ ):
380
+ return numpy.array_equal(new_value, current_value)
381
+ elif numpy.isscalar(current_value) and numpy.isscalar(new_value):
382
+ return current_value == new_value
383
+ else:
384
+ return False
385
+
386
+ @staticmethod
387
+ def __write_to_file(entry, path, value, name, expected_type, n_value=1, units=None):
388
+ if path is None:
389
+ # if the path does not exists (no handled by this version of nexus for example)
390
+ return
391
+
392
+ # try to cast the value
393
+ if isinstance(value, str):
394
+ value = value.replace(" ", "")
395
+ if value.lower() == "none" or "..." in value:
396
+ # if value is not defined or is an array not overwrite by the user (case of the ... )
397
+ return
398
+ elif value is None:
399
+ pass
400
+ else:
401
+ try:
402
+ value = expected_type(value)
403
+ except (ValueError, TypeError) as e:
404
+ _logger.error(f"Fail to overwrite {name} of {entry.name}. Error is {e}")
405
+ return
406
+
407
+ if path in entry:
408
+ if NXtomoEditor._newValueIsExistingValue(
409
+ dataset=entry[path], new_value=value, units=units
410
+ ):
411
+ # if no need to overwrite
412
+ return
413
+ else:
414
+ del entry[path]
415
+ if value is None:
416
+ return
417
+ elif n_value == 1:
418
+ entry[path] = value
419
+ else:
420
+ entry[path] = numpy.array([value] * n_value)
421
+ if units is not None:
422
+ entry[path].attrs["units"] = units
423
+
424
+
425
+ class _TranslationMetricEntry(MetricEntry):
426
+ LOADED_ARRAY = "loaded array"
427
+
428
+ class TranslationValidator(qt.QDoubleValidator):
429
+ def __init__(self, *args, **kwargs):
430
+ super().__init__(*args, **kwargs)
431
+ self.setNotation(qt.QDoubleValidator.ScientificNotation)
432
+
433
+ def validate(self, a0: str, a1: int):
434
+ if "..." in a0:
435
+ return (qt.QDoubleValidator.Acceptable, a0, a1)
436
+ else:
437
+ return super().validate(a0, a1)
438
+
439
+ def __init__(self, name, default_unit="m", parent=None):
440
+ super().__init__(name, default_unit, parent)
441
+ self._qlePixelSize.setValidator(self.TranslationValidator(self))
442
+
443
+ def getValue(self):
444
+ """
445
+
446
+ :return: the value in meter
447
+ :rtype: float
448
+ """
449
+ if "..." in self._qlePixelSize.text():
450
+ # in this case this is the representation of an array, we don;t wan't to overwrite it
451
+ return self.LOADED_ARRAY
452
+ if self._qlePixelSize.text() in ("unknown", ""):
453
+ return None
454
+ else:
455
+ return float(self._qlePixelSize.text()) * self.getCurrentUnit()
456
+
457
+
458
+ class EnergyEntry(qt.QLineEdit):
459
+ def __init__(self, *args, **kwargs):
460
+ super().__init__(*args, **kwargs)
461
+ self.setValidator(MetricEntry.DoubleValidator())
462
+
463
+ def setValue(self, a0):
464
+ if a0 is None:
465
+ a0 = "unknown"
466
+ else:
467
+ a0 = str(a0)
468
+ super().setText(a0)
469
+
470
+ def getValue(self) -> Optional[float]:
471
+ txt = self.text().replace(" ", "")
472
+ if txt in ("unknown", ""):
473
+ return None
474
+ else:
475
+ return float(txt)
@@ -28,17 +28,16 @@ __license__ = "MIT"
28
28
  __date__ = "28/10/2020"
29
29
 
30
30
 
31
- import unittest
32
31
  import pytest
33
32
  from silx.gui import qt
34
33
  from silx.gui.utils.testutils import TestCaseQt
35
- from tomwer.test.utils import skip_gui_test
34
+ from tomwer.tests.utils import skip_gui_test
36
35
  from tomwer.core.utils.scanutils import MockHDF5
37
36
  from tomwer.gui.edit.dkrfpatch import DarkRefPatchWidget
38
37
  from tomwer.gui.edit.dkrfpatch import _DarkOrFlatUrl
39
38
  from silx.io.url import DataUrl
40
39
  from tomoscan.esrf.scan.utils import get_data
41
- from tomoscan.esrf.hdf5scan import ImageKey
40
+ from tomoscan.esrf.scan.hdf5scan import ImageKey
42
41
  from nxtomomill.utils import add_dark_flat_nx_file
43
42
  from tomwer.core.scan.hdf5scan import HDF5TomoScan
44
43
  import tempfile
@@ -210,14 +209,3 @@ class TestDarkRefPatchWidget(TestCaseQt):
210
209
  darks_end=url_ed,
211
210
  flats_end=url_ef,
212
211
  )
213
-
214
-
215
- def suite():
216
- test_suite = unittest.TestSuite()
217
- for ui in (TestDarkRefPatchWidget, TestDarkOrFlatUrl):
218
- test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(ui))
219
- return test_suite
220
-
221
-
222
- if __name__ == "__main__":
223
- unittest.main(defaultTest="suite")
@@ -30,14 +30,14 @@ __date__ = "28/10/2020"
30
30
 
31
31
  from silx.gui import qt
32
32
  from silx.gui.utils.testutils import TestCaseQt, SignalListener
33
- from tomwer.test.utils import skip_gui_test
33
+ from tomwer.tests.utils import skip_gui_test
34
34
  from tomwer.gui.edit.imagekeyeditor import (
35
35
  ImageKeyDialog,
36
36
  ImageKeyUpgraderWidget,
37
37
  _AddImageKeyUpgradeOperation,
38
38
  )
39
39
  from tomwer.core.utils.scanutils import MockHDF5
40
- from tomoscan.esrf.hdf5scan import ImageKey
40
+ from tomoscan.esrf.scan.hdf5scan import ImageKey
41
41
  import tempfile
42
42
  import pytest
43
43
 
@@ -0,0 +1,155 @@
1
+ import os
2
+ import pytest
3
+ import numpy
4
+ from tomoscan.scanbase import _FOV
5
+ from tomoscan.esrf.scan.hdf5scan import ImageKey
6
+ from tomoscan.unitsystem.metricsystem import MetricSystem
7
+ from tomoscan.unitsystem.energysystem import EnergySI
8
+ from tomwer.core.scan.hdf5scan import HDF5TomoScan
9
+ from tomwer.gui.edit.nxtomoeditor import NXtomoEditor, _TranslationMetricEntry
10
+ from nxtomomill.nexus.nxtomo import NXtomo
11
+ from tomwer.tests.conftest import qtapp # noqa F401
12
+ from silx.gui import qt
13
+
14
+
15
+ @pytest.mark.parametrize("x_pixel_size", (None, 0.12))
16
+ @pytest.mark.parametrize("y_pixel_size", (None, 0.0065))
17
+ @pytest.mark.parametrize("field_of_view", _FOV.values())
18
+ @pytest.mark.parametrize("distance", (None, 1.2))
19
+ @pytest.mark.parametrize("energy", (None, 23.5))
20
+ @pytest.mark.parametrize("x_flipped", (True, False))
21
+ @pytest.mark.parametrize("y_flipped", (True, False))
22
+ @pytest.mark.parametrize("x_translation", (None, numpy.ones(12), numpy.arange(12)))
23
+ @pytest.mark.parametrize("z_translation", (None, numpy.zeros(12), numpy.arange(12, 24)))
24
+ def test_nx_editor(
25
+ tmp_path,
26
+ qtapp, # noqa F811
27
+ x_pixel_size,
28
+ y_pixel_size,
29
+ field_of_view,
30
+ distance,
31
+ energy,
32
+ x_flipped,
33
+ y_flipped,
34
+ x_translation,
35
+ z_translation,
36
+ ):
37
+ # 1.0 create nx tomo with raw data
38
+ nx_tomo = NXtomo()
39
+ nx_tomo.instrument.detector.x_pixel_size = x_pixel_size
40
+ nx_tomo.instrument.detector.y_pixel_size = y_pixel_size
41
+ nx_tomo.instrument.detector.field_of_view = field_of_view
42
+ nx_tomo.instrument.detector.distance = distance
43
+ nx_tomo.instrument.detector.x_flipped = x_flipped
44
+ nx_tomo.instrument.detector.y_flipped = y_flipped
45
+ nx_tomo.energy = energy
46
+ nx_tomo.sample.x_translation = x_translation
47
+ nx_tomo.sample.z_translation = z_translation
48
+ nx_tomo.instrument.detector.image_key_control = [ImageKey.PROJECTION.value] * 12
49
+ nx_tomo.instrument.detector.data = numpy.empty(shape=(12, 10, 10))
50
+ nx_tomo.sample.rotation_angle = numpy.linspace(0, 20, num=12)
51
+
52
+ file_path = os.path.join(tmp_path, "nxtomo.nx")
53
+ entry = "entry0000"
54
+ nx_tomo.save(
55
+ file_path=file_path,
56
+ data_path=entry,
57
+ )
58
+
59
+ scan = HDF5TomoScan(file_path, entry)
60
+
61
+ # 2.0 create the widget and do the edition
62
+ widget = NXtomoEditor()
63
+ widget.setScan(scan=scan)
64
+ # widget.show()
65
+
66
+ # 3.0 check data have been corrcetly loaded
67
+ def check_metric(expected_value, current_value):
68
+ if expected_value is None:
69
+ return current_value is None
70
+ return numpy.isclose(expected_value, float(current_value))
71
+
72
+ assert check_metric(x_pixel_size, widget._xPixelSizeMetricEntry.getValue())
73
+ assert widget._xPixelSizeMetricEntry._qcbUnit.currentText() == "m"
74
+ assert check_metric(y_pixel_size, widget._yPixelSizeMetricEntry.getValue())
75
+ assert widget._yPixelSizeMetricEntry._qcbUnit.currentText() == "m"
76
+
77
+ assert check_metric(distance, widget._distanceMetricEntry.getValue())
78
+ assert widget._distanceMetricEntry._qcbUnit.currentText() == "m"
79
+
80
+ assert field_of_view == widget._fieldOfViewCB.currentText()
81
+ assert x_flipped == widget._xFlippedCB.isChecked()
82
+ assert y_flipped == widget._yFlippedCB.isChecked()
83
+
84
+ if energy is None:
85
+ assert widget._energyQLE.getValue() is None
86
+ else:
87
+ assert numpy.isclose(energy, widget._energyQLE.getValue())
88
+
89
+ def check_translation(expected_value, current_value):
90
+ if expected_value is None:
91
+ return current_value is None
92
+ else:
93
+ u_values = numpy.unique(expected_value)
94
+ if u_values.size == 1:
95
+ return float(current_value) == u_values[0]
96
+ else:
97
+ return current_value is _TranslationMetricEntry.LOADED_ARRAY
98
+
99
+ assert check_translation(x_translation, widget._xTranslationQLE.getValue())
100
+ assert widget._xTranslationQLE._qcbUnit.currentText() == "m"
101
+ assert check_translation(z_translation, widget._zTranslationQLE.getValue())
102
+ assert widget._zTranslationQLE._qcbUnit.currentText() == "m"
103
+
104
+ # 4.0 edit some parameters
105
+ widget._energyQLE.setText("23.789")
106
+ widget._xPixelSizeMetricEntry.setUnit("nm")
107
+ widget._yPixelSizeMetricEntry.setValue(2.1e-7)
108
+ widget._distanceMetricEntry.setValue("unknown")
109
+ widget._fieldOfViewCB.setCurrentText(_FOV.HALF.value)
110
+ widget._xFlippedCB.setChecked(not x_flipped)
111
+ widget._xTranslationQLE.setValue(1.8)
112
+ widget._xTranslationQLE.setUnit("mm")
113
+ widget._zTranslationQLE.setValue(2.8)
114
+ widget._zTranslationQLE.setUnit("m")
115
+
116
+ # 5.0 save
117
+ widget.overwriteNXtomo()
118
+
119
+ # 6.0 make sure data have been overwrite
120
+ overwrite_nx_tomo = NXtomo().load(
121
+ file_path=file_path,
122
+ data_path=entry,
123
+ )
124
+
125
+ assert overwrite_nx_tomo.energy.value == 23.789
126
+ assert overwrite_nx_tomo.energy.unit == EnergySI.KILOELECTRONVOLT
127
+ if x_pixel_size is None:
128
+ assert overwrite_nx_tomo.instrument.detector.x_pixel_size.value is None
129
+ else:
130
+ assert numpy.isclose(
131
+ overwrite_nx_tomo.instrument.detector.x_pixel_size.value,
132
+ x_pixel_size * MetricSystem.NANOMETER.value,
133
+ )
134
+ assert overwrite_nx_tomo.instrument.detector.y_pixel_size.value == 2.1e-7
135
+
136
+ assert overwrite_nx_tomo.instrument.detector.distance.value is None
137
+ assert overwrite_nx_tomo.instrument.detector.field_of_view is _FOV.HALF
138
+
139
+ assert overwrite_nx_tomo.instrument.detector.x_flipped is not x_flipped
140
+ assert overwrite_nx_tomo.instrument.detector.y_flipped is y_flipped
141
+
142
+ numpy.testing.assert_array_almost_equal(
143
+ overwrite_nx_tomo.sample.x_translation.value,
144
+ numpy.array([1.8 * MetricSystem.MILLIMETER.value] * 12),
145
+ )
146
+ assert overwrite_nx_tomo.sample.x_translation.unit is MetricSystem.METER
147
+ numpy.testing.assert_array_almost_equal(
148
+ overwrite_nx_tomo.sample.z_translation.value,
149
+ numpy.array([2.8 * MetricSystem.METER.value] * 12),
150
+ )
151
+ assert overwrite_nx_tomo.sample.z_translation.unit is MetricSystem.METER
152
+ # end
153
+ widget.setAttribute(qt.Qt.WA_DeleteOnClose)
154
+ widget.close()
155
+ widget = None
tomwer/gui/icons.py CHANGED
@@ -283,7 +283,6 @@ def getAnimatedIcon(name):
283
283
  """
284
284
  key = name + "__anim"
285
285
  if key not in _cached_icons:
286
-
287
286
  qtMajorVersion = int(qt.qVersion().split(".")[0])
288
287
  icon = None
289
288