tomwer 1.2.9__py3-none-any.whl → 1.3.0a0__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 (253) hide show
  1. orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
  2. orangecontrib/tomwer/widgets/__init__.py +1 -0
  3. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
  4. orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
  5. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
  6. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +11 -5
  7. orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
  8. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +31 -18
  9. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +14 -7
  10. orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
  11. orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
  12. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
  13. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +4 -4
  14. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
  15. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
  16. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +2 -0
  17. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
  18. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
  19. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
  20. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
  21. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
  22. orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
  23. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  24. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
  25. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
  26. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
  27. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  28. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
  29. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
  30. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
  31. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +9 -8
  32. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +3 -3
  33. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +179 -169
  34. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
  35. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
  36. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +7 -13
  37. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +7 -17
  38. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +3 -4
  39. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
  40. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
  41. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
  42. tomwer/__main__.py +11 -58
  43. tomwer/app/canvas.py +8 -0
  44. tomwer/app/canvas_launcher/config.py +13 -11
  45. tomwer/app/darkref.py +1 -1
  46. tomwer/app/darkrefpatch.py +1 -1
  47. tomwer/app/imagekeyeditor.py +5 -5
  48. tomwer/app/imagekeyupgrader.py +5 -5
  49. tomwer/app/intensitynormalization.py +2 -2
  50. tomwer/app/radiostack.py +2 -2
  51. tomwer/app/zstitching.py +74 -3
  52. tomwer/core/cluster/cluster.py +26 -0
  53. tomwer/core/log/logger.py +7 -5
  54. tomwer/core/process/conditions/filters.py +1 -1
  55. tomwer/core/process/control/datalistener/datalistener.py +3 -3
  56. tomwer/core/process/control/nxtomoconcatenate.py +13 -13
  57. tomwer/core/process/control/nxtomomill.py +83 -25
  58. tomwer/core/process/control/scantransfer.py +11 -10
  59. tomwer/core/process/control/scanvalidator.py +3 -2
  60. tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
  61. tomwer/core/process/control/test/test_email.py +4 -4
  62. tomwer/core/process/control/test/test_h52nx_process.py +59 -7
  63. tomwer/core/process/control/test/test_volume_link.py +64 -64
  64. tomwer/core/process/control/timer.py +1 -1
  65. tomwer/core/process/control/volumesymlink.py +200 -200
  66. tomwer/core/process/edit/darkflatpatch.py +6 -6
  67. tomwer/core/process/edit/imagekeyeditor.py +17 -18
  68. tomwer/core/process/icat/__init__.py +0 -0
  69. tomwer/core/process/icat/createscreenshots.py +100 -0
  70. tomwer/core/process/icat/gallery.py +377 -0
  71. tomwer/core/process/icat/icatbase.py +36 -0
  72. tomwer/core/process/icat/publish.py +228 -0
  73. tomwer/core/process/icat/screenshots.py +26 -0
  74. tomwer/core/process/output.py +52 -0
  75. tomwer/core/process/reconstruction/axis/axis.py +17 -10
  76. tomwer/core/process/reconstruction/axis/mode.py +4 -0
  77. tomwer/core/process/reconstruction/axis/params.py +9 -4
  78. tomwer/core/process/reconstruction/darkref/darkrefs.py +8 -6
  79. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +1 -1
  80. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  81. tomwer/core/process/reconstruction/lamino/tofu.py +4 -4
  82. tomwer/core/process/reconstruction/nabu/castvolume.py +1 -1
  83. tomwer/core/process/reconstruction/nabu/helical.py +9 -5
  84. tomwer/core/process/reconstruction/nabu/nabucommon.py +32 -62
  85. tomwer/core/process/reconstruction/nabu/nabuscores.py +387 -61
  86. tomwer/core/process/reconstruction/nabu/nabuslices.py +33 -21
  87. tomwer/core/process/reconstruction/nabu/nabuvolume.py +37 -14
  88. tomwer/core/process/reconstruction/nabu/settings.py +2 -2
  89. tomwer/core/process/reconstruction/nabu/utils.py +129 -24
  90. tomwer/core/process/reconstruction/output.py +108 -0
  91. tomwer/core/process/reconstruction/saaxis/saaxis.py +233 -263
  92. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +140 -86
  93. tomwer/core/process/reconstruction/scores/params.py +4 -1
  94. tomwer/core/process/reconstruction/scores/scores.py +13 -0
  95. tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
  96. tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
  97. tomwer/core/process/reconstruction/test/test_darkref_copy.py +3 -3
  98. tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
  99. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
  100. tomwer/core/process/stitching/nabustitcher.py +2 -2
  101. tomwer/core/process/test/test_axis.py +6 -6
  102. tomwer/core/process/test/test_dark_and_flat.py +10 -7
  103. tomwer/core/process/test/test_data_transfer.py +7 -6
  104. tomwer/core/process/test/test_nabu.py +4 -4
  105. tomwer/core/process/test/test_normalization.py +2 -2
  106. tomwer/core/scan/edfscan.py +4 -1
  107. tomwer/core/scan/hdf5scan.py +19 -500
  108. tomwer/core/scan/nxtomoscan.py +532 -0
  109. tomwer/core/scan/scanbase.py +42 -20
  110. tomwer/core/scan/scanfactory.py +13 -13
  111. tomwer/core/scan/test/test_future_scan.py +2 -2
  112. tomwer/core/scan/test/test_h5.py +12 -10
  113. tomwer/core/scan/test/test_process_registration.py +2 -2
  114. tomwer/core/scan/test/test_scan.py +4 -3
  115. tomwer/core/settings.py +20 -0
  116. tomwer/core/test/test_scanutils.py +8 -7
  117. tomwer/core/test/test_utils.py +33 -26
  118. tomwer/core/utils/__init__.py +0 -466
  119. tomwer/core/utils/deprecation.py +1 -1
  120. tomwer/core/utils/dictutils.py +14 -0
  121. tomwer/core/utils/lbsram.py +35 -0
  122. tomwer/core/utils/nxtomoutils.py +1 -1
  123. tomwer/core/utils/scanutils.py +6 -6
  124. tomwer/core/utils/spec.py +263 -0
  125. tomwer/core/volume/volumefactory.py +2 -2
  126. tomwer/gui/cluster/slurm.py +260 -60
  127. tomwer/gui/cluster/test/test_cluster.py +13 -0
  128. tomwer/gui/cluster/test/test_supervisor.py +2 -2
  129. tomwer/gui/configuration/__init__.py +0 -0
  130. tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
  131. tomwer/gui/configuration/level.py +22 -0
  132. tomwer/gui/control/actions.py +54 -0
  133. tomwer/gui/control/datalist.py +78 -16
  134. tomwer/gui/control/datalistener.py +4 -16
  135. tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
  136. tomwer/gui/control/history.py +2 -2
  137. tomwer/gui/control/observations.py +2 -2
  138. tomwer/gui/control/reducedarkflatselector.py +1 -1
  139. tomwer/gui/control/selectorwidgetbase.py +36 -9
  140. tomwer/gui/control/serie/seriecreator.py +5 -22
  141. tomwer/gui/control/test/test_email.py +1 -1
  142. tomwer/gui/control/test/test_scanvalidator.py +6 -5
  143. tomwer/gui/control/test/test_single_tomo_obj.py +2 -2
  144. tomwer/gui/control/tomoobjdisplaymode.py +8 -0
  145. tomwer/gui/debugtools/datasetgenerator.py +3 -3
  146. tomwer/gui/edit/dkrfpatch.py +16 -22
  147. tomwer/gui/edit/imagekeyeditor.py +8 -11
  148. tomwer/gui/edit/nxtomoeditor.py +111 -44
  149. tomwer/gui/edit/nxtomowarmer.py +4 -4
  150. tomwer/gui/edit/test/test_dkrf_patch.py +7 -7
  151. tomwer/gui/edit/test/test_image_key_editor.py +3 -3
  152. tomwer/gui/edit/test/test_nx_editor.py +40 -16
  153. tomwer/gui/icat/__init__.py +0 -0
  154. tomwer/gui/icat/createscreenshots.py +80 -0
  155. tomwer/gui/icat/gallery.py +214 -0
  156. tomwer/gui/icat/publish.py +187 -0
  157. tomwer/gui/reconstruction/axis/axis.py +171 -57
  158. tomwer/gui/reconstruction/axis/radioaxis.py +80 -95
  159. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
  160. tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
  161. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +3 -6
  162. tomwer/gui/reconstruction/nabu/castvolume.py +1 -1
  163. tomwer/gui/reconstruction/nabu/check.py +9 -9
  164. tomwer/gui/reconstruction/nabu/helical.py +29 -12
  165. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
  166. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +110 -33
  167. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +9 -12
  168. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +219 -29
  169. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +3 -6
  170. tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
  171. tomwer/gui/reconstruction/nabu/slices.py +6 -7
  172. tomwer/gui/reconstruction/nabu/volume.py +22 -10
  173. tomwer/gui/reconstruction/normalization/intensity.py +15 -23
  174. tomwer/gui/reconstruction/saaxis/corrangeselector.py +7 -23
  175. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
  176. tomwer/gui/reconstruction/saaxis/saaxis.py +7 -9
  177. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +2 -1
  178. tomwer/gui/reconstruction/scores/control.py +2 -9
  179. tomwer/gui/reconstruction/scores/scoreplot.py +11 -5
  180. tomwer/gui/reconstruction/test/test_axis.py +23 -12
  181. tomwer/gui/reconstruction/test/test_lamino.py +8 -3
  182. tomwer/gui/reconstruction/test/test_nabu.py +28 -9
  183. tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
  184. tomwer/gui/reconstruction/test/test_sadeltabeta.py +2 -2
  185. tomwer/gui/settings.py +5 -28
  186. tomwer/gui/stackplot.py +2 -5
  187. tomwer/gui/stitching/action.py +49 -0
  188. tomwer/gui/stitching/config/axisparams.py +7 -24
  189. tomwer/gui/stitching/config/output.py +10 -8
  190. tomwer/gui/stitching/config/positionoveraxis.py +22 -23
  191. tomwer/gui/stitching/normalization.py +117 -0
  192. tomwer/gui/stitching/stitchandbackground.py +4 -6
  193. tomwer/gui/stitching/stitching.py +265 -43
  194. tomwer/gui/stitching/stitching_preview.py +62 -5
  195. tomwer/gui/stitching/stitching_raw.py +2 -5
  196. tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
  197. tomwer/gui/utils/buttons.py +112 -29
  198. tomwer/gui/utils/inputwidget.py +33 -25
  199. tomwer/gui/utils/scandescription.py +4 -0
  200. tomwer/gui/utils/step.py +144 -0
  201. tomwer/gui/utils/unitsystem.py +2 -5
  202. tomwer/gui/utils/vignettes.py +176 -15
  203. tomwer/gui/visualization/dataviewer.py +1 -4
  204. tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
  205. tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
  206. tomwer/gui/visualization/scanoverview.py +1 -1
  207. tomwer/gui/visualization/sinogramviewer.py +1 -10
  208. tomwer/gui/visualization/test/test_diffviewer.py +3 -3
  209. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
  210. tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
  211. tomwer/gui/visualization/test/test_stacks.py +3 -3
  212. tomwer/gui/visualization/test/test_volumeviewer.py +2 -2
  213. tomwer/io/utils/raw_and_processed_data.py +84 -0
  214. tomwer/io/utils/tomoobj.py +4 -6
  215. tomwer/resources/gui/icons/ruler.png +0 -0
  216. tomwer/resources/gui/icons/ruler.svg +273 -0
  217. tomwer/resources/gui/icons/short_description.png +0 -0
  218. tomwer/resources/gui/icons/short_description.svg +58 -0
  219. tomwer/resources/gui/icons/url.png +0 -0
  220. tomwer/resources/gui/icons/url.svg +58 -0
  221. tomwer/synctools/stacks/edit/darkflatpatch.py +2 -2
  222. tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
  223. tomwer/synctools/stacks/reconstruction/axis.py +4 -4
  224. tomwer/synctools/stacks/reconstruction/castvolume.py +2 -2
  225. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +4 -10
  226. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  227. tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
  228. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
  229. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
  230. tomwer/synctools/test/test_darkRefs.py +7 -58
  231. tomwer/synctools/test/test_foldertransfer.py +6 -6
  232. tomwer/synctools/utils/scanstages.py +6 -6
  233. tomwer/tests/conftest.py +34 -0
  234. tomwer/tests/datasets.py +13 -0
  235. tomwer/tests/test_scripts.py +92 -39
  236. tomwer/tests/utils.py +5 -0
  237. tomwer/version.py +3 -3
  238. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/METADATA +39 -39
  239. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/RECORD +248 -209
  240. tomwer/resources/gui/icons/esrf_1.svg +0 -307
  241. tomwer/resources/gui/icons/triangle.svg +0 -80
  242. tomwer/synctools/test/test_scanstages.py +0 -162
  243. tomwer/tests/utils/__init__.py +0 -247
  244. tomwer/tests/utils/utilstest.py +0 -220
  245. /tomwer/app/{saaxis.py → multicor.py} +0 -0
  246. /tomwer/app/{sadeltabeta.py → multipag.py} +0 -0
  247. /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
  248. /tomwer-1.2.9-py3.11-nspkg.pth → /tomwer-1.3.0a0-py3.11-nspkg.pth +0 -0
  249. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/LICENSE +0 -0
  250. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/WHEEL +0 -0
  251. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/entry_points.txt +0 -0
  252. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/namespace_packages.txt +0 -0
  253. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/top_level.txt +0 -0
