tomwer 1.2.1__py3-none-any.whl → 1.3.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
  2. orangecontrib/tomwer/widgets/__init__.py +11 -11
  3. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
  4. orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
  5. orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
  6. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
  7. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
  8. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +24 -7
  9. orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
  10. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
  11. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +64 -23
  12. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +20 -8
  13. orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
  14. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
  15. orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
  16. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
  17. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +5 -5
  18. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
  19. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
  20. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +8 -1
  21. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
  22. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
  23. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
  24. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
  25. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
  26. orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
  27. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  28. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
  29. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
  30. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
  31. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  32. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
  33. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
  34. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
  35. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +22 -20
  36. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +19 -3
  37. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -169
  38. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
  39. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
  40. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +18 -22
  41. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +18 -26
  42. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +15 -19
  43. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
  44. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  45. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
  46. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
  47. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
  48. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
  49. tomwer/__main__.py +7 -64
  50. tomwer/app/axis.py +3 -3
  51. tomwer/app/canvas.py +8 -0
  52. tomwer/app/canvas_launcher/config.py +16 -14
  53. tomwer/app/canvas_launcher/environ.py +1 -0
  54. tomwer/app/canvas_launcher/mainwindow.py +4 -1
  55. tomwer/app/darkref.py +1 -1
  56. tomwer/app/darkrefpatch.py +1 -1
  57. tomwer/app/diffframe.py +3 -3
  58. tomwer/app/imagekeyeditor.py +5 -5
  59. tomwer/app/imagekeyupgrader.py +5 -5
  60. tomwer/app/intensitynormalization.py +14 -13
  61. tomwer/app/{saaxis.py → multicor.py} +3 -3
  62. tomwer/app/{sadeltabeta.py → multipag.py} +3 -3
  63. tomwer/app/nabuapp.py +0 -11
  64. tomwer/app/radiostack.py +6 -4
  65. tomwer/app/samplemoved.py +3 -2
  66. tomwer/app/scanviewer.py +4 -2
  67. tomwer/app/sinogramviewer.py +3 -2
  68. tomwer/app/slicestack.py +3 -2
  69. tomwer/app/zstitching.py +88 -6
  70. tomwer/core/cluster/cluster.py +26 -0
  71. tomwer/core/log/logger.py +7 -5
  72. tomwer/core/process/conditions/filters.py +1 -1
  73. tomwer/core/process/control/datalistener/datalistener.py +19 -14
  74. tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
  75. tomwer/core/process/control/nxtomoconcatenate.py +13 -13
  76. tomwer/core/process/control/nxtomomill.py +92 -34
  77. tomwer/core/process/control/scantransfer.py +20 -43
  78. tomwer/core/process/control/scanvalidator.py +3 -2
  79. tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
  80. tomwer/core/process/control/test/test_email.py +4 -4
  81. tomwer/core/process/control/test/test_h52nx_process.py +59 -7
  82. tomwer/core/process/control/test/test_volume_link.py +64 -64
  83. tomwer/core/process/control/timer.py +1 -1
  84. tomwer/core/process/control/volumesymlink.py +200 -200
  85. tomwer/core/process/edit/darkflatpatch.py +14 -15
  86. tomwer/core/process/edit/imagekeyeditor.py +41 -39
  87. tomwer/core/process/icat/__init__.py +0 -0
  88. tomwer/core/process/icat/createscreenshots.py +100 -0
  89. tomwer/core/process/icat/gallery.py +377 -0
  90. tomwer/core/process/icat/icatbase.py +36 -0
  91. tomwer/core/process/icat/publish.py +228 -0
  92. tomwer/core/process/icat/screenshots.py +27 -0
  93. tomwer/core/process/output.py +52 -0
  94. tomwer/core/process/reconstruction/axis/axis.py +280 -69
  95. tomwer/core/process/reconstruction/axis/mode.py +163 -48
  96. tomwer/core/process/reconstruction/axis/params.py +29 -21
  97. tomwer/core/process/reconstruction/darkref/darkrefs.py +41 -127
  98. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +4 -3
  99. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  100. tomwer/core/process/reconstruction/nabu/castvolume.py +4 -4
  101. tomwer/core/process/reconstruction/nabu/helical.py +9 -5
  102. tomwer/core/process/reconstruction/nabu/nabucommon.py +71 -78
  103. tomwer/core/process/reconstruction/nabu/nabuscores.py +425 -53
  104. tomwer/core/process/reconstruction/nabu/nabuslices.py +114 -93
  105. tomwer/core/process/reconstruction/nabu/nabuvolume.py +54 -27
  106. tomwer/core/process/reconstruction/nabu/plane.py +9 -0
  107. tomwer/core/process/reconstruction/nabu/settings.py +2 -2
  108. tomwer/core/process/reconstruction/nabu/utils.py +164 -26
  109. tomwer/core/process/reconstruction/output.py +108 -0
  110. tomwer/core/process/reconstruction/saaxis/saaxis.py +238 -264
  111. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +151 -87
  112. tomwer/core/process/reconstruction/scores/params.py +7 -4
  113. tomwer/core/process/reconstruction/scores/scores.py +13 -0
  114. tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
  115. tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
  116. tomwer/core/process/reconstruction/test/test_darkref_copy.py +7 -7
  117. tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
  118. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
  119. tomwer/core/process/stitching/nabustitcher.py +13 -12
  120. tomwer/core/process/task.py +34 -26
  121. tomwer/core/process/test/test_axis.py +13 -12
  122. tomwer/core/process/test/test_dark_and_flat.py +10 -7
  123. tomwer/core/process/test/test_data_transfer.py +10 -8
  124. tomwer/core/process/test/test_nabu.py +14 -6
  125. tomwer/core/process/test/test_normalization.py +4 -4
  126. tomwer/core/scan/blissscan.py +3 -3
  127. tomwer/core/scan/edfscan.py +13 -10
  128. tomwer/core/scan/hdf5scan.py +19 -530
  129. tomwer/core/scan/nxtomoscan.py +534 -0
  130. tomwer/core/scan/scanbase.py +72 -44
  131. tomwer/core/scan/scanfactory.py +13 -13
  132. tomwer/core/scan/test/test_edf.py +2 -2
  133. tomwer/core/scan/test/test_future_scan.py +3 -3
  134. tomwer/core/scan/test/test_h5.py +18 -16
  135. tomwer/core/scan/test/test_process_registration.py +4 -40
  136. tomwer/core/scan/test/test_scan.py +5 -78
  137. tomwer/core/settings.py +22 -2
  138. tomwer/core/test/test_scanutils.py +8 -7
  139. tomwer/core/test/test_utils.py +35 -28
  140. tomwer/core/tomwer_object.py +1 -1
  141. tomwer/core/utils/__init__.py +0 -466
  142. tomwer/core/utils/deprecation.py +1 -1
  143. tomwer/core/utils/dictutils.py +14 -0
  144. tomwer/core/utils/lbsram.py +35 -0
  145. tomwer/core/utils/nxtomoutils.py +1 -1
  146. tomwer/core/utils/scanutils.py +6 -6
  147. tomwer/core/utils/spec.py +263 -0
  148. tomwer/core/volume/edfvolume.py +6 -6
  149. tomwer/core/volume/hdf5volume.py +6 -6
  150. tomwer/core/volume/jp2kvolume.py +6 -6
  151. tomwer/core/volume/rawvolume.py +6 -6
  152. tomwer/core/volume/tiffvolume.py +12 -12
  153. tomwer/core/volume/volumefactory.py +2 -2
  154. tomwer/gui/cluster/slurm.py +274 -65
  155. tomwer/gui/cluster/supervisor.py +12 -0
  156. tomwer/gui/cluster/test/test_cluster.py +14 -2
  157. tomwer/gui/cluster/test/test_supervisor.py +3 -3
  158. tomwer/gui/configuration/__init__.py +0 -0
  159. tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
  160. tomwer/gui/configuration/level.py +22 -0
  161. tomwer/gui/control/actions.py +54 -0
  162. tomwer/gui/control/datalist.py +83 -16
  163. tomwer/gui/control/datalistener.py +4 -16
  164. tomwer/gui/control/datawatcher/controlwidget.py +2 -4
  165. tomwer/gui/control/datawatcher/datawatcher.py +1 -24
  166. tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
  167. tomwer/gui/control/history.py +2 -2
  168. tomwer/gui/control/observations.py +2 -2
  169. tomwer/gui/control/reducedarkflatselector.py +9 -9
  170. tomwer/gui/control/selectorwidgetbase.py +36 -9
  171. tomwer/gui/control/serie/seriecreator.py +5 -22
  172. tomwer/gui/control/test/test_email.py +1 -1
  173. tomwer/gui/control/test/test_scanvalidator.py +6 -5
  174. tomwer/gui/control/test/test_single_tomo_obj.py +3 -3
  175. tomwer/gui/control/tomoobjdisplaymode.py +8 -0
  176. tomwer/gui/debugtools/datasetgenerator.py +3 -3
  177. tomwer/gui/edit/dkrfpatch.py +20 -26
  178. tomwer/gui/edit/imagekeyeditor.py +11 -12
  179. tomwer/gui/edit/nxtomoeditor.py +111 -44
  180. tomwer/gui/edit/nxtomowarmer.py +7 -6
  181. tomwer/gui/edit/test/test_dkrf_patch.py +13 -13
  182. tomwer/gui/edit/test/test_image_key_editor.py +3 -3
  183. tomwer/gui/edit/test/test_nx_editor.py +40 -16
  184. tomwer/gui/icat/__init__.py +0 -0
  185. tomwer/gui/icat/createscreenshots.py +80 -0
  186. tomwer/gui/icat/gallery.py +214 -0
  187. tomwer/gui/icat/publish.py +187 -0
  188. tomwer/gui/imagefromfile.py +2 -2
  189. tomwer/gui/qfolderdialog.py +24 -1
  190. tomwer/gui/reconstruction/axis/CompareImages.py +88 -168
  191. tomwer/gui/reconstruction/axis/axis.py +171 -57
  192. tomwer/gui/reconstruction/axis/radioaxis.py +122 -257
  193. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
  194. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
  195. tomwer/gui/reconstruction/nabu/castvolume.py +14 -3
  196. tomwer/gui/reconstruction/nabu/check.py +9 -9
  197. tomwer/gui/reconstruction/nabu/helical.py +29 -12
  198. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
  199. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +2 -1
  200. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +126 -35
  201. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +39 -32
  202. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +222 -31
  203. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +57 -27
  204. tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
  205. tomwer/gui/reconstruction/nabu/slices.py +10 -11
  206. tomwer/gui/reconstruction/nabu/volume.py +22 -10
  207. tomwer/gui/reconstruction/normalization/intensity.py +18 -48
  208. tomwer/gui/reconstruction/saaxis/corrangeselector.py +8 -24
  209. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
  210. tomwer/gui/reconstruction/saaxis/saaxis.py +9 -21
  211. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +45 -12
  212. tomwer/gui/reconstruction/scores/control.py +2 -9
  213. tomwer/gui/reconstruction/scores/scoreplot.py +13 -11
  214. tomwer/gui/reconstruction/test/test_axis.py +41 -16
  215. tomwer/gui/reconstruction/test/test_nabu.py +31 -9
  216. tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
  217. tomwer/gui/reconstruction/test/test_sadeltabeta.py +12 -2
  218. tomwer/gui/settings.py +5 -28
  219. tomwer/gui/stackplot.py +2 -5
  220. tomwer/gui/stitching/action.py +49 -0
  221. tomwer/gui/stitching/config/axisparams.py +7 -24
  222. tomwer/gui/stitching/config/output.py +10 -8
  223. tomwer/gui/stitching/config/positionoveraxis.py +22 -23
  224. tomwer/gui/stitching/normalization.py +117 -0
  225. tomwer/gui/stitching/stitchandbackground.py +4 -6
  226. tomwer/gui/stitching/stitching.py +267 -45
  227. tomwer/gui/stitching/stitching_preview.py +62 -55
  228. tomwer/gui/stitching/stitching_raw.py +13 -12
  229. tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
  230. tomwer/gui/utils/buttons.py +112 -29
  231. tomwer/gui/utils/inputwidget.py +43 -25
  232. tomwer/gui/utils/lineselector/lineselector.py +1 -1
  233. tomwer/gui/utils/scandescription.py +4 -0
  234. tomwer/gui/utils/step.py +144 -0
  235. tomwer/gui/utils/unitsystem.py +2 -5
  236. tomwer/gui/utils/vignettes.py +176 -15
  237. tomwer/gui/visualization/dataviewer.py +48 -35
  238. tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
  239. tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
  240. tomwer/gui/visualization/scanoverview.py +1 -1
  241. tomwer/gui/visualization/sinogramviewer.py +20 -36
  242. tomwer/gui/visualization/test/test_diffviewer.py +3 -3
  243. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
  244. tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
  245. tomwer/gui/visualization/test/test_stacks.py +3 -3
  246. tomwer/gui/visualization/test/test_volumeviewer.py +65 -67
  247. tomwer/gui/visualization/volumeviewer.py +114 -113
  248. tomwer/io/utils/h5pyutils.py +3 -3
  249. tomwer/io/utils/raw_and_processed_data.py +84 -0
  250. tomwer/io/utils/tomoobj.py +4 -6
  251. tomwer/io/utils/utils.py +7 -7
  252. tomwer/resources/gui/icons/parameters.svg +1 -1
  253. tomwer/resources/gui/icons/ruler.png +0 -0
  254. tomwer/resources/gui/icons/ruler.svg +273 -0
  255. tomwer/resources/gui/icons/short_description.png +0 -0
  256. tomwer/resources/gui/icons/short_description.svg +58 -0
  257. tomwer/resources/gui/icons/url.png +0 -0
  258. tomwer/resources/gui/icons/url.svg +58 -0
  259. tomwer/resources/gui/illustrations/no_rot.svg +1 -1
  260. tomwer/synctools/stacks/edit/darkflatpatch.py +19 -14
  261. tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
  262. tomwer/synctools/stacks/reconstruction/axis.py +4 -4
  263. tomwer/synctools/stacks/reconstruction/castvolume.py +22 -7
  264. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +25 -20
  265. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  266. tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
  267. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
  268. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
  269. tomwer/synctools/test/test_darkRefs.py +7 -58
  270. tomwer/synctools/test/test_foldertransfer.py +6 -6
  271. tomwer/synctools/utils/scanstages.py +6 -6
  272. tomwer/tests/conftest.py +34 -0
  273. tomwer/tests/datasets.py +13 -0
  274. tomwer/tests/test_scripts.py +91 -41
  275. tomwer/tests/utils.py +5 -0
  276. tomwer/third_part/WaitingOverlay.py +110 -0
  277. tomwer/third_part/__init__.py +0 -0
  278. tomwer/version.py +2 -2
  279. tomwer-1.3.12-py3.11-nspkg.pth +1 -0
  280. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/METADATA +73 -58
  281. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/RECORD +287 -286
  282. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/WHEEL +1 -1
  283. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
  284. orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
  285. orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
  286. orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
  287. tomwer/app/lamino.py +0 -143
  288. tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
  289. tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
  290. tomwer/core/process/test/test_lamino.py +0 -76
  291. tomwer/core/test/test_lamino.py +0 -92
  292. tomwer/gui/reconstruction/lamino/__init__.py +0 -31
  293. tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
  294. tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
  295. tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
  296. tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
  297. tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
  298. tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
  299. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
  300. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -760
  301. tomwer/gui/reconstruction/test/test_lamino.py +0 -189
  302. tomwer/resources/gui/icons/esrf_1.svg +0 -307
  303. tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
  304. tomwer/resources/gui/icons/triangle.svg +0 -80
  305. tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
  306. tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
  307. tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
  308. tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
  309. tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
  310. tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
  311. tomwer/resources/gui/illustrations/manual_slice.png +0 -0
  312. tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
  313. tomwer/resources/gui/illustrations/psi_angle.png +0 -0
  314. tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
  315. tomwer/resources/gui/illustrations/rotation_center.png +0 -0
  316. tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
  317. tomwer/resources/gui/illustrations/slice_stack.png +0 -0
  318. tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
  319. tomwer/resources/gui/illustrations/xy_slice.png +0 -0
  320. tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
  321. tomwer/resources/gui/illustrations/xz_slice.png +0 -0
  322. tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
  323. tomwer/resources/gui/illustrations/yz_slice.png +0 -0
  324. tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
  325. tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
  326. tomwer/synctools/test/test_scanstages.py +0 -162
  327. tomwer/tests/utils/__init__.py +0 -247
  328. tomwer/tests/utils/utilstest.py +0 -220
  329. tomwer-1.2.1-py3.11-nspkg.pth +0 -1
  330. /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
  331. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/LICENSE +0 -0
  332. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/entry_points.txt +0 -0
  333. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/namespace_packages.txt +0 -0
  334. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,144 @@
