tomwer 1.2.1__py3-none-any.whl → 1.3.12__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 (334) hide show
  1. orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
  2. orangecontrib/tomwer/widgets/__init__.py +11 -11
  3. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
  4. orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
  5. orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
  6. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
  7. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
  8. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +24 -7
  9. orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
  10. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
  11. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +64 -23
  12. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +20 -8
  13. orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
  14. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
  15. orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
  16. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
  17. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +5 -5
  18. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
  19. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
  20. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +8 -1
  21. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
  22. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
  23. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
  24. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
  25. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
  26. orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
  27. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  28. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
  29. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
  30. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
  31. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  32. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
  33. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
  34. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
  35. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +22 -20
  36. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +19 -3
  37. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -169
  38. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
  39. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
  40. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +18 -22
  41. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +18 -26
  42. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +15 -19
  43. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
  44. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  45. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
  46. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
  47. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
  48. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
  49. tomwer/__main__.py +7 -64
  50. tomwer/app/axis.py +3 -3
  51. tomwer/app/canvas.py +8 -0
  52. tomwer/app/canvas_launcher/config.py +16 -14
  53. tomwer/app/canvas_launcher/environ.py +1 -0
  54. tomwer/app/canvas_launcher/mainwindow.py +4 -1
  55. tomwer/app/darkref.py +1 -1
  56. tomwer/app/darkrefpatch.py +1 -1
  57. tomwer/app/diffframe.py +3 -3
  58. tomwer/app/imagekeyeditor.py +5 -5
  59. tomwer/app/imagekeyupgrader.py +5 -5
  60. tomwer/app/intensitynormalization.py +14 -13
  61. tomwer/app/{saaxis.py → multicor.py} +3 -3
  62. tomwer/app/{sadeltabeta.py → multipag.py} +3 -3
  63. tomwer/app/nabuapp.py +0 -11
  64. tomwer/app/radiostack.py +6 -4
  65. tomwer/app/samplemoved.py +3 -2
  66. tomwer/app/scanviewer.py +4 -2
  67. tomwer/app/sinogramviewer.py +3 -2
  68. tomwer/app/slicestack.py +3 -2
  69. tomwer/app/zstitching.py +88 -6
  70. tomwer/core/cluster/cluster.py +26 -0
  71. tomwer/core/log/logger.py +7 -5
  72. tomwer/core/process/conditions/filters.py +1 -1
  73. tomwer/core/process/control/datalistener/datalistener.py +19 -14
  74. tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
  75. tomwer/core/process/control/nxtomoconcatenate.py +13 -13
  76. tomwer/core/process/control/nxtomomill.py +92 -34
  77. tomwer/core/process/control/scantransfer.py +20 -43
  78. tomwer/core/process/control/scanvalidator.py +3 -2
  79. tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
  80. tomwer/core/process/control/test/test_email.py +4 -4
  81. tomwer/core/process/control/test/test_h52nx_process.py +59 -7
  82. tomwer/core/process/control/test/test_volume_link.py +64 -64
  83. tomwer/core/process/control/timer.py +1 -1
  84. tomwer/core/process/control/volumesymlink.py +200 -200
  85. tomwer/core/process/edit/darkflatpatch.py +14 -15
  86. tomwer/core/process/edit/imagekeyeditor.py +41 -39
  87. tomwer/core/process/icat/__init__.py +0 -0
  88. tomwer/core/process/icat/createscreenshots.py +100 -0
  89. tomwer/core/process/icat/gallery.py +377 -0
  90. tomwer/core/process/icat/icatbase.py +36 -0
  91. tomwer/core/process/icat/publish.py +228 -0
  92. tomwer/core/process/icat/screenshots.py +27 -0
  93. tomwer/core/process/output.py +52 -0
  94. tomwer/core/process/reconstruction/axis/axis.py +280 -69
  95. tomwer/core/process/reconstruction/axis/mode.py +163 -48
  96. tomwer/core/process/reconstruction/axis/params.py +29 -21
  97. tomwer/core/process/reconstruction/darkref/darkrefs.py +41 -127
  98. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +4 -3
  99. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  100. tomwer/core/process/reconstruction/nabu/castvolume.py +4 -4
  101. tomwer/core/process/reconstruction/nabu/helical.py +9 -5
  102. tomwer/core/process/reconstruction/nabu/nabucommon.py +71 -78
  103. tomwer/core/process/reconstruction/nabu/nabuscores.py +425 -53
  104. tomwer/core/process/reconstruction/nabu/nabuslices.py +114 -93
  105. tomwer/core/process/reconstruction/nabu/nabuvolume.py +54 -27
  106. tomwer/core/process/reconstruction/nabu/plane.py +9 -0
  107. tomwer/core/process/reconstruction/nabu/settings.py +2 -2
  108. tomwer/core/process/reconstruction/nabu/utils.py +164 -26
  109. tomwer/core/process/reconstruction/output.py +108 -0
  110. tomwer/core/process/reconstruction/saaxis/saaxis.py +238 -264
  111. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +151 -87
  112. tomwer/core/process/reconstruction/scores/params.py +7 -4
  113. tomwer/core/process/reconstruction/scores/scores.py +13 -0
  114. tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
  115. tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
  116. tomwer/core/process/reconstruction/test/test_darkref_copy.py +7 -7
  117. tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
  118. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
  119. tomwer/core/process/stitching/nabustitcher.py +13 -12
  120. tomwer/core/process/task.py +34 -26
  121. tomwer/core/process/test/test_axis.py +13 -12
  122. tomwer/core/process/test/test_dark_and_flat.py +10 -7
  123. tomwer/core/process/test/test_data_transfer.py +10 -8
  124. tomwer/core/process/test/test_nabu.py +14 -6
  125. tomwer/core/process/test/test_normalization.py +4 -4
  126. tomwer/core/scan/blissscan.py +3 -3
  127. tomwer/core/scan/edfscan.py +13 -10
  128. tomwer/core/scan/hdf5scan.py +19 -530
  129. tomwer/core/scan/nxtomoscan.py +534 -0
  130. tomwer/core/scan/scanbase.py +72 -44
  131. tomwer/core/scan/scanfactory.py +13 -13
  132. tomwer/core/scan/test/test_edf.py +2 -2
  133. tomwer/core/scan/test/test_future_scan.py +3 -3
  134. tomwer/core/scan/test/test_h5.py +18 -16
  135. tomwer/core/scan/test/test_process_registration.py +4 -40
  136. tomwer/core/scan/test/test_scan.py +5 -78
  137. tomwer/core/settings.py +22 -2
  138. tomwer/core/test/test_scanutils.py +8 -7
  139. tomwer/core/test/test_utils.py +35 -28
  140. tomwer/core/tomwer_object.py +1 -1
  141. tomwer/core/utils/__init__.py +0 -466
  142. tomwer/core/utils/deprecation.py +1 -1
  143. tomwer/core/utils/dictutils.py +14 -0
  144. tomwer/core/utils/lbsram.py +35 -0
  145. tomwer/core/utils/nxtomoutils.py +1 -1
  146. tomwer/core/utils/scanutils.py +6 -6
  147. tomwer/core/utils/spec.py +263 -0
  148. tomwer/core/volume/edfvolume.py +6 -6
  149. tomwer/core/volume/hdf5volume.py +6 -6
  150. tomwer/core/volume/jp2kvolume.py +6 -6
  151. tomwer/core/volume/rawvolume.py +6 -6
  152. tomwer/core/volume/tiffvolume.py +12 -12
  153. tomwer/core/volume/volumefactory.py +2 -2
  154. tomwer/gui/cluster/slurm.py +274 -65
  155. tomwer/gui/cluster/supervisor.py +12 -0
  156. tomwer/gui/cluster/test/test_cluster.py +14 -2
  157. tomwer/gui/cluster/test/test_supervisor.py +3 -3
  158. tomwer/gui/configuration/__init__.py +0 -0
  159. tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
  160. tomwer/gui/configuration/level.py +22 -0
  161. tomwer/gui/control/actions.py +54 -0
  162. tomwer/gui/control/datalist.py +83 -16
  163. tomwer/gui/control/datalistener.py +4 -16
  164. tomwer/gui/control/datawatcher/controlwidget.py +2 -4
  165. tomwer/gui/control/datawatcher/datawatcher.py +1 -24
  166. tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
  167. tomwer/gui/control/history.py +2 -2
  168. tomwer/gui/control/observations.py +2 -2
  169. tomwer/gui/control/reducedarkflatselector.py +9 -9
  170. tomwer/gui/control/selectorwidgetbase.py +36 -9
  171. tomwer/gui/control/serie/seriecreator.py +5 -22
  172. tomwer/gui/control/test/test_email.py +1 -1
  173. tomwer/gui/control/test/test_scanvalidator.py +6 -5
  174. tomwer/gui/control/test/test_single_tomo_obj.py +3 -3
  175. tomwer/gui/control/tomoobjdisplaymode.py +8 -0
  176. tomwer/gui/debugtools/datasetgenerator.py +3 -3
  177. tomwer/gui/edit/dkrfpatch.py +20 -26
  178. tomwer/gui/edit/imagekeyeditor.py +11 -12
  179. tomwer/gui/edit/nxtomoeditor.py +111 -44
  180. tomwer/gui/edit/nxtomowarmer.py +7 -6
  181. tomwer/gui/edit/test/test_dkrf_patch.py +13 -13
  182. tomwer/gui/edit/test/test_image_key_editor.py +3 -3
  183. tomwer/gui/edit/test/test_nx_editor.py +40 -16
  184. tomwer/gui/icat/__init__.py +0 -0
  185. tomwer/gui/icat/createscreenshots.py +80 -0
  186. tomwer/gui/icat/gallery.py +214 -0
  187. tomwer/gui/icat/publish.py +187 -0
  188. tomwer/gui/imagefromfile.py +2 -2
  189. tomwer/gui/qfolderdialog.py +24 -1
  190. tomwer/gui/reconstruction/axis/CompareImages.py +88 -168
  191. tomwer/gui/reconstruction/axis/axis.py +171 -57
  192. tomwer/gui/reconstruction/axis/radioaxis.py +122 -257
  193. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
  194. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
  195. tomwer/gui/reconstruction/nabu/castvolume.py +14 -3
  196. tomwer/gui/reconstruction/nabu/check.py +9 -9
  197. tomwer/gui/reconstruction/nabu/helical.py +29 -12
  198. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
  199. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +2 -1
  200. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +126 -35
  201. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +39 -32
  202. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +222 -31
  203. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +57 -27
  204. tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
  205. tomwer/gui/reconstruction/nabu/slices.py +10 -11
  206. tomwer/gui/reconstruction/nabu/volume.py +22 -10
  207. tomwer/gui/reconstruction/normalization/intensity.py +18 -48
  208. tomwer/gui/reconstruction/saaxis/corrangeselector.py +8 -24
  209. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
  210. tomwer/gui/reconstruction/saaxis/saaxis.py +9 -21
  211. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +45 -12
  212. tomwer/gui/reconstruction/scores/control.py +2 -9
  213. tomwer/gui/reconstruction/scores/scoreplot.py +13 -11
  214. tomwer/gui/reconstruction/test/test_axis.py +41 -16
  215. tomwer/gui/reconstruction/test/test_nabu.py +31 -9
  216. tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
  217. tomwer/gui/reconstruction/test/test_sadeltabeta.py +12 -2
  218. tomwer/gui/settings.py +5 -28
  219. tomwer/gui/stackplot.py +2 -5
  220. tomwer/gui/stitching/action.py +49 -0
  221. tomwer/gui/stitching/config/axisparams.py +7 -24
  222. tomwer/gui/stitching/config/output.py +10 -8
  223. tomwer/gui/stitching/config/positionoveraxis.py +22 -23
  224. tomwer/gui/stitching/normalization.py +117 -0
  225. tomwer/gui/stitching/stitchandbackground.py +4 -6
  226. tomwer/gui/stitching/stitching.py +267 -45
  227. tomwer/gui/stitching/stitching_preview.py +62 -55
  228. tomwer/gui/stitching/stitching_raw.py +13 -12
  229. tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
  230. tomwer/gui/utils/buttons.py +112 -29
  231. tomwer/gui/utils/inputwidget.py +43 -25
  232. tomwer/gui/utils/lineselector/lineselector.py +1 -1
  233. tomwer/gui/utils/scandescription.py +4 -0
  234. tomwer/gui/utils/step.py +144 -0
  235. tomwer/gui/utils/unitsystem.py +2 -5
  236. tomwer/gui/utils/vignettes.py +176 -15
  237. tomwer/gui/visualization/dataviewer.py +48 -35
  238. tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
  239. tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
  240. tomwer/gui/visualization/scanoverview.py +1 -1
  241. tomwer/gui/visualization/sinogramviewer.py +20 -36
  242. tomwer/gui/visualization/test/test_diffviewer.py +3 -3
  243. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
  244. tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
  245. tomwer/gui/visualization/test/test_stacks.py +3 -3
  246. tomwer/gui/visualization/test/test_volumeviewer.py +65 -67
  247. tomwer/gui/visualization/volumeviewer.py +114 -113
  248. tomwer/io/utils/h5pyutils.py +3 -3
  249. tomwer/io/utils/raw_and_processed_data.py +84 -0
  250. tomwer/io/utils/tomoobj.py +4 -6
  251. tomwer/io/utils/utils.py +7 -7
  252. tomwer/resources/gui/icons/parameters.svg +1 -1
  253. tomwer/resources/gui/icons/ruler.png +0 -0
  254. tomwer/resources/gui/icons/ruler.svg +273 -0
  255. tomwer/resources/gui/icons/short_description.png +0 -0
  256. tomwer/resources/gui/icons/short_description.svg +58 -0
  257. tomwer/resources/gui/icons/url.png +0 -0
  258. tomwer/resources/gui/icons/url.svg +58 -0
  259. tomwer/resources/gui/illustrations/no_rot.svg +1 -1
  260. tomwer/synctools/stacks/edit/darkflatpatch.py +19 -14
  261. tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
  262. tomwer/synctools/stacks/reconstruction/axis.py +4 -4
  263. tomwer/synctools/stacks/reconstruction/castvolume.py +22 -7
  264. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +25 -20
  265. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  266. tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
  267. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
  268. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
  269. tomwer/synctools/test/test_darkRefs.py +7 -58
  270. tomwer/synctools/test/test_foldertransfer.py +6 -6
  271. tomwer/synctools/utils/scanstages.py +6 -6
  272. tomwer/tests/conftest.py +34 -0
  273. tomwer/tests/datasets.py +13 -0
  274. tomwer/tests/test_scripts.py +91 -41
  275. tomwer/tests/utils.py +5 -0
  276. tomwer/third_part/WaitingOverlay.py +110 -0
  277. tomwer/third_part/__init__.py +0 -0
  278. tomwer/version.py +2 -2
  279. tomwer-1.3.12-py3.11-nspkg.pth +1 -0
  280. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/METADATA +73 -58
  281. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/RECORD +287 -286
  282. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/WHEEL +1 -1
  283. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
  284. orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
  285. orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
  286. orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
  287. tomwer/app/lamino.py +0 -143
  288. tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
  289. tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
  290. tomwer/core/process/test/test_lamino.py +0 -76
  291. tomwer/core/test/test_lamino.py +0 -92
  292. tomwer/gui/reconstruction/lamino/__init__.py +0 -31
  293. tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
  294. tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
  295. tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
  296. tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
  297. tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
  298. tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
  299. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
  300. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -760
  301. tomwer/gui/reconstruction/test/test_lamino.py +0 -189
  302. tomwer/resources/gui/icons/esrf_1.svg +0 -307
  303. tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
  304. tomwer/resources/gui/icons/triangle.svg +0 -80
  305. tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
  306. tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
  307. tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
  308. tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
  309. tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
  310. tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
  311. tomwer/resources/gui/illustrations/manual_slice.png +0 -0
  312. tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
  313. tomwer/resources/gui/illustrations/psi_angle.png +0 -0
  314. tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
  315. tomwer/resources/gui/illustrations/rotation_center.png +0 -0
  316. tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
  317. tomwer/resources/gui/illustrations/slice_stack.png +0 -0
  318. tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
  319. tomwer/resources/gui/illustrations/xy_slice.png +0 -0
  320. tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
  321. tomwer/resources/gui/illustrations/xz_slice.png +0 -0
  322. tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
  323. tomwer/resources/gui/illustrations/yz_slice.png +0 -0
  324. tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
  325. tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
  326. tomwer/synctools/test/test_scanstages.py +0 -162
  327. tomwer/tests/utils/__init__.py +0 -247
  328. tomwer/tests/utils/utilstest.py +0 -220
  329. tomwer-1.2.1-py3.11-nspkg.pth +0 -1
  330. /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
  331. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/LICENSE +0 -0
  332. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/entry_points.txt +0 -0
  333. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/namespace_packages.txt +0 -0
  334. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/top_level.txt +0 -0