@@ -21,11 +21,11 @@ class AlphaChannelWidget(qt.QWidget):
21
21
  self._alphaImgSlider = qt.QSlider(qt.Qt.Horizontal, self)
22
22
  self._alphaImgSlider.setMinimumWidth(40)
23
23
  self._alphaImgSlider.setRange(0, 100)
24
- self.layout().addRow(f"{ALPHA_CHAR} stitching", self._alphaImgSlider)
24
+ self.layout().addRow(f"{ALPHA_CHAR} image", self._alphaImgSlider)
25
25
  self._alphaBackgroundSlider = qt.QSlider(qt.Qt.Horizontal, self)
26
26
  self._alphaBackgroundSlider.setMinimumWidth(40)
27
27
  self._alphaBackgroundSlider.setRange(0, 100)
28
- self.layout().addRow(f"{ALPHA_CHAR} background", self._alphaBackgroundSlider)
28
+ self.layout().addRow(f"{ALPHA_CHAR} overlay", self._alphaBackgroundSlider)
29
29
 
30
30
  # connect signal / slot
31
31
  self._alphaImgSlider.valueChanged.connect(self._alphaImgchanged)
@@ -62,8 +62,8 @@ class StitchAndBackgroundAlphaMixIn:
62
62
  self._stitched_image = None