1
+ import functools
2
+ from silx.gui import qt
3
+ from typing import Optional, Union
4
+ from tomwer.gui.utils.qt_utils import block_signals
5
+
6
+
7
+ class StepSizeSelectorWidget(qt.QGroupBox):
8
+ """
9
+ Widget to define some steps size (as float). Used by the Axis and the AxisOrdered widgets
10
+
11
+ :param str title: title to provide to the group box
12
+ :param str label: text for the label set at the left of the QLineEdit
13
+ :param fine_value: (optional) value to provide for 'fine' step
14
+ :param medium_value: (optional) value to provide for 'medium' step
15
+ :param rough_value: (optional) value to provide for 'rough' step
16
+ :param dtype: type of the step. Can be int or float
17
+ """
18
+
19
+ valueChanged = qt.Signal()
20
+
21
+ def __init__(
22
+ self,
23
+ parent=None,
24
+ title="",
25
+ label: str = "step size",
26
+ fine_value: [float, int, None] = 0.1,
27
+ medium_value: [float, int, None] = 0.5,
28
+ rough_value: [float, int, None] = 1.0,
29
+ unit: Optional[str] = "px",
30
+ dtype: Union[int, float] = float,
31
+ ):
32
+ assert fine_value is None or isinstance(
33
+ fine_value, dtype
34
+ ), f"fine_value is expected to be None or a {dtype}. Get {type(fine_value)}"
35
+ assert medium_value is None or isinstance(
36
+ medium_value, dtype
37
+ ), f"medium_value is expected to be None or a {dtype}. Get {type(medium_value)}"
38
+ assert rough_value is None or isinstance(
39
+ rough_value, dtype
40
+ ), f"rough_value is expected to be None or a {dtype}. Get {type(rough_value)}"
41
+ super().__init__(title, parent)
42
+ self._dtype = dtype
43
+ if unit is None:
44
+ unit = ""
45
+ else:
46
+ unit = f" {unit}"
47
+ self.setLayout(qt.QGridLayout())
48
+ self.layout().setSpacing(2)
49
+
50
+ # label
51
+ self.layout().addWidget(qt.QLabel(label), 0, 0, 3, 1)
52
+
53
+ # QLE manual step size
54
+ default_value = medium_value or fine_value or rough_value
55
+ self._manualLE = qt.QLineEdit(str(default_value), parent=self)
56
+ if dtype is float:
57
+ validator = qt.QDoubleValidator(parent=self._manualLE, decimals=2)
58
+ validator.setBottom(0.0)
59
+ elif dtype is int:
60
+ validator = qt.QIntValidator(parent=self._manualLE)
61
+ validator.setBottom(0)
62
+ else:
63
+ raise ValueError("dtype is expected to be int or float")
64
+ self._manualLE.setValidator(validator)
65
+ self.layout().addWidget(self._manualLE, 0, 1, 3, 1)
66
+
67
+ # buttons
68
+ buttons_font = self.font()
69
+ buttons_font.setPixelSize(10)
70
+ self._expectedValues = {}
71
+ # for each button associate the expected value
72
+
73
+ # fine
74
+ if fine_value is not None:
75
+ self._fineButton = qt.QPushButton(f"fine ({fine_value}{unit})", parent=self)
76
+ self._fineButton.setCheckable(True)
77
+ self._fineButton.setFont(buttons_font)
78
+ self.layout().addWidget(self._fineButton, 0, 2, 1, 1)
79
+ self._fineButton.released.connect(
80
+ functools.partial(self.setStepSize, fine_value)
81
+ )
82
+ self._expectedValues[self._fineButton] = fine_value
83
+ else:
84
+ self._fineButton = None
85
+
86
+ # medium
87
+ if medium_value is not None:
88
+ self._mediumButton = qt.QPushButton(
89
+ f"medium ({medium_value}{unit})", parent=self
90
+ )
91
+ self._mediumButton.setCheckable(True)
92
+ self._mediumButton.setFont(buttons_font)
93
+ self.layout().addWidget(self._mediumButton, 1, 2, 1, 1)
94
+ self._mediumButton.released.connect(
95
+ functools.partial(self.setStepSize, medium_value)
96
+ )
97
+ self._expectedValues[self._mediumButton] = medium_value
98
+ else:
99
+ self._mediumButton = None
100
+
101
+ # rough
102
+ if rough_value is not None:
103
+ self._roughButton = qt.QPushButton(
104
+ f"rough ({rough_value}{unit})", parent=self
105
+ )
106
+ self._roughButton.setCheckable(True)
107
+ self._roughButton.setFont(buttons_font)
108
+ self.layout().addWidget(self._roughButton, 2, 2, 1, 1)
109
+ self._roughButton.released.connect(
110
+ functools.partial(self.setStepSize, rough_value)
111
+ )
112
+ self._expectedValues[self._roughButton] = rough_value
113
+ else:
114
+ self._roughButton = None
115
+
116
+ # connect signal / slot
117
+ self._manualLE.textChanged.connect(self._updateButtonChecked)
118
+ self._manualLE.textChanged.connect(self.valueChanged)
119
+ # set up
120
+ self._updateButtonChecked(self._manualLE.text())
121
+
122
+ def _updateButtonChecked(self, text):
123
+ if text == "":
124
+ return
125
+ current_value = self._dtype(text)
126
+ for button, activation_value in self._expectedValues.items():
127
+ with block_signals(button):
128
+ button.setChecked(activation_value == current_value)
129
+
130
+ def getStepSize(self) -> Union[float, int]:
131
+ """
132
+
133
+ :return: displacement shift defined
134
+ :rtype: float
135
+ """
136
+ return self._dtype(self._manualLE.text())
137
+
138
+ def setStepSize(self, value: Union[float, int]):
139
+ """
140
+
141
+ :param float value: shift step
142
+ """
143
+ assert isinstance(value, self._dtype)
144
+ self._manualLE.setText(str(value))
@@ -35,7 +35,7 @@ __date__ = "21/09/2018"
35
35
  import logging
