tomwer 1.3.26__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (638) 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 +51 -60
  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 +41 -84
  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 -10
  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 +16 -5
  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 +47 -804
  138. tomwer/core/process/reconstruction/axis/mode.py +89 -25
  139. tomwer/core/process/reconstruction/axis/params.py +131 -283
  140. tomwer/core/process/reconstruction/axis/projectiontype.py +0 -28
  141. tomwer/core/process/reconstruction/axis/side.py +8 -0
  142. tomwer/core/process/reconstruction/darkref/darkrefs.py +14 -39
  143. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +7 -39
  144. tomwer/core/process/reconstruction/darkref/params.py +1 -28
  145. tomwer/core/process/reconstruction/nabu/castvolume.py +19 -34
  146. tomwer/core/process/reconstruction/nabu/nabucommon.py +18 -43
  147. tomwer/core/process/reconstruction/nabu/nabuscores.py +64 -68
  148. tomwer/core/process/reconstruction/nabu/nabuslices.py +63 -105
  149. tomwer/core/process/reconstruction/nabu/nabuvolume.py +44 -70
  150. tomwer/core/process/reconstruction/nabu/plane.py +10 -0
  151. tomwer/core/process/reconstruction/nabu/settings.py +1 -28
  152. tomwer/core/process/reconstruction/nabu/target.py +0 -28
  153. tomwer/core/process/reconstruction/nabu/test/test_castvolume.py +116 -0
  154. tomwer/core/process/reconstruction/nabu/test/test_nabu_utils.py +277 -0
  155. tomwer/core/process/reconstruction/nabu/test/test_nabunormalization.py +199 -0
  156. tomwer/core/process/reconstruction/nabu/utils.py +11 -60
  157. tomwer/core/process/reconstruction/normalization/normalization.py +2 -32
  158. tomwer/core/process/reconstruction/normalization/params.py +3 -35
  159. tomwer/core/process/reconstruction/output.py +14 -19
  160. tomwer/core/process/reconstruction/paramsbase.py +4 -33
  161. tomwer/core/process/reconstruction/saaxis/params.py +5 -33
  162. tomwer/core/process/reconstruction/saaxis/saaxis.py +22 -51
  163. tomwer/core/process/reconstruction/sadeltabeta/params.py +2 -31
  164. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +18 -46
  165. tomwer/core/process/reconstruction/scores/params.py +9 -39
  166. tomwer/core/process/reconstruction/scores/scores.py +10 -42
  167. tomwer/core/process/reconstruction/tests/__init__.py +0 -0
  168. tomwer/core/process/reconstruction/tests/test_axis.py +46 -0
  169. tomwer/core/process/reconstruction/tests/test_darkref.py +33 -0
  170. tomwer/core/process/reconstruction/{test → tests}/test_saaxis.py +1 -27
  171. tomwer/core/process/reconstruction/tests/test_sadeltabeta.py +48 -0
  172. tomwer/core/process/reconstruction/{test → tests}/test_utils.py +4 -4
  173. tomwer/core/process/reconstruction/utils/cor.py +8 -4
  174. tomwer/core/process/script/python.py +1 -27
  175. tomwer/core/process/script/tests/test_script.py +41 -0
  176. tomwer/core/process/stitching/metadataholder.py +5 -4
  177. tomwer/core/process/stitching/nabustitcher.py +35 -5
  178. tomwer/core/process/stitching/tests/test_metadataholder.py +17 -0
  179. tomwer/core/process/task.py +20 -63
  180. tomwer/core/process/tests/__init__.py +0 -0
  181. tomwer/core/process/{test → tests}/test_conditions.py +1 -28
  182. tomwer/core/process/{test → tests}/test_dark_and_flat.py +1 -27
  183. tomwer/core/process/{test → tests}/test_data_listener.py +1 -27
  184. tomwer/core/process/{test → tests}/test_data_transfer.py +2 -28
  185. tomwer/core/process/{test → tests}/test_data_watcher.py +1 -27
  186. tomwer/core/process/{test → tests}/test_nabu.py +2 -33
  187. tomwer/core/process/{test → tests}/test_normalization.py +1 -26
  188. tomwer/core/process/{test → tests}/test_timer.py +1 -27
  189. tomwer/core/process/utils.py +2 -31
  190. tomwer/core/process/visualization/dataviewer.py +1 -26
  191. tomwer/core/process/visualization/diffviewer.py +1 -26
  192. tomwer/core/process/visualization/imagestackviewer.py +0 -26
  193. tomwer/core/process/visualization/radiostack.py +0 -26
  194. tomwer/core/process/visualization/samplemoved.py +0 -28
  195. tomwer/core/process/visualization/sinogramviewer.py +0 -27
  196. tomwer/core/process/visualization/slicestack.py +0 -28
  197. tomwer/core/process/visualization/tests/test_data_viewer.py +12 -0
  198. tomwer/core/process/visualization/tests/test_diff_viewer.py +12 -0
  199. tomwer/core/process/visualization/tests/test_image_stack_viewer.py +14 -0
  200. tomwer/core/process/visualization/tests/test_radio_stack.py +12 -0
  201. tomwer/core/process/visualization/tests/test_sample_moved.py +14 -0
  202. tomwer/core/process/visualization/tests/test_sinogram_viewer.py +13 -0
  203. tomwer/core/process/visualization/tests/test_slice_stack.py +13 -0
  204. tomwer/core/process/visualization/tests/test_volume_viewer.py +12 -0
  205. tomwer/core/process/visualization/volumeviewer.py +0 -29
  206. tomwer/core/scan/__init__.py +3 -27
  207. tomwer/core/scan/blissscan.py +5 -34
  208. tomwer/core/scan/edfscan.py +19 -53
  209. tomwer/core/scan/hdf5scan.py +2 -2
  210. tomwer/core/scan/nxtomoscan.py +48 -54
  211. tomwer/core/scan/scanbase.py +107 -203
  212. tomwer/core/scan/scanfactory.py +11 -41
  213. tomwer/core/scan/tests/__init__.py +0 -0
  214. tomwer/core/scan/tests/test_edf.py +25 -0
  215. tomwer/core/scan/tests/test_future_scan.py +35 -0
  216. tomwer/core/scan/tests/test_nxtomoscan.py +143 -0
  217. tomwer/core/scan/tests/test_process_registration.py +64 -0
  218. tomwer/core/settings.py +18 -40
  219. tomwer/core/tests/__init__.py +0 -0
  220. tomwer/core/tests/test_scanutils.py +26 -0
  221. tomwer/core/{test → tests}/test_utils.py +1 -28
  222. tomwer/core/tomwer_object.py +4 -0
  223. tomwer/core/utils/__init__.py +2 -0
  224. tomwer/core/utils/char.py +0 -28
  225. tomwer/core/utils/deprecation.py +12 -38
  226. tomwer/core/utils/dictutils.py +1 -3
  227. tomwer/core/utils/ftseriesutils.py +1 -27
  228. tomwer/core/utils/gpu.py +0 -28
  229. tomwer/core/utils/image.py +8 -39
  230. tomwer/core/utils/locker.py +1 -28
  231. tomwer/core/utils/logconfig.py +0 -27
  232. tomwer/core/utils/normalization.py +4 -31
  233. tomwer/core/utils/nxtomoutils.py +8 -38
  234. tomwer/core/utils/resource.py +0 -26
  235. tomwer/core/utils/scanutils.py +23 -52
  236. tomwer/core/utils/slurm.py +7 -30
  237. tomwer/core/utils/spec.py +6 -6
  238. tomwer/core/utils/tests/__init__.py +0 -0
  239. tomwer/core/utils/tests/test_image.py +30 -0
  240. tomwer/core/utils/tests/test_nxtomo.py +38 -0
  241. tomwer/core/utils/tests/test_scan_utils.py +46 -0
  242. tomwer/core/utils/tests/test_time.py +6 -0
  243. tomwer/core/utils/threads.py +0 -26
  244. tomwer/core/utils/time.py +0 -27
  245. tomwer/core/volume/__init__.py +4 -0
  246. tomwer/core/volume/edfvolume.py +1 -28
  247. tomwer/core/volume/hdf5volume.py +1 -28
  248. tomwer/core/volume/jp2kvolume.py +1 -27
  249. tomwer/core/volume/rawvolume.py +1 -28
  250. tomwer/core/volume/tests/test_volumes.py +21 -0
  251. tomwer/core/volume/tiffvolume.py +1 -28
  252. tomwer/core/volume/volumebase.py +5 -0
  253. tomwer/core/volume/volumefactory.py +7 -33
  254. tomwer/gui/__init__.py +0 -28
  255. tomwer/gui/cluster/__init__.py +2 -0
  256. tomwer/gui/cluster/slurm.py +297 -95
  257. tomwer/gui/cluster/supervisor.py +1 -27
  258. tomwer/gui/cluster/tests/__init__.py +0 -0
  259. tomwer/gui/cluster/{test → tests}/test_cluster.py +21 -26
  260. tomwer/gui/cluster/{test → tests}/test_supervisor.py +1 -27
  261. tomwer/gui/conditions/__init__.py +2 -0
  262. tomwer/gui/conditions/filter.py +1 -26
  263. tomwer/gui/configuration/__init__.py +2 -0
  264. tomwer/gui/control/__init__.py +2 -0
  265. tomwer/gui/control/actions.py +2 -28
  266. tomwer/gui/control/datadiscovery.py +4 -3
  267. tomwer/gui/control/datalist.py +64 -69
  268. tomwer/gui/control/datalistener.py +1 -39
  269. tomwer/gui/control/datareacheractions.py +1 -28
  270. tomwer/gui/control/datatransfert.py +2 -29
  271. tomwer/gui/control/datavalidator.py +3 -37
  272. tomwer/gui/control/datawatcher/__init__.py +0 -28
  273. tomwer/gui/control/datawatcher/configuration.py +1 -28
  274. tomwer/gui/control/datawatcher/datawatcher.py +3 -32
  275. tomwer/gui/control/datawatcher/datawatcherobserver.py +2 -28
  276. tomwer/gui/control/history.py +5 -35
  277. tomwer/gui/control/nxtomomill.py +3 -30
  278. tomwer/gui/control/observations.py +61 -55
  279. tomwer/gui/control/reducedarkflatselector.py +24 -20
  280. tomwer/gui/control/scanselectorwidget.py +2 -28
  281. tomwer/gui/control/selectorwidgetbase.py +17 -17
  282. tomwer/gui/control/series/__init__.py +0 -0
  283. tomwer/gui/control/{serie/seriecreator.py → series/seriescreator.py} +214 -235
  284. tomwer/gui/control/series/serieswaiter.py +0 -0
  285. tomwer/gui/control/series/test/test_creator.py +424 -0
  286. tomwer/gui/control/series/test/test_nxtomo_concatenate.py +21 -0
  287. tomwer/gui/control/singletomoobj.py +1 -1
  288. tomwer/gui/control/tests/__init__.py +0 -0
  289. tomwer/gui/control/tests/test_datalist.py +47 -0
  290. tomwer/gui/control/{test → tests}/test_datalistener.py +1 -28
  291. tomwer/gui/control/tests/test_datavalidator.py +27 -0
  292. tomwer/gui/control/{test → tests}/test_inputwidget.py +1 -27
  293. tomwer/gui/control/tests/test_process_manager.py +38 -0
  294. tomwer/gui/control/tests/test_scan_observations.py +23 -0
  295. tomwer/gui/control/tests/test_scanselector.py +42 -0
  296. tomwer/gui/control/{test → tests}/test_scanvalidator.py +1 -27
  297. tomwer/gui/control/{test → tests}/test_volume_dialog.py +2 -30
  298. tomwer/gui/control/{test → tests}/test_volumeselector.py +1 -27
  299. tomwer/gui/control/volumeselectorwidget.py +2 -30
  300. tomwer/gui/dataportal/__init__.py +2 -0
  301. tomwer/gui/{icat → dataportal}/createscreenshots.py +3 -2
  302. tomwer/gui/dataportal/gallery.py +133 -0
  303. tomwer/gui/dataportal/outputformat.py +0 -0
  304. tomwer/gui/dataportal/publish.py +96 -0
  305. tomwer/gui/dataportal/tests/test_create_screenshots_gui.py +23 -0
  306. tomwer/gui/dataportal/tests/test_gallery_gui.py +21 -0
  307. tomwer/gui/debugtools/__init__.py +2 -0
  308. tomwer/gui/debugtools/datasetgenerator.py +1 -30
  309. tomwer/gui/debugtools/objectinspector.py +1 -29
  310. tomwer/gui/dialog/QDataDialog.py +89 -0
  311. tomwer/gui/{qfolderdialog.py → dialog/QVolumeDialog.py} +8 -107
  312. tomwer/gui/dialog/__init__.py +1 -0
  313. tomwer/gui/edit/__init__.py +2 -0
  314. tomwer/gui/edit/dkrfpatch.py +52 -87
  315. tomwer/gui/edit/imagekeyeditor.py +18 -54
  316. tomwer/gui/edit/nxtomoeditor.py +113 -300
  317. tomwer/gui/edit/nxtomowarmer.py +3 -2
  318. tomwer/gui/edit/tests/__init__.py +0 -0
  319. tomwer/gui/edit/{test → tests}/test_dkrf_patch.py +3 -29
  320. tomwer/gui/edit/{test → tests}/test_image_key_editor.py +1 -27
  321. tomwer/gui/edit/{test → tests}/test_nx_editor.py +45 -23
  322. tomwer/gui/fonts.py +5 -0
  323. tomwer/gui/icons.py +28 -66
  324. tomwer/gui/illustrations.py +4 -34
  325. tomwer/gui/imagefromfile.py +5 -30
  326. tomwer/gui/metadataloader.py +36 -0
  327. tomwer/gui/qconfigfile.py +3 -0
  328. tomwer/gui/qlefilesystem.py +3 -0
  329. tomwer/gui/reconstruction/__init__.py +2 -0
  330. tomwer/gui/reconstruction/axis/AxisMainWindow.py +275 -0
  331. tomwer/gui/reconstruction/axis/AxisOptionsWidget.py +313 -0
  332. tomwer/gui/reconstruction/axis/AxisSettingsWidget.py +797 -0
  333. tomwer/gui/reconstruction/axis/AxisWidget.py +534 -0
  334. tomwer/gui/reconstruction/axis/CalculationWidget.py +218 -0
  335. tomwer/gui/reconstruction/axis/CompareImages.py +11 -44
  336. tomwer/gui/reconstruction/axis/ControlWidget.py +285 -0
  337. tomwer/gui/reconstruction/axis/EstimatedCORWidget.py +394 -0
  338. tomwer/gui/reconstruction/axis/EstimatedCorComboBox.py +118 -0
  339. tomwer/gui/reconstruction/axis/InputWidget.py +347 -0
  340. tomwer/gui/reconstruction/axis/ManualFramesSelection.py +168 -0
  341. tomwer/gui/reconstruction/axis/__init__.py +2 -2
  342. tomwer/gui/reconstruction/darkref/__init__.py +0 -27
  343. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +5 -34
  344. tomwer/gui/reconstruction/darkref/darkrefwidget.py +1 -27
  345. tomwer/gui/reconstruction/nabu/castvolume.py +40 -59
  346. tomwer/gui/reconstruction/nabu/check.py +7 -33
  347. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +7 -34
  348. tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +6 -5
  349. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +10 -69
  350. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +3 -47
  351. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +54 -36
  352. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +103 -54
  353. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +116 -65
  354. tomwer/gui/reconstruction/nabu/nabuflow.py +31 -61
  355. tomwer/gui/reconstruction/nabu/platform.py +94 -0
  356. tomwer/gui/reconstruction/nabu/slices.py +81 -45
  357. tomwer/gui/reconstruction/nabu/slurm.py +1 -27
  358. tomwer/gui/reconstruction/nabu/test/test_cast_volume.py +82 -0
  359. tomwer/gui/reconstruction/nabu/test/test_check.py +66 -0
  360. tomwer/gui/reconstruction/nabu/test/test_ctf.py +46 -0
  361. tomwer/gui/reconstruction/nabu/test/test_helical.py +21 -0
  362. tomwer/gui/reconstruction/nabu/test/test_nabu_preprocessing.py +81 -0
  363. tomwer/gui/reconstruction/nabu/volume.py +62 -90
  364. tomwer/gui/reconstruction/normalization/intensity.py +5 -32
  365. tomwer/gui/reconstruction/normalization/test/test_intensity.py +89 -0
  366. tomwer/gui/reconstruction/saaxis/corrangeselector.py +32 -56
  367. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +56 -96
  368. tomwer/gui/reconstruction/saaxis/saaxis.py +17 -38
  369. tomwer/gui/reconstruction/saaxis/sliceselector.py +8 -39
  370. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +19 -37
  371. tomwer/gui/reconstruction/scores/control.py +2 -30
  372. tomwer/gui/reconstruction/scores/scoreplot.py +23 -49
  373. tomwer/gui/reconstruction/tests/__init__.py +0 -0
  374. tomwer/gui/reconstruction/{test → tests}/test_axis.py +23 -49
  375. tomwer/gui/reconstruction/{test → tests}/test_nabu.py +16 -31
  376. tomwer/gui/reconstruction/{test → tests}/test_saaxis.py +10 -37
  377. tomwer/gui/reconstruction/{test → tests}/test_sadeltabeta.py +1 -26
  378. tomwer/gui/samplemoved/__init__.py +2 -30
  379. tomwer/gui/samplemoved/selectiontable.py +3 -33
  380. tomwer/gui/settings.py +7 -0
  381. tomwer/gui/stackplot.py +33 -661
  382. tomwer/gui/stacks.py +261 -135
  383. tomwer/gui/stitching/SingleAxisStitchingWidget.py +326 -0
  384. tomwer/gui/stitching/StitchingOptionsWidget.py +438 -0
  385. tomwer/gui/stitching/StitchingWindow.py +586 -0
  386. tomwer/gui/stitching/__init__.py +2 -0
  387. tomwer/gui/stitching/alignment.py +90 -0
  388. tomwer/gui/stitching/axisorderedlist.py +44 -34
  389. tomwer/gui/stitching/config/axisparams.py +25 -11
  390. tomwer/gui/stitching/config/output.py +6 -5
  391. tomwer/gui/stitching/config/positionoveraxis.py +313 -51
  392. tomwer/gui/stitching/config/stitchingstrategies.py +22 -16
  393. tomwer/gui/stitching/config/tests/test_axisparams.py +25 -0
  394. tomwer/gui/stitching/config/tomoobjdetails.py +3 -5
  395. tomwer/gui/stitching/normalization.py +1 -0
  396. tomwer/gui/stitching/preview.py +59 -0
  397. tomwer/gui/stitching/singleaxis.py +57 -0
  398. tomwer/gui/stitching/stitchandbackground.py +3 -2
  399. tomwer/gui/stitching/stitching_preview.py +44 -36
  400. tomwer/gui/stitching/stitching_raw.py +5 -3
  401. tomwer/gui/stitching/tests/test_ZStitchingWindow.py +88 -0
  402. tomwer/gui/stitching/tests/test_axis_ordered_list.py +21 -0
  403. tomwer/gui/stitching/tests/test_normalization.py +27 -0
  404. tomwer/gui/stitching/tests/test_preview.py +68 -0
  405. tomwer/gui/stitching/tests/test_stitching_raw.py +110 -0
  406. tomwer/gui/stitching/tests/utils.py +92 -0
  407. tomwer/gui/stitching/utils.py +14 -0
  408. tomwer/gui/stitching/z_stitching/fineestimation.py +5 -33
  409. tomwer/gui/stitching/z_stitching/tests/test_fine_estimation.py +35 -0
  410. tomwer/gui/stitching/z_stitching/tests/test_raw_estimation.py +215 -0
  411. tomwer/gui/stitching/z_stitching/tests/test_stitching_window.py +51 -0
  412. tomwer/gui/tests/__init__.py +0 -0
  413. tomwer/gui/tests/test_axis_gui.py +43 -0
  414. tomwer/gui/{test → tests}/test_qfolder_dialog.py +1 -1
  415. tomwer/gui/utils/RangeWidget.py +44 -0
  416. tomwer/gui/utils/buttons.py +4 -3
  417. tomwer/gui/utils/completer.py +2 -33
  418. tomwer/gui/utils/flow.py +11 -40
  419. tomwer/gui/utils/gpu.py +60 -0
  420. tomwer/gui/utils/illustrations.py +1 -26
  421. tomwer/gui/utils/inputwidget.py +35 -73
  422. tomwer/gui/utils/lineselector/lineselector.py +9 -46
  423. tomwer/gui/utils/loadingmode.py +81 -0
  424. tomwer/gui/utils/qt_utils.py +9 -0
  425. tomwer/gui/utils/sandboxes.py +1 -26
  426. tomwer/gui/utils/scandescription.py +2 -31
  427. tomwer/gui/utils/scrollarea.py +6 -55
  428. tomwer/gui/utils/slider.py +4 -28
  429. tomwer/gui/utils/splashscreen.py +0 -28
  430. tomwer/gui/utils/step.py +14 -13
  431. tomwer/gui/utils/tests/test_completer.py +41 -0
  432. tomwer/gui/utils/tests/test_line_selector.py +21 -0
  433. tomwer/gui/utils/tests/test_splashscreen.py +8 -0
  434. tomwer/gui/utils/tests/test_vignettes.py +68 -0
  435. tomwer/gui/utils/unitsystem.py +15 -69
  436. tomwer/gui/utils/utils.py +4 -5
  437. tomwer/gui/utils/vignettes.py +10 -41
  438. tomwer/gui/utils/waiterthread.py +0 -26
  439. tomwer/gui/visualization/__init__.py +2 -0
  440. tomwer/gui/visualization/dataviewer.py +68 -421
  441. tomwer/gui/visualization/diffviewer/diffviewer.py +2 -30
  442. tomwer/gui/visualization/diffviewer/shiftwidget.py +4 -29
  443. tomwer/gui/visualization/fullscreenplot.py +5 -5
  444. tomwer/gui/visualization/imagestack.py +403 -0
  445. tomwer/gui/visualization/nxtomometadata.py +0 -4
  446. tomwer/gui/visualization/reconstructionparameters.py +14 -32
  447. tomwer/gui/visualization/scanoverview.py +33 -66
  448. tomwer/gui/visualization/sinogramviewer.py +2 -28
  449. tomwer/gui/visualization/test/__init__.py +0 -28
  450. tomwer/gui/visualization/test/test_dataviewer.py +1 -28
  451. tomwer/gui/visualization/test/test_diffviewer.py +1 -28
  452. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +0 -5
  453. tomwer/gui/visualization/test/test_reconstruction_parameters.py +1 -27
  454. tomwer/gui/visualization/test/test_sinogramviewer.py +1 -28
  455. tomwer/gui/visualization/test/test_stacks.py +184 -115
  456. tomwer/gui/visualization/test/test_volumeviewer.py +3 -2
  457. tomwer/gui/visualization/tomoobjoverview.py +2 -2
  458. tomwer/gui/visualization/volumeoverview.py +3 -2
  459. tomwer/gui/visualization/volumeviewer.py +47 -43
  460. tomwer/io/__init__.py +2 -0
  461. tomwer/io/utils/h5pyutils.py +1 -27
  462. tomwer/io/utils/test/test_raw_and_processed_data.py +10 -0
  463. tomwer/io/utils/test/test_utils.py +67 -0
  464. tomwer/io/utils/utils.py +2 -31
  465. tomwer/resources/__init__.py +13 -33
  466. tomwer/resources/gui/icons/edit_downstream.svg +114 -0
  467. tomwer/resources/gui/icons/edit_upstream.svg +112 -0
  468. tomwer/resources/gui/icons/free_edition.svg +23 -0
  469. tomwer/resources/gui/icons/icat_gallery_opts.png +0 -0
  470. tomwer/resources/gui/icons/icat_gallery_opts.svg +80 -0
  471. tomwer/resources/gui/icons/search.png +0 -0
  472. tomwer/resources/gui/icons/search.svg +23 -0
  473. tomwer/resources/gui/icons/warning.png +0 -0
  474. tomwer/synctools/__init__.py +2 -0
  475. tomwer/synctools/axis.py +1 -27
  476. tomwer/synctools/darkref.py +1 -26
  477. tomwer/synctools/datalistener.py +1 -27
  478. tomwer/synctools/datatransfert.py +2 -27
  479. tomwer/synctools/imageloaderthread.py +1 -28
  480. tomwer/synctools/rsyncmanager.py +1 -25
  481. tomwer/synctools/saaxis.py +1 -26
  482. tomwer/synctools/sadeltabeta.py +1 -26
  483. tomwer/synctools/stacks/control/datalistener.py +1 -26
  484. tomwer/synctools/stacks/processingstack.py +4 -33
  485. tomwer/synctools/stacks/reconstruction/axis.py +6 -53
  486. tomwer/synctools/stacks/reconstruction/castvolume.py +12 -43
  487. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +4 -27
  488. tomwer/synctools/stacks/reconstruction/nabu.py +3 -28
  489. tomwer/synctools/stacks/reconstruction/normalization.py +2 -27
  490. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -27
  491. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -27
  492. tomwer/synctools/tests/__init__.py +0 -0
  493. tomwer/synctools/{test → tests}/test_darkRefs.py +16 -40
  494. tomwer/synctools/{test → tests}/test_foldertransfer.py +2 -33
  495. tomwer/synctools/utils/scanstages.py +2 -31
  496. tomwer/tests/__init__.py +1 -0
  497. tomwer/tests/app/test_stitching.py +95 -0
  498. tomwer/tests/datasets.py +1 -5
  499. tomwer/tests/orangecontrib/tomwer/widgets/cluster/tests/test_future_supervisorow.py +48 -0
  500. tomwer/tests/orangecontrib/tomwer/widgets/cluster/tests/test_slurm_clusterow.py +40 -0
  501. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_advancement.py +8 -0
  502. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_data_validator.py +55 -0
  503. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_datadiscovery.py +129 -0
  504. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_datalistener.py +111 -0
  505. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_dataselector.py +69 -0
  506. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_datawatcher.py +411 -0
  507. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_emailow.py +29 -0
  508. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_notifier.py +24 -0
  509. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_nxtomo_concatenate_ow.py +64 -0
  510. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_nxtomomill.py +133 -0
  511. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_reduce_dark_flat_selector.py +40 -0
  512. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_singletomoobj.py +40 -0
  513. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_timerow.py +25 -0
  514. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_tomoobj_series.py +96 -0
  515. tomwer/tests/orangecontrib/tomwer/widgets/control/tests/test_volume_selector.py +69 -0
  516. orangecontrib/tomwer/widgets/edit/test/test_image_key_editor.py → tomwer/tests/orangecontrib/tomwer/widgets/debugtools/tests/test_dataset_generator.py +17 -16
  517. tomwer/tests/orangecontrib/tomwer/widgets/debugtools/tests/test_object_inspector.py +36 -0
  518. {orangecontrib/tomwer/widgets/edit/test → tomwer/tests/orangecontrib/tomwer/widgets/edit/tests}/test_dark_flat_patch.py +1 -27
  519. tomwer/tests/orangecontrib/tomwer/widgets/edit/tests/test_image_key_editor.py +30 -0
  520. tomwer/tests/orangecontrib/tomwer/widgets/edit/tests/test_nxtomo_editor.py +138 -0
  521. tomwer/tests/orangecontrib/tomwer/widgets/other/tests/test_pythonscript.py +31 -0
  522. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_axis.py +199 -0
  523. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_cast_volumeow.py +58 -0
  524. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_dark_refs_widget.py +136 -0
  525. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_delta_beta_selector.py +15 -0
  526. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_i_norm.py +200 -0
  527. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_helical_prepare_weights_double.py +20 -0
  528. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_volume.py +74 -0
  529. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_nabu_widget.py +107 -0
  530. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_sa_delta_beta.py +194 -0
  531. tomwer/tests/orangecontrib/tomwer/widgets/reconstruction/tests/test_saaxis.py +194 -0
  532. tomwer/tests/orangecontrib/tomwer/widgets/stitching/tests/test_zstitching.py +313 -0
  533. tomwer/tests/orangecontrib/tomwer/widgets/tests/test_conditions.py +85 -0
  534. tomwer/tests/orangecontrib/tomwer/widgets/tests/test_darkref.py +225 -0
  535. tomwer/tests/orangecontrib/tomwer/widgets/tests/test_foldertransfert.py +105 -0
  536. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_dataviewerow.py +57 -0
  537. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_diffviewerow.py +39 -0
  538. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_nxtomo_metadata_viewer.py +29 -0
  539. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_radio_stackow.py +31 -0
  540. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_sample_movedow.py +46 -0
  541. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_sinogram_viewerow.py +31 -0
  542. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_slice_stackow.py +31 -0
  543. tomwer/tests/orangecontrib/tomwer/widgets/visualization/tests/test_volume_viewerow.py +32 -0
  544. tomwer/tests/test_ewoks/test_conversion.py +104 -0
  545. tomwer/tests/test_ewoks/test_single_node_execution.py +87 -0
  546. tomwer/tests/test_ewoks/test_workflows.py +138 -0
  547. tomwer/utils.py +11 -39
  548. tomwer/version.py +2 -2
  549. {tomwer-1.3.26.dist-info → tomwer-1.4.0.dist-info}/LICENSE +3 -3
  550. tomwer-1.4.0.dist-info/METADATA +337 -0
  551. tomwer-1.4.0.dist-info/RECORD +912 -0
  552. {tomwer-1.3.26.dist-info → tomwer-1.4.0.dist-info}/WHEEL +1 -1
  553. {tomwer-1.3.26.dist-info → tomwer-1.4.0.dist-info}/entry_points.txt +1 -0
  554. orangecontrib/tomwer/widgets/control/DataListOW.py +0 -129
  555. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +0 -86
  556. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +0 -182
  557. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +0 -141
  558. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +0 -115
  559. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +0 -98
  560. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +0 -129
  561. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  562. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +0 -82
  563. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  564. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +0 -143
  565. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +0 -87
  566. orangecontrib/tomwer/widgets/visualization/icons/liveslice.png +0 -0
  567. orangecontrib/tomwer/widgets/visualization/icons/liveslice.svg +0 -110
  568. tomwer/core/log/logger.py +0 -130
  569. tomwer/core/process/control/test/test_volume_link.py +0 -74
  570. tomwer/core/process/control/tomoobjserie.py +0 -12
  571. tomwer/core/process/control/volumesymlink.py +0 -200
  572. tomwer/core/process/icat/createscreenshots.py +0 -100
  573. tomwer/core/process/icat/gallery.py +0 -377
  574. tomwer/core/process/icat/icatbase.py +0 -36
  575. tomwer/core/process/icat/publish.py +0 -228
  576. tomwer/core/process/icat/screenshots.py +0 -27
  577. tomwer/core/process/reconstruction/test/test_darkref.py +0 -60
  578. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +0 -74
  579. tomwer/core/process/test/test_axis.py +0 -309
  580. tomwer/core/process/visualization/liveslice.py +0 -6
  581. tomwer/core/progress.py +0 -100
  582. tomwer/core/scan/test/test_edf.py +0 -53
  583. tomwer/core/scan/test/test_future_scan.py +0 -61
  584. tomwer/core/scan/test/test_h5.py +0 -96
  585. tomwer/core/scan/test/test_process_registration.py +0 -109
  586. tomwer/core/test/test_scanutils.py +0 -53
  587. tomwer/gui/control/emailnotifier.py +0 -174
  588. tomwer/gui/control/serie/seriewaiter.py +0 -28
  589. tomwer/gui/control/test/__init__.py +0 -28
  590. tomwer/gui/control/test/test_datalist.py +0 -96
  591. tomwer/gui/control/test/test_datavalidator.py +0 -54
  592. tomwer/gui/control/test/test_email.py +0 -35
  593. tomwer/gui/control/test/test_process_manager.py +0 -65
  594. tomwer/gui/control/test/test_scanselector.py +0 -67
  595. tomwer/gui/edit/test/__init__.py +0 -28
  596. tomwer/gui/icat/gallery.py +0 -214
  597. tomwer/gui/icat/publish.py +0 -187
  598. tomwer/gui/reconstruction/axis/axis.py +0 -733
  599. tomwer/gui/reconstruction/axis/radioaxis.py +0 -2467
  600. tomwer/gui/stitching/stitching.py +0 -1392
  601. tomwer/gui/test/__init__.py +0 -28
  602. tomwer/gui/test/test_axis_gui.py +0 -34
  603. tomwer/synctools/stacks/edit/darkflatpatch.py +0 -169
  604. tomwer/synctools/stacks/edit/imagekeyeditor.py +0 -135
  605. tomwer/third_part/WaitingOverlay.py +0 -110
  606. tomwer-1.3.26-py3.11-nspkg.pth +0 -1
  607. tomwer-1.3.26.dist-info/METADATA +0 -292
  608. tomwer-1.3.26.dist-info/RECORD +0 -785
  609. tomwer-1.3.26.dist-info/namespace_packages.txt +0 -1
  610. /orangecontrib/tomwer/{widgets/edit/test → tests}/__init__.py +0 -0
  611. {tomwer/core/process/control/test → orangecontrib/tomwer/tutorials/id16b}/__init__.py +0 -0
  612. {tomwer/core/process/icat → orangecontrib/tomwer/widgets/cluster/tests}/__init__.py +0 -0
  613. /orangecontrib/tomwer/widgets/control/icons/{tomoobjserie.png → tomoobjseries.png} +0 -0
  614. {tomwer/core/process/reconstruction/test → orangecontrib/tomwer/widgets/control/tests}/__init__.py +0 -0
  615. /orangecontrib/tomwer/widgets/{icat → dataportal}/icons/publish_processed_data.png +0 -0
  616. /orangecontrib/tomwer/widgets/{icat → dataportal}/icons/publish_processed_data.svg +0 -0
  617. {tomwer/core/process/test → orangecontrib/tomwer/widgets/debugtools/tests}/__init__.py +0 -0
  618. {tomwer/core/scan/test → orangecontrib/tomwer/widgets/edit/tests}/__init__.py +0 -0
  619. {tomwer/core/test → orangecontrib/tomwer/widgets/other/tests}/__init__.py +0 -0
  620. {tomwer/gui/cluster/test → orangecontrib/tomwer/widgets/reconstruction/tests}/__init__.py +0 -0
  621. {tomwer/gui/control/serie → orangecontrib/tomwer/widgets/stitching/tests}/__init__.py +0 -0
  622. {tomwer/gui/icat → orangecontrib/tomwer/widgets/tests}/__init__.py +0 -0
  623. {tomwer/gui/reconstruction/test → orangecontrib/tomwer/widgets/visualization/tests}/__init__.py +0 -0
  624. /tomwer/{synctools/stacks/edit → app/stitching}/__init__.py +0 -0
  625. /tomwer/{synctools/test → core/process/control/tests}/__init__.py +0 -0
  626. /tomwer/core/process/control/{test → tests}/test_email.py +0 -0
  627. /tomwer/core/process/control/{test → tests}/test_h52nx_process.py +0 -0
  628. /tomwer/{third_part → core/process/drac}/__init__.py +0 -0
  629. /tomwer/core/process/reconstruction/{test → tests}/test_axis_params.py +0 -0
  630. /tomwer/core/process/reconstruction/{test → tests}/test_darkref_copy.py +0 -0
  631. /tomwer/core/process/reconstruction/{test → tests}/test_paramsbase.py +0 -0
  632. /tomwer/core/scan/{test → tests}/test_scan.py +0 -0
  633. /tomwer/gui/control/{serie → series}/nxtomoconcatenate.py +0 -0
  634. /tomwer/gui/control/{test → tests}/test_datadiscovery.py +0 -0
  635. /tomwer/gui/control/{test → tests}/test_reducedarkflat_selector.py +0 -0
  636. /tomwer/gui/control/{test → tests}/test_single_tomo_obj.py +0 -0
  637. {orangecontrib/tomwer/widgets/edit/test → tomwer/tests/orangecontrib/tomwer/widgets/edit/tests}/test_image_key_upgrader.py +0 -0
  638. {tomwer-1.3.26.dist-info → tomwer-1.4.0.dist-info}/top_level.txt +0 -0