63
63
  self._composition_background = None
64
64
  # background action to show / hide the backgrounds
65
- self._backGroundAction = qt.QAction("background", self)
66
- self._backGroundAction.setToolTip("show / hide colored background")
65
+ self._backGroundAction = qt.QAction("overlay", self)
66
+ self._backGroundAction.setToolTip("show / hide overlay on top of image")
67
67
  self._backGroundAction.setCheckable(True)
68
68
  save_icon = tomwer_icons.getQIcon("background")
69
69
  self._backGroundAction.setIcon(save_icon)
@@ -128,8 +128,6 @@ class StitchAndBackgroundAlphaMixIn:
128
128
  )
129
129
  background_data = self.getImage(legend=self.LEGEND_BACKGROUND)
130
130
  background_data.setAlpha(self.getBackgroundImgAlpha())
131
- # TODO: active image is always the image data
132
-
133
131
  else:
134
132
  if self.getImage(self.LEGEND_BACKGROUND) is not None:
135
133
  self.removeImage(self.LEGEND_BACKGROUND)
@@ -12,6 +12,7 @@ from nabu.stitching.z_stitching import (
12
12
  PreProcessZStitcher,
13
13
  )
14
14
  from nabu.pipeline.config import generate_nabu_configfile, parse_nabu_config_file
15
+ from nabu.stitching.alignment import AlignmentAxis1, AlignmentAxis2
15
16
  from nabu.stitching.config import (
16
17
  get_default_stitching_config,
17
18
  identifiers_as_str_to_instances,
@@ -27,19 +28,18 @@ from nxtomomill.io.utils import (
27
28
  convert_str_to_tuple as _convert_str_to_tuple,
28
29
  convert_str_to_bool as _convert_str_to_bool,
29
30
  )
30
- from silx.gui import icons as silx_icons
31
31
  from silx.gui import qt
32
32
  from tomoscan.serie import Serie
33
33
  from tomoscan.scanbase import TomoScanBase as _TomoScanBase
34
34
  from tomoscan.volumebase import VolumeBase as _VolumeBase
35
35
 
36
- from tomwer.core.scan.hdf5scan import HDF5TomoScan, HDF5TomoScanIdentifier
36
+ from tomwer.core.scan.nxtomoscan import NXtomoScan, NXtomoScanIdentifier
37
37
  from tomwer.core.scan.scanfactory import ScanFactory
38
38
  from tomwer.core.volume.volumefactory import VolumeFactory
39
+ from tomwer.core.volume.volumebase import TomwerVolumeBase
39
40
  from tomwer.core.tomwer_object import TomwerObject
40
41
  from tomwer.core.volume.hdf5volume import HDF5Volume, HDF5VolumeIdentifier
41
42
  from tomwer.io.utils.utils import str_to_dict
42
- from tomwer.gui import icons as tomwer_icons
43
43
  from tomwer.gui.qconfigfile import QConfigFileDialog
44
44
  from tomwer.gui.stitching.config.axisparams import StitcherAxisParams
45
45
  from tomwer.gui.stitching.config.positionoveraxis import PosEditorOverOneAxis
@@ -49,6 +49,15 @@ from tomwer.gui.stitching.stitching_preview import PreviewStitchingPlot
49
49
  from tomwer.gui.stitching.stitching_raw import RawStitchingPlot
50
50
  from tomwer.gui.stitching.axisorderedlist import EditableZOrderedTomoObjWidget
51
51
  from tomwer.gui.stitching.z_stitching.fineestimation import _SliceGetter
52
+ from tomwer.gui.configuration.action import (
53
+ BasicConfigurationAction,
54
+ ExpertConfigurationAction,
55
+ MinimalisticConfigurationAction,
56
+ )
57
+ from tomwer.gui.configuration.level import ConfigurationLevel
58
+ from tomwer.gui.stitching import action as stitching_action
59
+ from tomwer.gui.stitching.normalization import NormalizationBySampleGroupBox
60
+
52
61
 
53
62
  _logger = logging.getLogger(__name__)
54
63
 
@@ -104,6 +113,8 @@ class ZStitchingCentralWidget(qt.QWidget):
104
113
  raw_display_idx,
105
114
  "If toggled will keep the raw display up to date from axis 0 modifications",
106
115
  )