36
36
 
37
37
  from silx.gui import qt
38
- from tomoscan.unitsystem import metricsystem
38
+ from pyunitsystem import metricsystem
39
39
 
40
40
  from tomwer.core.utils.char import MU_CHAR
41
41
 
@@ -64,10 +64,7 @@ class PixelEntry(qt.QWidget):
64
64
  self.layout().addWidget(self._qlePixelSize)
65
65
 
66
66
  # connect signal / slot
67
- self._qlePixelSize.editingFinished.connect(self._callbackEditingFinished)
68
-
69
- def _callbackEditingFinished(self):
70
- self.valueChanged.emit()
67
+ self._qlePixelSize.editingFinished.connect(self.valueChanged)
71
68
 
72
69
  def getValue(self):
73
70
  if self._qlePixelSize.text() in ("unknown", ""):
@@ -36,7 +36,7 @@ __date__ = "26/02/2021"
36
36
 
37
37
 
38
38
  import logging
39
- from typing import Iterable, Union
39
+ from typing import Iterable, Union, Optional
40
40
 
41
41
  import numpy
42
42
  from silx.gui import qt
@@ -44,8 +44,9 @@ from silx.gui.plot import PlotWindow
44
44
  from silx.gui.plot.utils.axis import SyncAxes
45
45
  from silx.io.url import DataUrl