@@ -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
@@ -14,27 +15,61 @@ from tomwer.gui import icons as tomwer_icons
14
15
  from tomwer.gui import settings
15
16
  from tomwer.gui.stitching.stitchandbackground import StitchAndBackgroundAlphaMixIn
16
17
 
17
- try:
18
- from silx.gui.plot.ImageStack import ( # noqa F401
19
- PlotWithWaitingLabel as _PlotWithWaitingLabel,
20
- )
21
- except ImportError:
22
- from silx.gui.plot.ImageStack import _PlotWithWaitingLabel # noqa F401
18
+ from silx.gui.plot import Plot2D, items
19
+ from silx.gui.widgets.WaitingOverlay import WaitingOverlay
23
20
 
24
21
  _logger = logging.getLogger(__name__)
25
22
 
26
23
 
27
- class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn):
24
+ class _PreviewPlot2D(Plot2D):
25
+ """simple inheritance to get data from the 'stitched image' instead of the 'background / overlay' image"""
26
+
27
+ def _getImageValue(self, x, y):
28
+ pickedMask = None
29
+ for picked in self.pickItems(
30
+ *self.dataToPixel(x, y, check=False),
31
+ lambda item: isinstance(item, items.ImageBase),
32
+ ):
33
+ if isinstance(picked.getItem(), items.MaskImageData):
34
+ if pickedMask is None: # Use top-most if many masks
35
+ pickedMask = picked
36
+ else:
37
+ # we want to avoid displaying the value of the background image. So just ignore it.
38
+ # TODO: see with Thomas if there is a better way to do this.
39
+ # like starting by the active image ?
40
+ image = picked.getItem()
41
+ if image.getLegend() == StitchAndBackgroundAlphaMixIn.LEGEND_BACKGROUND:
42
+ continue
43
+
44
+ indices = picked.getIndices(copy=False)
45
+ if indices is not None:
46
+ row, col = indices[0][0], indices[1][0]
47
+ value = image.getData(copy=False)[row, col]
48
+
49
+ if pickedMask is not None: # Check if masked
50
+ maskItem = pickedMask.getItem()
51
+ indices = pickedMask.getIndices()
52
+ row, col = indices[0][0], indices[1][0]
53
+ if maskItem.getData(copy=False)[row, col] != 0:
54
+ return value, "Masked"
55
+ return value
56
+
57
+ return "-" # No image picked
58
+
59
+
60
+ class PreviewStitchingPlot(Plot2D, StitchAndBackgroundAlphaMixIn):
28
61
  DEFAULT_STITCHED_IMG_ALPHA = 0.95
