tomwer 1.3.27__py3-none-any.whl → 1.4.0rc0__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 (632) hide show
  1. orangecontrib/tomwer/orange/managedprocess.py +1 -29
  2. orangecontrib/tomwer/orange/settings.py +2 -28
  3. orangecontrib/tomwer/tests/TestAcquisition.py +220 -0
  4. orangecontrib/tomwer/tutorials/append_raw_darks_and_flats_frames_to_NXtomos.ows +2 -2
  5. orangecontrib/tomwer/tutorials/copy_reduced_darks_and_flats_meth1.ows +1 -1
  6. orangecontrib/tomwer/tutorials/{icat_publication.ows → drac_publication.ows} +7 -21
  7. orangecontrib/tomwer/tutorials/id16b/ID16b_full_volume.ows +22 -0
  8. orangecontrib/tomwer/tutorials/id16b/ID16b_insitu.ows +302 -0
  9. orangecontrib/tomwer/tutorials/id16b/ID16b_normalization.ows +218 -0
  10. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +29 -24
  11. orangecontrib/tomwer/tutorials/test_cor.ows +19 -0
  12. orangecontrib/tomwer/tutorials/volume_casting_on_slurm.ows +54 -0
  13. orangecontrib/tomwer/widgets/__init__.py +3 -5
  14. orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +35 -54
  15. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +1 -31
  16. orangecontrib/tomwer/widgets/control/AdvancementOW.py +1 -29
  17. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +5 -4
  18. orangecontrib/tomwer/widgets/control/DataListenerOW.py +1 -29
  19. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +11 -30
  20. orangecontrib/tomwer/widgets/control/DataTransfertOW.py +2 -28
  21. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +1 -28
  22. orangecontrib/tomwer/widgets/control/DataWatcherOW.py +1 -28
  23. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +48 -51
  24. orangecontrib/tomwer/widgets/control/EmailOW.py +12 -2
  25. orangecontrib/tomwer/widgets/control/FilterOW.py +1 -28
  26. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +37 -53
  27. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +21 -20
  28. orangecontrib/tomwer/widgets/control/NotifierOW.py +9 -28
  29. orangecontrib/tomwer/widgets/control/ReduceDarkFlatSelectorOW.py +3 -2
  30. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +1 -27
  31. orangecontrib/tomwer/widgets/control/TimerOW.py +9 -28
  32. orangecontrib/tomwer/widgets/control/TomoObjSeriesOW.py +58 -0
  33. orangecontrib/tomwer/widgets/control/VolumeSelector.py +8 -30
  34. orangecontrib/tomwer/widgets/control/icons/nxtomomill.svg +173 -119
  35. orangecontrib/tomwer/widgets/control/icons/reduced_darkflat_selector.png +0 -0
  36. orangecontrib/tomwer/widgets/control/icons/reduced_darkflat_selector.svg +55 -195
  37. orangecontrib/tomwer/widgets/control/icons/{tomoobjserie.svg → tomoobjseries.svg} +2 -2
  38. orangecontrib/tomwer/widgets/dataportal/PublishProcessedDataOW.py +172 -0
  39. orangecontrib/tomwer/widgets/{icat → dataportal}/__init__.py +2 -2
  40. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +2 -29
  41. orangecontrib/tomwer/widgets/debugtools/ObjectInspectorOW.py +1 -29
  42. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +48 -73
  43. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +48 -75
  44. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +5 -30
  45. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +51 -52
  46. orangecontrib/tomwer/widgets/other/PythonScriptOW.py +22 -10
  47. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +48 -49
  48. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +22 -68
  49. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +3 -30
  50. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +21 -19
  51. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +7 -70
  52. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +40 -85
  53. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +15 -40
  54. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +13 -35
  55. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +2 -30
  56. orangecontrib/tomwer/widgets/stitching/ZStitchingConfigOW.py +11 -11
  57. orangecontrib/tomwer/widgets/utils.py +2 -29
  58. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +1 -27
  59. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -27
  60. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +1 -1
  61. orangecontrib/tomwer/widgets/visualization/RadioStackOW.py +2 -28
  62. orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +1 -27
  63. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +1 -27
  64. orangecontrib/tomwer/widgets/visualization/SliceStackOW.py +2 -28
  65. tomwer/__init__.py +3 -28
  66. tomwer/__main__.py +8 -4
  67. tomwer/app/__init__.py +2 -0
  68. tomwer/app/axis.py +8 -7
  69. tomwer/app/canvas.py +23 -29
  70. tomwer/app/canvas_launcher/config.py +14 -102
  71. tomwer/app/canvas_launcher/environ.py +5 -8
  72. tomwer/app/canvas_launcher/mainwindow.py +175 -69
  73. tomwer/app/canvas_launcher/splash.py +1 -3
  74. tomwer/app/canvas_launcher/utils.py +47 -0
  75. tomwer/app/canvas_launcher/widgetsscheme.py +3 -10
  76. tomwer/app/diffframe.py +2 -0
  77. tomwer/app/imagekeyeditor.py +2 -1
  78. tomwer/app/imagekeyupgrader.py +2 -0
  79. tomwer/app/multicor.py +5 -2
  80. tomwer/app/multipag.py +7 -4
  81. tomwer/app/nabuapp.py +2 -1
  82. tomwer/app/nxtomoeditor.py +17 -12
  83. tomwer/app/patchrawdarkflat.py +2 -0
  84. tomwer/app/radiostack.py +3 -2
  85. tomwer/app/reducedarkflat.py +3 -0
  86. tomwer/app/samplemoved.py +2 -0
  87. tomwer/app/scanviewer.py +2 -0
  88. tomwer/app/sinogramviewer.py +2 -0
  89. tomwer/app/slicestack.py +11 -13
  90. tomwer/app/stitching/common.py +431 -0
  91. tomwer/app/stopdatalistener.py +3 -0
  92. tomwer/app/ystitching.py +26 -0
  93. tomwer/app/zstitching.py +8 -363
  94. tomwer/core/__init__.py +2 -28
  95. tomwer/core/cluster/__init__.py +3 -0
  96. tomwer/core/cluster/cluster.py +10 -26
  97. tomwer/core/futureobject.py +17 -43
  98. tomwer/core/log/__init__.py +2 -0
  99. tomwer/core/log/processlog.py +0 -28
  100. tomwer/core/process/cluster/supervisor.py +52 -34
  101. tomwer/core/process/conditions/filters.py +3 -28
  102. tomwer/core/process/control/datalistener/datalistener.py +7 -75
  103. tomwer/core/process/control/datalistener/rpcserver.py +8 -38
  104. tomwer/core/process/control/datawatcher/datawatcher.py +11 -40
  105. tomwer/core/process/control/datawatcher/datawatcherobserver.py +30 -64
  106. tomwer/core/process/control/datawatcher/datawatcherprocess.py +11 -32
  107. tomwer/core/process/control/datawatcher/edfdwprocess.py +2 -27
  108. tomwer/core/process/control/datawatcher/hdf5dwprocess.py +1 -26
  109. tomwer/core/process/control/datawatcher/status.py +1 -26
  110. tomwer/core/process/control/emailnotifier.py +11 -23
  111. tomwer/core/process/control/nxtomoconcatenate.py +20 -18
  112. tomwer/core/process/control/nxtomomill.py +9 -44
  113. tomwer/core/process/control/scanlist.py +0 -27
  114. tomwer/core/process/control/scantransfer.py +15 -13
  115. tomwer/core/process/control/scanvalidator.py +4 -30
  116. tomwer/core/process/control/{test → tests}/test_concatenate_nxtomos.py +5 -5
  117. tomwer/core/process/control/timer.py +1 -27
  118. tomwer/core/process/control/tomoobjseries.py +12 -0
  119. tomwer/core/process/drac/binning.py +37 -0
  120. tomwer/core/process/drac/dracbase.py +170 -0
  121. tomwer/core/process/drac/gallery.py +109 -0
  122. tomwer/core/process/drac/output.py +12 -0
  123. tomwer/core/process/drac/processeddataset.py +147 -0
  124. tomwer/core/process/drac/publish.py +118 -0
  125. tomwer/core/process/drac/rawdataset.py +142 -0
  126. tomwer/core/process/drac/tests/test_gallery.py +71 -0
  127. tomwer/core/process/drac/tests/test_icat_processed_dataset.py +80 -0
  128. tomwer/core/process/drac/tests/test_icat_raw_dataset.py +90 -0
  129. tomwer/core/process/edit/darkflatpatch.py +1 -28
  130. tomwer/core/process/edit/imagekeyeditor.py +4 -32
  131. tomwer/core/process/edit/nxtomoeditor.py +307 -0
  132. tomwer/core/process/edit/tests/test_darkflatpatch.py +243 -0
  133. tomwer/core/process/edit/tests/test_imagekey_editor.py +99 -0
  134. tomwer/core/process/output.py +9 -2
  135. tomwer/core/process/reconstruction/__init__.py +0 -26
  136. tomwer/core/process/reconstruction/axis/anglemode.py +1 -29
  137. tomwer/core/process/reconstruction/axis/axis.py +63 -111
  138. tomwer/core/process/reconstruction/axis/mode.py +5 -3
  139. tomwer/core/process/reconstruction/axis/params.py +5 -146
  140. tomwer/core/process/reconstruction/axis/projectiontype.py +0 -28
  141. tomwer/core/process/reconstruction/darkref/darkrefs.py +14 -39
  142. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +7 -39
  143. tomwer/core/process/reconstruction/darkref/params.py +1 -28
  144. tomwer/core/process/reconstruction/nabu/castvolume.py +19 -34
  145. tomwer/core/process/reconstruction/nabu/nabucommon.py +18 -43
  146. tomwer/core/process/reconstruction/nabu/nabuscores.py +47 -48
  147. tomwer/core/process/reconstruction/nabu/nabuslices.py +59 -105
  148. tomwer/core/process/reconstruction/nabu/nabuvolume.py +44 -70
  149. tomwer/core/process/reconstruction/nabu/plane.py +10 -0
  150. tomwer/core/process/reconstruction/nabu/settings.py +1 -28
  151. tomwer/core/process/reconstruction/nabu/target.py +0 -28
  152. tomwer/core/process/reconstruction/nabu/test/test_castvolume.py +116 -0
  153. tomwer/core/process/reconstruction/nabu/test/test_nabu_utils.py +277 -0
  154. tomwer/core/process/reconstruction/nabu/test/test_nabunormalization.py +199 -0
  155. tomwer/core/process/reconstruction/nabu/utils.py +11 -60
  156. tomwer/core/process/reconstruction/normalization/normalization.py +2 -32
  157. tomwer/core/process/reconstruction/normalization/params.py +3 -35
  158. tomwer/core/process/reconstruction/output.py +14 -19
  159. tomwer/core/process/reconstruction/paramsbase.py +4 -33
  160. tomwer/core/process/reconstruction/saaxis/params.py +5 -33
  161. tomwer/core/process/reconstruction/saaxis/saaxis.py +18 -47
  162. tomwer/core/process/reconstruction/sadeltabeta/params.py +2 -31
  163. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +16 -44
  164. tomwer/core/process/reconstruction/scores/params.py +9 -39
  165. tomwer/core/process/reconstruction/scores/scores.py +10 -42
  166. tomwer/core/process/reconstruction/tests/__init__.py +0 -0
  167. tomwer/core/process/reconstruction/tests/test_axis.py +46 -0
  168. tomwer/core/process/reconstruction/tests/test_darkref.py +33 -0
  169. tomwer/core/process/reconstruction/{test → tests}/test_saaxis.py +1 -27
  170. tomwer/core/process/reconstruction/tests/test_sadeltabeta.py +48 -0
  171. tomwer/core/process/script/python.py +1 -27
  172. tomwer/core/process/script/tests/test_script.py +41 -0
  173. tomwer/core/process/stitching/metadataholder.py +5 -4
  174. tomwer/core/process/stitching/nabustitcher.py +35 -5
  175. tomwer/core/process/stitching/tests/test_metadataholder.py +17 -0
  176. tomwer/core/process/task.py +20 -63
  177. tomwer/core/process/tests/__init__.py +0 -0
  178. tomwer/core/process/{test → tests}/test_axis.py +1 -79
  179. tomwer/core/process/{test → tests}/test_conditions.py +1 -28
  180. tomwer/core/process/{test → tests}/test_dark_and_flat.py +1 -27
  181. tomwer/core/process/{test → tests}/test_data_listener.py +1 -27
  182. tomwer/core/process/{test → tests}/test_data_transfer.py +2 -28
  183. tomwer/core/process/{test → tests}/test_data_watcher.py +1 -27
  184. tomwer/core/process/{test → tests}/test_nabu.py +4 -33
  185. tomwer/core/process/{test → tests}/test_normalization.py +1 -26
  186. tomwer/core/process/{test → tests}/test_timer.py +1 -27
  187. tomwer/core/process/utils.py +2 -31
  188. tomwer/core/process/visualization/dataviewer.py +1 -26
  189. tomwer/core/process/visualization/diffviewer.py +1 -26
  190. tomwer/core/process/visualization/imagestackviewer.py +0 -26
  191. tomwer/core/process/visualization/radiostack.py +0 -26
  192. tomwer/core/process/visualization/samplemoved.py +0 -28
  193. tomwer/core/process/visualization/sinogramviewer.py +0 -27
  194. tomwer/core/process/visualization/slicestack.py +0 -28
  195. tomwer/core/process/visualization/tests/test_data_viewer.py +12 -0
  196. tomwer/core/process/visualization/tests/test_diff_viewer.py +12 -0
  197. tomwer/core/process/visualization/tests/test_image_stack_viewer.py +14 -0
  198. tomwer/core/process/visualization/tests/test_radio_stack.py +12 -0
  199. tomwer/core/process/visualization/tests/test_sample_moved.py +14 -0
  200. tomwer/core/process/visualization/tests/test_sinogram_viewer.py +13 -0
  201. tomwer/core/process/visualization/tests/test_slice_stack.py +13 -0
  202. tomwer/core/process/visualization/tests/test_volume_viewer.py +12 -0
  203. tomwer/core/process/visualization/volumeviewer.py +0 -29
  204. tomwer/core/scan/__init__.py +3 -27
  205. tomwer/core/scan/blissscan.py +5 -34
  206. tomwer/core/scan/edfscan.py +19 -53
  207. tomwer/core/scan/hdf5scan.py +2 -2
  208. tomwer/core/scan/nxtomoscan.py +46 -54
  209. tomwer/core/scan/scanbase.py +104 -200
  210. tomwer/core/scan/scanfactory.py +11 -41
  211. tomwer/core/scan/tests/__init__.py +0 -0
  212. tomwer/core/scan/tests/test_edf.py +25 -0
  213. tomwer/core/scan/tests/test_future_scan.py +35 -0
  214. tomwer/core/scan/tests/test_nxtomoscan.py +143 -0
  215. tomwer/core/scan/{test → tests}/test_process_registration.py +1 -28
  216. tomwer/core/settings.py +18 -40
  217. tomwer/core/tests/__init__.py +0 -0
  218. tomwer/core/tests/test_scanutils.py +26 -0
  219. tomwer/core/{test → tests}/test_utils.py +1 -28
  220. tomwer/core/tomwer_object.py +4 -0
  221. tomwer/core/utils/__init__.py +2 -0
  222. tomwer/core/utils/char.py +0 -28
  223. tomwer/core/utils/deprecation.py +12 -38
  224. tomwer/core/utils/dictutils.py +1 -3
  225. tomwer/core/utils/ftseriesutils.py +1 -27
  226. tomwer/core/utils/gpu.py +0 -28
  227. tomwer/core/utils/image.py +8 -39
  228. tomwer/core/utils/locker.py +1 -28
  229. tomwer/core/utils/logconfig.py +0 -27
  230. tomwer/core/utils/normalization.py +4 -31
  231. tomwer/core/utils/nxtomoutils.py +8 -38
  232. tomwer/core/utils/resource.py +0 -26
  233. tomwer/core/utils/scanutils.py +23 -52
  234. tomwer/core/utils/slurm.py +7 -30
  235. tomwer/core/utils/spec.py +6 -6
  236. tomwer/core/utils/tests/__init__.py +0 -0
  237. tomwer/core/utils/tests/test_image.py +30 -0
  238. tomwer/core/utils/tests/test_nxtomo.py +38 -0
  239. tomwer/core/utils/tests/test_scan_utils.py +46 -0
  240. tomwer/core/utils/tests/test_time.py +6 -0
  241. tomwer/core/utils/threads.py +0 -26
  242. tomwer/core/utils/time.py +0 -27
  243. tomwer/core/volume/__init__.py +4 -0
  244. tomwer/core/volume/edfvolume.py +1 -28
  245. tomwer/core/volume/hdf5volume.py +1 -28
  246. tomwer/core/volume/jp2kvolume.py +1 -27
  247. tomwer/core/volume/rawvolume.py +1 -28
  248. tomwer/core/volume/tests/test_volumes.py +21 -0
  249. tomwer/core/volume/tiffvolume.py +1 -28
  250. tomwer/core/volume/volumebase.py +5 -0
  251. tomwer/core/volume/volumefactory.py +7 -33
  252. tomwer/gui/__init__.py +0 -28
  253. tomwer/gui/cluster/__init__.py +2 -0
  254. tomwer/gui/cluster/slurm.py +297 -95
  255. tomwer/gui/cluster/supervisor.py +1 -27
  256. tomwer/gui/cluster/tests/__init__.py +0 -0
  257. tomwer/gui/cluster/{test → tests}/test_cluster.py +21 -26
  258. tomwer/gui/cluster/{test → tests}/test_supervisor.py +1 -27
  259. tomwer/gui/conditions/__init__.py +2 -0
  260. tomwer/gui/conditions/filter.py +1 -26
  261. tomwer/gui/configuration/__init__.py +2 -0
  262. tomwer/gui/control/__init__.py +2 -0
  263. tomwer/gui/control/actions.py +2 -28
  264. tomwer/gui/control/datadiscovery.py +4 -3
  265. tomwer/gui/control/datalist.py +64 -69
  266. tomwer/gui/control/datalistener.py +1 -39
  267. tomwer/gui/control/datareacheractions.py +1 -28
  268. tomwer/gui/control/datatransfert.py +2 -29
  269. tomwer/gui/control/datavalidator.py +3 -37
  270. tomwer/gui/control/datawatcher/__init__.py +0 -28
  271. tomwer/gui/control/datawatcher/configuration.py +1 -28
  272. tomwer/gui/control/datawatcher/datawatcher.py +3 -32
  273. tomwer/gui/control/datawatcher/datawatcherobserver.py +2 -28
  274. tomwer/gui/control/history.py +5 -35
  275. tomwer/gui/control/nxtomomill.py +3 -30
  276. tomwer/gui/control/observations.py +61 -55
  277. tomwer/gui/control/reducedarkflatselector.py +24 -20
  278. tomwer/gui/control/scanselectorwidget.py +2 -28
  279. tomwer/gui/control/selectorwidgetbase.py +17 -17
  280. tomwer/gui/control/series/__init__.py +0 -0
  281. tomwer/gui/control/{serie/seriecreator.py → series/seriescreator.py} +214 -235
  282. tomwer/gui/control/series/serieswaiter.py +0 -0
  283. tomwer/gui/control/series/test/test_creator.py +424 -0
  284. tomwer/gui/control/series/test/test_nxtomo_concatenate.py +21 -0
  285. tomwer/gui/control/singletomoobj.py +1 -1
  286. tomwer/gui/control/tests/__init__.py +0 -0
  287. tomwer/gui/control/tests/test_datalist.py +47 -0
  288. tomwer/gui/control/{test → tests}/test_datalistener.py +1 -28
  289. tomwer/gui/control/tests/test_datavalidator.py +27 -0
  290. tomwer/gui/control/{test → tests}/test_inputwidget.py +1 -27
  291. tomwer/gui/control/tests/test_process_manager.py +38 -0
  292. tomwer/gui/control/tests/test_scan_observations.py +23 -0
  293. tomwer/gui/control/tests/test_scanselector.py +42 -0
  294. tomwer/gui/control/{test → tests}/test_scanvalidator.py +1 -27
  295. tomwer/gui/control/{test → tests}/test_volume_dialog.py +2 -30
  296. tomwer/gui/control/{test → tests}/test_volumeselector.py +1 -27
  297. tomwer/gui/control/volumeselectorwidget.py +2 -30
  298. tomwer/gui/dataportal/__init__.py +2 -0
  299. tomwer/gui/{icat → dataportal}/createscreenshots.py +3 -2
  300. tomwer/gui/dataportal/gallery.py +133 -0
  301. tomwer/gui/dataportal/outputformat.py +0 -0
  302. tomwer/gui/dataportal/publish.py +96 -0
  303. tomwer/gui/dataportal/tests/test_create_screenshots_gui.py +23 -0
  304. tomwer/gui/dataportal/tests/test_gallery_gui.py +21 -0
  305. tomwer/gui/debugtools/__init__.py +2 -0
  306. tomwer/gui/debugtools/datasetgenerator.py +1 -30
  307. tomwer/gui/debugtools/objectinspector.py +1 -29
  308. tomwer/gui/dialog/QDataDialog.py +89 -0
  309. tomwer/gui/{qfolderdialog.py → dialog/QVolumeDialog.py} +8 -107
  310. tomwer/gui/dialog/__init__.py +1 -0
  311. tomwer/gui/edit/__init__.py +2 -0
  312. tomwer/gui/edit/dkrfpatch.py +52 -87
  313. tomwer/gui/edit/imagekeyeditor.py +18 -54
  314. tomwer/gui/edit/nxtomoeditor.py +113 -300
  315. tomwer/gui/edit/nxtomowarmer.py +3 -2
  316. tomwer/gui/edit/tests/__init__.py +0 -0
  317. tomwer/gui/edit/{test → tests}/test_dkrf_patch.py +3 -29
  318. tomwer/gui/edit/{test → tests}/test_image_key_editor.py +1 -27
  319. tomwer/gui/edit/{test → tests}/test_nx_editor.py +45 -23
  320. tomwer/gui/icons.py +28 -66
  321. tomwer/gui/illustrations.py +4 -34
  322. tomwer/gui/imagefromfile.py +5 -30
  323. tomwer/gui/metadataloader.py +36 -0
  324. tomwer/gui/qconfigfile.py +3 -0
  325. tomwer/gui/qlefilesystem.py +3 -0
  326. tomwer/gui/reconstruction/__init__.py +2 -0
  327. tomwer/gui/reconstruction/axis/AxisMainWindow.py +264 -0
  328. tomwer/gui/reconstruction/axis/AxisOptionsWidget.py +153 -0
  329. tomwer/gui/reconstruction/axis/AxisSettingsWidget.py +776 -0
  330. tomwer/gui/reconstruction/axis/AxisWidget.py +526 -0
  331. tomwer/gui/reconstruction/axis/CalculationWidget.py +374 -0
  332. tomwer/gui/reconstruction/axis/CompareImages.py +11 -44
  333. tomwer/gui/reconstruction/axis/ControlWidget.py +277 -0
  334. tomwer/gui/reconstruction/axis/InputWidget.py +491 -0
  335. tomwer/gui/reconstruction/axis/ManualFramesSelection.py +168 -0
  336. tomwer/gui/reconstruction/axis/__init__.py +2 -2
  337. tomwer/gui/reconstruction/darkref/__init__.py +0 -27
  338. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +5 -34
  339. tomwer/gui/reconstruction/darkref/darkrefwidget.py +1 -27
  340. tomwer/gui/reconstruction/nabu/castvolume.py +40 -59
  341. tomwer/gui/reconstruction/nabu/check.py +7 -33
  342. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +7 -34
  343. tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +6 -5
  344. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +10 -69
  345. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +3 -47
  346. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +54 -36
  347. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +103 -54
  348. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +116 -65
  349. tomwer/gui/reconstruction/nabu/nabuflow.py +31 -61
  350. tomwer/gui/reconstruction/nabu/platform.py +94 -0
  351. tomwer/gui/reconstruction/nabu/slices.py +81 -45
  352. tomwer/gui/reconstruction/nabu/slurm.py +1 -27
  353. tomwer/gui/reconstruction/nabu/test/test_cast_volume.py +82 -0
  354. tomwer/gui/reconstruction/nabu/test/test_check.py +66 -0
  355. tomwer/gui/reconstruction/nabu/test/test_ctf.py +46 -0
  356. tomwer/gui/reconstruction/nabu/test/test_helical.py +21 -0
  357. tomwer/gui/reconstruction/nabu/test/test_nabu_preprocessing.py +81 -0
  358. tomwer/gui/reconstruction/nabu/volume.py +62 -90
  359. tomwer/gui/reconstruction/normalization/intensity.py +5 -32
  360. tomwer/gui/reconstruction/normalization/test/test_intensity.py +89 -0
  361. tomwer/gui/reconstruction/saaxis/corrangeselector.py +16 -49
  362. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +56 -96
  363. tomwer/gui/reconstruction/saaxis/saaxis.py +17 -38
  364. tomwer/gui/reconstruction/saaxis/sliceselector.py +8 -39
  365. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +19 -37
  366. tomwer/gui/reconstruction/scores/control.py +2 -30
  367. tomwer/gui/reconstruction/scores/scoreplot.py +19 -48
  368. tomwer/gui/reconstruction/tests/__init__.py +0 -0
  369. tomwer/gui/reconstruction/{test → tests}/test_axis.py +23 -49
  370. tomwer/gui/reconstruction/{test → tests}/test_nabu.py +8 -31
  371. tomwer/gui/reconstruction/{test → tests}/test_saaxis.py +10 -37
  372. tomwer/gui/reconstruction/{test → tests}/test_sadeltabeta.py +1 -26
  373. tomwer/gui/samplemoved/__init__.py +2 -30
  374. tomwer/gui/samplemoved/selectiontable.py +3 -33
  375. tomwer/gui/settings.py +7 -0
  376. tomwer/gui/stackplot.py +33 -661
  377. tomwer/gui/stacks.py +261 -135
  378. tomwer/gui/stitching/SingleAxisStitchingWidget.py +326 -0
  379. tomwer/gui/stitching/StitchingOptionsWidget.py +438 -0
  380. tomwer/gui/stitching/StitchingWindow.py +586 -0
  381. tomwer/gui/stitching/__init__.py +2 -0
  382. tomwer/gui/stitching/alignment.py +90 -0
  383. tomwer/gui/stitching/axisorderedlist.py +44 -34
  384. tomwer/gui/stitching/config/axisparams.py +23 -11
  385. tomwer/gui/stitching/config/output.py +6 -5
  386. tomwer/gui/stitching/config/positionoveraxis.py +313 -51
  387. tomwer/gui/stitching/config/stitchingstrategies.py +22 -16
  388. tomwer/gui/stitching/config/tests/test_axisparams.py +25 -0
  389. tomwer/gui/stitching/config/tomoobjdetails.py +3 -5
  390. tomwer/gui/stitching/normalization.py +1 -0
  391. tomwer/gui/stitching/preview.py +59 -0
  392. tomwer/gui/stitching/singleaxis.py +57 -0
  393. tomwer/gui/stitching/stitchandbackground.py +3 -2
  394. tomwer/gui/stitching/stitching_preview.py +44 -36
  395. tomwer/gui/stitching/stitching_raw.py +5 -3
  396. tomwer/gui/stitching/tests/test_ZStitchingWindow.py +88 -0
  397. tomwer/gui/stitching/tests/test_axis_ordered_list.py +21 -0
  398. tomwer/gui/stitching/tests/test_normalization.py +27 -0
  399. tomwer/gui/stitching/tests/test_preview.py +68 -0
  400. tomwer/gui/stitching/tests/test_stitching_raw.py +110 -0
  401. tomwer/gui/stitching/tests/utils.py +92 -0
  402. tomwer/gui/stitching/utils.py +14 -0
  403. tomwer/gui/stitching/z_stitching/fineestimation.py +4 -32
  404. tomwer/gui/stitching/z_stitching/tests/test_fine_estimation.py +35 -0
  405. tomwer/gui/stitching/z_stitching/tests/test_raw_estimation.py +215 -0
  406. tomwer/gui/stitching/z_stitching/tests/test_stitching_window.py +51 -0
  407. tomwer/gui/tests/__init__.py +0 -0
  408. tomwer/gui/tests/test_axis_gui.py +27 -0
  409. tomwer/gui/{test → tests}/test_qfolder_dialog.py +1 -1
  410. tomwer/gui/utils/RangeWidget.py +44 -0
  411. tomwer/gui/utils/buttons.py +4 -3
  412. tomwer/gui/utils/completer.py +2 -33
  413. tomwer/gui/utils/flow.py +11 -40
  414. tomwer/gui/utils/gpu.py +60 -0
  415. tomwer/gui/utils/illustrations.py +1 -26
  416. tomwer/gui/utils/inputwidget.py +35 -73
  417. tomwer/gui/utils/lineselector/lineselector.py +9 -46
  418. tomwer/gui/utils/loadingmode.py +81 -0
  419. tomwer/gui/utils/qt_utils.py +9 -0
  420. tomwer/gui/utils/sandboxes.py +1 -26
  421. tomwer/gui/utils/scandescription.py +2 -31
  422. tomwer/gui/utils/scrollarea.py +6 -55
  423. tomwer/gui/utils/slider.py +4 -28
  424. tomwer/gui/utils/splashscreen.py +0 -28
  425. tomwer/gui/utils/step.py +14 -13
  426. tomwer/gui/utils/tests/test_completer.py +41 -0
  427. tomwer/gui/utils/tests/test_line_selector.py +21 -0
  428. tomwer/gui/utils/tests/test_splashscreen.py +8 -0
  429. tomwer/gui/utils/tests/test_vignettes.py +68 -0
  430. tomwer/gui/utils/unitsystem.py +15 -69
  431. tomwer/gui/utils/utils.py +4 -5
  432. tomwer/gui/utils/vignettes.py +10 -41
  433. tomwer/gui/utils/waiterthread.py +0 -26
  434. tomwer/gui/visualization/__init__.py +2 -0
  435. tomwer/gui/visualization/dataviewer.py +68 -421
  436. tomwer/gui/visualization/diffviewer/diffviewer.py +2 -30
  437. tomwer/gui/visualization/diffviewer/shiftwidget.py +4 -29
  438. tomwer/gui/visualization/fullscreenplot.py +5 -5
  439. tomwer/gui/visualization/imagestack.py +403 -0
  440. tomwer/gui/visualization/nxtomometadata.py +0 -4
  441. tomwer/gui/visualization/reconstructionparameters.py +14 -32
  442. tomwer/gui/visualization/scanoverview.py +33 -66
  443. tomwer/gui/visualization/sinogramviewer.py +2 -28
  444. tomwer/gui/visualization/test/__init__.py +0 -28
  445. tomwer/gui/visualization/test/test_dataviewer.py +1 -28
  446. tomwer/gui/visualization/test/test_diffviewer.py +1 -28
  447. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +0 -5
  448. tomwer/gui/visualization/test/test_reconstruction_parameters.py +1 -27
  449. tomwer/gui/visualization/test/test_sinogramviewer.py +1 -28
  450. tomwer/gui/visualization/test/test_stacks.py +184 -115
  451. tomwer/gui/visualization/test/test_volumeviewer.py +3 -2
  452. tomwer/gui/visualization/tomoobjoverview.py +2 -2
  453. tomwer/gui/visualization/volumeoverview.py +3 -2
  454. tomwer/gui/visualization/volumeviewer.py +47 -43
  455. tomwer/io/__init__.py +2 -0
  456. tomwer/io/utils/h5pyutils.py +1 -27
  457. tomwer/io/utils/test/test_raw_and_processed_data.py +10 -0
  458. tomwer/io/utils/test/test_utils.py +67 -0
  459. tomwer/io/utils/utils.py +2 -31
  460. tomwer/resources/__init__.py +13 -33
  461. tomwer/resources/gui/icons/edit_downstream.svg +114 -0
  462. tomwer/resources/gui/icons/edit_upstream.svg +112 -0
  463. tomwer/resources/gui/icons/free_edition.svg +23 -0
  464. tomwer/resources/gui/icons/icat_gallery_opts.png +0 -0
  465. tomwer/resources/gui/icons/icat_gallery_opts.svg +80 -0
  466. tomwer/resources/gui/icons/search.png +0 -0
  467. tomwer/resources/gui/icons/search.svg +23 -0
  468. tomwer/resources/gui/icons/warning.png +0 -0
  469. tomwer/synctools/__init__.py +2 -0
  470. tomwer/synctools/axis.py +1 -27
  471. tomwer/synctools/darkref.py +1 -26
  472. tomwer/synctools/datalistener.py +1 -27
  473. tomwer/synctools/datatransfert.py +2 -27
  474. tomwer/synctools/imageloaderthread.py +1 -28
  475. tomwer/synctools/rsyncmanager.py +1 -25
  476. tomwer/synctools/saaxis.py +1 -26
  477. tomwer/synctools/sadeltabeta.py +1 -26
  478. tomwer/synctools/stacks/control/datalistener.py +1 -26
  479. tomwer/synctools/stacks/processingstack.py +4 -33
  480. tomwer/synctools/stacks/reconstruction/axis.py +2 -31
  481. tomwer/synctools/stacks/reconstruction/castvolume.py +12 -43
  482. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +3 -26
  483. tomwer/synctools/stacks/reconstruction/nabu.py +1 -26
  484. tomwer/synctools/stacks/reconstruction/normalization.py +1 -26
  485. tomwer/synctools/stacks/reconstruction/saaxis.py +1 -26
  486. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +1 -26
  487. tomwer/synctools/tests/__init__.py +0 -0
  488. tomwer/synctools/{test → tests}/test_darkRefs.py +16 -40
  489. tomwer/synctools/{test → tests}/test_foldertransfer.py +2 -33
  490. tomwer/synctools/utils/scanstages.py +2 -31
  491. tomwer/tests/__init__.py +1 -0
  492. tomwer/tests/app/test_stitching.py +95 -0
  493. tomwer/tests/datasets.py +1 -5
  494. tomwer/tests/orangecontrib/tomwer/widgets/cluster/tests/test_future_supervisorow.py +48 -0
  495. tomwer/tests/orangecontrib/tomwer/widgets/cluster/tests/test_slurm_clusterow.py +40 -0
  496. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_advancement.py +8 -0
  497. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_data_validator.py +55 -0
  498. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_datadiscovery.py +129 -0
  499. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_datalistener.py +111 -0
  500. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_dataselector.py +69 -0
  501. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_datawatcher.py +411 -0
  502. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_emailow.py +29 -0
  503. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_notifier.py +24 -0
  504. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_nxtomo_concatenate_ow.py +64 -0
  505. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_nxtomomill.py +133 -0
  506. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_reduce_dark_flat_selector.py +40 -0
  507. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_singletomoobj.py +40 -0
  508. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_timerow.py +25 -0
  509. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_tomoobj_series.py +96 -0
  510. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_volume_selector.py +69 -0
  511. orangecontrib/tomwer/widgets/edit/test/test_image_key_editor.py → tomwer/tests/orangecontrib/tomwer/widgets/debugtools/tests/test_dataset_generator.py +17 -16
  512. tomwer/tests/orangecontrib/tomwer/widgets/debugtools/tests/test_object_inspector.py +36 -0
  513. {orangecontrib/tomwer/widgets/edit/test → tomwer/tests/orangecontrib/tomwer/widgets/edit/tests}/test_dark_flat_patch.py +1 -27
  514. tomwer/tests/orangecontrib/tomwer/widgets/edit/tests/test_image_key_editor.py +30 -0
  515. tomwer/tests/orangecontrib/tomwer/widgets/edit/tests/test_nxtomo_editor.py +138 -0
  516. tomwer/tests/orangecontrib/tomwer/widgets/other/tests/test_pythonscript.py +31 -0
  517. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_axis.py +215 -0
  518. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_cast_volumeow.py +58 -0
  519. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_dark_refs_widget.py +136 -0
  520. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_delta_beta_selector.py +15 -0
  521. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_i_norm.py +200 -0
  522. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_helical_prepare_weights_double.py +20 -0
  523. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_volume.py +74 -0
  524. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_widget.py +107 -0
  525. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_sa_delta_beta.py +194 -0
  526. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_saaxis.py +194 -0
  527. tomwer/tests/orangecontrib/tomwer/widgets/stitching/tests/test_zstitching.py +313 -0
  528. tomwer/tests/orangecontrib/tomwer/widgets/tests/test_conditions.py +85 -0
  529. tomwer/tests/orangecontrib/tomwer/widgets/tests/test_darkref.py +225 -0
  530. tomwer/tests/orangecontrib/tomwer/widgets/tests/test_foldertransfert.py +105 -0
  531. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_dataviewerow.py +57 -0
  532. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_diffviewerow.py +39 -0
  533. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_nxtomo_metadata_viewer.py +29 -0
  534. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_radio_stackow.py +31 -0
  535. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_sample_movedow.py +46 -0
  536. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_sinogram_viewerow.py +31 -0
  537. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_slice_stackow.py +31 -0
  538. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_volume_viewerow.py +32 -0
  539. tomwer/tests/test_ewoks/test_conversion.py +104 -0
  540. tomwer/tests/test_ewoks/test_single_node_execution.py +87 -0
  541. tomwer/tests/test_ewoks/test_workflows.py +138 -0
  542. tomwer/utils.py +11 -39
  543. tomwer/version.py +3 -3
  544. {tomwer-1.3.27.dist-info → tomwer-1.4.0rc0.dist-info}/LICENSE +3 -3
  545. tomwer-1.4.0rc0.dist-info/METADATA +337 -0
  546. tomwer-1.4.0rc0.dist-info/RECORD +909 -0
  547. {tomwer-1.3.27.dist-info → tomwer-1.4.0rc0.dist-info}/WHEEL +1 -1
  548. {tomwer-1.3.27.dist-info → tomwer-1.4.0rc0.dist-info}/entry_points.txt +1 -0
  549. orangecontrib/tomwer/widgets/control/DataListOW.py +0 -129
  550. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +0 -86
  551. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +0 -182
  552. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +0 -141
  553. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +0 -115
  554. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +0 -98
  555. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +0 -129
  556. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  557. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +0 -82
  558. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  559. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +0 -143
  560. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +0 -87
  561. orangecontrib/tomwer/widgets/visualization/icons/liveslice.png +0 -0
  562. orangecontrib/tomwer/widgets/visualization/icons/liveslice.svg +0 -110
  563. tomwer/core/log/logger.py +0 -130
  564. tomwer/core/process/control/test/test_volume_link.py +0 -74
  565. tomwer/core/process/control/tomoobjserie.py +0 -12
  566. tomwer/core/process/control/volumesymlink.py +0 -200
  567. tomwer/core/process/icat/createscreenshots.py +0 -100
  568. tomwer/core/process/icat/gallery.py +0 -377
  569. tomwer/core/process/icat/icatbase.py +0 -36
  570. tomwer/core/process/icat/publish.py +0 -228
  571. tomwer/core/process/icat/screenshots.py +0 -27
  572. tomwer/core/process/reconstruction/test/test_darkref.py +0 -60
  573. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +0 -74
  574. tomwer/core/process/visualization/liveslice.py +0 -6
  575. tomwer/core/progress.py +0 -100
  576. tomwer/core/scan/test/test_edf.py +0 -53
  577. tomwer/core/scan/test/test_future_scan.py +0 -61
  578. tomwer/core/scan/test/test_h5.py +0 -96
  579. tomwer/core/test/test_scanutils.py +0 -53
  580. tomwer/gui/control/emailnotifier.py +0 -174
  581. tomwer/gui/control/serie/seriewaiter.py +0 -28
  582. tomwer/gui/control/test/__init__.py +0 -28
  583. tomwer/gui/control/test/test_datalist.py +0 -96
  584. tomwer/gui/control/test/test_datavalidator.py +0 -54
  585. tomwer/gui/control/test/test_email.py +0 -35
  586. tomwer/gui/control/test/test_process_manager.py +0 -65
  587. tomwer/gui/control/test/test_scanselector.py +0 -67
  588. tomwer/gui/edit/test/__init__.py +0 -28
  589. tomwer/gui/icat/gallery.py +0 -214
  590. tomwer/gui/icat/publish.py +0 -187
  591. tomwer/gui/reconstruction/axis/axis.py +0 -733
  592. tomwer/gui/reconstruction/axis/radioaxis.py +0 -2467
  593. tomwer/gui/stitching/stitching.py +0 -1388
  594. tomwer/gui/test/__init__.py +0 -28
  595. tomwer/gui/test/test_axis_gui.py +0 -34
  596. tomwer/synctools/stacks/edit/darkflatpatch.py +0 -169
  597. tomwer/synctools/stacks/edit/imagekeyeditor.py +0 -135
  598. tomwer/third_part/WaitingOverlay.py +0 -110
  599. tomwer-1.3.27-py3.11-nspkg.pth +0 -1
  600. tomwer-1.3.27.dist-info/METADATA +0 -292
  601. tomwer-1.3.27.dist-info/RECORD +0 -785
  602. tomwer-1.3.27.dist-info/namespace_packages.txt +0 -1
  603. /orangecontrib/tomwer/{widgets/edit/test → tests}/__init__.py +0 -0
  604. {tomwer/core/process/control/test → orangecontrib/tomwer/tutorials/id16b}/__init__.py +0 -0
  605. {tomwer/core/process/icat → orangecontrib/tomwer/widgets/cluster/tests}/__init__.py +0 -0
  606. /orangecontrib/tomwer/widgets/control/icons/{tomoobjserie.png → tomoobjseries.png} +0 -0
  607. {tomwer/core/process/reconstruction/test → orangecontrib/tomwer/widgets/control/tests}/__init__.py +0 -0
  608. /orangecontrib/tomwer/widgets/{icat → dataportal}/icons/publish_processed_data.png +0 -0
  609. /orangecontrib/tomwer/widgets/{icat → dataportal}/icons/publish_processed_data.svg +0 -0
  610. {tomwer/core/process/test → orangecontrib/tomwer/widgets/debugtools/tests}/__init__.py +0 -0
  611. {tomwer/core/scan/test → orangecontrib/tomwer/widgets/edit/tests}/__init__.py +0 -0
  612. {tomwer/core/test → orangecontrib/tomwer/widgets/other/tests}/__init__.py +0 -0
  613. {tomwer/gui/cluster/test → orangecontrib/tomwer/widgets/reconstruction/tests}/__init__.py +0 -0
  614. {tomwer/gui/control/serie → orangecontrib/tomwer/widgets/stitching/tests}/__init__.py +0 -0
  615. {tomwer/gui/icat → orangecontrib/tomwer/widgets/tests}/__init__.py +0 -0
  616. {tomwer/gui/reconstruction/test → orangecontrib/tomwer/widgets/visualization/tests}/__init__.py +0 -0
  617. /tomwer/{synctools/stacks/edit → app/stitching}/__init__.py +0 -0
  618. /tomwer/{synctools/test → core/process/control/tests}/__init__.py +0 -0
  619. /tomwer/core/process/control/{test → tests}/test_email.py +0 -0
  620. /tomwer/core/process/control/{test → tests}/test_h52nx_process.py +0 -0
  621. /tomwer/{third_part → core/process/drac}/__init__.py +0 -0
  622. /tomwer/core/process/reconstruction/{test → tests}/test_axis_params.py +0 -0
  623. /tomwer/core/process/reconstruction/{test → tests}/test_darkref_copy.py +0 -0
  624. /tomwer/core/process/reconstruction/{test → tests}/test_paramsbase.py +0 -0
  625. /tomwer/core/process/reconstruction/{test → tests}/test_utils.py +0 -0
  626. /tomwer/core/scan/{test → tests}/test_scan.py +0 -0
  627. /tomwer/gui/control/{serie → series}/nxtomoconcatenate.py +0 -0
  628. /tomwer/gui/control/{test → tests}/test_datadiscovery.py +0 -0
  629. /tomwer/gui/control/{test → tests}/test_reducedarkflat_selector.py +0 -0
  630. /tomwer/gui/control/{test → tests}/test_single_tomo_obj.py +0 -0
  631. {orangecontrib/tomwer/widgets/edit/test → tomwer/tests/orangecontrib/tomwer/widgets/edit/tests}/test_image_key_upgrader.py +0 -0
  632. {tomwer-1.3.27.dist-info → tomwer-1.4.0rc0.dist-info}/top_level.txt +0 -0