46
46
  from tomoscan.esrf.scan.utils import get_data
47
+ from math import floor
47
48
 
48
- from tomwer.core.process.reconstruction.scores.scores import ComputedScore
49
+ from tomwer.core.process.reconstruction.scores.scores import ComputedScore, ScoreMethod
49
50
 
50
51
  _logger = logging.getLogger(__name__)
51
52
 
@@ -55,6 +56,12 @@ class VignettesQDialog(qt.QDialog):
55
56
 
56
57
  SIZE_HINT = qt.QSize(820, 820)
57
58
 
59
+ AUTO_NB_COLUMN = "auto"
60
+
61
+ COLUMN_VALUES = (AUTO_NB_COLUMN, 1, 2, 3, 4, 5, 6, 8, 10)
62
+
63
+ RESIZE_MAX_TIME = 500
64
+
58
65
  def __init__(
59
66
  self,
60
67
  value_name,
@@ -65,10 +72,24 @@ class VignettesQDialog(qt.QDialog):
65
72
  colormap=None,
66
73
  ):
67
74
  qt.QDialog.__init__(self, parent)
75
+ self.setWindowFlags(qt.Qt.Widget)
68
76
  self._scan = None
69
77
  self._selectedValue = None
70
78
  self.setLayout(qt.QVBoxLayout())