116
+ # set up: turn overlay one by default
117
+ self._previewPlot._backGroundAction.setChecked(True)
107
118
 
108
119
  def _handleRawDisplayconnection(self, toggled: bool):
109
120
  if toggled:
@@ -192,9 +203,33 @@ class ZStitchingCentralWidget(qt.QWidget):
192
203
 
193
204
  def addTomoObj(self, tomo_obj: TomwerObject):
194
205
  self._mainWidget.addTomoObj(tomo_obj)
206
+ self._updatePreviewPixelSize()
195
207
 
196
208
  def removeTomoObj(self, tomo_obj: TomwerObject):
197
209
  self._mainWidget.removeTomoObj(tomo_obj)
210
+ self._updatePreviewPixelSize()
211
+
212
+ def _updatePreviewPixelSize(self):
213
+ """update the pixel size of the preview from existing tomo obj"""
214
+
215
+ def get_pixel_size():
216
+ tomo_objs = self._mainWidget.getTomoObjs()
217
+ for tomo_obj in tomo_objs:
218
+ if (
219
+ isinstance(tomo_obj, NXtomoScan)
220
+ and tomo_obj.x_pixel_size is not None
221
+ and tomo_obj.y_pixel_size is not None
222
+ ):
223
+ return tomo_obj.x_pixel_size, tomo_obj.y_pixel_size
224
+ elif (
225
+ isinstance(tomo_obj, TomwerVolumeBase)
226
+ and tomo_obj.voxel_size is not None
227
+ ):
228
+ return tomo_obj.voxel_size[1], tomo_obj.voxel_size[2]
229
+ return None, None
230
+
231
+ pixel_size = get_pixel_size()
232
+ self._mainWidget._previewPlot.setPixelSize(pixel_size=pixel_size)
198
233
 
199
234
  def getConfiguration(self) -> dict:
200
235
  # missing parameters:
@@ -303,12 +338,15 @@ class ZStitchingCentralWidget(qt.QWidget):
303
338
  class ZStitchingWindow(qt.QMainWindow):
304
339
  """
305
340
  Main widget containing all the options to define the stitching to be done
341
+
342
+ :param bool with_configuration_action: if True append the load and save stitching configuration tool button.
343
+ In some cases those can also be part of Menu so we want to avoid having those twice
306
344
  """
307
345
 
308
346
  sigChanged = qt.Signal()
309
347
  """Signal emit each time the configuration is modified"""
310
348
 
311
- def __init__(self, parent=None) -> None:
349
+ def __init__(self, parent=None, with_configuration_action=True) -> None:
312
350
  super().__init__(parent)
313
351
  self._previewFolder = None
314
352
  # folder to store files (volume or NXtomo) for previews
@@ -335,42 +373,56 @@ class ZStitchingWindow(qt.QMainWindow):
335
373
  # separator
336
374
  toolbar.addSeparator()
337
375
 
338
- # load action
339
- self.__loadAction = qt.QAction(self)
340
- self.__loadAction.setToolTip("load nabu-stitching configuration")
341
- load_icon = silx_icons.getQIcon("document-open")
342
- self.__loadAction.setIcon(load_icon)
343
- toolbar.addAction(self.__loadAction)
344
- self.__loadAction.triggered.connect(
345
- functools.partial(self._loadSettings, file_path=None)
346
- )
376
+ if with_configuration_action:
377
+ # load action
378
+ self.__loadAction = stitching_action.LoadConfigurationAction(self)
379
+ toolbar.addAction(self.__loadAction)
380
+ self.__loadAction.triggered.connect(
381
+ functools.partial(self._loadSettings, file_path=None)
382
+ )
347
383
 
348
- # save action
349
- self.__saveAction = qt.QAction(self)
350
- self.__saveAction.setToolTip("save nabu-stitching configuration")
351
- save_icon = silx_icons.getQIcon("document-save")
352
- self.__saveAction.setIcon(save_icon)
353
- toolbar.addAction(self.__saveAction)
354
- self.__saveAction.triggered.connect(
355
- functools.partial(self._saveSettings, file_path=None)
356
- )
384
+ # save action
385
+ self.__saveAction = stitching_action.SaveConfigurationAction(self)
386
+ toolbar.addAction(self.__saveAction)
387
+ self.__saveAction.triggered.connect(
388
+ functools.partial(self._saveSettings, file_path=None)
389
+ )
357
390
 
358
- # separator
359
- toolbar.addSeparator()
391
+ # separator
392
+ toolbar.addSeparator()
360
393
 
361
394
  # update preview action
362
- self.__updatePreviewAction = qt.QAction(self)
363
- self.__updatePreviewAction.setToolTip(
364
- "Compute preview with current settings (shortcut: F5)"
365
- )
366
- update_preview_icon = tomwer_icons.getQIcon("update_stitching_preview")
367
- self.__updatePreviewAction.setIcon(update_preview_icon)
395
+ self.__updatePreviewAction = stitching_action.PreviewAction(self)
368
396
  toolbar.addAction(self.__updatePreviewAction)
369
397
  self.__updatePreviewAction.triggered.connect(self._trigger_update_preview)
370
398
 
371
399
  # separator
372
400
  toolbar.addSeparator()
373
401
 
402
+ # configuraiton level / mode
403
+ self.__configurationModesAction = qt.QAction(self)
404
+ self.__configurationModesAction.setCheckable(False)
405
+ menu = qt.QMenu(self)
406
+ self.__configurationModesAction.setMenu(menu)
407
+ toolbar.addAction(self.__configurationModesAction)
408
+
409
+ self.__configurationModesGroup = qt.QActionGroup(self)
410
+ self.__configurationModesGroup.setExclusive(True)
411
+ self.__configurationModesGroup.triggered.connect(self._userModeChanged)
412
+
413
+ self._minimalisticAction = MinimalisticConfigurationAction(toolbar)
414
+ menu.addAction(self._minimalisticAction)
415
+ self.__configurationModesGroup.addAction(self._minimalisticAction)
416
+ self._basicConfigAction = BasicConfigurationAction(toolbar)
417
+ menu.addAction(self._basicConfigAction)
418
+ self.__configurationModesGroup.addAction(self._basicConfigAction)
419
+ self._expertConfiguration = ExpertConfigurationAction(toolbar)
420
+ menu.addAction(self._expertConfiguration)
421
+ self.__configurationModesGroup.addAction(self._expertConfiguration)
422
+
423
+ # separator
424
+ toolbar.addSeparator()
425
+
374
426
  # create central widget