29
- DEFAULT_BACKGROUND_IMG_ALPHA = 0.15
62
+ DEFAULT_BACKGROUND_IMG_ALPHA = 0.20
30
63
 
31
64
  def __init__(self, parent=None) -> None:
32
65
  super().__init__(parent=parent) # pylint: disable=E1123
66
+ self._waitingOverlay = WaitingOverlay(self)
67
+ self._waitingOverlay.setIconSize(qt.QSize(30, 30))
33
68
  self._stitched_image = None
34
69
  self._composition_background = None
35
70
 
36
71
  # tune plot
37
- self.getPlotWidget().setYAxisInverted(settings.Y_AXIS_DOWNWARD)
72
+ self.setYAxisInverted(settings.Y_AXIS_DOWNWARD)
38
73
  # by default we want to have a full screen display
39
74
  self.setAxesDisplayed(False)
40
75
 
@@ -47,6 +82,11 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
47
82
  self.setKeepDataAspectRatio(True)
48
83
  self.getColorBarWidget().hide()
49
84
 
85
+ # tape mesure action
86
+ self._tapeMeasureButton = TapeMeasureToolButton(parent=self, plot=self)
87
+ self._tapeMeasureButton.setCheckable(True)
88
+ self.toolBar().addWidget(self._tapeMeasureButton)
89
+
50
90
  # removing some plot action to clear toolbar