79
+
80
+ self._nbColumnCB = qt.QComboBox(self)
81
+ for nb_column in self.COLUMN_VALUES:
82
+ self._nbColumnCB.addItem(str(nb_column))
83
+
84
+ self._columnWidget = qt.QWidget(self)
85
+ # needed intermediary widget because the 'setWidgetResizable' fails with QFormLayout on PyQt 5.14.1 and will want to keep compatiblity for now
86
+ self._columnWidget.setLayout(qt.QFormLayout())
87
+ self._columnWidget.layout().addRow("number of column", self._nbColumnCB)
88
+ self.layout().addWidget(self._columnWidget)
89
+
71
90
  self._mainWidget = qt.QScrollArea(self)
91
+ self._mainWidget.setHorizontalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff)
92
+ self._mainWidget.setVerticalScrollBarPolicy(qt.Qt.ScrollBarAsNeeded)
72
93
  self._vignettesWidget = VignettesWidget(
73
94
  self,
74
95
  with_spacer=True,
@@ -86,18 +107,30 @@ class VignettesQDialog(qt.QDialog):
86
107
  types = qt.QDialogButtonBox.Ok | qt.QDialogButtonBox.Cancel
87
108
  self._buttons = qt.QDialogButtonBox(parent=self)
88
109
  self._buttons.setStandardButtons(types)
110
+ self._buttons.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum)
89
111
  self.layout().addWidget(self._buttons)
90
112
 
91
113
  # connect signal slot
92
114
  self._buttons.button(qt.QDialogButtonBox.Ok).clicked.connect(self.accept)
93
-
94
115
  self._buttons.button(qt.QDialogButtonBox.Cancel).clicked.connect(self.reject)
116
+ self._nbColumnCB.currentIndexChanged.connect(self._updateNbColumn)
117
+
118
+ self._resizeCallback = None
119
+ self._firstResizeEvent = True
95
120
 
96
121
  def sizeHint(self):
97
122
  """Return a reasonable default size for usage in :class:`PlotWindow`"""
98
123
  return self.SIZE_HINT
99
124
 
100
- def setScores(self, scores, score_method):
125
+ def setScores(self, scores: dict, score_method: ScoreMethod):
126
+ """
127
+ Expect a dictionary with possible values to select as key and
128
+ (2D numpy.array, score) as value.
129
+ Where the 2D numpy.array is the frame to display and the score if the
130
+ "indicator" score to display with the frame.
131
+ :param dict scores: with score as key and a tuple (url|numpy array, ComputedScore) as value
132
+ :param ScoreMethod score_method: score kind to be display
133
+ """
101
134
  self._vignettesWidget.setScores(scores=scores, score_method=score_method)
102
135
 
103
136
  def selectedValue(self):
@@ -116,6 +149,55 @@ class VignettesQDialog(qt.QDialog):
116
149
  self._vignettesWidget.close()
117
150
  super().reject()
118
151
 
152
+ def getNColumn(self) -> Optional[int]:
153
+ n_column = self._nbColumnCB.currentText()
154
+ if n_column == self.AUTO_NB_COLUMN:
155
+ return None
156
+ else:
157
+ return int(n_column)
158
+
159
+ def setNbColumn(self, value: Union[int, str]):
160
+ if value not in self.COLUMN_VALUES:
161
+ raise ValueError(
162
+ f"Unhandled number of column requested ({value}). Valid values are {self.COLUMN_VALUES}"
163
+ )
164
+ idx = self._nbColumnCB.findText(str(value))
165
+ self._nbColumnCB.setCurrentIndex(idx)
166
+
167
+ @staticmethod
168
+ def computeOptimalNColumn(window_width, vignette_width):
169
+ n = floor(window_width / vignette_width)
170
+ # To check with Jerome; He seems to have work on the topic.
171
+ # might have some method for a smart way to do it
172
+ return max(1, n)
173
+
174
+ def _updateNbColumn(self, force_update=False):
175
+ n_colum = self.getNColumn()
176
+ if n_colum is None:
177
+ n_colum = self.computeOptimalNColumn(
178
+ window_width=self.width(),
179
+ vignette_width=400,
180
+ )
181
+ self._vignettesWidget.setNElementsPerRow(n_colum, force_update=force_update)
182
+
183
+ def resizeEvent(self, event):
184
+ if self._firstResizeEvent:
185
+ # cheap way to filter the first resize and avoid updating it
186
+ self._firstResizeEvent = False
187
+ else:
188
+ # print("resize event received...", event)
189
+ if self._resizeCallback is not None:
190
+ self._resizeCallback.timeout.disconnect(self._updateNbColumn)
191
+ self._resizeCallback.stop()
192
+ self._resizeCallback.deleteLater()
193
+ self._resizeCallback = None
194
+ self._resizeCallback = qt.QTimer(self)
195
+ self._resizeCallback.setSingleShot(True)
196
+ self._resizeCallback.timeout.connect(self._updateNbColumn)
197
+ self._resizeCallback.start(self.RESIZE_MAX_TIME)
198
+
199
+ super().resizeEvent(event)
200
+
119
201
 