375
427
  self._widget = ZStitchingCentralWidget(parent=self)
376
428
  self.setCentralWidget(self._widget)
@@ -390,11 +442,16 @@ class ZStitchingWindow(qt.QMainWindow):
390
442
  )
391
443
  ## stitching strategies
392
444
  self._stitchingOptsWidget = StitchingOptions(parent=self)
393
- self._stitchingOptsWidget.setObjectName("options")
445
+ self._stitchingOptsScrollArea = qt.QScrollArea(self)
446
+ self._stitchingOptsScrollArea.setWidget(self._stitchingOptsWidget)
447
+ self._stitchingOptsScrollArea.setWidgetResizable(True)
448
+ self._stitchingOptsScrollArea.setHorizontalScrollBarPolicy(
449
+ qt.Qt.ScrollBarAlwaysOff
450
+ )
394
451
  self._stitchingOptsDockWidget = qt.QDockWidget(parent=self)
395
452
  self._stitchingOptsDockWidget.layout().setContentsMargins(0, 0, 0, 0)
396
453
  self._stitchingOptsDockWidget.setFeatures(qt.QDockWidget.DockWidgetMovable)
397
- self._stitchingOptsDockWidget.setWidget(self._stitchingOptsWidget)
454
+ self._stitchingOptsDockWidget.setWidget(self._stitchingOptsScrollArea)
398
455
  self._stitchingOptsDockWidget.setWindowTitle("processing options")
399
456
  self.addDockWidget(qt.Qt.RightDockWidgetArea, self._stitchingOptsDockWidget)
400
457
 
@@ -425,13 +482,13 @@ class ZStitchingWindow(qt.QMainWindow):
425
482
  )
426
483
  ### add a check box to update position from preview if asked by the user
427
484
  self._updateAxis0PosFromPreviewCalc = qt.QCheckBox(
428
- "update position 0 from preview calc"
485
+ "update position 0 from preview calc",
486
+ self,
429
487
  )
430
488
  self._updateAxis0PosFromPreviewCalc.setToolTip(
431
- "When the user trigger a preview, if some shift search refined over axis 0 is done then will update the axis 0 positions"
489
+ "When the user trigger a preview, if some shift search refined over axis 0 is done then will update the axis 0 positions",
432
490
  )
433
- self._updateAxis0PosFromPreviewCalc.setChecked(False)
434
- self._updateAxis0PosFromPreviewCalc.setVisible(False)
491
+ self._updateAxis0PosFromPreviewCalc.setChecked(True)
435
492
  self._editTomoObjAxis0PositionsWidget.layout().insertWidget(
436
493
  0, self._updateAxis0PosFromPreviewCalc
437
494
  )
@@ -463,13 +520,12 @@ class ZStitchingWindow(qt.QMainWindow):
463
520
  )
464
521
  ### add a check box to update position from preview if asked by the user
465
522
  self._updateAxis2PosFromPreviewCalc = qt.QCheckBox(
466
- "update position 2 from preview calc"
523
+ "update position 2 from preview calc", self
467
524
  )
468
525
  self._updateAxis2PosFromPreviewCalc.setToolTip(
469
526
  "When the user trigger a preview, if some shift search refined over axis 2 is done then will update the axis 2 positions"
470
527
  )
471
- self._updateAxis2PosFromPreviewCalc.setChecked(False)
472
- self._updateAxis2PosFromPreviewCalc.setVisible(False)
528
+ self._updateAxis2PosFromPreviewCalc.setChecked(True)
473
529
  self._editTomoObjAxis2PositionsWidget.layout().insertWidget(
474
530
  0, self._updateAxis2PosFromPreviewCalc
475
531
  )
@@ -511,6 +567,9 @@ class ZStitchingWindow(qt.QMainWindow):
511
567
  self._widget.sigStitchingTypeChanged.connect(
512
568
  self._outputWidget._updateOutputForStitchingType
513
569
  )
570
+ self._widget.sigStitchingTypeChanged.connect(
571
+ self._stitchingOptsWidget._stitchingTypeChanged
572
+ )
514
573
 
515
574
  ## handle raw plot preview
516
575
  self._stitchingOptsWidget.sigFlipLRChanged.connect(
@@ -532,6 +591,10 @@ class ZStitchingWindow(qt.QMainWindow):
532
591
  )
533
592
  self._widget.sigTomoObjsLoaded.connect(self.getRawDisplayPlot().setTomoObjs)
534
593
 
594
+ # set up
595
+ self._basicConfigAction.setChecked(True)
596
+ self._userModeChanged(self._basicConfigAction)
597
+
535
598
  def setCallbackToGetSlurmConfig(self, callback):
536
599
  self._callbackToGetSlurmConfig = callback
537
600
 
@@ -570,8 +633,8 @@ class ZStitchingWindow(qt.QMainWindow):
570
633
 
571
634
  def getNXtomoIdentifierForPreview(self):
572
635
  folder = self.getPreviewFolder()