51
91
  self.getMaskAction().setVisible(False)
52
92
  self.getCopyAction().setVisible(False)
@@ -62,7 +102,7 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
62
102
  # set up
63
103
  self.setAlphaBackgroundImg(value=self.DEFAULT_BACKGROUND_IMG_ALPHA)
64
104
  self.setAlphaStitchedImg(value=self.DEFAULT_STITCHED_IMG_ALPHA)
65
- self.setWaiting(False)
105
+ self._waitingOverlay.hide()
66
106
 
67
107
  # connect signal / slot
68
108
  self._backGroundAction.toggled.connect(self._update)
@@ -111,15 +151,19 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
111
151
  shape=(
112
152
  (raw_compositon.global_end_y[-1] - raw_compositon.global_start_y[0]),
113
153
  frame_width,
154
+ 4,
114
155
  ),
115
156
  )
116
- assert background.ndim == 2
157
+ assert background.ndim == 3
117
158
 
118
159
  def get_next_color():
160
+ yellow = qt.QColor(qt.Qt.yellow)
161
+ magenta = qt.QColor(qt.Qt.magenta)
162
+ blue = qt.QColor(qt.Qt.blue)
119
163
  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()
164
+ yield yellow.red(), yellow.green(), yellow.blue(), 255
165
+ yield magenta.red(), magenta.green(), magenta.blue(), 255
166
+ yield blue.red(), blue.green(), blue.blue(), 255
123
167
 
124
168
  colors_raw_frames = []
125
169
  for _, color in zip(raw_compositon.local_end_y, get_next_color()):
@@ -127,6 +171,7 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
127
171
  shape=(
128
172
  raw_compositon.global_end_y[-1] - raw_compositon.global_start_y[0],
129
173
  frame_width,
174
+ 4,
130
175
  ),
131
176
  fill_value=color,
132
177
  )
@@ -196,44 +241,6 @@ class PreviewStitchingPlot(_PlotWithWaitingLabel, StitchAndBackgroundAlphaMixIn)
196
241
  def stitched_image(self):
197
242
  return self._stitched_image
198
243
 
199
- # expose API
200
- def getImage(self, *args, **kwargs):
201
- return self.getPlotWidget().getImage(*args, **kwargs)
202
-
203
- def addImage(self, *args, **kwargs):
204
- self.getPlotWidget().addImage(*args, **kwargs)
205
-
206
- def removeImage(self, *args, **kwargs):
207
- self.getPlotWidget().removeImage(*args, **kwargs)
208
-
209
- def toolBar(self, *args, **kwargs):
210
- return self.getPlotWidget().toolBar(*args, **kwargs)
211
-
212
- def setKeepDataAspectRatio(self, *args, **kwargs):
213
- return self.getPlotWidget().setKeepDataAspectRatio(*args, **kwargs)
214
-
215
- def getColorBarWidget(self, *args, **kwargs):
216
- return self.getPlotWidget().getColorBarWidget(*args, **kwargs)
217
-
218
- def getMaskAction(self, *args, **kwargs):
219
- return self.getPlotWidget().getMaskAction(*args, **kwargs)
220
-
221
- def getCopyAction(self, *args, **kwargs):
222
- return self.getPlotWidget().getCopyAction(*args, **kwargs)
223
-
224
- def addToolBar(self, *args, **kwargs):
225
- return self.getPlotWidget().addToolBar(*args, **kwargs)
226
-
227
- def setActiveImage(self, *args, **kwargs):
228
- return self.getPlotWidget().setActiveImage(*args, **kwargs)
229
-
230
- def setAxesDisplayed(self, *args, **kwargs):
231
- plotWidget = self.getPlotWidget()
232
- if hasattr(plotWidget, "setAxesDisplayed"):
233
- plotWidget.setAxesDisplayed(*args, **kwargs)
234
-
235
- def getDefaultColormap(self, *args, **kwargs):
236
- return self.getPlotWidget().getDefaultColormap()
237
-
238
- def setDefaultColormap(self, *args, **kwargs):
239
- return self.getPlotWidget().setDefaultColormap(*args, **kwargs)
244
+ def setPixelSize(self, pixel_size: Union[tuple, float]):
245
+ """set the pixel size to be used by the ruler"""
246
+ 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
@@ -189,9 +189,9 @@ class RawStitchingPlot(PlotWindow):
189
189
 