120
202
  class VignettesWidget(qt.QWidget):
121
203
  """
@@ -132,6 +214,7 @@ class VignettesWidget(qt.QWidget):
132
214
  """
133
215
 
134
216
  DEFAULT_PLOT_PER_ROW = 2
217
+ MAX_NB_COLUMN = 999999
135
218
 
136
219
  def __init__(
137
220
  self,
@@ -155,6 +238,9 @@ class VignettesWidget(qt.QWidget):
155
238
  self._scoreFormat = score_format
156
239
  self._colormap = colormap
157
240
  self._vignettes = []
241
+ self.__score_method = None
242
+
243
+ self.setLayout(qt.QGridLayout())
158
244
 
159
245
  def close(self):
160
246
  for vignette in self._vignettes:
@@ -164,24 +250,81 @@ class VignettesWidget(qt.QWidget):
164
250
  def selectedValue(self):
165
251
  sel_vignette = self._vignettesGroup.checkedButton()
166
252
  if sel_vignette is not None:
167
- return sel_vignette.getValue()
253
+ return sel_vignette.getScoreValue()
168
254
  else:
169
255
  return None
170
256
 
171
- def setNElementsPerRow(self, n: int):
172
- self._nPlotPerRow = n
257
+ def setNElementsPerRow(self, n: int, force_update):
258
+ assert isinstance(n, int), "number of column must be an int"
259
+ if self._nPlotPerRow != n:
260
+ self._nPlotPerRow = n
261
+ self.__update()
262
+ elif force_update:
263
+ self.__update()
264
+
265
+ def __update(self):
266
+ scores, score_method = self.getScores()
267
+ self.setScores(scores, score_method)
268
+ # raise NotImplementedError
269
+
270
+ def getNElementsPerRow(self):
271
+ return self._nPlotPerRow
272
+
273
+ def getScores(self) -> tuple:
274
+ """
275
+ Return currently displayed scores as (scores, scores_method)
276
+ """
277
+ scores = {}
278
+ scores_method = self.__score_method
279
+ for vignette in self._vignettes:
280
+ assert isinstance(vignette, Vignette)
281
+ if scores_method in (ScoreMethod.STD, ScoreMethod.STD_INVERSE):
282
+ compute_score_args = {
283
+ "std": vignette.getScoreValue(),
284
+ "tv": None,
285
+ }
286
+ elif scores_method in (ScoreMethod.TV, ScoreMethod.TV_INVERSE):
287
+ compute_score_args = {
288
+ "std": None,
289
+ "tv": vignette.getScoreValue(),
290
+ }
291
+ elif scores_method in (ScoreMethod.TOMO_CONSISTENCY):
292
+ compute_score_args = {
293
+ "std": None,
294
+ "tv": None,
295
+ "tomo_consitency": vignette.getScoreValue(),
296
+ }
297
+ else:
298
+ raise ValueError(f"score method unhandled: {scores_method.value}")
299
+
300
+ scores[vignette.getValue()] = (
301
+ vignette.getData(),
302
+ ComputedScore(**compute_score_args),
303
+ )
304
+ return scores, scores_method
305
+
306
+ def clearVignettes(self):
307
+ for vignette in self._vignettes:
308
+ self.layout().removeWidget(vignette)
309
+ vignette.setParent(None)
310
+ self._vignettesGroup.removeButton(vignette)
311
+ # vignette.deleteLater()
312
+ self._vignettes = []
173
313
 
174
- def setScores(self, scores: dict, score_method):
314
+ def setScores(self, scores: dict, score_method: ScoreMethod):
175
315
  """
176
316
  Expect a dictionary with possible values to select as key and
177
317
  (2D numpy.array, score) as value.
178
318
  Where the 2D numpy.array is the frame to display and the score if the
179
319
  "indicator" score to display with the frame.
180
- :param dict scores: with score as key and url or numpy array as value
320
+ :param dict scores: with score as key and a tuple (url|numpy array, ComputedScore) as value
321
+ :param ScoreMethod score_method: score kind to be display
181
322
  """
182
323
  if len(scores) < 1:
183
324
  return
184
- self.setLayout(qt.QGridLayout())
325
+
326
+ self.clearVignettes()
327
+
185
328
  i_row = 0
186
329
  scores_values = []
187
330
  for i_score, (value, (data, score_cls)) in enumerate(scores.items()):
@@ -190,6 +333,7 @@ class VignettesWidget(qt.QWidget):
190
333
  f"score is expected to be a dict with values as (v1: numpy.ndarray, v2: ComputedScore). v2 type Found: {type(score_cls)}"
191
334
  )
192
335
  scores_values.append(score_cls.get(score_method))
336
+ self.__score_method = ScoreMethod.from_value(score_method)
193
337
  highest_score_indices = numpy.nanargmax(scores_values)
194
338
  self._vignettesGroup = qt.QButtonGroup(self)
195
339
  self._vignettesGroup.setExclusive(True)
@@ -202,10 +346,9 @@ class VignettesWidget(qt.QWidget):
202
346
 