573
- return HDF5TomoScanIdentifier(
574
- object=HDF5TomoScan,
636
+ return NXtomoScanIdentifier(
637
+ object=NXtomoScan,
575
638
  hdf5_file=os.path.join(folder, "nxtomo_stiching_preview.hdf5"),
576
639
  entry="entry0000",
577
640
  )
@@ -679,7 +742,9 @@ class ZStitchingWindow(qt.QMainWindow):
679
742
  assert isinstance(sender, PreviewThread)
680
743
  composition = sender.frame_composition
681
744
  tomo_objs_new_axis_positions = sender.final_tomo_objs_positions
682
- assert isinstance(tomo_objs_new_axis_positions, dict)
745
+ assert isinstance(
746
+ tomo_objs_new_axis_positions, dict
747
+ ), "final_tomo_objs_positions is expected to be a dict with obj identifier as key and the tuple of position as value"
683
748
  # expect it to be a dict with tomo obj identifier as key and a tuple of (axis_2_pos, axis_1_pos, axis_0_pos) as value
684
749
  output_obj_identifier = sender.output_identifier
685
750
 
@@ -780,6 +845,20 @@ class ZStitchingWindow(qt.QMainWindow):
780
845
  def setStitchingType(self, stitching_type: StitchingType):
781
846
  self._widget.setStitchingType(stitching_type)
782
847
 
848
+ def _userModeChanged(self, action):
849
+ self.__configurationModesAction.setIcon(action.icon())
850
+ self.__configurationModesAction.setToolTip(action.tooltip())
851
+ if action is self._basicConfigAction:
852
+ level = ConfigurationLevel.OPTIONAL
853
+ elif action is self._expertConfiguration:
854
+ level = ConfigurationLevel.ADVANCED
855
+ else:
856
+ level = ConfigurationLevel.REQUIRED
857
+ self._stitchingOptsWidget.setConfigurationLevel(level)
858
+ self._editTomoObjAxis2PositionsDockWidget.setVisible(
859
+ level >= ConfigurationLevel.ADVANCED
860
+ )
861
+
783
862
 
784
863
  def concatenate_dict(dict_1, dict_2) -> dict:
785
864
  """update dict which has dict as values. And we want concatenate those values to"""
@@ -921,6 +1000,92 @@ class _SlicesSelector(qt.QGroupBox):
921
1000
  self._stepSliceSB.setValue(int(step))
922
1001
 
923
1002
 
1003
+ class _AlignmentGroupBox(qt.QGroupBox):
1004
+ DEFAULT_PAD_MODE = "constant"
1005
+
1006
+ ALIGNMENT_DOC = (
1007
+ "https://tomotools.gitlab-pages.esrf.fr/nabu/stitching/alignment.html"
1008
+ )
1009
+
1010
+ DEFAULT_ALIGNMENT_AXIS_1 = AlignmentAxis1.CENTER
1011
+ DEFAULT_ALIGNMENT_AXIS_2 = AlignmentAxis2.CENTER
1012
+
1013
+ _PAD_MODES = (
1014
+ "constant", # Pads with a constant value.
1015
+ "edge", # Pads with the edge values of array.
1016
+ "linear_ramp", # Pads with the linear ramp between end_value and the array edge value.
1017
+ "maximum", # Pads with the maximum value of all or part of the vector along each axis.
1018
+ "mean", # Pads with the mean value of all or part of the vector along each axis.
1019
+ "median", # Pads with the median value of all or part of the vector along each axis.
1020
+ "minimum", # Pads with the minimum value of all or part of the vector along each axis.
1021
+ "reflect", # Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis.
1022
+ "symmetric", # Pads with the reflection of the vector mirrored along the edge of the array.
1023
+ "wrap", # Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning.
1024
+ )
1025
+
1026
+ def __init__(self, parent: qt.QWidget = None, title="alignment") -> None:
1027
+ super().__init__(title, parent)
1028
+ self.setLayout(qt.QFormLayout())
1029
+
1030
+ # alignment axis 1
1031
+ self._alignmentAxis1CB = qt.QComboBox(self)
1032
+ for alignment in AlignmentAxis1.values():
1033
+ self._alignmentAxis1CB.addItem(alignment)
1034
+ self.layout().addRow("Axis 1 alignment", self._alignmentAxis1CB)
1035
+ self._alignmentAxis1CB.setToolTip(
1036
+ f"Alignment to do in case of volumes with different size over axis 1. Only possible for post-processing (reconstructed volume). See {self.ALIGNMENT_DOC} for details."
1037
+ )
1038
+
1039
+ # alignment axis 2
1040
+ self._alignmentAxis2CB = qt.QComboBox(self)
1041
+ for alignment in AlignmentAxis2.values():
1042
+ self._alignmentAxis2CB.addItem(alignment)
1043
+ self.layout().addRow("Axis 2 alignment", self._alignmentAxis2CB)
1044
+ self._alignmentAxis2CB.setToolTip(
1045
+ f"Alignment to do in case of frames with different size over axis 2. See {self.ALIGNMENT_DOC} for details."
1046
+ )
1047
+
1048
+ # pad mode
1049
+ self._padModeCB = qt.QComboBox(self)
1050
+ for pad_mode in self._PAD_MODES:
1051
+ self._padModeCB.addItem(pad_mode)
1052
+ self.layout().addRow("pad mode", self._padModeCB)
1053
+ self._padModeCB.setToolTip("padding mode to apply for alignment")
1054
+
1055
+ # set up
1056
+ self.setAlignmentAxis1(self.DEFAULT_ALIGNMENT_AXIS_1)
1057
+ self.setAlignmentAxis2(self.DEFAULT_ALIGNMENT_AXIS_2)
1058
+
1059
+ def getAlignmentAxis1(self) -> AlignmentAxis1:
1060
+ return AlignmentAxis1.from_value(self._alignmentAxis1CB.currentText())
1061
+
1062
+ def setAlignmentAxis1(self, alignment: AlignmentAxis1):
1063
+ alignment = AlignmentAxis1.from_value(alignment)
1064
+ self._alignmentAxis1CB.setCurrentIndex(
1065
+ self._alignmentAxis1CB.findText(alignment.value)
1066
+ )
1067
+
1068
+ def getAlignmentAxis2(self) -> AlignmentAxis2:
1069
+ return AlignmentAxis2.from_value(self._alignmentAxis2CB.currentText())
1070
+
1071
+ def setAlignmentAxis2(self, alignment: AlignmentAxis2):
1072
+ alignment = AlignmentAxis2.from_value(alignment)
1073
+ self._alignmentAxis2CB.setCurrentIndex(
1074
+ self._alignmentAxis2CB.findText(alignment.value)
1075
+ )
1076
+
1077
+ def getPadMode(self) -> str:
1078
+ return self._padModeCB.currentText()
1079
+
1080
+ def setPadMode(self, pad_mode: str):
1081
+ idx = self._padModeCB.findText(pad_mode)
1082
+ if idx >= 0:
1083
+ self._padModeCB.setCurrentIndex(idx)
1084
+
1085
+ def setAlignmentAxis1Enabled(self, enabled: bool):
1086
+ self._alignmentAxis1CB.setEnabled(enabled)
1087
+
1088
+
924
1089
  class StitchingOptions(qt.QWidget):
925
1090
  """
926
1091
  Widget to let the user define the different options for z-stitching such as which algorithm to search shift,
@@ -968,6 +1133,10 @@ class StitchingOptions(qt.QWidget):
968
1133
  )
969
1134
  self.layout().addRow(self._flipUD_CB)
970
1135
 
1136
+ # alignment options
1137
+ self._alignmentGroup = _AlignmentGroupBox(self)
1138
+ self.layout().addRow(self._alignmentGroup)
1139
+
971
1140
  # slices to be reconstructed
972
1141
  self._slices = _SlicesSelector(parent=self)
973
1142
  self._slices.setToolTip(
@@ -999,6 +1168,11 @@ class StitchingOptions(qt.QWidget):
999
1168
  self._rescalingWidget = RescalingWidget(parent=self)
1000
1169
  self.layout().addRow(self._rescalingWidget)
1001
1170
 
1171
+ # normalization by sample
1172
+ self._normalizationBySampleWidget = NormalizationBySampleGroupBox(parent=self)
1173
+ self._normalizationBySampleWidget.setChecked(False)
1174
+ self.layout().addRow(self._normalizationBySampleWidget)
1175
+
1002
1176
  # connect signal / slot
1003
1177
  self._stitchingStrategiesWidget.sigChanged.connect(self._updated)
1004
1178
  self._previewSlices.editingFinished.connect(self._sliceForPreviewHasChanged)
@@ -1052,10 +1226,14 @@ class StitchingOptions(qt.QWidget):
1052
1226
  stitching_config.STITCHING_SECTION: {
1053
1227
  stitching_config.FLIP_LR: self._flipLR_CB.isChecked(),
1054
1228
  stitching_config.FLIP_UD: self._flipUD_CB.isChecked(),
1229
+ stitching_config.ALIGNMENT_AXIS_1_FIELD: self._alignmentGroup.getAlignmentAxis1().value,
1230
+ stitching_config.ALIGNMENT_AXIS_2_FIELD: self._alignmentGroup.getAlignmentAxis2().value,
1231
+ stitching_config.PAD_MODE_FIELD: self._alignmentGroup.getPadMode(),
1055
1232
  },
1056
1233
  stitching_config.INPUTS_SECTION: {
1057
1234
  stitching_config.STITCHING_SLICES: slices,
1058
1235
  },
1236
+ stitching_config.NORMALIZATION_BY_SAMPLE_SECTION: self._normalizationBySampleWidget.getConfiguration(),
1059
1237
  }
1060
1238
 
1061
1239
  for ddict in (
@@ -1075,21 +1253,65 @@ class StitchingOptions(qt.QWidget):
1075
1253
  slices = config.get(stitching_config.STITCHING_SECTION, {}).get(
1076
1254
  stitching_config.STITCHING_SLICES, ""
1077
1255
  )
1256
+ # slices
1078
1257
  if slices == "":
1079
1258
  slices = None
1080
1259
  if slices is not None:
1081
1260
  self.setSlices(slices)
1261
+ # flip_lr
1082
1262
  flip_lr = config.get(stitching_config.STITCHING_SECTION, {}).get(
1083
1263
  stitching_config.FLIP_LR, None
1084
1264
  )
1085
1265
  if flip_lr is not None:
1086
1266
  self._flipLR_CB.setChecked(flip_lr in (True, "True", 1, "1"))
1087
-
1267
+ # flip_ud
1088
1268
  flip_ud = config.get(stitching_config.STITCHING_SECTION, {}).get(
1089
1269
  stitching_config.FLIP_UD, None
1090
1270
  )
1091
1271
  if flip_ud is not None:
1092
1272
  self._flipUD_CB.setChecked(flip_ud in (True, "True", 1, "1"))
1273
+ # alignment
1274
+ alignment_axis_1 = config.get(stitching_config.STITCHING_SECTION, {}).get(
1275
+ stitching_config.ALIGNMENT_AXIS_1_FIELD, None
1276
+ )
1277
+ if alignment_axis_1 is not None:
1278
+ self._alignmentGroup.setAlignmentAxis1(alignment_axis_1)
1279
+ alignment_axis_2 = config.get(stitching_config.STITCHING_SECTION, {}).get(
1280
+ stitching_config.ALIGNMENT_AXIS_2_FIELD, None
1281
+ )
1282
+ if alignment_axis_2 is not None:
1283
+ self._alignmentGroup.setAlignmentAxis2(alignment_axis_2)
1284
+ # pad_mode
1285
+ pad_mode = config.get(stitching_config.STITCHING_SECTION, {}).get(
1286
+ stitching_config.PAD_MODE_FIELD, None
1287
+ )
1288
+ if pad_mode is not None:
1289
+ self._alignmentGroup.setPadMode(pad_mode=pad_mode)
1290
+
1291
+ # normalization by sample
1292
+ normalization_by_sample = config.get(
1293
+ stitching_config.NORMALIZATION_BY_SAMPLE_SECTION, None
1294
+ )
1295
+ if normalization_by_sample is not None:
1296
+ self._normalizationBySampleWidget.setConfiguration(normalization_by_sample)
1297
+
1298
+ def _stitchingTypeChanged(self, stiching_type: str):
1299
+ stiching_type = StitchingType.from_value(stiching_type)
1300
+ self._alignmentGroup.setAlignmentAxis1Enabled(
1301
+ stiching_type is StitchingType.Z_POSTPROC
1302
+ )
1303
+
1304
+ def setConfigurationLevel(self, level: ConfigurationLevel):
1305
+ self._alignmentGroup.setVisible(level >= ConfigurationLevel.ADVANCED)
1306
+ self._previewSlices.setVisible(level >= ConfigurationLevel.OPTIONAL)
1307
+ self._flipLR_CB.setVisible(level >= ConfigurationLevel.ADVANCED)
1308
+ self._flipUD_CB.setVisible(level >= ConfigurationLevel.ADVANCED)
1309
+ self._rescalingWidget.setVisible(level >= ConfigurationLevel.ADVANCED)
1310
+ self._axis0ShiftSearchParams.setConfigurationLevel(level)
1311
+ self._axis2ShiftSearchParams.setConfigurationLevel(level)
1312
+ self._normalizationBySampleWidget.setVisible(
1313
+ level >= ConfigurationLevel.ADVANCED
1314
+ )
1093
1315
 
1094
1316
 
1095
1317
  class RescalingWidget(qt.QWidget):
@@ -4,6 +4,7 @@ from typing import Union
4
4
  import numpy
5
5
  from nabu.stitching.frame_composition import ZFrameComposition
6
6
  from silx.gui import qt
7
+ from tomwer.gui.utils.buttons import TapeMeasureToolButton
7
8
  from tomoscan.esrf.scan.utils import get_data
8
9
  from tomoscan.identifier import BaseIdentifier
9
10
  from tomoscan.scanbase import TomoScanBase
@@ -13,6 +14,7 @@ from tomwer.core.volume.volumefactory import VolumeFactory
13
14
  from tomwer.gui import icons as tomwer_icons
14
15
  from tomwer.gui import settings
15
16
  from tomwer.gui.stitching.stitchandbackground import StitchAndBackgroundAlphaMixIn
17
+ from silx.gui.plot import Plot2D, items
16
18
 
17
19
  try:
18
20
  from silx.gui.plot.ImageStack import ( # noqa F401
@@ -24,15 +26,56 @@ except ImportError:
24
26
  _logger = logging.getLogger(__name__)
25
27
 
26
28
 
29
+ class _PreviewPlot2D(Plot2D):
30
+ """simple inheritance to get data from the 'stitched image' instead of the 'background / overlay' image"""
31
+
32
+ def _getImageValue(self, x, y):
33
+ pickedMask = None
34
+ for picked in self.pickItems(
35
+ *self.dataToPixel(x, y, check=False),
36
+ lambda item: isinstance(item, items.ImageBase),
37
+ ):
38
+ if isinstance(picked.getItem(), items.MaskImageData):
39
+ if pickedMask is None: # Use top-most if many masks
40
+ pickedMask = picked
41
+ else:
42
+ # we want to avoid displaying the value of the background image. So just ignore it.
43
+ # TODO: see with Thomas if there is a better way to do this.
44
+ # like starting by the active image ?
45
+ image = picked.getItem()
46
+ if image.getLegend() == StitchAndBackgroundAlphaMixIn.LEGEND_BACKGROUND:
47
+ continue
48
+
49
+ indices = picked.getIndices(copy=False)
50
+ if indices is not None:
51
+ row, col = indices[0][0], indices[1][0]
52
+ value = image.getData(copy=False)[row, col]
53
+
54
+ if pickedMask is not None: # Check if masked
55
+ maskItem = pickedMask.getItem()
56
+ indices = pickedMask.getIndices()
57
+ row, col = indices[0][0], indices[1][0]
58
+ if maskItem.getData(copy=False)[row, col] != 0:
59
+ return value, "Masked"
60
+ return value
61
+
62
+ return "-" # No image picked
63
+
64
+
27
65
  class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn):
28
66
  DEFAULT_STITCHED_IMG_ALPHA = 0.95
29
- DEFAULT_BACKGROUND_IMG_ALPHA = 0.15
67
+ DEFAULT_BACKGROUND_IMG_ALPHA = 0.20
30
68
 
31
69
  def __init__(self, parent=None) -> None:
32
70
  super().__init__(parent=parent) # pylint: disable=E1123
33
71
  self._stitched_image = None
34
72
  self._composition_background = None
35
73
 
74
+ # replace the simple Plot2D by a `_PreviewPlot2D` (to filter overlay image value to be displayed)
75
+ self.layout().removeWidget(self._plot)
76
+ self._plot = _PreviewPlot2D(parent=self)
77
+ self.layout().addWidget(self._plot)
78
+
36
79
  # tune plot
37
80
  self.getPlotWidget().setYAxisInverted(settings.Y_AXIS_DOWNWARD)
38
81
  # by default we want to have a full screen display
@@ -47,6 +90,11 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
47
90
  self.setKeepDataAspectRatio(True)
48
91
  self.getColorBarWidget().hide()
49
92
 
93
+ # tape mesure action
94
+ self._tapeMeasureButton = TapeMeasureToolButton(parent=self, plot=self._plot)
95
+ self._tapeMeasureButton.setCheckable(True)
96
+ self.toolBar().addWidget(self._tapeMeasureButton)
97
+
50
98
  # removing some plot action to clear toolbar
51
99
  self.getMaskAction().setVisible(False)
52
100
  self.getCopyAction().setVisible(False)
@@ -111,15 +159,19 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
111
159
  shape=(
112
160
  (raw_compositon.global_end_y[-1] - raw_compositon.global_start_y[0]),
113
161
  frame_width,
162
+ 4,
114
163
  ),
115
164
  )
116
- assert background.ndim == 2
165
+ assert background.ndim == 3
117
166
 
118
167
  def get_next_color():
168
+ yellow = qt.QColor(qt.Qt.yellow)
169
+ magenta = qt.QColor(qt.Qt.magenta)
170
+ blue = qt.QColor(qt.Qt.blue)
119
171
  while True:
120
- yield qt.QColor(qt.Qt.yellow).hue()
121
- yield qt.QColor(qt.Qt.magenta).hue()
122
- yield qt.QColor(qt.Qt.blue).hue()
172
+ yield yellow.red(), yellow.green(), yellow.blue(), 255
173
+ yield magenta.red(), magenta.green(), magenta.blue(), 255
174
+ yield blue.red(), blue.green(), blue.blue(), 255
123
175
 
124
176
  colors_raw_frames = []
125
177
  for _, color in zip(raw_compositon.local_end_y, get_next_color()):
@@ -127,6 +179,7 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
127
179
  shape=(
128
180
  raw_compositon.global_end_y[-1] - raw_compositon.global_start_y[0],
129
181
  frame_width,
182
+ 4,
130
183
  ),
131
184
  fill_value=color,
132
185
  )
@@ -237,3 +290,7 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
237
290
 
238
291
  def setDefaultColormap(self, *args, **kwargs):
239
292
  return self.getPlotWidget().setDefaultColormap(*args, **kwargs)
293
+
294
+ def setPixelSize(self, pixel_size: Union[tuple, float]):
295
+ """set the pixel size to be used by the ruler"""
296
+ self._tapeMeasureButton.setPixelSize(pixel_size_m=pixel_size)
@@ -6,7 +6,7 @@ from contextlib import AbstractContextManager
6
6
 
7
7
  from silx.gui import qt
8
8
  from silx.gui.plot import PlotWindow
9
- from tomoscan.esrf.scan.hdf5scan import ImageKey
9
+ from nxtomo.nxobject.nxdetector import ImageKey
10
10
  from tomwer.core.volume.volumebase import TomwerVolumeBase
11
11
  from tomwer.core.scan.scanbase import TomwerScanBase
12
12
  from tomwer.core.tomwer_object import TomwerObject
@@ -561,10 +561,7 @@ class AlphaValueSlider(qt.QTableWidget):
561
561
  self._visibleButton.released.connect(self._setVisible)
562
562
 
563
563
  # expose API
564
- self._slider.valueChanged.connect(self._changed)
565
-
566
- def _changed(self, value: int):
567
- self.valueChanged.emit(value)
564
+ self._slider.valueChanged.connect(self.valueChanged)
568
565
 
569
566
  def _setInvisible(self, *args, **kwargs):
570
567
  self._slider.setValue(0)