@@ -1,1392 +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
- def close(self):
864
- shutil.rmtree(self._previewFolder, ignore_errors=True)
865
- super().close()
866
-
867
-
868
- def concatenate_dict(dict_1, dict_2) -> dict:
869
- """update dict which has dict as values. And we want concatenate those values to"""
870
- res = dict_1.copy()
871
- for key in dict_2:
872
- if key in dict_1:
873
- if key in [f"axis_{axis}_params" for axis in (0, 1, 2)]:
874
- res[key] = ";".join((dict_1[key], dict_2[key]))
875
- elif isinstance(dict_1[key], dict):
876
- res[key] = concatenate_dict(dict_1=dict_1[key], dict_2=dict_2[key])
877
- else:
878
- res[key].update(dict_2[key])
879
- else:
880
- res[key] = dict_2[key]
881
- return res
882
-
883
-
884
- class PreviewThread(qt.QThread):
885
- """
886
- Thread to compute an overview of the stitching
887
- """
888
-
889
- def __init__(self, stitching_config: dict, *args, **kwargs) -> None:
890
- super().__init__(*args, **kwargs)
891
- self._stitching_config = dict_to_config_obj(stitching_config)
892
- self._output_identifier = None
893
- self._frame_composition = None
894
- self._final_tomo_objs_positions = None
895
- # store position of all the tomo objects (scan, volumes) used for the final stitching (after shift refinement)
896
-
897
- @property
898
- def stiching_config(self):
899
- return self._stitching_config
900
-
901
- @property
902
- def output_identifier(self):
903
- return self._output_identifier
904
-
905
- @property
906
- def frame_composition(self):
907
- return self._frame_composition
908
-
909
- @property
910
- def final_tomo_objs_positions(self) -> dict:
911
- """
912
- :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)
913
- :rtype: dict
914
- """
915
- return self._final_tomo_objs_positions
916
-
917
- @property
918
- def final_axis_2_pos(self):
919
- return self._final_axis_2_pos
920
-
921
- def run(self):
922
- stitching_type = self.stiching_config.stitching_type
923
- if stitching_type.value == "z-preproc":
924
- stitcher = PreProcessZStitcher(configuration=self.stiching_config)
925
- elif stitching_type.value == "z-postproc":
926
- stitcher = PostProcessZStitcher(configuration=self.stiching_config)
927
- else:
928
- raise NotImplementedError
929
- self._output_identifier = stitcher.stitch()
930
- if self._output_identifier is not None:
931
- self._output_identifier = self._output_identifier.to_str()
932
- # store in cache the frame composition to be able to provide them to the PreviewPlot
933
- self._frame_composition = stitcher.frame_composition
934
- self._final_tomo_objs_positions = stitcher.get_final_axis_positions_in_px()
935
-
936
-
937
- class _SlicesSelector(qt.QGroupBox):
938
- """
939
- Widget to determine the slices values (to be stitched)
940
- """
941
-
942
- def __init__(self, parent=None) -> None:
943
- super().__init__("slices", parent)
944
- # start interface
945
- self.setLayout(qt.QHBoxLayout())
946
- self._startSliceCB = qt.QCheckBox("start", self)
947
- self.layout().addWidget(self._startSliceCB)
948
- self._startSliceSB = qt.QSpinBox(self)
949
- self._startSliceSB.setMinimum(0)
950
- self._startSliceSB.setMaximum(9999999)
951
- self._startSliceSB.setValue(0)
952
- self.layout().addWidget(self._startSliceSB)
953
- # stop interface
954
- self._stopSliceCB = qt.QCheckBox("stop", self)
955
- self.layout().addWidget(self._stopSliceCB)
956
- self._stopSliceSB = qt.QSpinBox(self)
957
- self._stopSliceSB.setMinimum(-1)
958
- self._stopSliceSB.setMaximum(9999999)
959
- self._stopSliceSB.setValue(-1)
960
- self.layout().addWidget(self._stopSliceSB)
961
- # step interface
962
- self._stepSliceLabel = qt.QLabel("step", self)
963
- self.layout().addWidget(self._stepSliceLabel)
964
- self._stepSliceSB = qt.QSpinBox(self)
965
- self._stepSliceSB.setMinimum(1)
966
- self._stepSliceSB.setMaximum(9999999)
967
- self._stepSliceSB.setValue(1)
968
- self.layout().addWidget(self._stepSliceSB)
969
-
970
- # connect signal / slot
971
- self._startSliceCB.toggled.connect(self._startSliceSB.setDisabled)
972
- self._stopSliceCB.toggled.connect(self._stopSliceSB.setDisabled)
973
-
974
- self._startSliceCB.setChecked(True)
975
- self._stopSliceCB.setChecked(True)
976
-
977
- def getSlices(self) -> tuple:
978
- if self._startSliceCB.isChecked():
979
- start = 0
980
- else:
981
- start = self._startSliceSB.value()
982
- if self._stopSliceCB.isChecked():
983
- stop = -1
984
- else:
985
- stop = self._stopSliceSB.value()
986
- step = self._stepSliceSB.value()
987
- return (start, stop, step)
988
-
989
- def setSlices(self, start: int, stop: int, step: Optional[int] = None):
990
- start = int(start)
991
- stop = int(stop)
992
- if start == 0:
993
- self._startSliceCB.setChecked(True)
994
- else:
995
- self._startSliceCB.setChecked(False)
996
- self._startSliceSB.setValue(start)
997
-
998
- if stop == -1:
999
- self._stopSliceCB.setChecked(True)
1000
- else:
1001
- self._stopSliceCB.setChecked(False)
1002
- self._stopSliceSB.setValue(stop)
1003
-
1004
- if step is not None:
1005
- self._stepSliceSB.setValue(int(step))
1006
-
1007
-
1008
- class _AlignmentGroupBox(qt.QGroupBox):
1009
- DEFAULT_PAD_MODE = "constant"
1010
-
1011
- ALIGNMENT_DOC = (
1012
- "https://tomotools.gitlab-pages.esrf.fr/nabu/stitching/alignment.html"
1013
- )
1014
-
1015
- DEFAULT_ALIGNMENT_AXIS_1 = AlignmentAxis1.CENTER
1016
- DEFAULT_ALIGNMENT_AXIS_2 = AlignmentAxis2.CENTER
1017
-
1018
- _PAD_MODES = (
1019
- "constant", # Pads with a constant value.
1020
- "edge", # Pads with the edge values of array.
1021
- "linear_ramp", # Pads with the linear ramp between end_value and the array edge value.
1022
- "maximum", # Pads with the maximum value of all or part of the vector along each axis.
1023
- "mean", # Pads with the mean value of all or part of the vector along each axis.
1024
- "median", # Pads with the median value of all or part of the vector along each axis.
1025
- "minimum", # Pads with the minimum value of all or part of the vector along each axis.
1026
- "reflect", # Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis.
1027
- "symmetric", # Pads with the reflection of the vector mirrored along the edge of the array.
1028
- "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.
1029
- )
1030
-
1031
- def __init__(self, parent: qt.QWidget = None, title="alignment") -> None:
1032
- super().__init__(title, parent)
1033
- self.setLayout(qt.QFormLayout())
1034
-
1035
- # alignment axis 1
1036
- self._alignmentAxis1CB = qt.QComboBox(self)
1037
- for alignment in AlignmentAxis1.values():
1038
- self._alignmentAxis1CB.addItem(alignment)
1039
- self.layout().addRow("Axis 1 alignment", self._alignmentAxis1CB)
1040
- self._alignmentAxis1CB.setToolTip(
1041
- 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."
1042
- )
1043
-
1044
- # alignment axis 2
1045
- self._alignmentAxis2CB = qt.QComboBox(self)
1046
- for alignment in AlignmentAxis2.values():
1047
- self._alignmentAxis2CB.addItem(alignment)
1048
- self.layout().addRow("Axis 2 alignment", self._alignmentAxis2CB)
1049
- self._alignmentAxis2CB.setToolTip(
1050
- f"Alignment to do in case of frames with different size over axis 2. See {self.ALIGNMENT_DOC} for details."
1051
- )
1052
-
1053
- # pad mode
1054
- self._padModeCB = qt.QComboBox(self)
1055
- for pad_mode in self._PAD_MODES:
1056
- self._padModeCB.addItem(pad_mode)
1057
- self.layout().addRow("pad mode", self._padModeCB)
1058
- self._padModeCB.setToolTip("padding mode to apply for alignment")
1059
-
1060
- # set up
1061
- self.setAlignmentAxis1(self.DEFAULT_ALIGNMENT_AXIS_1)
1062
- self.setAlignmentAxis2(self.DEFAULT_ALIGNMENT_AXIS_2)
1063
-
1064
- def getAlignmentAxis1(self) -> AlignmentAxis1:
1065
- return AlignmentAxis1.from_value(self._alignmentAxis1CB.currentText())
1066
-
1067
- def setAlignmentAxis1(self, alignment: AlignmentAxis1):
1068
- alignment = AlignmentAxis1.from_value(alignment)
1069
- self._alignmentAxis1CB.setCurrentIndex(
1070
- self._alignmentAxis1CB.findText(alignment.value)
1071
- )
1072
-
1073
- def getAlignmentAxis2(self) -> AlignmentAxis2:
1074
- return AlignmentAxis2.from_value(self._alignmentAxis2CB.currentText())
1075
-
1076
- def setAlignmentAxis2(self, alignment: AlignmentAxis2):
1077
- alignment = AlignmentAxis2.from_value(alignment)
1078
- self._alignmentAxis2CB.setCurrentIndex(
1079
- self._alignmentAxis2CB.findText(alignment.value)
1080
- )
1081
-
1082
- def getPadMode(self) -> str:
1083
- return self._padModeCB.currentText()
1084
-
1085
- def setPadMode(self, pad_mode: str):
1086
- idx = self._padModeCB.findText(pad_mode)
1087
- if idx >= 0:
1088
- self._padModeCB.setCurrentIndex(idx)
1089
-
1090
- def setAlignmentAxis1Enabled(self, enabled: bool):
1091
- self._alignmentAxis1CB.setEnabled(enabled)
1092
-
1093
-
1094
- class StitchingOptions(qt.QWidget):
1095
- """
1096
- Widget to let the user define the different options for z-stitching such as which algorithm to search shift,
1097
- which stitching strategy...
1098
- """
1099
-
1100
- sigChanged = qt.Signal()
1101
- """Signal emit when the options change"""
1102
- sigSliceForPreviewChanged = qt.Signal(object)
1103
- """Signal emit when the slice requested for the preview has changed. Parameter is a str or an int"""
1104
- sigFlipLRChanged = qt.Signal(bool)
1105
- """Signal emit when the request to flip LR frame has changed"""
1106
- sigFlipUDChanged = qt.Signal(bool)
1107
- """Signal emit when the request to flip UD frame has changed"""
1108
-
1109
- def __init__(self, parent=None, *args, **kwargs) -> None:
1110
- super().__init__(parent, *args, **kwargs)
1111
- # stitching strategy (aka stitcher behavior)
1112
- self.setLayout(qt.QFormLayout())
1113
- self._stitchingStrategiesWidget = StitchingStrategies(parent=self)
1114
- self._stitchingStrategiesWidget.setObjectName("strategy")
1115
- self.layout().addRow(self._stitchingStrategiesWidget)
1116
- # slice for preview
1117
- self._previewSlices = _SliceGetter("middle", parent=self)
1118
- self._previewSlices.setPlaceholderText(
1119
- "slice index or one of ('middle', 'first', 'last')"
1120
- )
1121
- self._previewSlices.setToolTip(
1122
- "expects a slice index (int > 0) or a str in ('first', 'middle', 'last')"
1123
- )
1124
- self.layout().addRow("slice for preview", self._previewSlices)
1125
-
1126
- # invert frame up - down
1127
- self._flipLR_CB = qt.QCheckBox("flip frame left-right", self)
1128
- self._flipLR_CB.setChecked(False)
1129
- self._flipLR_CB.setToolTip(
1130
- "Flip frame for stitching. This is mostly for volume has no metadata are existing to specify the direction of the frame"
1131
- )
1132
- self.layout().addRow(self._flipLR_CB)
1133
- # invert frame left-right
1134
- self._flipUD_CB = qt.QCheckBox("flip frame up-down", self)
1135
- self._flipUD_CB.setChecked(False)
1136
- self._flipUD_CB.setToolTip(
1137
- "Flip frame for stitching. This is mostly for volume has no metadata are existing to specify the direction of the frame"
1138
- )
1139
- self.layout().addRow(self._flipUD_CB)
1140
-
1141
- # alignment options
1142
- self._alignmentGroup = _AlignmentGroupBox(self)
1143
- self.layout().addRow(self._alignmentGroup)
1144
-
1145
- # slices to be reconstructed
1146
- self._slices = _SlicesSelector(parent=self)
1147
- self._slices.setToolTip(
1148
- "for pre processing stitchting those are projections and for post prcessing stitching those are volume slices"
1149
- )
1150
- self.layout().addRow(self._slices)
1151
-
1152
- # axis 0 params for shift search
1153
- self._axis0Group = qt.QGroupBox("axis 0 (aka z)", self)
1154
- self._axis0Group.setLayout(qt.QVBoxLayout())
1155
- self.layout().addRow(self._axis0Group)
1156
- self._axis0ShiftSearchParams = StitcherAxisParams(axis=0, parent=self)
1157
- self._axis0ShiftSearchParams.layout().setContentsMargins(0, 0, 0, 0)
1158
- self._axis0ShiftSearchParams.layout().setSpacing(0)
1159
- self._axis0Group.layout().addWidget(self._axis0ShiftSearchParams)
1160
-
1161
- # axis 2 params for shift search
1162
- self._axis2Group = qt.QGroupBox("axis 2 (aka x)", self)
1163
- self._axis2Group.setLayout(qt.QVBoxLayout())
1164
- self.layout().addRow(self._axis2Group)
1165
- self._axis2ShiftSearchParams = StitcherAxisParams(axis=2, parent=self)
1166
- self._axis2ShiftSearchParams.layout().setContentsMargins(0, 0, 0, 0)
1167
- self._axis2ShiftSearchParams.layout().setSpacing(0)
1168
- self._axis2Group.layout().addWidget(self._axis2ShiftSearchParams)
1169
- # by default avoid doing x shift research
1170
- self._axis2ShiftSearchParams.setShiftSearchMethod(None)
1171
-
1172
- # frame rescaling option
1173
- self._rescalingWidget = RescalingWidget(parent=self)
1174
- self.layout().addRow(self._rescalingWidget)
1175
-
1176
- # normalization by sample
1177
- self._normalizationBySampleWidget = NormalizationBySampleGroupBox(parent=self)
1178
- self._normalizationBySampleWidget.setChecked(False)
1179
- self.layout().addRow(self._normalizationBySampleWidget)
1180
-
1181
- # connect signal / slot
1182
- self._stitchingStrategiesWidget.sigChanged.connect(self._updated)
1183
- self._previewSlices.editingFinished.connect(self._sliceForPreviewHasChanged)
1184
- self._flipLR_CB.toggled.connect(self._flipLRHasChanged)
1185
- self._flipUD_CB.toggled.connect(self._flipUDHasChanged)
1186
-
1187
- def _sliceForPreviewHasChanged(self):
1188
- slice_for_preview = self.getSlicesForPreview()
1189
- try:
1190
- slice_for_preview = int(slice_for_preview)
1191
- except ValueError:
1192
- pass
1193
- self.sigSliceForPreviewChanged.emit(slice_for_preview)
1194
- self._updated()
1195
-
1196
- def _flipLRHasChanged(self):
1197
- self.sigFlipLRChanged.emit(self._flipLR_CB.isChecked())
1198
-
1199
- def _flipUDHasChanged(self):
1200
- self.sigFlipUDChanged.emit(self._flipUD_CB.isChecked())
1201
-
1202
- def _updated(self, *args, **kwargs):
1203
- self.sigChanged.emit()
1204
-
1205
- def getSlicesForPreview(self):
1206
- return self._previewSlices.text()
1207
-
1208
- def getSlices(self):
1209
- slices = self._slices.getSlices()
1210
- if slices == (0, -1, 1):
1211
- return None
1212
- else:
1213
- return (str(slices[0]), str(slices[1]), str(slices[2]))
1214
-
1215
- def setSlices(self, slices: tuple):
1216
- if isinstance(slices, str):
1217
- slices = slices.replace(" ", "").split(":")
1218
- if len(slices) > 2:
1219
- step = int(slices[2])
1220
- else:
1221
- step = None
1222
- self._slices.setSlices(int(slices[0]), int(slices[1]), step)
1223
-
1224
- def getConfiguration(self) -> dict:
1225
- slices = self.getSlices()
1226
- if slices is None:
1227
- slices = ""
1228
- else:
1229
- slices = ":".join(slices)
1230
- res = {
1231
- stitching_config.STITCHING_SECTION: {
1232
- stitching_config.FLIP_LR: self._flipLR_CB.isChecked(),
1233
- stitching_config.FLIP_UD: self._flipUD_CB.isChecked(),
1234
- stitching_config.ALIGNMENT_AXIS_1_FIELD: self._alignmentGroup.getAlignmentAxis1().value,
1235
- stitching_config.ALIGNMENT_AXIS_2_FIELD: self._alignmentGroup.getAlignmentAxis2().value,
1236
- stitching_config.PAD_MODE_FIELD: self._alignmentGroup.getPadMode(),
1237
- },
1238
- stitching_config.INPUTS_SECTION: {
1239
- stitching_config.STITCHING_SLICES: slices,
1240
- },
1241
- stitching_config.NORMALIZATION_BY_SAMPLE_SECTION: self._normalizationBySampleWidget.getConfiguration(),
1242
- }
1243
-
1244
- for ddict in (
1245
- self._stitchingStrategiesWidget.getConfiguration(),
1246
- self._axis0ShiftSearchParams.getConfiguration(),
1247
- self._axis2ShiftSearchParams.getConfiguration(),
1248
- self._rescalingWidget.getConfiguration(),
1249
- ):
1250
- res = concatenate_dict(res, ddict)
1251
- return res
1252
-
1253
- def setConfiguration(self, config: dict):
1254
- self._stitchingStrategiesWidget.setConfiguration(config)
1255
- self._axis0ShiftSearchParams.setConfiguration(config)
1256
- self._axis2ShiftSearchParams.setConfiguration(config)
1257
- self._rescalingWidget.setConfiguration(config)
1258
- slices = config.get(stitching_config.STITCHING_SECTION, {}).get(
1259
- stitching_config.STITCHING_SLICES, ""
1260
- )
1261
- # slices
1262
- if slices == "":
1263
- slices = None
1264
- if slices is not None:
1265
- self.setSlices(slices)
1266
- # flip_lr
1267
- flip_lr = config.get(stitching_config.STITCHING_SECTION, {}).get(
1268
- stitching_config.FLIP_LR, None
1269
- )
1270
- if flip_lr is not None:
1271
- self._flipLR_CB.setChecked(flip_lr in (True, "True", 1, "1"))
1272
- # flip_ud
1273
- flip_ud = config.get(stitching_config.STITCHING_SECTION, {}).get(
1274
- stitching_config.FLIP_UD, None
1275
- )
1276
- if flip_ud is not None:
1277
- self._flipUD_CB.setChecked(flip_ud in (True, "True", 1, "1"))
1278
- # alignment
1279
- alignment_axis_1 = config.get(stitching_config.STITCHING_SECTION, {}).get(
1280
- stitching_config.ALIGNMENT_AXIS_1_FIELD, None
1281
- )
1282
- if alignment_axis_1 is not None:
1283
- self._alignmentGroup.setAlignmentAxis1(alignment_axis_1)
1284
- alignment_axis_2 = config.get(stitching_config.STITCHING_SECTION, {}).get(
1285
- stitching_config.ALIGNMENT_AXIS_2_FIELD, None
1286
- )
1287
- if alignment_axis_2 is not None:
1288
- self._alignmentGroup.setAlignmentAxis2(alignment_axis_2)
1289
- # pad_mode
1290
- pad_mode = config.get(stitching_config.STITCHING_SECTION, {}).get(
1291
- stitching_config.PAD_MODE_FIELD, None
1292
- )
1293
- if pad_mode is not None:
1294
- self._alignmentGroup.setPadMode(pad_mode=pad_mode)
1295
-
1296
- # normalization by sample
1297
- normalization_by_sample = config.get(
1298
- stitching_config.NORMALIZATION_BY_SAMPLE_SECTION, None
1299
- )
1300
- if normalization_by_sample is not None:
1301
- self._normalizationBySampleWidget.setConfiguration(normalization_by_sample)
1302
-
1303
- def _stitchingTypeChanged(self, stiching_type: str):
1304
- stiching_type = StitchingType.from_value(stiching_type)
1305
- self._alignmentGroup.setAlignmentAxis1Enabled(
1306
- stiching_type is StitchingType.Z_POSTPROC
1307
- )
1308
-
1309
- def setConfigurationLevel(self, level: ConfigurationLevel):
1310
- self._alignmentGroup.setVisible(level >= ConfigurationLevel.ADVANCED)
1311
- self._previewSlices.setVisible(level >= ConfigurationLevel.OPTIONAL)
1312
- self._flipLR_CB.setVisible(level >= ConfigurationLevel.ADVANCED)
1313
- self._flipUD_CB.setVisible(level >= ConfigurationLevel.ADVANCED)
1314
- self._rescalingWidget.setVisible(level >= ConfigurationLevel.ADVANCED)
1315
- self._axis0ShiftSearchParams.setConfigurationLevel(level)
1316
- self._axis2ShiftSearchParams.setConfigurationLevel(level)
1317
- self._normalizationBySampleWidget.setVisible(
1318
- level >= ConfigurationLevel.ADVANCED
1319
- )
1320
-
1321
-
1322
- class RescalingWidget(qt.QWidget):
1323
- DEFAULT_MIN_PERCENTILE = 0
1324
-
1325
- DEFAULT_MAX_PERCENTILE = 100
1326
-
1327
- def __init__(self, parent, *args, **kwargs) -> None:
1328
- super().__init__(parent, *args, **kwargs)
1329
- self.setLayout(qt.QHBoxLayout())
1330
- self._activatedCB = qt.QCheckBox("rescale frames", self)
1331
- self.layout().addWidget(self._activatedCB)
1332
-
1333
- self._minPercentileQSB = qt.QSpinBox(self)
1334
- self._minPercentileQSB.setRange(0, 100)
1335
- self._minPercentileQSB.setPrefix("min:")
1336
- self._minPercentileQSB.setSuffix("%")
1337
- self._minPercentileQSB.setValue(self.DEFAULT_MIN_PERCENTILE)
1338
- self.layout().addWidget(self._minPercentileQSB)
1339
-
1340
- self._maxPercentileQSB = qt.QSpinBox(self)
1341
- self._maxPercentileQSB.setRange(0, 100)
1342
- self._maxPercentileQSB.setPrefix("max:")
1343
- self._maxPercentileQSB.setSuffix("%")
1344
- self._maxPercentileQSB.setValue(self.DEFAULT_MAX_PERCENTILE)
1345
- self.layout().addWidget(self._maxPercentileQSB)
1346
-
1347
- # set up
1348
- self._activatedCB.setChecked(False)
1349
- self._minPercentileQSB.setEnabled(False)
1350
- self._maxPercentileQSB.setEnabled(False)
1351
-
1352
- # connect signal / slot
1353
- self._activatedCB.toggled.connect(self._activationChanged)
1354
- self._activatedCB.toggled.connect(self._activationChanged)
1355
-
1356
- def _activationChanged(self):
1357
- self._minPercentileQSB.setEnabled(self._activatedCB.isChecked())
1358
- self._maxPercentileQSB.setEnabled(self._activatedCB.isChecked())
1359
-
1360
- def getConfiguration(self):
1361
- return {
1362
- STITCHING_SECTION: {
1363
- RESCALE_FRAMES: self._activatedCB.isChecked(),
1364
- RESCALE_PARAMS: ";".join(
1365
- [
1366
- f"{KEY_RESCALE_MIN_PERCENTILES}={self._minPercentileQSB.value()}",
1367
- f"{KEY_RESCALE_MAX_PERCENTILES}={self._maxPercentileQSB.value()}",
1368
- ]
1369
- ),
1370
- }
1371
- }
1372
-
1373
- def setConfiguration(self, config: dict):
1374
- def cast_percentile(percentile) -> int:
1375
- if isinstance(percentile, str):
1376
- percentile.replace(" ", "").rstrip("%")
1377
- return int(percentile)
1378
-
1379
- stitching_config = config.get(STITCHING_SECTION, {})
1380
- rescale_params = str_to_dict(stitching_config.get(RESCALE_PARAMS, {}))
1381
- rescale_min_percentile = rescale_params.get(KEY_RESCALE_MIN_PERCENTILES, None)
1382
- if rescale_min_percentile is not None:
1383
- rescale_min_percentile = cast_percentile(rescale_min_percentile)
1384
- self._minPercentileQSB.setValue(rescale_min_percentile)
1385
- rescale_max_percentile = rescale_params.get(KEY_RESCALE_MAX_PERCENTILES, None)
1386
- if rescale_max_percentile is not None:
1387
- rescale_max_percentile = cast_percentile(rescale_max_percentile)
1388
- self._maxPercentileQSB.setValue(rescale_max_percentile)
1389
-
1390
- rescale = stitching_config.get(RESCALE_FRAMES, None)
1391
- if rescale is not None:
1392
- self._activatedCB.setChecked(_convert_str_to_bool(rescale))