203
347
  for i_score, (value, (data, score_cls)) in enumerate(scores.items()):
204
348
  score = score_cls.get(score_method)
205
- i_column = i_score % self.DEFAULT_PLOT_PER_ROW
349
+ i_column = i_score % self.getNElementsPerRow()
206
350
  # TODO: instead of having a binary color we could use
207
- # colormap
208
- # TODO: synchronize zooms
351
+ # colormap from green (good score) to red (bad score score)
209
352
  if i_score == highest_score_indices or i_score in highest_score_indices:
210
353
  frame_color = qt.Qt.green
211
354
  else:
@@ -227,7 +370,7 @@ class VignettesWidget(qt.QWidget):
227
370
  yAxis.append(widget.getPlotWidget().getYAxis())
228
371
 
229
372
  self.layout().addWidget(widget, i_row, i_column)
230
- if i_column == self.DEFAULT_PLOT_PER_ROW - 1:
373
+ if i_column == self.getNElementsPerRow() - 1:
231
374
  i_row += 1
232
375
  if i_score == 0:
233
376
  # we cannot request all widget to keep the aspect ratio
@@ -238,7 +381,8 @@ class VignettesWidget(qt.QWidget):
238
381
  if self._withSpacer:
239
382
  spacer = qt.QWidget(self)
240
383
  spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding)
241
- self.layout().addWidget(spacer, i_row + 1, self.DEFAULT_PLOT_PER_ROW - 1)
384
+ # to simplify we add a spacer at the end. We do not expect to have more than 999999 column
385
+ self.layout().addWidget(spacer, i_row + 1, self.MAX_NB_COLUMN)
242
386
 
243
387
  # constrain axis synchronization
244
388
  self.__constraintXAxis = SyncAxes(
@@ -339,6 +483,18 @@ class Vignette(qt.QToolButton):
339
483
  def getValue(self):
340
484
  return self._value
341
485
 
486
+ def getData(self) -> numpy.ndarray:
487
+ return self._plot.getImage().getData()
488
+
489
+ def getScoreValue(self):
490
+ return self._valueLabel.score
491
+
492
+ def getScoreName(self):
493
+ return self._scoreName
494
+
495
+ def getValueName(self):
496
+ return self._valueName
497
+
342
498
  def paintEvent(self, event):
343
499
  super().paintEvent(event)
344
500
  painter = qt.QPainter(self)
@@ -375,6 +531,7 @@ class ValueLabel(qt.QWidget):
375
531
  self, parent, value, score, value_name, score_name, value_format, score_format
376
532
  ):
377
533
  qt.QWidget.__init__(self, parent)
534
+ self._score = score
378
535
  self.setLayout(qt.QHBoxLayout())
379
536
  if value_format is not None:
380
537
  str_value = value_format.format(value)
@@ -390,3 +547,7 @@ class ValueLabel(qt.QWidget):
390
547
  txt = f"({score_name}: {str_score})"
391
548
  self._scoreLabel = qt.QLabel(txt, self)
392
549
  self.layout().addWidget(self._scoreLabel)
550
+
551
+ @property
552
+ def score(self):
553
+ return self._score
@@ -31,6 +31,7 @@ import logging
31
31
  import weakref
32
32
 
33
33
  from silx.gui import qt
34
+ from silx.gui.dialog.ColormapDialog import DisplayMode
34
35
  from silx.gui.plot.ImageStack import ImageStack as _ImageStack
35
36
  from silx.gui.plot.ImageStack import UrlLoader
36
37
  from silx.gui.utils.signal import SignalProxy
@@ -54,12 +55,6 @@ except ImportError: # pragma: no cover
54
55
  has_PIL = False # pragma: no cover
55
56
  else:
56
57
  has_PIL = True
57
- try:
58
- import cv2 # pragma: no cover
59
- except ImportError: # pragma: no cover
60
- has_cv2 = False # pragma: no cover
61
- else:
62
- has_cv2 = True
63
58
  import os
64
59
  import time
65
60
 
@@ -85,6 +80,7 @@ class DataViewer(qt.QMainWindow):
85
80
  self._viewer = ImageStack(
86
81
  parent=self, show_overview=show_overview, backend=backend
87
82
  )
83
+ self._viewer.getPlotWidget().getMaskAction().setVisible(False)
88
84
  self._viewer.getPlotWidget().setYAxisInverted(Y_AXIS_DOWNWARD)
89
85
  # set an UrlLoader managing .npy and .vol
90
86
  self._viewer.getPlotWidget().setKeepDataAspectRatio(True)
@@ -112,7 +108,7 @@ class DataViewer(qt.QMainWindow):
112
108
 
113
109
  # connect signal / slot
114
110
  self._controls.sigDisplayModeChanged.connect(self._updateDisplay)
115
- self._controls.sigDisplayModeChanged.connect(self._reportSettingsUpdate)
111
+ self._controls.sigDisplayModeChanged.connect(self.sigConfigChanged)
116
112
 
117
113
  def getPlotWidget(self):
118
114
  return self._viewer.getPlotWidget()
@@ -129,9 +125,6 @@ class DataViewer(qt.QMainWindow):
129
125
  def setScanOverviewVisible(self, visible: bool) -> None:
130
126
  self._viewer.setScanOverviewVisible(visible=visible)
131
127
 
132
- def cleanBeforeQuit(self):
133
- self._viewer._plot.updateThread.stop()
134
-
135
128
  def getUrlListDockWidget(self):
136
129
  return self._viewer.getUrlListDockWidget()
137
130
 
@@ -259,9 +252,6 @@ class DataViewer(qt.QMainWindow):
259
252
  else:
260
253
  self._viewer.reset()
261
254
 
262
- def _reportSettingsUpdate(self):
263
- self.sigConfigChanged.emit()
264
-
265
255
  def clear(self):
266
256
  self._scan = None
267
257
  self._viewer.reset()
@@ -421,14 +411,27 @@ class DisplayControl(qt.QWidget):
421
411
 