@@ -1,1388 +0,0 @@
1
- import logging
2
- import os
3
- import shutil
4
- import tempfile
5
- import functools
6
-
7
- from typing import Optional
8
-
9
- from nabu.stitching import config as stitching_config
10
- from nabu.stitching.config import StitchingType, dict_to_config_obj
11
- from nabu.stitching.z_stitching import (
12
- PostProcessZStitcher,
13
- PreProcessZStitcher,
14
- )
15
- from nabu.pipeline.config import generate_nabu_configfile, parse_nabu_config_file
16
- from nabu.stitching.alignment import AlignmentAxis1, AlignmentAxis2
17
- from nabu.stitching.config import (
18
- get_default_stitching_config,
19
- identifiers_as_str_to_instances,
20
- KEY_RESCALE_MAX_PERCENTILES,
21
- KEY_RESCALE_MIN_PERCENTILES,
22
- RESCALE_FRAMES,
23
- RESCALE_PARAMS,
24
- SECTIONS_COMMENTS as _SECTIONS_COMMENTS,
25
- STITCHING_SECTION,
26
- )
27
-
28
- from nxtomomill.io.utils import (
29
- convert_str_to_tuple as _convert_str_to_tuple,
30
- convert_str_to_bool as _convert_str_to_bool,
31
- )
32
- from silx.gui import qt
33
- from tomoscan.serie import Serie
34
- from tomoscan.scanbase import TomoScanBase as _TomoScanBase
35
- from tomoscan.volumebase import VolumeBase as _VolumeBase
36
-
37
- from tomwer.core.scan.nxtomoscan import NXtomoScan, NXtomoScanIdentifier
38
- from tomwer.core.scan.scanfactory import ScanFactory
39
- from tomwer.core.volume.volumefactory import VolumeFactory
40
- from tomwer.core.volume.volumebase import TomwerVolumeBase
41
- from tomwer.core.tomwer_object import TomwerObject
42
- from tomwer.core.volume.hdf5volume import HDF5Volume, HDF5VolumeIdentifier
43
- from tomwer.io.utils.utils import str_to_dict
44
- from tomwer.gui.qconfigfile import QConfigFileDialog
45
- from tomwer.gui.stitching.config.axisparams import StitcherAxisParams
46
- from tomwer.gui.stitching.config.positionoveraxis import PosEditorOverOneAxis
47
- from tomwer.gui.stitching.config.output import StitchingOutput
48
- from tomwer.gui.stitching.config.stitchingstrategies import StitchingStrategies
49
- from tomwer.gui.stitching.stitching_preview import PreviewStitchingPlot
50
- from tomwer.gui.stitching.stitching_raw import RawStitchingPlot
51
- from tomwer.gui.stitching.axisorderedlist import EditableZOrderedTomoObjWidget
52
- from tomwer.gui.stitching.z_stitching.fineestimation import _SliceGetter
53
- from tomwer.gui.configuration.action import (
54
- BasicConfigurationAction,
55
- ExpertConfigurationAction,
56
- MinimalisticConfigurationAction,
57
- )
58
- from tomwer.gui.configuration.level import ConfigurationLevel
59
- from tomwer.gui.stitching import action as stitching_action
60
- from tomwer.gui.stitching.normalization import NormalizationBySampleGroupBox
61
-
62
-
63
- _logger = logging.getLogger(__name__)
64
-
65
-
66
- def convert_str_to_tuple(input_str, none_if_empty):
67
- if input_str is None:
68
- return None
69
- elif isinstance(input_str, (tuple, list)):
70
- return tuple(input_str)
71
- else:
72
- return _convert_str_to_tuple(input_str=input_str, none_if_empty=none_if_empty)
73
-
74
-
75
- class ZStitchingCentralWidget(qt.QWidget):
76
- sigStitchingTypeChanged = qt.Signal(str)
77
- """emit when stitching type changes"""
78
- sigTomoObjsLoaded = qt.Signal(tuple)
79
- """Signal emit when during setting a configuration this trigger some addition of tomo object"""
80
-
81
- class _ZStitchingCentralTabWidget(qt.QTabWidget):
82
- def __init__(self, parent=None) -> None:
83
- super().__init__(parent)
84
- self._serieName = None
85
- self._zOrderedList = EditableZOrderedTomoObjWidget(parent=self)
86
- self.addTab(self._zOrderedList, "axis 0 ordered list")
87
- self._previewPlot = PreviewStitchingPlot(parent=self)
88
- self.addTab(self._previewPlot, "stitching preview")
89
- # TODO: add a raw display to print frame from raw position z positions ...
90
- self._rawDisplayPlot = RawStitchingPlot(
91
- parent=self,
92
- aspectRatio=True,
93
- logScale=False,
94
- copy=False,
95
- save=False,
96
- print_=False,
97
- grid=False,
98
- curveStyle=False,
99
- mask=False,
100
- alpha_values=True,
101
- )
102
- self._rawDisplayPlot.setKeepDataAspectRatio(True)
103
- self._rawDisplayPlot.setAxesDisplayed(False)
104
- self.addTab(self._rawDisplayPlot, "raw display")
105
- # add an option to activate / deactivate auto update of the raw display as it can be time consuming.
106
- raw_display_idx = self.indexOf(self._rawDisplayPlot)
107
- self._rawDisplayCB = qt.QCheckBox(self)
108
- self.tabBar().setTabButton(
109
- raw_display_idx,
110
- qt.QTabBar.LeftSide,
111
- self._rawDisplayCB,
112
- )
113
- self.setTabToolTip(
114
- raw_display_idx,
115
- "If toggled will keep the raw display up to date from axis 0 modifications",
116
- )
117
- # set up: turn overlay one by default
118
- self._previewPlot._backGroundAction.setChecked(True)
119
-
120
- def _handleRawDisplayconnection(self, toggled: bool):
121
- if toggled:
122
- self._connectRawDisplayConnection()
123
- else:
124
- self._disconnectRawDisplayConnection()
125
-
126
- def setSerie(self, serie: Serie):
127
- for elmt in serie:
128
- self._zOrderedList.addTomoObj(elmt)
129
- self.setSerieName(serie.name)
130
-
131
- def addTomoObj(self, tomo_obj: TomwerObject):
132
- self._zOrderedList.addTomoObj(tomo_obj)
133
-
134
- def removeTomoObj(self, tomo_obj: TomwerObject):
135
- self._zOrderedList.removeTomoObj(tomo_obj=tomo_obj)
136
-
137
- def getSerieName(self) -> str:
138
- return self._serieName
139
-
140
- def setSerieName(self, name: str):
141
- self._serieName = name
142
-
143
- def getTomoObjs(self) -> tuple:
144
- return self._zOrderedList.getTomoObjsZOrdered()
145
-
146
- def clearTomoObjs(self):
147
- self._zOrderedList.clearTomoObjs()
148
-
149
- def clean(self) -> None:
150
- self.clearTomoObjs()
151
- self._previewPlot.clear()
152
-
153
- def close(self):
154
- self._previewPlot.close()
155
- # requested for the waiting plot update
156
- super().close()
157
-
158
- def setAddTomoObjCallbacks(self, *args, **kwargs):
159
- self._zOrderedList.setAddTomoObjCallbacks(*args, **kwargs)
160
-
161
- def setRemoveTomoObjCallbacks(self, *args, **kwargs):
162
- self._zOrderedList.setRemoveTomoObjCallbacks(*args, **kwargs)
163
-
164
- def __init__(self, parent=None) -> None:
165
- super().__init__(parent)
166
- self.setLayout(qt.QGridLayout())
167
-
168
- self._stitchingTypeCB = qt.QComboBox(parent=self)
169
- for mode in StitchingType.values():
170
- self._stitchingTypeCB.addItem(mode)
171
- self._stitchingTypeCB.currentIndexChanged.connect(self._stitchingTypeChanged)
172
- self.layout().addWidget(qt.QLabel("stitching method:"), 0, 0, 1, 1)
173
- self.layout().addWidget(self._stitchingTypeCB, 0, 1, 1, 1)
174
-
175
- self._mainWidget = self._ZStitchingCentralTabWidget(parent=self)
176
- self.layout().addWidget(self._mainWidget, 1, 0, 4, 4)
177
-
178
- # set up
179
- self.setStitchingType(self.getStitchingType())
180
- self._mainWidget.setCurrentWidget(self._mainWidget._previewPlot)
181
-
182
- # conenct signal / slot
183
- self._stitchingTypeCB.currentIndexChanged.connect(self._stitchingTypeChanged)
184
-
185
- def close(self):
186
- self._mainWidget.close()
187
- # requested for the waiting plot update
188
- super().close()
189
-
190
- def clean(self):
191
- self._mainWidget.clean()
192
-
193
- def _stitchingTypeChanged(self, *args, **kwargs):
194
- self.sigStitchingTypeChanged.emit(self.getStitchingType().value)
195
-
196
- def getStitchingType(self):
197
- return StitchingType.from_value(self._stitchingTypeCB.currentText())
198
-
199
- def setStitchingType(self, mode):
200
- mode = StitchingType.from_value(mode)
201
- idx = self._stitchingTypeCB.findText(mode.value)
202
- if idx >= 0:
203
- self._stitchingTypeCB.setCurrentIndex(idx)
204
-
205
- def addTomoObj(self, tomo_obj: TomwerObject):
206
- self._mainWidget.addTomoObj(tomo_obj)
207
- self._updatePreviewPixelSize()
208
-
209
- def removeTomoObj(self, tomo_obj: TomwerObject):
210
- self._mainWidget.removeTomoObj(tomo_obj)
211
- self._updatePreviewPixelSize()
212
-
213
- def _updatePreviewPixelSize(self):
214
- """update the pixel size of the preview from existing tomo obj"""
215
-
216
- def get_pixel_size():
217
- tomo_objs = self._mainWidget.getTomoObjs()
218
- for tomo_obj in tomo_objs:
219
- if (
220
- isinstance(tomo_obj, NXtomoScan)
221
- and tomo_obj.x_pixel_size is not None
222
- and tomo_obj.y_pixel_size is not None
223
- ):
224
- return tomo_obj.x_pixel_size, tomo_obj.y_pixel_size
225
- elif (
226
- isinstance(tomo_obj, TomwerVolumeBase)
227
- and tomo_obj.voxel_size is not None
228
- ):
229
- return tomo_obj.voxel_size[1], tomo_obj.voxel_size[2]
230
- return None, None
231
-
232
- pixel_size = get_pixel_size()
233
- self._mainWidget._previewPlot.setPixelSize(pixel_size=pixel_size)
234
-
235
- def getConfiguration(self) -> dict:
236
- # missing parameters:
237
- # * overwrite
238
- # * slices
239
- # * slurm stuff...
240
-
241
- tomo_objs = self._mainWidget.getTomoObjs()
242
-
243
- def filter_empty_list_and_cast_as_int(elmts):
244
- new_list = [int(elmt) for elmt in elmts if elmt is not None]
245
- if len(new_list) == 0:
246
- return None
247
- else:
248
- return elmts
249
-
250
- axis_0_pos_px = filter_empty_list_and_cast_as_int(
251
- [
252
- obj.stitching_metadata.get_abs_position_px(axis=0) or 0
253
- for obj in tomo_objs
254
- ]
255
- )
256
- axis_2_pos_px = filter_empty_list_and_cast_as_int(
257
- [
258
- obj.stitching_metadata.get_abs_position_px(axis=2) or 0
259
- for obj in tomo_objs
260
- ]
261
- )
262
- return {
263
- "stitching": {
264
- "type": self.getStitchingType().value,
265
- "axis_0_pos_px": "" if axis_0_pos_px is None else axis_0_pos_px,
266
- "axis_2_pos_px": "" if axis_2_pos_px is None else axis_2_pos_px,
267
- },
268
- "inputs": {
269
- "input_datasets": [obj.get_identifier().to_str() for obj in tomo_objs],
270
- },
271
- }
272
-
273
- def setConfiguration(self, config: dict) -> None:
274
- stitching_type = config.get("stitching", {}).get("type", None)
275
- if stitching_type is not None:
276
- self.setStitchingType(stitching_type)
277
- tomo_obj_ids = config.get("inputs", {}).get("input_datasets", None)
278
- tomo_obj_ids = identifiers_as_str_to_instances(tomo_obj_ids)
279
- axis_0_pos = convert_str_to_tuple(
280
- config.get("stitching", {}).get("axis_0_pos_px", None),
281
- none_if_empty=True,
282
- )
283
- axis_2_pos = convert_str_to_tuple(
284
- config.get("stitching", {}).get("axis_2_pos_px", None),
285
- none_if_empty=True,
286
- )
287
- if tomo_obj_ids is not None:
288
- self._mainWidget.clearTomoObjs()
289
- if axis_0_pos is None:
290
- axis_0_pos = [None] * len(tomo_obj_ids)
291
- if axis_2_pos is None:
292
- axis_2_pos = [None] * len(tomo_obj_ids)
293
- if len(axis_0_pos) != len(tomo_obj_ids):
294
- _logger.error(
295
- "incoherent axis 0 position compared to the number of input datasets. Will ignore those"
296
- )
297
- axis_0_pos = [None] * len(tomo_obj_ids)
298
- if len(axis_2_pos) != len(tomo_obj_ids):
299
- _logger.error(
300
- "incoherent axis 2 position compared to the number of input datasets. Will ignore those"
301
- )
302
- axis_2_pos = [None] * len(tomo_obj_ids)
303
-
304
- new_tomo_objs = []
305
- for tomo_obj_id, axis_0_v, axis_2_v in zip(
306
- tomo_obj_ids, axis_0_pos, axis_2_pos
307
- ):
308
- if isinstance(tomo_obj_id, TomwerObject):
309
- tomo_obj = tomo_obj_id
310
- elif isinstance(tomo_obj_id, _TomoScanBase):
311
- # for now we need to convert it back because object are not the same
312
- tomo_obj = ScanFactory.create_tomo_object_from_identifier(
313
- tomo_obj_id.get_identifier().to_str()
314
- )
315
- elif isinstance(tomo_obj_id, _VolumeBase):
316
- tomo_obj = VolumeFactory.create_tomo_object_from_identifier(
317
- tomo_obj_id.get_identifier().to_str()
318
- )
319
- else:
320
- tomo_obj = ScanFactory.create_tomo_object_from_identifier(
321
- tomo_obj_id
322
- )
323
- self.addTomoObj(tomo_obj=tomo_obj)
324
- # set metadata information if any
325
- for axis, axis_value in zip((0, 2), (axis_0_v, axis_2_v)):
326
- if axis_value is not None:
327
- tomo_obj.stitching_metadata.setPxPos(int(axis_value), axis=axis)
328
- new_tomo_objs.append(tomo_obj)
329
- self.sigTomoObjsLoaded.emit(tuple(new_tomo_objs))
330
-
331
- # expose API
332
- def setAddTomoObjCallbacks(self, *args, **kwargs):
333
- self._mainWidget.setAddTomoObjCallbacks(*args, **kwargs)
334
-
335
- def setRemoveTomoObjCallbacks(self, *args, **kwargs):
336
- self._mainWidget.setRemoveTomoObjCallbacks(*args, **kwargs)
337
-
338
-
339
- class ZStitchingWindow(qt.QMainWindow):
340
- """
341
- Main widget containing all the options to define the stitching to be done
342
-
343
- :param bool with_configuration_action: if True append the load and save stitching configuration tool button.
344
- In some cases those can also be part of Menu so we want to avoid having those twice
345
- """
346
-
347
- sigChanged = qt.Signal()
348
- """Signal emit each time the configuration is modified"""
349
-
350
- def __init__(self, parent=None, with_configuration_action=True) -> None:
351
- super().__init__(parent)
352
- self._previewFolder = None
353
- # folder to store files (volume or NXtomo) for previews
354
- self._previewThread = None
355
- # thread to compute the stitching for preview
356
- self._callbackToGetSlurmConfig = None
357
- self._callbackToSetSlurmConfig = None
358
- # convenient work arounds to avoid having to redefine the n=interface for slurm and the API
359
- # to load and save settings
360
- # if it is defined upper
361
-
362
- toolbar = qt.QToolBar(self)
363
- self.addToolBar(qt.Qt.TopToolBarArea, toolbar)
364
- style = qt.QApplication.instance().style()
365
-
366
- # clean option
367
- self.__cleanAction = qt.QAction(self)
368
- self.__cleanAction.setToolTip("clear")
369
- clear_icon = style.standardIcon(qt.QStyle.SP_DialogResetButton)
370
- self.__cleanAction.setIcon(clear_icon)
371
- toolbar.addAction(self.__cleanAction)
372
- self.__cleanAction.triggered.connect(self.clean)
373
-
374
- # separator
375
- toolbar.addSeparator()
376
-
377
- if with_configuration_action:
378
- # load action
379
- self.__loadAction = stitching_action.LoadConfigurationAction(self)
380
- toolbar.addAction(self.__loadAction)
381
- self.__loadAction.triggered.connect(
382
- functools.partial(self._loadSettings, file_path=None)
383
- )
384
-
385
- # save action
386
- self.__saveAction = stitching_action.SaveConfigurationAction(self)
387
- toolbar.addAction(self.__saveAction)
388
- self.__saveAction.triggered.connect(
389
- functools.partial(self._saveSettings, file_path=None)
390
- )
391
-
392
- # separator
393
- toolbar.addSeparator()
394
-
395
- # update preview action
396
- self.__updatePreviewAction = stitching_action.PreviewAction(self)
397
- toolbar.addAction(self.__updatePreviewAction)
398
- self.__updatePreviewAction.triggered.connect(self._trigger_update_preview)
399
-
400
- # separator
401
- toolbar.addSeparator()
402
-
403
- # configuration level / mode
404
- self.__configurationModesAction = qt.QAction(self)
405
- self.__configurationModesAction.setCheckable(False)
406
- menu = qt.QMenu(self)
407
- self.__configurationModesAction.setMenu(menu)
408
- toolbar.addAction(self.__configurationModesAction)
409
-
410
- self.__configurationModesGroup = qt.QActionGroup(self)
411
- self.__configurationModesGroup.setExclusive(True)
412
- self.__configurationModesGroup.triggered.connect(self._userModeChanged)
413
-
414
- self._minimalisticAction = MinimalisticConfigurationAction(toolbar)
415
- menu.addAction(self._minimalisticAction)
416
- self.__configurationModesGroup.addAction(self._minimalisticAction)
417
- self._basicConfigAction = BasicConfigurationAction(toolbar)
418
- menu.addAction(self._basicConfigAction)
419
- self.__configurationModesGroup.addAction(self._basicConfigAction)
420
- self._expertConfiguration = ExpertConfigurationAction(toolbar)
421
- menu.addAction(self._expertConfiguration)
422
- self.__configurationModesGroup.addAction(self._expertConfiguration)
423
-
424
- # separator
425
- toolbar.addSeparator()
426
-
427
- # create central widget
428
- self._widget = ZStitchingCentralWidget(parent=self)
429
- self.setCentralWidget(self._widget)
430
-
431
- # create Dock widgets
432
- ## output
433
- self._outputWidget = StitchingOutput(parent=self)
434
- self._outputWidget.setObjectName("outputSettingsWidget")
435
- self._outputDockWidget = qt.QDockWidget(parent=self)
436
- self._outputDockWidget.setWindowTitle("output")
437
- self._outputDockWidget.layout().setContentsMargins(0, 0, 0, 0)
438
- self._outputDockWidget.setFeatures(qt.QDockWidget.DockWidgetMovable)
439
- self._outputDockWidget.setWidget(self._outputWidget)
440
- self.addDockWidget(qt.Qt.RightDockWidgetArea, self._outputDockWidget)
441
- self._outputDockWidget.setToolTip(
442
- "options to where and how to save the stitching"
443
- )
444
- ## stitching strategies
445
- self._stitchingOptsWidget = StitchingOptions(parent=self)
446
- self._stitchingOptsScrollArea = qt.QScrollArea(self)
447
- self._stitchingOptsScrollArea.setWidget(self._stitchingOptsWidget)
448
- self._stitchingOptsScrollArea.setWidgetResizable(True)
449
- self._stitchingOptsScrollArea.setHorizontalScrollBarPolicy(
450
- qt.Qt.ScrollBarAlwaysOff
451
- )
452
- self._stitchingOptsDockWidget = qt.QDockWidget(parent=self)
453
- self._stitchingOptsDockWidget.layout().setContentsMargins(0, 0, 0, 0)
454
- self._stitchingOptsDockWidget.setFeatures(qt.QDockWidget.DockWidgetMovable)
455
- self._stitchingOptsDockWidget.setWidget(self._stitchingOptsScrollArea)
456
- self._stitchingOptsDockWidget.setWindowTitle("processing options")
457
- self.addDockWidget(qt.Qt.RightDockWidgetArea, self._stitchingOptsDockWidget)
458
-
459
- ## all scan z positions
460
- self._editTomoObjAxis0PositionsWidget = PosEditorOverOneAxis(
461
- parent=self,
462
- axis_edited=0,
463
- axis_order=0,
464
- )
465
- self._editTomoObjAxis0PositionsDockWidget = qt.QDockWidget(parent=self)
466
- self._editTomoObjAxis0PositionsDockWidget.layout().setContentsMargins(
467
- 0, 0, 0, 0
468
- )
469
- self._editTomoObjAxis0PositionsDockWidget.setFeatures(
470
- qt.QDockWidget.DockWidgetMovable
471
- )
472
- self._editTomoObjAxis0PositionsDockWidget.setWidget(
473
- self._editTomoObjAxis0PositionsWidget
474
- )
475
- self._editTomoObjAxis0PositionsDockWidget.setWindowTitle(
476
- "edit positions over axis 0 (px) - aka z"
477
- )
478
- self._editTomoObjAxis0PositionsDockWidget.setToolTip(
479
- "This allows to edit tomo objects positions along the axis 0 (also aka z)"
480
- )
481
- self.addDockWidget(
482
- qt.Qt.RightDockWidgetArea, self._editTomoObjAxis0PositionsDockWidget
483
- )
484
- ### add a check box to update position from preview if asked by the user
485
- self._updateAxis0PosFromPreviewCalc = qt.QCheckBox(
486
- "update position 0 from preview calc",
487
- self,
488
- )
489
- self._updateAxis0PosFromPreviewCalc.setToolTip(
490
- "When the user trigger a preview, if some shift search refined over axis 0 is done then will update the axis 0 positions",
491
- )
492
- self._updateAxis0PosFromPreviewCalc.setChecked(True)
493
- self._editTomoObjAxis0PositionsWidget.layout().insertWidget(
494
- 0, self._updateAxis0PosFromPreviewCalc
495
- )
496
-
497
- ## all scan axis 2 positions
498
- self._editTomoObjAxis2PositionsWidget = PosEditorOverOneAxis(
499
- parent=self,
500
- axis_edited=2,
501
- axis_order=0,
502
- )
503
- self._editTomoObjAxis2PositionsDockWidget = qt.QDockWidget(parent=self)
504
- self._editTomoObjAxis2PositionsDockWidget.layout().setContentsMargins(
505
- 0, 0, 0, 0
506
- )
507
- self._editTomoObjAxis2PositionsDockWidget.setFeatures(
508
- qt.QDockWidget.DockWidgetMovable
509
- )
510
- self._editTomoObjAxis2PositionsDockWidget.setWidget(
511
- self._editTomoObjAxis2PositionsWidget
512
- )
513
- self._editTomoObjAxis2PositionsDockWidget.setWindowTitle(
514
- "edit positions over axis 2 (px)"
515
- )
516
- self._editTomoObjAxis2PositionsDockWidget.setToolTip(
517
- "This allows to edit tomo objects positions along the axis 2"
518
- )
519
- self.addDockWidget(
520
- qt.Qt.RightDockWidgetArea, self._editTomoObjAxis2PositionsDockWidget
521
- )
522
- ### add a check box to update position from preview if asked by the user
523
- self._updateAxis2PosFromPreviewCalc = qt.QCheckBox(
524
- "update position 2 from preview calc", self
525
- )
526
- self._updateAxis2PosFromPreviewCalc.setToolTip(
527
- "When the user trigger a preview, if some shift search refined over axis 2 is done then will update the axis 2 positions"
528
- )
529
- self._updateAxis2PosFromPreviewCalc.setChecked(True)
530
- self._editTomoObjAxis2PositionsWidget.layout().insertWidget(
531
- 0, self._updateAxis2PosFromPreviewCalc
532
- )
533
-
534
- self._widget.setAddTomoObjCallbacks(
535
- (
536
- self._editTomoObjAxis0PositionsWidget.addTomoObj,
537
- self._editTomoObjAxis2PositionsWidget.addTomoObj,
538
- self.getRawDisplayPlot().addTomoObj,
539
- )
540
- )
541
- self._widget.setRemoveTomoObjCallbacks(
542
- (
543
- self._editTomoObjAxis0PositionsWidget.removeTomoObj,
544
- self._editTomoObjAxis2PositionsWidget.removeTomoObj,
545
- self.getRawDisplayPlot().removeTomoObj,
546
- )
547
- )
548
-
549
- # update layout: for now lets tabify sime widget
550
- self.tabifyDockWidget(self._outputDockWidget, self._stitchingOptsDockWidget)
551
- self.tabifyDockWidget(
552
- self._outputDockWidget, self._editTomoObjAxis2PositionsDockWidget
553
- )
554
- self.tabifyDockWidget(
555
- self._outputDockWidget, self._editTomoObjAxis0PositionsDockWidget
556
- )
557
-
558
- # handle raw display plot. By display avoid displaying raw data as this can be ressource consuming
559
- self._widget._mainWidget._rawDisplayCB.setChecked(False)
560
- self._widget._mainWidget._rawDisplayPlot.setActive(False)
561
-
562
- # connect signal / slot
563
- self._widget._mainWidget._rawDisplayCB.toggled.connect(
564
- self._handleRawDisplayconnection
565
- )
566
- self._outputWidget.sigChanged.connect(self._changed)
567
- self._stitchingOptsWidget.sigChanged.connect(self._changed)
568
- self._widget.sigStitchingTypeChanged.connect(
569
- self._outputWidget._updateOutputForStitchingType
570
- )
571
- self._widget.sigStitchingTypeChanged.connect(
572
- self._stitchingOptsWidget._stitchingTypeChanged
573
- )
574
-
575
- ## handle raw plot preview
576
- self._stitchingOptsWidget.sigFlipLRChanged.connect(
577
- self.getRawDisplayPlot().setFlipLRFrames
578
- )
579
- self._stitchingOptsWidget.sigFlipUDChanged.connect(
580
- self.getRawDisplayPlot().setFlipUDFrames
581
- )
582
- self._stitchingOptsWidget.sigSliceForPreviewChanged.connect(
583
- self.getRawDisplayPlot().setSliceForPreview
584
- )
585
-
586
- ## handle tomo obj loading from settings
587
- self._widget.sigTomoObjsLoaded.connect(
588
- self._editTomoObjAxis0PositionsWidget.setTomoObjs
589
- )
590
- self._widget.sigTomoObjsLoaded.connect(
591
- self._editTomoObjAxis2PositionsWidget.setTomoObjs
592
- )
593
- self._widget.sigTomoObjsLoaded.connect(self.getRawDisplayPlot().setTomoObjs)
594
-
595
- # set up
596
- self._basicConfigAction.setChecked(True)
597
- self._userModeChanged(self._basicConfigAction)
598
-
599
- def setCallbackToGetSlurmConfig(self, callback):
600
- self._callbackToGetSlurmConfig = callback
601
-
602
- def setCallbackToSetSlurmConfig(self, callback):
603
- self._callbackToSetSlurmConfig = callback
604
-
605
- def close(self):
606
- # remove folder used for preview
607
- shutil.rmtree(self._previewFolder, ignore_errors=True)
608
- self._widget.close()
609
- # requested for the waiting plot update
610
- super().close()
611
-
612
- def getRawDisplayPlot(self):
613
- return self._widget._mainWidget._rawDisplayPlot
614
-
615
- def _handleRawDisplayconnection(self, toggled: bool):
616
- raw_display_plot = self.getRawDisplayPlot()
617
- raw_display_plot.setActive(toggled)
618
-
619
- def getPreviewAction(self):
620
- return self.__updatePreviewAction
621
-
622
- def getPreviewFolder(self):
623
- if self._previewFolder is None:
624
- self._previewFolder = tempfile.mkdtemp(prefix="tomwer_stitcher_preview")
625
- return self._previewFolder
626
-
627
- def getVolumeIdentifierPreview(self) -> HDF5VolumeIdentifier:
628
- folder = self.getPreviewFolder()
629
- # for now use hdf5 by default
630
- return HDF5VolumeIdentifier(
631
- object=HDF5Volume,
632
- hdf5_file=os.path.join(folder, "vol_stitching_preview.hdf5"),
633
- entry="my_volume",
634
- )
635
-
636
- def getNXtomoIdentifierForPreview(self):
637
- folder = self.getPreviewFolder()
638
- return NXtomoScanIdentifier(
639
- object=NXtomoScan,
640
- hdf5_file=os.path.join(folder, "nxtomo_stiching_preview.hdf5"),
641
- entry="entry0000",
642
- )
643
-
644
- def _changed(self, *args, **kwargs):
645
- self.sigChanged.emit()
646
-
647
- def _saveSettings(self, file_path=None, **kwargs):
648
- """
649
- dump current configuration into a txt file
650
- """
651
- # get a file if necessary
652
- if file_path is None:
653
- dialog = QConfigFileDialog(self)
654
- dialog.setAcceptMode(qt.QFileDialog.AcceptSave)
655
- if not dialog.exec_():
656
- return
657
-
658
- selected_file = dialog.selectedFiles()
659
- if len(selected_file) == 0:
660
- return
661
- file_path = selected_file[0]
662
-
663
- configuration = self.getConfiguration()
664
- if self._callbackToGetSlurmConfig is not None:
665
- slurm_config = {"slurm": self._callbackToGetSlurmConfig()}
666
- configuration = concatenate_dict(configuration, slurm_config)
667
-
668
- # dump configuration
669
- generate_nabu_configfile(
670
- fname=file_path,
671
- default_config=get_default_stitching_config(self.getStitchingType()),
672
- comments=True,
673
- sections_comments=_SECTIONS_COMMENTS,
674
- options_level="advanced",
675
- prefilled_values=configuration,
676
- )
677
-
678
- def _loadSettings(self, file_path=None, **kwargs):
679
- """
680
- load configuration from a txt file
681
- """
682
- # get a file if necessary
683
- if file_path is None:
684
- dialog = QConfigFileDialog(self)
685
- dialog.setAcceptMode(qt.QFileDialog.AcceptOpen)
686
- dialog.setFileMode(qt.QFileDialog.ExistingFiles)
687
-
688
- if not dialog.exec_():
689
- return
690
-
691
- selected_file = dialog.selectedFiles()
692
- if len(selected_file) == 0:
693
- return
694
- file_path = selected_file[0]
695
-
696
- # Do configuration load
697
- conf_dict = parse_nabu_config_file(file_path, allow_no_value=True)
698
- self.setConfiguration(config=conf_dict)
699
- if self._callbackToSetSlurmConfig is not None:
700
- self._callbackToSetSlurmConfig(conf_dict.get("slurm", {}))
701
-
702
- def _trigger_update_preview(self):
703
- if self._previewThread is not None:
704
- _logger.warning(
705
- "some preview is already running. Please wait before relaunching it"
706
- )
707
- return
708
- config = self.getConfiguration()
709
- # update the output file to set if from raw...
710
- stitching_type = config.get("stitching", {}).get("type", None)
711
- if stitching_type == "z-preproc":
712
- output_identifier = self.getNXtomoIdentifierForPreview()
713
- config["z-preproc"]["location"] = output_identifier.file_path
714
- config["z-preproc"]["data_path"] = output_identifier.data_path
715
- assert "z-postproc" not in config
716
- elif stitching_type == "z-postproc":
717
- config["z-postproc"][
718
- "output_volume"
719
- ] = self.getVolumeIdentifierPreview().to_str()
720
- assert "z-preproc" not in config
721
- else:
722
- raise NotImplementedError
723
-
724
- # update the slice to avoid doing the stitching on all the frames
725
- config["inputs"]["slices"] = self.getSlicesForPreview()
726
-
727
- # update to force overwrite
728
- config["output"]["overwrite_results"] = True
729
-
730
- # clean current preview to notify some calculation is going on
731
- preview_plot = self._widget._mainWidget._previewPlot
732
- preview_plot._waitingOverlay.show()
733
-
734
- # start sitching on a thread
735
- self._previewThread = PreviewThread(stitching_config=config)
736
- self._previewThread.finished.connect(self._previewCalculationFinished)
737
- self._previewThread.start()
738
-
739
- def getSlicesForPreview(self):
740
- return self._stitchingOptsWidget.getSlicesForPreview()
741
-
742
- def _previewCalculationFinished(self):
743
- sender = self.sender()
744
- assert isinstance(sender, PreviewThread)
745
- composition = sender.frame_composition
746
- tomo_objs_new_axis_positions = sender.final_tomo_objs_positions
747
- assert isinstance(
748
- tomo_objs_new_axis_positions, dict
749
- ), "final_tomo_objs_positions is expected to be a dict with obj identifier as key and the tuple of position as value"
750
- # 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
751
- output_obj_identifier = sender.output_identifier
752
-
753
- preview_plot = self._widget._mainWidget._previewPlot
754
- preview_plot._waitingOverlay.hide()
755
-
756
- self._previewThread.finished.disconnect(self._previewCalculationFinished)
757
- self._previewThread = None
758
-
759
- if output_obj_identifier is None:
760
- _logger.error("preview of stitching failed")
761
- else:
762
- preview_plot.setStitchedTomoObj(
763
- tomo_obj_id=output_obj_identifier,
764
- composition=composition,
765
- )
766
-
767
- # update object values if requested
768
- update_requested = {
769
- 0: self._updateAxis0PosFromPreviewCalc.isChecked(),
770
- 2: self._updateAxis2PosFromPreviewCalc.isChecked(),
771
- }
772
-
773
- if update_requested[0] or update_requested[2]:
774
- existing_tomo_obj = {
775
- tomo_obj.get_identifier().to_str(): tomo_obj
776
- for tomo_obj in self._widget._mainWidget.getTomoObjs()
777
- }
778
-
779
- for tomo_obj_id, value in tomo_objs_new_axis_positions.items():
780
- assert (
781
- isinstance(value, tuple) and len(value) == 3
782
- ), "value is expected to be (new_pos_axis_0, new_pos_axis_1, new_pos_axis_2)"
783
- new_axis_0_pos, _, new_axis_2_pos = value
784
- tomo_obj = existing_tomo_obj.get(tomo_obj_id, None)
785
- if tomo_obj is None:
786
- continue
787
- if update_requested[0]:
788
- tomo_obj.stitching_metadata.setPxPos(int(new_axis_0_pos), 0)
789
- if update_requested[2]:
790
- tomo_obj.stitching_metadata.setPxPos(int(new_axis_2_pos), 2)
791
- if update_requested[0]:
792
- self._editTomoObjAxis0PositionsWidget._orderedMightHavechanged(
793
- force_sb_update=True
794
- )
795
- if update_requested[2]:
796
- self._editTomoObjAxis2PositionsWidget._orderedMightHavechanged(
797
- force_sb_update=True
798
- )
799
-
800
- def clean(self):
801
- self._widget.clean()
802
- self._editTomoObjAxis0PositionsWidget.clean()
803
- self._editTomoObjAxis2PositionsWidget.clean()
804
-
805
- def setSerie(self, serie):
806
- self.clean()
807
- self._widget._mainWidget.setSerie(serie)
808
- self._editTomoObjAxis0PositionsWidget.clean()
809
- self._editTomoObjAxis2PositionsWidget.clean()
810
- for tomo_obj in serie:
811
- self._editTomoObjAxis0PositionsWidget.addTomoObj(tomo_obj)
812
- self._editTomoObjAxis2PositionsWidget.addTomoObj(tomo_obj)
813
- self.getRawDisplayPlot().setTomoObjs(tomo_objs=serie[:])
814
-
815
- def addTomoObj(self, tomo_obj):
816
- self._widget.addTomoObj(tomo_obj)
817
- self._editTomoObjAxis0PositionsWidget.addTomoObj(tomo_obj)
818
- self._editTomoObjAxis2PositionsWidget.addTomoObj(tomo_obj)
819
- self.getRawDisplayPlot().addTomoObj(tomo_obj=tomo_obj)
820
-
821
- def removeTomoObj(self, tomo_obj):
822
- self._widget.removeTomoObj(tomo_obj)
823
- self.getRawDisplayPlot().removeTomoObj(tomo_obj=tomo_obj)
824
-
825
- def getConfiguration(self) -> dict:
826
- # make sure the sync is fine between the two
827
- configs = (
828
- self._widget.getConfiguration(),
829
- self._outputWidget.getConfiguration(),
830
- self._stitchingOptsWidget.getConfiguration(),
831
- )
832
- result = {}
833
- for config in configs:
834
- result = concatenate_dict(result, config)
835
- return result
836
-
837
- def setConfiguration(self, config: dict):
838
- self._widget.setConfiguration(config)
839
- self._outputWidget.setConfiguration(config)
840
- self._stitchingOptsWidget.setConfiguration(config)
841
-
842
- # expose API
843
- def getStitchingType(self) -> StitchingType:
844
- return self._widget.getStitchingType()
845
-
846
- def setStitchingType(self, stitching_type: StitchingType):
847
- self._widget.setStitchingType(stitching_type)
848
-
849
- def _userModeChanged(self, action):
850
- self.__configurationModesAction.setIcon(action.icon())
851
- self.__configurationModesAction.setToolTip(action.tooltip())
852
- if action is self._basicConfigAction:
853
- level = ConfigurationLevel.OPTIONAL
854
- elif action is self._expertConfiguration:
855
- level = ConfigurationLevel.ADVANCED
856
- else:
857
- level = ConfigurationLevel.REQUIRED
858
- self._stitchingOptsWidget.setConfigurationLevel(level)
859
- self._editTomoObjAxis2PositionsDockWidget.setVisible(
860
- level >= ConfigurationLevel.ADVANCED
861
- )
862
-
863
-
864
- def concatenate_dict(dict_1, dict_2) -> dict:
865
- """update dict which has dict as values. And we want concatenate those values to"""
866
- res = dict_1.copy()
867
- for key in dict_2:
868
- if key in dict_1:
869
- if key in [f"axis_{axis}_params" for axis in (0, 1, 2)]:
870
- res[key] = ";".join((dict_1[key], dict_2[key]))
871
- elif isinstance(dict_1[key], dict):
872
- res[key] = concatenate_dict(dict_1=dict_1[key], dict_2=dict_2[key])
873
- else:
874
- res[key].update(dict_2[key])
875
- else:
876
- res[key] = dict_2[key]
877
- return res
878
-
879
-
880
- class PreviewThread(qt.QThread):
881
- """
882
- Thread to compute an overview of the stitching
883
- """
884
-
885
- def __init__(self, stitching_config: dict, *args, **kwargs) -> None:
886
- super().__init__(*args, **kwargs)
887
- self._stitching_config = dict_to_config_obj(stitching_config)
888
- self._output_identifier = None
889
- self._frame_composition = None
890
- self._final_tomo_objs_positions = None
891
- # store position of all the tomo objects (scan, volumes) used for the final stitching (after shift refinement)
892
-
893
- @property
894
- def stiching_config(self):
895
- return self._stitching_config
896
-
897
- @property
898
- def output_identifier(self):
899
- return self._output_identifier
900
-
901
- @property
902
- def frame_composition(self):
903
- return self._frame_composition
904
-
905
- @property
906
- def final_tomo_objs_positions(self) -> dict:
907
- """
908
- :return: dict with tomo object identifier (str) as key and a tuple of position in pixel (axis_0_pos, axis_1_pos, axis_2_pos)
909
- :rtype: dict
910
- """
911
- return self._final_tomo_objs_positions
912
-
913
- @property
914
- def final_axis_2_pos(self):
915
- return self._final_axis_2_pos
916
-
917
- def run(self):
918
- stitching_type = self.stiching_config.stitching_type
919
- if stitching_type.value == "z-preproc":
920
- stitcher = PreProcessZStitcher(configuration=self.stiching_config)
921
- elif stitching_type.value == "z-postproc":
922
- stitcher = PostProcessZStitcher(configuration=self.stiching_config)
923
- else:
924
- raise NotImplementedError
925
- self._output_identifier = stitcher.stitch()
926
- if self._output_identifier is not None:
927
- self._output_identifier = self._output_identifier.to_str()
928
- # store in cache the frame composition to be able to provide them to the PreviewPlot
929
- self._frame_composition = stitcher.frame_composition
930
- self._final_tomo_objs_positions = stitcher.get_final_axis_positions_in_px()
931
-
932
-
933
- class _SlicesSelector(qt.QGroupBox):
934
- """
935
- Widget to determine the slices values (to be stitched)
936
- """
937
-
938
- def __init__(self, parent=None) -> None:
939
- super().__init__("slices", parent)
940
- # start interface
941
- self.setLayout(qt.QHBoxLayout())
942
- self._startSliceCB = qt.QCheckBox("start", self)
943
- self.layout().addWidget(self._startSliceCB)
944
- self._startSliceSB = qt.QSpinBox(self)
945
- self._startSliceSB.setMinimum(0)
946
- self._startSliceSB.setMaximum(9999999)
947
- self._startSliceSB.setValue(0)
948
- self.layout().addWidget(self._startSliceSB)
949
- # stop interface
950
- self._stopSliceCB = qt.QCheckBox("stop", self)
951
- self.layout().addWidget(self._stopSliceCB)
952
- self._stopSliceSB = qt.QSpinBox(self)
953
- self._stopSliceSB.setMinimum(-1)
954
- self._stopSliceSB.setMaximum(9999999)
955
- self._stopSliceSB.setValue(-1)
956
- self.layout().addWidget(self._stopSliceSB)
957
- # step interface
958
- self._stepSliceLabel = qt.QLabel("step", self)
959
- self.layout().addWidget(self._stepSliceLabel)
960
- self._stepSliceSB = qt.QSpinBox(self)
961
- self._stepSliceSB.setMinimum(1)
962
- self._stepSliceSB.setMaximum(9999999)
963
- self._stepSliceSB.setValue(1)
964
- self.layout().addWidget(self._stepSliceSB)
965
-
966
- # connect signal / slot
967
- self._startSliceCB.toggled.connect(self._startSliceSB.setDisabled)
968
- self._stopSliceCB.toggled.connect(self._stopSliceSB.setDisabled)
969
-
970
- self._startSliceCB.setChecked(True)
971
- self._stopSliceCB.setChecked(True)
972
-
973
- def getSlices(self) -> tuple:
974
- if self._startSliceCB.isChecked():
975
- start = 0
976
- else:
977
- start = self._startSliceSB.value()
978
- if self._stopSliceCB.isChecked():
979
- stop = -1
980
- else:
981
- stop = self._stopSliceSB.value()
982
- step = self._stepSliceSB.value()
983
- return (start, stop, step)
984
-
985
- def setSlices(self, start: int, stop: int, step: Optional[int] = None):
986
- start = int(start)
987
- stop = int(stop)
988
- if start == 0:
989
- self._startSliceCB.setChecked(True)
990
- else:
991
- self._startSliceCB.setChecked(False)
992
- self._startSliceSB.setValue(start)
993
-
994
- if stop == -1:
995
- self._stopSliceCB.setChecked(True)
996
- else:
997
- self._stopSliceCB.setChecked(False)
998
- self._stopSliceSB.setValue(stop)
999
-
1000
- if step is not None:
1001
- self._stepSliceSB.setValue(int(step))
1002
-
1003
-
1004
- class _AlignmentGroupBox(qt.QGroupBox):
1005
- DEFAULT_PAD_MODE = "constant"
1006
-
1007
- ALIGNMENT_DOC = (
1008
- "https://tomotools.gitlab-pages.esrf.fr/nabu/stitching/alignment.html"
1009
- )
1010
-
1011
- DEFAULT_ALIGNMENT_AXIS_1 = AlignmentAxis1.CENTER
1012
- DEFAULT_ALIGNMENT_AXIS_2 = AlignmentAxis2.CENTER
1013
-
1014
- _PAD_MODES = (
1015
- "constant", # Pads with a constant value.
1016
- "edge", # Pads with the edge values of array.
1017
- "linear_ramp", # Pads with the linear ramp between end_value and the array edge value.
1018
- "maximum", # Pads with the maximum value of all or part of the vector along each axis.
1019
- "mean", # Pads with the mean value of all or part of the vector along each axis.
1020
- "median", # Pads with the median value of all or part of the vector along each axis.
1021
- "minimum", # Pads with the minimum value of all or part of the vector along each axis.
1022
- "reflect", # Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis.
1023
- "symmetric", # Pads with the reflection of the vector mirrored along the edge of the array.
1024
- "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.
1025
- )
1026
-
1027
- def __init__(self, parent: qt.QWidget = None, title="alignment") -> None:
1028
- super().__init__(title, parent)
1029
- self.setLayout(qt.QFormLayout())
1030
-
1031
- # alignment axis 1
1032
- self._alignmentAxis1CB = qt.QComboBox(self)
1033
- for alignment in AlignmentAxis1.values():
1034
- self._alignmentAxis1CB.addItem(alignment)
1035
- self.layout().addRow("Axis 1 alignment", self._alignmentAxis1CB)
1036
- self._alignmentAxis1CB.setToolTip(
1037
- 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."
1038
- )
1039
-
1040
- # alignment axis 2
1041
- self._alignmentAxis2CB = qt.QComboBox(self)
1042
- for alignment in AlignmentAxis2.values():
1043
- self._alignmentAxis2CB.addItem(alignment)
1044
- self.layout().addRow("Axis 2 alignment", self._alignmentAxis2CB)
1045
- self._alignmentAxis2CB.setToolTip(
1046
- f"Alignment to do in case of frames with different size over axis 2. See {self.ALIGNMENT_DOC} for details."
1047
- )
1048
-
1049
- # pad mode
1050
- self._padModeCB = qt.QComboBox(self)
1051
- for pad_mode in self._PAD_MODES:
1052
- self._padModeCB.addItem(pad_mode)
1053
- self.layout().addRow("pad mode", self._padModeCB)
1054
- self._padModeCB.setToolTip("padding mode to apply for alignment")
1055
-
1056
- # set up
1057
- self.setAlignmentAxis1(self.DEFAULT_ALIGNMENT_AXIS_1)
1058
- self.setAlignmentAxis2(self.DEFAULT_ALIGNMENT_AXIS_2)
1059
-
1060
- def getAlignmentAxis1(self) -> AlignmentAxis1:
1061
- return AlignmentAxis1.from_value(self._alignmentAxis1CB.currentText())
1062
-
1063
- def setAlignmentAxis1(self, alignment: AlignmentAxis1):
1064
- alignment = AlignmentAxis1.from_value(alignment)
1065
- self._alignmentAxis1CB.setCurrentIndex(
1066
- self._alignmentAxis1CB.findText(alignment.value)
1067
- )
1068
-
1069
- def getAlignmentAxis2(self) -> AlignmentAxis2:
1070
- return AlignmentAxis2.from_value(self._alignmentAxis2CB.currentText())
1071
-
1072
- def setAlignmentAxis2(self, alignment: AlignmentAxis2):
1073
- alignment = AlignmentAxis2.from_value(alignment)
1074
- self._alignmentAxis2CB.setCurrentIndex(
1075
- self._alignmentAxis2CB.findText(alignment.value)
1076
- )
1077
-
1078
- def getPadMode(self) -> str:
1079
- return self._padModeCB.currentText()
1080
-
1081
- def setPadMode(self, pad_mode: str):
1082
- idx = self._padModeCB.findText(pad_mode)
1083
- if idx >= 0:
1084
- self._padModeCB.setCurrentIndex(idx)
1085
-
1086
- def setAlignmentAxis1Enabled(self, enabled: bool):
1087
- self._alignmentAxis1CB.setEnabled(enabled)
1088
-
1089
-
1090
- class StitchingOptions(qt.QWidget):
1091
- """
1092
- Widget to let the user define the different options for z-stitching such as which algorithm to search shift,
1093
- which stitching strategy...
1094
- """
1095
-
1096
- sigChanged = qt.Signal()
1097
- """Signal emit when the options change"""
1098
- sigSliceForPreviewChanged = qt.Signal(object)
1099
- """Signal emit when the slice requested for the preview has changed. Parameter is a str or an int"""
1100
- sigFlipLRChanged = qt.Signal(bool)
1101
- """Signal emit when the request to flip LR frame has changed"""
1102
- sigFlipUDChanged = qt.Signal(bool)
1103
- """Signal emit when the request to flip UD frame has changed"""
1104
-
1105
- def __init__(self, parent=None, *args, **kwargs) -> None:
1106
- super().__init__(parent, *args, **kwargs)
1107
- # stitching strategy (aka stitcher behavior)
1108
- self.setLayout(qt.QFormLayout())
1109
- self._stitchingStrategiesWidget = StitchingStrategies(parent=self)
1110
- self._stitchingStrategiesWidget.setObjectName("strategy")
1111
- self.layout().addRow(self._stitchingStrategiesWidget)
1112
- # slice for preview
1113
- self._previewSlices = _SliceGetter("middle", parent=self)
1114
- self._previewSlices.setPlaceholderText(
1115
- "slice index or one of ('middle', 'first', 'last')"
1116
- )
1117
- self._previewSlices.setToolTip(
1118
- "expects a slice index (int > 0) or a str in ('first', 'middle', 'last')"
1119
- )
1120
- self.layout().addRow("slice for preview", self._previewSlices)
1121
-
1122
- # invert frame up - down
1123
- self._flipLR_CB = qt.QCheckBox("flip frame left-right", self)
1124
- self._flipLR_CB.setChecked(False)
1125
- self._flipLR_CB.setToolTip(
1126
- "Flip frame for stitching. This is mostly for volume has no metadata are existing to specify the direction of the frame"
1127
- )
1128
- self.layout().addRow(self._flipLR_CB)
1129
- # invert frame left-right
1130
- self._flipUD_CB = qt.QCheckBox("flip frame up-down", self)
1131
- self._flipUD_CB.setChecked(False)
1132
- self._flipUD_CB.setToolTip(
1133
- "Flip frame for stitching. This is mostly for volume has no metadata are existing to specify the direction of the frame"
1134
- )
1135
- self.layout().addRow(self._flipUD_CB)
1136
-
1137
- # alignment options
1138
- self._alignmentGroup = _AlignmentGroupBox(self)
1139
- self.layout().addRow(self._alignmentGroup)
1140
-
1141
- # slices to be reconstructed
1142
- self._slices = _SlicesSelector(parent=self)
1143
- self._slices.setToolTip(
1144
- "for pre processing stitchting those are projections and for post prcessing stitching those are volume slices"
1145
- )
1146
- self.layout().addRow(self._slices)
1147
-
1148
- # axis 0 params for shift search
1149
- self._axis0Group = qt.QGroupBox("axis 0 (aka z)", self)
1150
- self._axis0Group.setLayout(qt.QVBoxLayout())
1151
- self.layout().addRow(self._axis0Group)
1152
- self._axis0ShiftSearchParams = StitcherAxisParams(axis=0, parent=self)
1153
- self._axis0ShiftSearchParams.layout().setContentsMargins(0, 0, 0, 0)
1154
- self._axis0ShiftSearchParams.layout().setSpacing(0)
1155
- self._axis0Group.layout().addWidget(self._axis0ShiftSearchParams)
1156
-
1157
- # axis 2 params for shift search
1158
- self._axis2Group = qt.QGroupBox("axis 2 (aka x)", self)
1159
- self._axis2Group.setLayout(qt.QVBoxLayout())
1160
- self.layout().addRow(self._axis2Group)
1161
- self._axis2ShiftSearchParams = StitcherAxisParams(axis=2, parent=self)
1162
- self._axis2ShiftSearchParams.layout().setContentsMargins(0, 0, 0, 0)
1163
- self._axis2ShiftSearchParams.layout().setSpacing(0)
1164
- self._axis2Group.layout().addWidget(self._axis2ShiftSearchParams)
1165
- # by default avoid doing x shift research
1166
- self._axis2ShiftSearchParams.setShiftSearchMethod(None)
1167
-
1168
- # frame rescaling option
1169
- self._rescalingWidget = RescalingWidget(parent=self)
1170
- self.layout().addRow(self._rescalingWidget)
1171
-
1172
- # normalization by sample
1173
- self._normalizationBySampleWidget = NormalizationBySampleGroupBox(parent=self)
1174
- self._normalizationBySampleWidget.setChecked(False)
1175
- self.layout().addRow(self._normalizationBySampleWidget)
1176
-
1177
- # connect signal / slot
1178
- self._stitchingStrategiesWidget.sigChanged.connect(self._updated)
1179
- self._previewSlices.editingFinished.connect(self._sliceForPreviewHasChanged)
1180
- self._flipLR_CB.toggled.connect(self._flipLRHasChanged)
1181
- self._flipUD_CB.toggled.connect(self._flipUDHasChanged)
1182
-
1183
- def _sliceForPreviewHasChanged(self):
1184
- slice_for_preview = self.getSlicesForPreview()
1185
- try:
1186
- slice_for_preview = int(slice_for_preview)
1187
- except ValueError:
1188
- pass
1189
- self.sigSliceForPreviewChanged.emit(slice_for_preview)
1190
- self._updated()
1191
-
1192
- def _flipLRHasChanged(self):
1193
- self.sigFlipLRChanged.emit(self._flipLR_CB.isChecked())
1194
-
1195
- def _flipUDHasChanged(self):
1196
- self.sigFlipUDChanged.emit(self._flipUD_CB.isChecked())
1197
-
1198
- def _updated(self, *args, **kwargs):
1199
- self.sigChanged.emit()
1200
-
1201
- def getSlicesForPreview(self):
1202
- return self._previewSlices.text()
1203
-
1204
- def getSlices(self):
1205
- slices = self._slices.getSlices()
1206
- if slices == (0, -1, 1):
1207
- return None
1208
- else:
1209
- return (str(slices[0]), str(slices[1]), str(slices[2]))
1210
-
1211
- def setSlices(self, slices: tuple):
1212
- if isinstance(slices, str):
1213
- slices = slices.replace(" ", "").split(":")
1214
- if len(slices) > 2:
1215
- step = int(slices[2])
1216
- else:
1217
- step = None
1218
- self._slices.setSlices(int(slices[0]), int(slices[1]), step)
1219
-
1220
- def getConfiguration(self) -> dict:
1221
- slices = self.getSlices()
1222
- if slices is None:
1223
- slices = ""
1224
- else:
1225
- slices = ":".join(slices)
1226
- res = {
1227
- stitching_config.STITCHING_SECTION: {
1228
- stitching_config.FLIP_LR: self._flipLR_CB.isChecked(),
1229
- stitching_config.FLIP_UD: self._flipUD_CB.isChecked(),
1230
- stitching_config.ALIGNMENT_AXIS_1_FIELD: self._alignmentGroup.getAlignmentAxis1().value,
1231
- stitching_config.ALIGNMENT_AXIS_2_FIELD: self._alignmentGroup.getAlignmentAxis2().value,
1232
- stitching_config.PAD_MODE_FIELD: self._alignmentGroup.getPadMode(),
1233
- },
1234
- stitching_config.INPUTS_SECTION: {
1235
- stitching_config.STITCHING_SLICES: slices,
1236
- },
1237
- stitching_config.NORMALIZATION_BY_SAMPLE_SECTION: self._normalizationBySampleWidget.getConfiguration(),
1238
- }
1239
-
1240
- for ddict in (
1241
- self._stitchingStrategiesWidget.getConfiguration(),
1242
- self._axis0ShiftSearchParams.getConfiguration(),
1243
- self._axis2ShiftSearchParams.getConfiguration(),
1244
- self._rescalingWidget.getConfiguration(),
1245
- ):
1246
- res = concatenate_dict(res, ddict)
1247
- return res
1248
-
1249
- def setConfiguration(self, config: dict):
1250
- self._stitchingStrategiesWidget.setConfiguration(config)
1251
- self._axis0ShiftSearchParams.setConfiguration(config)
1252
- self._axis2ShiftSearchParams.setConfiguration(config)
1253
- self._rescalingWidget.setConfiguration(config)
1254
- slices = config.get(stitching_config.STITCHING_SECTION, {}).get(
1255
- stitching_config.STITCHING_SLICES, ""
1256
- )
1257
- # slices
1258
- if slices == "":
1259
- slices = None
1260
- if slices is not None:
1261
- self.setSlices(slices)
1262
- # flip_lr
1263
- flip_lr = config.get(stitching_config.STITCHING_SECTION, {}).get(
1264
- stitching_config.FLIP_LR, None
1265
- )
1266
- if flip_lr is not None:
1267
- self._flipLR_CB.setChecked(flip_lr in (True, "True", 1, "1"))
1268
- # flip_ud
1269
- flip_ud = config.get(stitching_config.STITCHING_SECTION, {}).get(
1270
- stitching_config.FLIP_UD, None
1271
- )
1272
- if flip_ud is not None:
1273
- self._flipUD_CB.setChecked(flip_ud in (True, "True", 1, "1"))
1274
- # alignment
1275
- alignment_axis_1 = config.get(stitching_config.STITCHING_SECTION, {}).get(
1276
- stitching_config.ALIGNMENT_AXIS_1_FIELD, None
1277
- )
1278
- if alignment_axis_1 is not None:
1279
- self._alignmentGroup.setAlignmentAxis1(alignment_axis_1)
1280
- alignment_axis_2 = config.get(stitching_config.STITCHING_SECTION, {}).get(
1281
- stitching_config.ALIGNMENT_AXIS_2_FIELD, None
1282
- )
1283
- if alignment_axis_2 is not None:
1284
- self._alignmentGroup.setAlignmentAxis2(alignment_axis_2)
1285
- # pad_mode
1286
- pad_mode = config.get(stitching_config.STITCHING_SECTION, {}).get(
1287
- stitching_config.PAD_MODE_FIELD, None
1288
- )
1289
- if pad_mode is not None:
1290
- self._alignmentGroup.setPadMode(pad_mode=pad_mode)
1291
-
1292
- # normalization by sample
1293
- normalization_by_sample = config.get(
1294
- stitching_config.NORMALIZATION_BY_SAMPLE_SECTION, None
1295
- )
1296
- if normalization_by_sample is not None:
1297
- self._normalizationBySampleWidget.setConfiguration(normalization_by_sample)
1298
-
1299
- def _stitchingTypeChanged(self, stiching_type: str):
1300
- stiching_type = StitchingType.from_value(stiching_type)
1301
- self._alignmentGroup.setAlignmentAxis1Enabled(
1302
- stiching_type is StitchingType.Z_POSTPROC
1303
- )
1304
-
1305
- def setConfigurationLevel(self, level: ConfigurationLevel):
1306
- self._alignmentGroup.setVisible(level >= ConfigurationLevel.ADVANCED)
1307
- self._previewSlices.setVisible(level >= ConfigurationLevel.OPTIONAL)
1308
- self._flipLR_CB.setVisible(level >= ConfigurationLevel.ADVANCED)
1309
- self._flipUD_CB.setVisible(level >= ConfigurationLevel.ADVANCED)
1310
- self._rescalingWidget.setVisible(level >= ConfigurationLevel.ADVANCED)
1311
- self._axis0ShiftSearchParams.setConfigurationLevel(level)
1312
- self._axis2ShiftSearchParams.setConfigurationLevel(level)
1313
- self._normalizationBySampleWidget.setVisible(
1314
- level >= ConfigurationLevel.ADVANCED
1315
- )
1316
-
1317
-
1318
- class RescalingWidget(qt.QWidget):
1319
- DEFAULT_MIN_PERCENTILE = 0
1320
-
1321
- DEFAULT_MAX_PERCENTILE = 100
1322
-
1323
- def __init__(self, parent, *args, **kwargs) -> None:
1324
- super().__init__(parent, *args, **kwargs)
1325
- self.setLayout(qt.QHBoxLayout())
1326
- self._activatedCB = qt.QCheckBox("rescale frames", self)
1327
- self.layout().addWidget(self._activatedCB)
1328
-
1329
- self._minPercentileQSB = qt.QSpinBox(self)
1330
- self._minPercentileQSB.setRange(0, 100)
1331
- self._minPercentileQSB.setPrefix("min:")
1332
- self._minPercentileQSB.setSuffix("%")
1333
- self._minPercentileQSB.setValue(self.DEFAULT_MIN_PERCENTILE)
1334
- self.layout().addWidget(self._minPercentileQSB)
1335
-
1336
- self._maxPercentileQSB = qt.QSpinBox(self)
1337
- self._maxPercentileQSB.setRange(0, 100)
1338
- self._maxPercentileQSB.setPrefix("max:")
1339
- self._maxPercentileQSB.setSuffix("%")
1340
- self._maxPercentileQSB.setValue(self.DEFAULT_MAX_PERCENTILE)
1341
- self.layout().addWidget(self._maxPercentileQSB)
1342
-
1343
- # set up
1344
- self._activatedCB.setChecked(False)
1345
- self._minPercentileQSB.setEnabled(False)
1346
- self._maxPercentileQSB.setEnabled(False)
1347
-
1348
- # connect signal / slot
1349
- self._activatedCB.toggled.connect(self._activationChanged)
1350
- self._activatedCB.toggled.connect(self._activationChanged)
1351
-
1352
- def _activationChanged(self):
1353
- self._minPercentileQSB.setEnabled(self._activatedCB.isChecked())
1354
- self._maxPercentileQSB.setEnabled(self._activatedCB.isChecked())
1355
-
1356
- def getConfiguration(self):
1357
- return {
1358
- STITCHING_SECTION: {
1359
- RESCALE_FRAMES: self._activatedCB.isChecked(),
1360
- RESCALE_PARAMS: ";".join(
1361
- [
1362
- f"{KEY_RESCALE_MIN_PERCENTILES}={self._minPercentileQSB.value()}",
1363
- f"{KEY_RESCALE_MAX_PERCENTILES}={self._maxPercentileQSB.value()}",
1364
- ]
1365
- ),
1366
- }
1367
- }
1368
-
1369
- def setConfiguration(self, config: dict):
1370
- def cast_percentile(percentile) -> int:
1371
- if isinstance(percentile, str):
1372
- percentile.replace(" ", "").rstrip("%")
1373
- return int(percentile)
1374
-
1375
- stitching_config = config.get(STITCHING_SECTION, {})
1376
- rescale_params = str_to_dict(stitching_config.get(RESCALE_PARAMS, {}))
1377
- rescale_min_percentile = rescale_params.get(KEY_RESCALE_MIN_PERCENTILES, None)
1378
- if rescale_min_percentile is not None:
1379
- rescale_min_percentile = cast_percentile(rescale_min_percentile)
1380
- self._minPercentileQSB.setValue(rescale_min_percentile)
1381
- rescale_max_percentile = rescale_params.get(KEY_RESCALE_MAX_PERCENTILES, None)
1382
- if rescale_max_percentile is not None:
1383
- rescale_max_percentile = cast_percentile(rescale_max_percentile)
1384
- self._maxPercentileQSB.setValue(rescale_max_percentile)
1385
-
1386
- rescale = stitching_config.get(RESCALE_FRAMES, None)
1387
- if rescale is not None:
1388
- self._activatedCB.setChecked(_convert_str_to_bool(rescale))