190
190
  self._alphaValuesWidget.addTomoObj(tomo_obj)
191
191
  if isinstance(tomo_obj, TomwerScanBase):
192
- self._scan_reading_order[
193
- tomo_obj.get_identifier().to_str()
194
- ] = self.getRotationAngleDirection(tomo_obj)
192
+ self._scan_reading_order[tomo_obj.get_identifier().to_str()] = (
193
+ self.getRotationAngleDirection(tomo_obj)
194
+ )
195
195
  if first_tomo_obj_id != tuple(self._tomo_objs.keys())[0]:
196
196
  # if the order of scans might have change we need to update the full stack (to handle reading order)
197
197
  self._updateImages()
@@ -322,22 +322,26 @@ class RawStitchingPlot(PlotWindow):
322
322
  # compute flat field
323
323
  # TODO: this must be done in another thread... but sync with update might be difficult...
324
324
  try:
325
- reduced_darks = scan.load_reduced_darks()
325
+ reduced_darks, reduced_darks_infos = scan.load_reduced_darks(
326
+ return_info=True
327
+ )
326
328
  except Exception:
327
329
  _logger.warning(
328
330
  f"no reduced dark found for {scan}. Please compute them to get a better display."
329
331
  )
330
332
  else:
331
- scan.set_reduced_darks(reduced_darks)
333
+ scan.set_reduced_darks(reduced_darks, darks_infos=reduced_darks_infos)
332
334
 
333
335
  try:
334
- reduced_flats = scan.load_reduced_flats()
336
+ reduced_flats, reduced_flats_info = scan.load_reduced_flats(
337
+ return_info=True
338
+ )
335
339
  except Exception:
336
340
  _logger.warning(
337
341
  f"no reduced flat(s) found for {scan}. Please compute them to get a better display."
338
342
  )
339
343
  else:
340
- scan.set_reduced_flats(reduced_flats)
344
+ scan.set_reduced_flats(reduced_flats, flats_infos=reduced_flats_info)
341
345
 
342
346
  reading_order_ref = self._getReadingOrderRef()
343
347
  reading_order = self._scan_reading_order[scan.get_identifier().to_str()]
@@ -557,10 +561,7 @@ class AlphaValueSlider(qt.QTableWidget):
557
561
  self._visibleButton.released.connect(self._setVisible)
558
562
 
559
563
  # expose API
560
- self._slider.valueChanged.connect(self._changed)
561
-
562
- def _changed(self, value: int):
563
- self.valueChanged.emit(value)
564
+ self._slider.valueChanged.connect(self.valueChanged)
564
565
 
565
566
  def _setInvisible(self, *args, **kwargs):
566
567
  self._slider.setValue(0)
@@ -10,7 +10,6 @@ from typing import Union
10
10
  from nabu.stitching import config as _nabu_stitching_config
11
11
  from nabu.stitching.config import StitchingType
12
12
  from nabu.stitching.overlap import OverlapStitchingStrategy
13
- from nabu.stitching.utils import ScoreMethod as _NabuScoreMethod
14
13
  from nabu.stitching.utils import ShiftAlgorithm as _NabuShiftAlgorithm
15
14
  from silx.gui import qt
16
15
  from tomoscan.serie import Serie
@@ -39,44 +38,8 @@ class Axis_N_Params(qt.QGroupBox):
39
38
  )
40
39
  self.layout().addRow("image registration method", self._imageRegMethodCB)
41
40
 
42
- # window size
43
- self._windowSize = qt.QSpinBox(self)
44
- self._windowSizeLabel = qt.QLabel(
45
- _nabu_stitching_config.KEY_WINDOW_SIZE.replace("_", " "), self
46
- )
47
- self.layout().addRow(self._windowSizeLabel, self._windowSize)
48
- self._windowSize.setToolTip("research window width to find shifts")
49
-
50
- # score method
51
- self._scoreCB = qt.QComboBox(self)
52
- for score_method in _NabuScoreMethod.values():
53
- self._scoreCB.addItem(score_method)
54
- self._scoreCBLabel = qt.QLabel(
55
- _nabu_stitching_config.KEY_SCORE_METHOD.replace("_", " "), self
56
- )
57
- self.layout().addRow(self._scoreCBLabel, self._scoreCB)
58
- self._windowSize.setToolTip(
59
- "method to use in order to compute score and determine the best shift"
60
- )
61
-
62
- # connect signal / slot
63
- self._imageRegMethodCB.currentTextChanged.connect(self._updatePossibleOpts)
64
-
65
41
  # set up
66
42
  self._imageRegMethodCB.setCurrentText(_NabuShiftAlgorithm.NONE.value)
67
- self._updatePossibleOpts()
68
-
69
- def getWindowSize(self):
70
- return self._windowSize.value()
71
-
72
- def setWindowSize(self, window_size):
73
- return self._windowSize.setValue(int(window_size))
74
-
75
- def getScoreMethod(self):
76
- return _NabuScoreMethod.from_value(self._scoreCB.currentText())
77
-
78
- def setScoreMethod(self, method):
79
- self._scoreCB.setCurrentText(_NabuScoreMethod.from_value(method).value)
80
43
 
81
44
  def getCurrentMethod(self):
82
45
  return _NabuShiftAlgorithm.from_value(self._imageRegMethodCB.currentText())
@@ -89,13 +52,6 @@ class Axis_N_Params(qt.QGroupBox):
89
52
  def getOptsLine(self) -> str:
90
53
  current_method = self.getCurrentMethod()
91
54
  line_ = f"{_nabu_stitching_config.KEY_IMG_REG_METHOD}={current_method.value}"
92
- if current_method is _NabuShiftAlgorithm.SHIFT_GRID:
93
- line_ = ",".join(
94
- (
95
- line_,
96
- f"{_nabu_stitching_config.KEY_WINDOW_SIZE}={self.getWindowSize()},{_nabu_stitching_config.KEY_SCORE_METHOD}={self.getScoreMethod().value}",
97
- )
98
- )
99
55
  return line_