422
412
 
423
413
  class ImageStack(_ImageStack):
414
+ """
415
+ Image stack dedicated to data display.
416
+
417
+ It deal for example with data normalization...
418
+ """
419
+
424
420
  def __init__(self, parent, show_overview=True, backend=None):
425
421
  self._normFct = None
426
422
  self._url_indexes = None
427
423
  super().__init__(parent)
428
424
  self.getPlotWidget().setBackend(backend)
425
+
426
+ # tune colormap dialog to have histogram by default
427
+ colormapAction = self.getPlotWidget().getColormapAction()
428
+ colormapDialog = colormapAction.getColormapDialog()
429
+ colormapDialog.getHistogramWidget().setDisplayMode(DisplayMode.HISTOGRAM)
430
+ colormapAction.setColormapDialog(colormapDialog)
431
+
429
432
  self.setUrlLoaderClass(_TomwerUrlLoader)
430
433
  # hide axis to be display
431
- self._plot.getPlotWidget().setAxesDisplayed(False)
434
+ self._plot.setAxesDisplayed(False)
432
435
  self._loadSliceParams = False
433
436
  self._resetZoom = True
434
437
 
@@ -493,17 +496,15 @@ class ImageStack(_ImageStack):
493
496
  return self._tableDockWidget
494
497
 
495
498
  def resetZoom(self):
496
- self._plot.getPlotWidget().resetZoom()
499
+ self._plot.resetZoom()
497
500
 
498
501
  def setLimits(self, x_min, x_max, y_min, y_max):
499
- plot = self._plot.getPlotWidget()
500
- plot.setLimits(xmin=x_min, ymin=y_min, xmax=x_max, ymax=y_max)
502
+ self._plot.setLimits(xmin=x_min, ymin=y_min, xmax=x_max, ymax=y_max)
501
503
 
502
504
  def getLimits(self):
503
- plot = self._plot.getPlotWidget()
504
505
  limits = []
505
- limits.extend(plot.getGraphXLimits())
506
- limits.extend(plot.getGraphYLimits())
506
+ limits.extend(self._plot.getGraphXLimits())
507
+ limits.extend(self._plot.getGraphYLimits())
507
508
  return tuple(limits)
508
509
 
509
510
  def setSliceReconsParamsVisible(self, visible):
@@ -535,8 +536,19 @@ class ImageStack(_ImageStack):
535
536
  return
536
537
 
537
538
  if data.ndim != 2:
538
- if data.ndim == 3 and data.shape[0] == 1:
539
- data = data.reshape((data.shape[1], data.shape[2]))
539
+ if data.ndim == 3:
540
+ if data.shape[0] == 1:
541
+ # if reconstruction along z
542
+ data = data.reshape((data.shape[1], data.shape[2]))
543
+ elif data.shape[1] == 1:
544
+ # if reconstruction along y
545
+ data = data.reshape((data.shape[0], data.shape[2]))
546
+ elif data.shape[2] == 1:
547
+ # if reconstruction along z
548
+ data = data.reshape((data.shape[0], data.shape[1]))
549
+ else:
550
+ _logger.warning(f"Image Stack only manage 2D data. Url: {url}")
551
+ return
540
552
  else:
541
553
  _logger.warning(f"Image Stack only manage 2D data. Url: {url}")
542
554
  return
@@ -547,10 +559,14 @@ class ImageStack(_ImageStack):
547
559
  self._urlData[url] = norm_data
548
560
 
549
561
  if self.getCurrentUrl().path() == url:
550
- self._plot.setData(self._urlData[url])
562
+ self._plot.addImage(self._urlData[url])
563
+ if hasattr(self, "getWaiterOverlay"):
564
+ self.getWaiterOverlay().hide()
565
+ else:
566
+ self._waitingOverlay.hide()
551
567
  if self._resetZoom:
552
568
  self._resetZoom = False
553
- self._plot.getPlotWidget().resetZoom()
569
+ self._plot.resetZoom()
554
570
 
555
571
  if sender in self._loadingThreads:
556
572
  self._loadingThreads.remove(sender)
@@ -560,11 +576,16 @@ class ImageStack(_ImageStack):
560
576
  self._resetZoom = reset
561
577
 
562
578
  def setCurrentUrl(self, url: DataUrl):
563
- if isinstance(url, str):
579
+ if url in ("", None):
580
+ url = None
581
+ elif isinstance(url, str):
564
582
  url = DataUrl(path=url)
565
583
  elif not isinstance(url, DataUrl):
566
584
  raise TypeError
567
- if self._loadSliceParams:
585
+ if url is None:
586
+ pass
587
+ # FIXME: add a function to clear the metadata widget
588
+ elif self._loadSliceParams:
568
589
  try:
569
590
  self._reconsWidget.setVolumeMetadata(
570
591
  self._metadatas.get(url.path(), None)
@@ -575,7 +596,7 @@ class ImageStack(_ImageStack):
575
596
 
576
597
  def setUrls(self, urls: list):
577
598
  _ImageStack.setUrls(self, urls)
578
- listWidget = self._urlsTable._urlsTable._listWidget
599
+ listWidget = self._urlsTable._urlsTable
579
600
  items = []
580
601
  for i in range(listWidget.count()):
581
602
  # TODO: do this on the fly
@@ -607,14 +628,6 @@ class _TomwerUrlLoader(UrlLoader):
607
628
  def run(self):
608
629
  if self.url.file_path().endswith(".vol"):
609
630
  self.data = self._load_vol()
610
- elif self.url.scheme() == "cv2":
611
- if has_cv2:
612
- self.data = cv2.imread(self.url.file_path(), -1)
613
- else:
614
- self.data = None
615
- _logger.warning(
616
- f"need to install cv2 to read cast file {self.url.file_path()} because fabio fails to read float16"
617
- )
618
631
  elif self.url.scheme() == "tomwer":
619
632
  if has_PIL:
620
633
  self.data = numpy.array(Image.open(self.url.file_path()))