100
56
 
101
57
  def setOptsLine(self, opt_line: str) -> None:
@@ -113,13 +69,6 @@ class Axis_N_Params(qt.QGroupBox):
113
69
  except Exception as e:
114
70
  _logger.error(e)
115
71
 
116
- score_method = opts.get(_nabu_stitching_config.KEY_SCORE_METHOD, None)
117
- if score_method is not None:
118
- try:
119
- self.setScoreMethod(score_method)
120
- except Exception as e:
121
- _logger.error(e)
122
-
123
72
  img_reg_exp_method = opts.get(_nabu_stitching_config.KEY_IMG_REG_METHOD, None)
124
73
  if img_reg_exp_method is not None:
125
74
  try:
@@ -127,15 +76,6 @@ class Axis_N_Params(qt.QGroupBox):
127
76
  except Exception as e:
128
77
  _logger.error(e)
129
78
 
130
- def _updatePossibleOpts(self, *args, **kwargs):
131
- current_method = self.getCurrentMethod()
132
- self._windowSizeLabel.setVisible(
133
- current_method is _NabuShiftAlgorithm.SHIFT_GRID
134
- )
135
- self._windowSize.setVisible(current_method is _NabuShiftAlgorithm.SHIFT_GRID)
136
- self._scoreCBLabel.setVisible(current_method is _NabuShiftAlgorithm.SHIFT_GRID)
137
- self._scoreCB.setVisible(current_method is _NabuShiftAlgorithm.SHIFT_GRID)
138
-
139
79
 
140
80
  class AutoRefineWidget(qt.QWidget):
141
81
  """
@@ -1,39 +1,20 @@
1
- # coding: utf-8
2
- # /*##########################################################################
3
- # Copyright (C) 2017 European Synchrotron Radiation Facility
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
- #
23
- #############################################################################*/
24
-
25
1
  """
26
2
  Button of general usage.
27
3
  """
28
4
 
29
- __authors__ = ["H.Payno"]
30
- __license__ = "MIT"
31
- __date__ = "03/10/2018"
32
-
33
-
5
+ import numpy
6
+ import logging
7
+ from typing import Optional, Union
34
8
  from silx.gui import qt
9
+ from silx.gui.plot.tools.roi import RegionOfInterestManager
10
+ from silx.gui.plot.items.roi import LineROI
11
+ from silx.gui.plot.PlotToolButtons import PlotToolButton
35
12
 
36
13
  from tomwer.gui import icons
14
+ from pyunitsystem.metricsystem import MetricSystem
15
+
16
+
17
+ _logger = logging.getLogger(__file__)
37
18
 
38
19
 
39
20
  class PadlockButton(qt.QPushButton):
@@ -100,3 +81,105 @@ class TabBrowsersButtons(qt.QWidget):
100
81
 
101
82
  def _nextReleased(self, *args, **kwargs):
102
83
  self.sigNextReleased.emit()
84
+
85
+
86
+ class TapeMeasureToolButton(PlotToolButton):
87
+ """Button to active measurement between two point of the plot"""
88
+
89
+ class TapeMeasureROI(LineROI):
90
+ def __init__(self, parent=None, pixel_size_m=None):
91
+ super().__init__(parent)
92
+ self._pixel_size_m = None
93
+ self.setPixelSize(pixel_size_m)
94
+
95
+ def setEndPoints(self, startPoint, endPoint):
96
+ distance_px = numpy.linalg.norm(endPoint - startPoint)
97
+ super().setEndPoints(startPoint=startPoint, endPoint=endPoint)
98
+ if self._pixel_size_m is None:
99
+ self._updateText(f"{distance_px :.1f}px")
100
+ else:
101
+ distance_m = distance_px * self._pixel_size_m
102
+ value, unit = MetricSystem.cast_metric_to_best_unit(distance_m)
103
+ self._updateText(f"{distance_px :.1f}px ({value:.2f}{unit})")
104
+
105
+ def setPixelSize(self, pixel_size_m: Optional[Union[tuple, float]]):
106
+ if isinstance(pixel_size_m, (tuple, list)):
107
+ assert (
108
+ len(pixel_size_m) == 2
109
+ ), "expects at most two pixel size values (x and y values)"
110
+ if not numpy.isclose(pixel_size_m[0], pixel_size_m[1]):
111
+ value, unit = MetricSystem.cast_metric_to_best_unit(pixel_size_m[0])
112
+ _logger.warning(
113
+ f"TapeMeasure is only handling square pixels for now. Will consider the pixel is {value:.2f}{unit}x{value:.2f}{unit}"
114
+ )
115
+ pixel_size_m = pixel_size_m[0]
116
+ self._pixel_size_m = pixel_size_m
117
+
118
+ def __init__(self, parent=None, plot=None, pixel_size_mm=None):
119
+ super().__init__(parent=parent, plot=plot)
120
+ self._roiManager = None
121
+ self._lastRoiCreated = None
122
+ self._pixel_sixel_m = pixel_size_mm
123
+ self.setIcon(icons.getQIcon("ruler"))
124
+ self.setToolTip("measure distance between two pixels")
125
+ self.toggled.connect(self._callback)
126
+ self._connectPlot(plot)
127
+
128
+ def setPlot(self, plot):
129
+ return super().setPlot(plot)
130
+
131
+ def setPixelSize(self, pixel_size_m: Optional[Union[tuple, float]]):
132
+ self._pixel_sixel_m = pixel_size_m
133
+
134
+ def _callback(self, toggled):
135
+ if not self._roiManager:
136
+ return
137
+ if self._lastRoiCreated is not None:
138
+ self._lastRoiCreated.setVisible(self.isChecked())
139
+ if self.isChecked():
140
+ self._roiManager.start(
141
+ self.TapeMeasureROI,
142
+ self,
143
+ )
144
+ self.__interactiveModeStarted(self._roiManager)
145
+ else:
146
+ source = self._roiManager.getInteractionSource()
147
+ if source is self:
148
+ self._roiManager.stop()
149
+
150
+ def __interactiveModeStarted(self, roiManager):
151
+ roiManager.sigInteractiveModeFinished.connect(self.__interactiveModeFinished)
152
+
153
+ def __interactiveModeFinished(self):
154
+ roiManager = self._roiManager
155
+ if roiManager is not None:
156
+ roiManager.sigInteractiveModeFinished.disconnect(
157
+ self.__interactiveModeFinished
158
+ )
159
+ self.setChecked(False)
160
+
161
+ def _connectPlot(self, plot):
162
+ """
163
+ Called when the plot is connected to the widget
164
+ :param plot: :class:`.PlotWidget` instance
165
+ """
166
+ if plot is None:
167
+ return
168
+ self._roiManager = RegionOfInterestManager(plot)
169
+ self._roiManager.setColor("yellow") # Set the color of ROI
170
+ self._roiManager.sigRoiAdded.connect(self._registerCurrentROI)
171
+
172
+ def _disconnectPlot(self, plot):
173
+ if plot and self._lastRoiCreated is not None:
174
+ self._roiManager.removeRoi(self._lastRoiCreated)
175
+ self._lastRoiCreated = None
176
+ return super()._disconnectPlot(plot)
177
+
178
+ def _registerCurrentROI(self, currentRoi):
179
+ if self._lastRoiCreated is None:
180
+ self._lastRoiCreated = currentRoi
181
+ self._lastRoiCreated.setPixelSize(self._pixel_sixel_m)
182
+ elif currentRoi != self._lastRoiCreated and self._roiManager is not None:
183
+ self._roiManager.removeRoi(self._lastRoiCreated)
184
+ self._lastRoiCreated = currentRoi
185
+ self._lastRoiCreated.setPixelSize(self._pixel_sixel_m)
@@ -28,7 +28,7 @@ __date__ = "16/08/2018"
28
28
 
29
29
  import logging
30
30
  import os
31
- from typing import Optional
31
+ from typing import Optional, Union
32
32
 
33
33
  import numpy
34
34
  from nxtomomill.io.config import TomoEDFConfig as EDFConfig
@@ -41,7 +41,7 @@ from tomwer.gui import icons
41
41
  from tomwer.gui.qlefilesystem import QLFileSystem
42
42
  from tomwer.io.utils import get_default_directory
43
43
  from tomwer.core.volume.volumefactory import VolumeFactory
44
- from tomwer.core.process.control.nxtomomill import NXtomomillNXDefaultOutput
44
+ from tomwer.core.process.output import ProcessDataOutputDirMode
45
45
 
46
46
  from tomwer.core.volume import (
47
47
  EDFVolume,
@@ -271,42 +271,49 @@ class NXTomomillOutputDirSelector(qt.QWidget):
271
271
  self.__buttonGroup.setExclusive(True)
272
272
 
273
273
  tooltip = """Define the output directory of the nexus (.nx) file. Options are:
274
- \n - next to bliss file: create the NXtomos at the same level as the bliss input file
274
+ \n - same folder as scan: create the NXtomos at the same level as the bliss input file or spec folder
275
+ \n - 'RAW_DATA' folder: create NXtomos on the default 'RAW_DATA' folder (bliss default folder, nearby the 'raw' folder).
275
276
  \n - 'PROCESSED_DATA' folder: create NXtomos on the default 'PROCESSED_DATA' folder (bliss default folder, nearby the 'raw' folder).
276
277
  \n - user defined folder: users can provide their own folders using keywords for string formatting such as 'scan_dir_name', 'scan_basename' or 'scan_parent_dir_basename'
277
278
  """
278
279
 
279
280
  # output dir is the folder containing the .nx file
280
- self._closeToBlissFileRB = qt.QRadioButton("near input file", self)
281
- self._closeToBlissFileRB.setToolTip(tooltip)
282
- self.layout().addWidget(self._closeToBlissFileRB, 0, 0, 1, 1)
283
- self.__buttonGroup.addButton(self._closeToBlissFileRB)
281
+ self._inScanFolder = qt.QRadioButton("same folder as scan", self)
282
+ self._inScanFolder.setToolTip(tooltip)
283
+ self.layout().addWidget(self._inScanFolder, 0, 0, 1, 1)
284
+ self.__buttonGroup.addButton(self._inScanFolder)
284
285
  # output dir is the default 'reduced'folder
285
286
  self._processedDataFolderRB = qt.QRadioButton("'PROCESSED_DATA' folder", self)
286
287
  self._processedDataFolderRB.setToolTip(tooltip)
287
288
  self.layout().addWidget(self._processedDataFolderRB, 1, 0, 1, 1)
288
289
  self.__buttonGroup.addButton(self._processedDataFolderRB)
290
+ # output dir is the default 'reduced'folder
291
+ self._rawDataFolderRB = qt.QRadioButton("'RAW_DATA' folder", self)
292
+ self._rawDataFolderRB.setToolTip(tooltip)
293
+ self.layout().addWidget(self._rawDataFolderRB, 2, 0, 1, 1)
294
+ self.__buttonGroup.addButton(self._rawDataFolderRB)
289
295
  # manual
290
296
  self._manualRB = qt.QRadioButton("custom output directory", self)
291
297
  self._manualRB.setToolTip(tooltip)
292
- self.layout().addWidget(self._manualRB, 2, 0, 1, 1)
298
+ self.layout().addWidget(self._manualRB, 3, 0, 1, 1)
293
299
  self._outputFolderQLE = QLFileSystem("", self)
294
- self.layout().addWidget(self._outputFolderQLE, 2, 1, 1, 1)
300
+ self.layout().addWidget(self._outputFolderQLE, 3, 1, 1, 1)
295
301
  self._selectButton = qt.QPushButton("", self)
296
302
  style = qt.QApplication.style()
297
303
  icon_opendir = style.standardIcon(qt.QStyle.SP_DirOpenIcon)
298
304
  self._selectButton.setIcon(icon_opendir)
299
305
  self._selectButton.setToolTip("select output directory")
300
- self.layout().addWidget(self._selectButton, 2, 2, 1, 1)
306
+ self.layout().addWidget(self._selectButton, 3, 2, 1, 1)
301
307
  self.__buttonGroup.addButton(self._manualRB)
302
308
 
303
309
  # connect signal / slot
304
310
  self._selectButton.released.connect(self._selectOutpuFolder)
305
311
  self.__buttonGroup.buttonReleased.connect(self._updateVisiblity)
306
- self._closeToBlissFileRB.toggled.connect(self._outputDirChanged)
307
- self._processedDataFolderRB.toggled.connect(self._outputDirChanged)
308
- self._manualRB.toggled.connect(self._outputDirChanged)
309
- self._outputFolderQLE.editingFinished.connect(self._outputDirChanged)
312
+ self._inScanFolder.toggled.connect(self.sigChanged)
313
+ self._processedDataFolderRB.toggled.connect(self.sigChanged)
314
+ self._rawDataFolderRB.toggled.connect(self.sigChanged)
315
+ self._manualRB.toggled.connect(self.sigChanged)
316
+ self._outputFolderQLE.editingFinished.connect(self.sigChanged)
310
317
 
311
318
  # set up
312
319
  self._processedDataFolderRB.setChecked(True)
@@ -316,9 +323,6 @@ class NXTomomillOutputDirSelector(qt.QWidget):
316
323
  self._selectButton.setVisible(self._manualRB.isChecked())
317
324
  self._outputFolderQLE.setVisible(self._manualRB.isChecked())
318
325
 
319
- def _outputDirChanged(self):
320
- self.sigChanged.emit()
321
-
322
326
  def _selectOutpuFolder(self): # pragma: no cover
323
327
  defaultDirectory = self._outputFolderQLE.text()
324
328
  if os.path.isdir(defaultDirectory):
@@ -334,13 +338,15 @@ class NXTomomillOutputDirSelector(qt.QWidget):
334
338
  self._outputFolderQLE.setText(dialog.selectedFiles()[0])
335
339
  self.sigChanged.emit()
336
340
 
337
- def getOutputFolder(self) -> str:
341
+ def getOutputFolder(self) -> Union[str, ProcessDataOutputDirMode]:
338
342
  if self._manualRB.isChecked():
339
343
  return self._outputFolderQLE.text()
340
344
  elif self._processedDataFolderRB.isChecked():
341
- return NXtomomillNXDefaultOutput.PROCESSED_DATA.value
342
- elif self._closeToBlissFileRB.isChecked():
343
- return NXtomomillNXDefaultOutput.NEAR_INPUT_FILE.value
345
+ return ProcessDataOutputDirMode.PROCESSED_DATA_FOLDER
346
+ elif self._inScanFolder.isChecked():
347
+ return ProcessDataOutputDirMode.IN_SCAN_FOLDER
348
+ elif self._rawDataFolderRB.isChecked():
349
+ return ProcessDataOutputDirMode.RAW_DATA_FOLDER
344
350
  else:
345
351
  raise RuntimeError("Use case - h52nx output dir - not handled")
346
352
 
@@ -348,15 +354,17 @@ class NXTomomillOutputDirSelector(qt.QWidget):
348
354
  old = self.blockSignals(True)
349
355
  self._manualRB.setChecked(output_folder is not None)
350
356
  try:
351
- default_output = NXtomomillNXDefaultOutput.from_value(output_folder)
357
+ default_output = ProcessDataOutputDirMode.from_value(output_folder)
352
358
  except ValueError:
353
359
  self._outputFolderQLE.setText(output_folder)
354
360
  self._manualRB.setChecked(True)
355
361
  else:
356
- if default_output is NXtomomillNXDefaultOutput.NEAR_INPUT_FILE:
362
+ if default_output is ProcessDataOutputDirMode.IN_SCAN_FOLDER:
363
+ self._inScanFolder.setChecked(True)
364
+ elif default_output is ProcessDataOutputDirMode.PROCESSED_DATA_FOLDER:
357
365
  self._processedDataFolderRB.setChecked(True)
358
- elif default_output is NXtomomillNXDefaultOutput.PROCESSED_DATA:
359
- self._closeToBlissFileRB.setChecked(True)
366
+ elif default_output is ProcessDataOutputDirMode.RAW_DATA_FOLDER:
367
+ self._rawDataFolderRB.setChecked(True)
360
368
  else:
361
369
  raise ValueError(f"default output not handled ({default_output})")
362
370
  finally:
@@ -516,6 +524,11 @@ class OutputVolumeDefinition(qt.QWidget):
516
524
  os.environ["TOMWER_DEFAULT_INPUT_DIR"]
517
525
  ):
518
526
  dialog.setDirectory(os.environ["TOMWER_DEFAULT_INPUT_DIR"])
527
+ elif dialog.directory() != os.getcwd() or str(dialog.directory()).startswith(
528
+ "/data"
529
+ ):
530
+ # if the directory as already been set by the user. Avoid redefining it
531
+ pass
519
532
  elif os.path.isdir("/data"):
520
533
  dialog.setDirectory("/data")
521
534
 
@@ -532,6 +545,11 @@ class OutputVolumeDefinition(qt.QWidget):
532
545
  os.environ["TOMWER_DEFAULT_INPUT_DIR"]
533
546
  ):
534
547
  dialog.setDirectory(os.environ["TOMWER_DEFAULT_INPUT_DIR"])
548
+ elif dialog.directory() != os.getcwd() or str(dialog.directory()).startswith(
549
+ "/data"
550
+ ):
551
+ # if the directory as already been set by the user. Avoid redefining it
552
+ pass
535
553
  elif os.path.isdir("/data"):
536
554
  dialog.setDirectory("/data")
537
555
 
@@ -219,7 +219,7 @@ class QLineSelector(qt.QWidget):
219
219
  """
220
220
  legend = self._getLegend(row_n=row)
221
221
  if legend in self.__selection:
222
- self._plot.removeItem(legend)
222
+ self._plot.remove(legend=legend, kind="item")
223
223
  del self.__selection[legend]
224
224
 
225
225
  def _plotDrawEvent(self, event):
@@ -64,6 +64,10 @@ class ScanNameLabelAndShape(qt.QWidget):
64
64
  def setScan(self, scan: Optional[TomwerScanBase]):
65
65
  if scan is None or scan.path is None:
66
66
  self.clear()
67
+ elif not isinstance(scan, TomwerScanBase):
68
+ raise TypeError(
69
+ f"Scan is expected to be an instance of {TomwerScanBase}. Get {type(scan)} instead"
70
+ )
67
71
  else:
68
72
  assert isinstance(scan, TomwerScanBase)
69
73
  self._scanNameLabel.setText(scan.get_identifier().short_description())