tomwer 1.0.4__py3-none-any.whl → 1.1.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 (256) hide show
  1. orangecontrib/tomwer/tutorials/EBS_tomo_listener.ows +39 -0
  2. orangecontrib/tomwer/tutorials/cast_volume.ows +34 -0
  3. orangecontrib/tomwer/tutorials/simple_slice_reconstruction.ows +39 -0
  4. orangecontrib/tomwer/tutorials/simple_volume_local_reconstruction.ows +49 -0
  5. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +59 -0
  6. orangecontrib/tomwer/tutorials/using_saaxis_to_find_cor.ows +44 -0
  7. orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +1 -1
  8. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +14 -4
  9. orangecontrib/tomwer/widgets/cluster/__init__.py +1 -1
  10. orangecontrib/tomwer/widgets/control/DataListOW.py +12 -5
  11. orangecontrib/tomwer/widgets/control/DataListenerOW.py +18 -9
  12. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +13 -6
  13. orangecontrib/tomwer/widgets/control/DataTransfertOW.py +4 -5
  14. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +8 -4
  15. orangecontrib/tomwer/widgets/control/DataWatcherOW.py +4 -6
  16. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +49 -62
  17. orangecontrib/tomwer/widgets/control/FilterOW.py +2 -4
  18. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +93 -0
  19. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +135 -128
  20. orangecontrib/tomwer/widgets/control/NotifierOW.py +31 -7
  21. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +3 -5
  22. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +85 -0
  23. orangecontrib/tomwer/widgets/control/VolumeSelector.py +12 -4
  24. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +11 -7
  25. orangecontrib/tomwer/widgets/control/icons/notification.svg +4 -4
  26. orangecontrib/tomwer/widgets/control/icons/nxtomomill.png +0 -0
  27. orangecontrib/tomwer/widgets/control/icons/nxtomomill.svg +8 -5
  28. orangecontrib/tomwer/widgets/control/icons/tomoobjserie.png +0 -0
  29. orangecontrib/tomwer/widgets/control/icons/tomoobjserie.svg +138 -0
  30. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +16 -4
  31. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +100 -0
  32. orangecontrib/tomwer/widgets/edit/icons/image_key_editor.png +0 -0
  33. orangecontrib/tomwer/widgets/edit/icons/image_key_upgrader.png +0 -0
  34. orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.png +0 -0
  35. orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.svg +123 -0
  36. orangecontrib/tomwer/widgets/edit/test/test_dark_flat_patch.py +21 -1
  37. orangecontrib/tomwer/widgets/edit/test/test_image_key_editor.py +1 -1
  38. orangecontrib/tomwer/widgets/edit/test/test_image_key_upgrader.py +1 -1
  39. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +25 -0
  40. orangecontrib/tomwer/widgets/other/PythonScriptOW.py +19 -10
  41. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +20 -14
  42. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +24 -10
  43. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +26 -21
  44. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +29 -12
  45. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +44 -17
  46. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +28 -18
  47. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +24 -17
  48. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +6 -6
  49. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +4 -2
  50. orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.png +0 -0
  51. orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.svg +11 -8
  52. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +10 -4
  53. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  54. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +69 -0
  55. orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +2 -4
  56. orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.png +0 -0
  57. orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.svg +105 -0
  58. tomwer/__main__.py +10 -5
  59. tomwer/app/canvas_launcher/config.py +10 -10
  60. tomwer/app/canvas_launcher/mainwindow.py +68 -6
  61. tomwer/app/canvas_launcher/widgetsscheme.py +1 -3
  62. tomwer/app/darkref.py +16 -12
  63. tomwer/app/imagekeyeditor.py +2 -2
  64. tomwer/app/imagekeyupgrader.py +104 -0
  65. tomwer/app/nxtomoeditor.py +103 -0
  66. tomwer/app/rsync.py +1 -1
  67. tomwer/core/cluster/cluster.py +1 -1
  68. tomwer/core/futureobject.py +1 -0
  69. tomwer/core/process/control/datalistener/datalistener.py +7 -1
  70. tomwer/core/process/control/datalistener/rpcserver.py +3 -3
  71. tomwer/core/process/control/datawatcher/datawatcher.py +18 -18
  72. tomwer/core/process/control/datawatcher/datawatcherobserver.py +5 -8
  73. tomwer/core/process/control/datawatcher/datawatcherprocess.py +2 -3
  74. tomwer/core/process/control/datawatcher/edfdwprocess.py +2 -2
  75. tomwer/core/process/control/nxtomomill.py +33 -58
  76. tomwer/core/process/control/scanlist.py +2 -1
  77. tomwer/core/process/control/scanselector.py +7 -0
  78. tomwer/core/process/control/scantransfer.py +9 -18
  79. tomwer/core/process/control/scanvalidator.py +6 -5
  80. tomwer/core/process/control/singletomoobj.py +2 -1
  81. tomwer/core/process/control/timer.py +2 -1
  82. tomwer/core/process/control/tomoobjserie.py +8 -0
  83. tomwer/core/process/control/volumeselector.py +2 -1
  84. tomwer/core/process/control/volumesymlink.py +2 -1
  85. tomwer/core/process/edit/darkflatpatch.py +2 -1
  86. tomwer/core/process/edit/imagekeyeditor.py +4 -3
  87. tomwer/core/process/reconstruction/axis/axis.py +29 -32
  88. tomwer/core/process/reconstruction/axis/mode.py +3 -2
  89. tomwer/core/process/reconstruction/axis/params.py +35 -16
  90. tomwer/core/process/reconstruction/darkref/darkrefs.py +90 -707
  91. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +44 -16
  92. tomwer/core/process/reconstruction/darkref/params.py +62 -67
  93. tomwer/core/process/reconstruction/lamino/tofu.py +1 -1
  94. tomwer/core/process/reconstruction/nabu/castvolume.py +21 -26
  95. tomwer/core/process/reconstruction/nabu/nabucommon.py +36 -38
  96. tomwer/core/process/reconstruction/nabu/nabuscores.py +28 -13
  97. tomwer/core/process/reconstruction/nabu/nabuslices.py +41 -14
  98. tomwer/core/process/reconstruction/nabu/nabuvolume.py +21 -12
  99. tomwer/core/process/reconstruction/nabu/utils.py +12 -1
  100. tomwer/core/process/reconstruction/normalization/normalization.py +9 -8
  101. tomwer/core/process/reconstruction/saaxis/saaxis.py +46 -20
  102. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +38 -12
  103. tomwer/core/process/reconstruction/test/__init__.py +0 -39
  104. tomwer/core/process/reconstruction/test/test_axis_params.py +25 -3
  105. tomwer/core/process/reconstruction/test/test_darkref_copy.py +117 -1
  106. tomwer/core/process/script/python.py +16 -12
  107. tomwer/core/process/task.py +3 -7
  108. tomwer/core/process/test/test_axis.py +1 -1
  109. tomwer/core/process/test/test_dark_and_flat.py +41 -111
  110. tomwer/core/process/test/test_data_listener.py +0 -29
  111. tomwer/core/process/test/test_data_transfer.py +10 -14
  112. tomwer/core/process/test/test_nabu.py +1 -1
  113. tomwer/core/process/test/test_normalization.py +1 -1
  114. tomwer/core/process/visualization/liveslice.py +6 -0
  115. tomwer/core/scan/blissscan.py +37 -2
  116. tomwer/core/scan/edfscan.py +14 -4
  117. tomwer/core/scan/hdf5scan.py +10 -4
  118. tomwer/core/scan/scanbase.py +35 -29
  119. tomwer/core/scan/scanfactory.py +3 -17
  120. tomwer/core/scan/test/test_h5.py +1 -1
  121. tomwer/core/scan/test/test_process_registration.py +0 -11
  122. tomwer/core/scan/test/test_scan.py +32 -30
  123. tomwer/core/settings.py +2 -2
  124. tomwer/core/test/test_utils.py +1 -1
  125. tomwer/core/tomwer_object.py +19 -0
  126. tomwer/core/utils/__init__.py +0 -45
  127. tomwer/core/utils/char.py +2 -0
  128. tomwer/core/utils/gpu.py +5 -5
  129. tomwer/core/utils/nxtomoutils.py +2 -2
  130. tomwer/core/utils/scanutils.py +50 -0
  131. tomwer/core/utils/volumeutils.py +13 -0
  132. tomwer/core/volume/edfvolume.py +4 -0
  133. tomwer/core/volume/hdf5volume.py +4 -0
  134. tomwer/core/volume/jp2kvolume.py +4 -0
  135. tomwer/core/volume/rawvolume.py +4 -0
  136. tomwer/core/volume/tiffvolume.py +4 -0
  137. tomwer/core/volume/volumebase.py +19 -12
  138. tomwer/core/volume/volumefactory.py +20 -1
  139. tomwer/gui/cluster/slurm.py +1 -1
  140. tomwer/gui/cluster/test/test_cluster.py +2 -2
  141. tomwer/gui/control/datalist.py +109 -34
  142. tomwer/gui/control/datatransfert.py +1 -1
  143. tomwer/gui/control/datawatcher/datawatcher.py +23 -13
  144. tomwer/gui/control/datawatcher/datawatcherobserver.py +1 -1
  145. tomwer/gui/control/observations.py +0 -3
  146. tomwer/gui/control/selectorwidgetbase.py +42 -11
  147. tomwer/gui/control/serie/seriecreator.py +967 -0
  148. tomwer/{web/__init__.py → gui/control/serie/seriewaiter.py} +5 -7
  149. tomwer/gui/control/singletomoobj.py +15 -3
  150. tomwer/gui/control/test/test_datalist.py +1 -1
  151. tomwer/gui/control/test/test_datalistener.py +1 -1
  152. tomwer/gui/control/test/test_inputwidget.py +1 -1
  153. tomwer/gui/control/test/test_process_manager.py +1 -13
  154. tomwer/gui/control/test/test_scanselector.py +1 -1
  155. tomwer/gui/control/test/test_scanvalidator.py +1 -1
  156. tomwer/gui/control/test/test_single_tomo_obj.py +1 -1
  157. tomwer/gui/control/test/test_volume_dialog.py +19 -7
  158. tomwer/gui/control/test/test_volumeselector.py +4 -4
  159. tomwer/gui/debugtools/datasetgenerator.py +1 -8
  160. tomwer/gui/edit/dkrfpatch.py +2 -2
  161. tomwer/gui/edit/imagekeyeditor.py +12 -9
  162. tomwer/gui/edit/nxtomoeditor.py +475 -0
  163. tomwer/gui/edit/test/test_dkrf_patch.py +2 -14
  164. tomwer/gui/edit/test/test_image_key_editor.py +2 -2
  165. tomwer/gui/edit/test/test_nx_editor.py +155 -0
  166. tomwer/gui/qfolderdialog.py +11 -0
  167. tomwer/gui/reconstruction/axis/CompareImages.py +27 -29
  168. tomwer/gui/reconstruction/axis/axis.py +2 -0
  169. tomwer/gui/reconstruction/axis/radioaxis.py +67 -11
  170. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +7 -9
  171. tomwer/gui/reconstruction/darkref/darkrefwidget.py +22 -24
  172. tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
  173. tomwer/gui/reconstruction/lamino/tofu/tofu.py +3 -3
  174. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +4 -4
  175. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +10 -4
  176. tomwer/gui/reconstruction/nabu/castvolume.py +80 -11
  177. tomwer/gui/reconstruction/nabu/check.py +1 -1
  178. tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +352 -0
  179. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +0 -9
  180. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +1 -1
  181. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +18 -19
  182. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +30 -7
  183. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +26 -13
  184. tomwer/gui/reconstruction/nabu/slices.py +10 -2
  185. tomwer/gui/reconstruction/nabu/slurm.py +1 -1
  186. tomwer/gui/reconstruction/nabu/volume.py +13 -7
  187. tomwer/gui/reconstruction/normalization/intensity.py +1 -1
  188. tomwer/gui/reconstruction/saaxis/corrangeselector.py +10 -34
  189. tomwer/gui/reconstruction/saaxis/saaxis.py +11 -6
  190. tomwer/gui/reconstruction/saaxis/sliceselector.py +11 -26
  191. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +13 -8
  192. tomwer/gui/reconstruction/scores/scoreplot.py +67 -61
  193. tomwer/gui/reconstruction/test/test_axis.py +2 -2
  194. tomwer/gui/reconstruction/test/test_lamino.py +2 -2
  195. tomwer/gui/reconstruction/test/test_nabu.py +14 -1
  196. tomwer/gui/reconstruction/test/test_saaxis.py +8 -17
  197. tomwer/gui/reconstruction/test/test_sadeltabeta.py +7 -13
  198. tomwer/gui/stackplot.py +11 -28
  199. tomwer/gui/test/test_axis_gui.py +4 -4
  200. tomwer/gui/test/test_qfolder_dialog.py +12 -0
  201. tomwer/gui/utils/inputwidget.py +42 -21
  202. tomwer/gui/utils/lineselector/lineselector.py +13 -21
  203. tomwer/gui/utils/scandescription.py +2 -4
  204. tomwer/gui/utils/slider.py +1 -102
  205. tomwer/gui/utils/unitsystem.py +48 -11
  206. tomwer/gui/visualization/dataviewer.py +24 -17
  207. tomwer/gui/visualization/diffviewer/diffviewer.py +2 -11
  208. tomwer/gui/visualization/nxtomometadata.py +21 -0
  209. tomwer/gui/visualization/scanoverview.py +0 -1
  210. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +72 -0
  211. tomwer/gui/visualization/test/test_stacks.py +1 -1
  212. tomwer/gui/visualization/tomoobjoverview.py +49 -0
  213. tomwer/gui/visualization/volumeoverview.py +64 -0
  214. tomwer/gui/visualization/volumeviewer.py +1 -1
  215. tomwer/resources/gui/icons/multi-document-save.png +0 -0
  216. tomwer/resources/gui/icons/multi-document-save.svg +101 -0
  217. tomwer/resources/gui/illustrations/ctf_z1.png +0 -0
  218. tomwer/resources/gui/illustrations/ctf_z1.svg +471 -0
  219. tomwer/synctools/datalistener.py +5 -1
  220. tomwer/synctools/imageloaderthread.py +2 -2
  221. tomwer/synctools/stacks/edit/imagekeyeditor.py +1 -1
  222. tomwer/synctools/stacks/processingstack.py +2 -2
  223. tomwer/synctools/stacks/reconstruction/castvolume.py +1 -0
  224. tomwer/synctools/stacks/reconstruction/lamino.py +1 -3
  225. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +0 -2
  226. tomwer/synctools/test/test_darkRefs.py +32 -149
  227. tomwer/synctools/test/test_foldertransfer.py +1 -1
  228. tomwer/synctools/test/test_scanstages.py +2 -2
  229. tomwer/tests/__init__.py +0 -0
  230. tomwer/tests/conftest.py +51 -0
  231. tomwer/{test → tests}/test_scripts.py +1 -1
  232. tomwer/tests/test_utils.py +10 -0
  233. tomwer/{test → tests}/utils/utilstest.py +0 -11
  234. tomwer/version.py +3 -3
  235. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/METADATA +14 -16
  236. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/RECORD +245 -217
  237. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/WHEEL +1 -1
  238. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/entry_points.txt +6 -0
  239. orangecontrib/tomwer/setup.py +0 -45
  240. orangecontrib/tomwer/widgets/setup.py +0 -49
  241. tomwer/app/process.py +0 -153
  242. tomwer/core/process/reconstruction/nabu/slurm.py +0 -36
  243. tomwer/core/process/reconstruction/utils/nabu_slice_exec.py +0 -10
  244. tomwer/core/utils/laminoutils.py +0 -80
  245. tomwer/gui/utils/lineselector/lineselection.py +0 -76
  246. tomwer/setup.py +0 -52
  247. tomwer/web/client.py +0 -43
  248. tomwer/web/config.py +0 -36
  249. tomwer/web/test/test_graylog_connection.py +0 -59
  250. {tomwer/test → orangecontrib/tomwer/tutorials}/__init__.py +0 -0
  251. /tomwer/{web/test → gui/control/serie}/__init__.py +0 -0
  252. /tomwer/{test → tests}/utils/__init__.py +0 -0
  253. /tomwer-1.0.4-py3.8-nspkg.pth → /tomwer-1.1.0-py3.9-nspkg.pth +0 -0
  254. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/LICENSE +0 -0
  255. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/namespace_packages.txt +0 -0
  256. {tomwer-1.0.4.dist-info → tomwer-1.1.0.dist-info}/top_level.txt +0 -0
@@ -29,10 +29,12 @@ __authors__ = ["H. Payno"]
29
29
  __license__ = "MIT"
30
30
  __date__ = "11/08/2022"
31
31
 
32
+ import logging
32
33
  import h5py
33
34
  import numpy
34
35
  import time
35
36
  import os
37
+ from typing import Any
36
38
  from tomoscan.io import HDF5File
37
39
  from tomoscan.esrf.scan.mock import MockEDF as _MockEDF, MockHDF5 as _MockHDF5
38
40
  from tomoscan.test.utils import MockContext
@@ -41,6 +43,8 @@ from tomwer.core.scan.hdf5scan import HDF5TomoScan
41
43
  from tomwer.core.scan.scanfactory import ScanFactory
42
44
  from silx.io.utils import h5py_read_dataset
43
45
 
46
+ _logger = logging.getLogger(__name__)
47
+
44
48
 
45
49
  class MockHDF5(_MockHDF5):
46
50
  def __init__(self, *args, **kwargs):
@@ -391,3 +395,49 @@ class HDF5MockContext(MockContext, mock_class=MockHDF5):
391
395
  return MockHDF5(
392
396
  scan_path=self._scan_path, n_proj=self._n_proj, **self._mocks_params
393
397
  ).scan
398
+
399
+
400
+ def data_identifier_to_scan(data_identifier: Any):
401
+ """
402
+ from a identifier (as str) try to create a scan.
403
+ Mostly used when 'data' parameter is set from ewoks. In this case we expect it to be a (string) identifier
404
+ """
405
+ if isinstance(data_identifier, str):
406
+ return ScanFactory.create_tomo_object_from_identifier(data_identifier)
407
+ else:
408
+ return data_identifier
409
+
410
+
411
+ def format_output_location(location: str, scan):
412
+ """
413
+ format possible keys from the location like {scan_dir} or {scan_path}
414
+
415
+ :param location:
416
+ :param scan:
417
+ :return:
418
+ """
419
+ if scan is None:
420
+ _logger.warning("scan is !none, enable to format the nabu output location")
421
+
422
+ keywords = {
423
+ "scan_dir_name": scan.scan_dir_name(),
424
+ "scan_basename": scan.scan_basename(),
425
+ "scan_parent_dir_basename": scan.scan_parent_dir_basename(),
426
+ }
427
+
428
+ # filter necessary keywords
429
+ def get_necessary_keywords():
430
+ import string
431
+
432
+ formatter = string.Formatter()
433
+ return [field for _, field, _, _ in formatter.parse(location) if field]
434
+
435
+ requested_keywords = get_necessary_keywords()
436
+
437
+ def keyword_needed(pair):
438
+ keyword, _ = pair
439
+ return keyword in requested_keywords
440
+
441
+ keywords = dict(filter(keyword_needed, keywords.items()))
442
+ location = os.path.abspath(location.format(**keywords))
443
+ return location
@@ -0,0 +1,13 @@
1
+ from tomwer.core.volume.volumefactory import VolumeFactory
2
+ from typing import Any
3
+
4
+
5
+ def volume_identifier_to_volume(volume_identifier: Any):
6
+ """
7
+ from a str identifier try to create a volume.
8
+ Mostly used when 'volume' parameter is set from ewoks. In this case we expect it to be a (string) identifier
9
+ """
10
+ if isinstance(volume_identifier, str):
11
+ return VolumeFactory.create_tomo_object_from_identifier(volume_identifier)
12
+ else:
13
+ return volume_identifier
@@ -49,6 +49,10 @@ class EDFVolumeIdentifier(_EDFVolumeIdentifier, DatasetIdentifier):
49
49
  ObjClass=EDFVolume,
50
50
  )
51
51
 
52
+ def long_description(self) -> str:
53
+ """used for processview header tooltip for now"""
54
+ return self.to_str()
55
+
52
56
 
53
57
  class EDFVolume(_EDFVolume, TomwerVolumeBase, Dataset):
54
58
  @staticmethod
@@ -69,6 +69,10 @@ class HDF5VolumeIdentifier(_HDF5VolumeIdentifier, DatasetIdentifier):
69
69
  raise ValueError("expects to get a data_path")
70
70
  return HDF5VolumeIdentifier(object=HDF5Volume, hdf5_file=hdf5_file, entry=entry)
71
71
 
72
+ def long_description(self) -> str:
73
+ """used for processview header tooltip for now"""
74
+ return self.to_str()
75
+
72
76
 
73
77
  class HDF5Volume(_HDF5Volume, TomwerVolumeBase, Dataset):
74
78
  @staticmethod
@@ -51,6 +51,10 @@ class JP2KVolumeIdentifier(_JP2KVolumeIdentifier, DatasetIdentifier):
51
51
  ObjClass=JP2KVolume,
52
52
  )
53
53
 
54
+ def long_description(self) -> str:
55
+ """used for processview header tooltip for now"""
56
+ return self.to_str()
57
+
54
58
 
55
59
  class JP2KVolume(_JP2KVolume, TomwerVolumeBase, Dataset):
56
60
  @staticmethod
@@ -64,6 +64,10 @@ class RawVolumeIdentifier(_RawVolumeIdentifier, DatasetIdentifier):
64
64
  )
65
65
  return RawVolumeIdentifier(object=RawVolume, file_path=vol_file)
66
66
 
67
+ def long_description(self) -> str:
68
+ """used for processview header tooltip for now"""
69
+ return self.to_str()
70
+
67
71
 
68
72
  class RawVolume(_RawVolume, TomwerVolumeBase, Dataset):
69
73
  @staticmethod
@@ -54,6 +54,10 @@ class TIFFVolumeIdentifier(_TIFFVolumeIdentifier, DatasetIdentifier):
54
54
  ObjClass=TIFFVolume,
55
55
  )
56
56
 
57
+ def long_description(self) -> str:
58
+ """used for processview header tooltip for now"""
59
+ return self.to_str()
60
+
57
61
 
58
62
  class MultiTiffVolumeIdentifier(_MultiTiffVolumeIdentifier, DatasetIdentifier):
59
63
  def __init__(self, object, tiff_file):
@@ -3,14 +3,12 @@ from tomwer.core.tomwer_object import TomwerObject
3
3
 
4
4
 
5
5
  class TomwerVolumeBase(TomwerObject):
6
- def get_identifier(self):
7
- return self.get_identifier()
8
-
9
6
  def _clear_heavy_cache(self):
10
7
  """util user"""
11
8
  self.data = None
12
9
  self.metadata = None
13
10
 
11
+ @staticmethod
14
12
  def format_output_location(location: str, volume):
15
13
  if not isinstance(volume, TomwerVolumeBase):
16
14
  raise TypeError(
@@ -20,20 +18,29 @@ class TomwerVolumeBase(TomwerObject):
20
18
  keywords = {
21
19
  "volume_data_parent_folder": volume.volume_data_parent_folder(),
22
20
  }
23
- for keyword, value in keywords.items():
24
- if value is None:
25
- continue
26
- try:
27
- location = location.format(**{keyword: value})
28
- except KeyError:
29
- pass
21
+
22
+ # filter necessary keywords
23
+ def get_necessary_keywords():
24
+ import string
25
+
26
+ formatter = string.Formatter()
27
+ return [field for _, field, _, _ in formatter.parse(location) if field]
28
+
29
+ requested_keywords = get_necessary_keywords()
30
+
31
+ def keyword_needed(pair):
32
+ keyword, _ = pair
33
+ return keyword in requested_keywords
34
+
35
+ keywords = dict(filter(keyword_needed, keywords.items()))
36
+ location = os.path.abspath(location.format(**keywords))
30
37
  return location
31
38
 
32
39
  def volume_data_parent_folder(self):
33
- if self.data_url is None:
40
+ if self.data_url is None: # pylint: disable=E1101
34
41
  raise ValueError("data_url doesn't exists")
35
42
  else:
36
- return os.path.dirname(self.data_url.file_path())
43
+ return os.path.dirname(self.data_url.file_path()) # pylint: disable=E1101
37
44
 
38
45
  def __str__(self) -> str:
39
46
  try:
@@ -28,6 +28,7 @@ __license__ = "MIT"
28
28
  __date__ = "13/07/2022"
29
29
 
30
30
 
31
+ from typing import Optional
31
32
  from urllib.parse import urlparse
32
33
  from typing import Union
33
34
  from tomoscan.factory import Factory as _oVolumeFactory
@@ -60,6 +61,22 @@ _logger = logging.getLogger(__name__)
60
61
 
61
62
 
62
63
  class VolumeFactory(_oVolumeFactory):
64
+ @staticmethod
65
+ def from_identifier_to_vol_urls(identifier) -> Optional[tuple]:
66
+ """
67
+ convert an identifier to a volume
68
+ and return all the existing url of this volume
69
+ """
70
+ try:
71
+ vol = VolumeFactory.create_tomo_object_from_identifier(
72
+ identifier=identifier
73
+ )
74
+ except Exception as e:
75
+ _logger.error(e)
76
+ return None
77
+ else:
78
+ return tuple(vol.browse_data_urls())
79
+
63
80
  @docstring(_oVolumeFactory.create_tomo_object_from_identifier)
64
81
  @staticmethod
65
82
  def create_tomo_object_from_identifier(identifier: Union[str, BaseIdentifier]):
@@ -74,7 +91,9 @@ class VolumeFactory(_oVolumeFactory):
74
91
  :rtype: tomwer.core.scan.scanbase.TomwerScanBase
75
92
  :raises: ValueError if scan_path is not containing a scan
76
93
  """
77
- if not isinstance(identifier, (str, BaseIdentifier)):
94
+ if isinstance(identifier, BaseIdentifier):
95
+ identifier = identifier.to_str()
96
+ elif not isinstance(identifier, str):
78
97
  raise TypeError(
79
98
  f"identifier is expected to be a str or an instance of {BaseIdentifier} not {type(identifier)}"
80
99
  )
@@ -162,7 +162,7 @@ class SlurmSettingsWidget(qt.QWidget):
162
162
  self._nCores.setValue(SlurmSettings.N_CORES_PER_TASK)
163
163
  self._nWorkers.setValue(SlurmSettings.N_TASKS)
164
164
  self._memory.setValue(SlurmSettings.MEMORY_PER_WORKER)
165
- self._queue.setText(SlurmSettings.PARTIION)
165
+ self._queue.setText(SlurmSettings.PARTITION)
166
166
  self._nGpu.setValue(SlurmSettings.N_GPUS_PER_WORKER)
167
167
  self._jobName.setText(SlurmSettings.PROJECT_NAME)
168
168
  self._wallTimeQLE.setText(SlurmSettings.DEFAULT_WALLTIME)
@@ -31,7 +31,7 @@ from tomwer.gui.reconstruction.nabu.slurm import SlurmSettingsWidget
31
31
  from tomwer.core.settings import SlurmSettings
32
32
  from silx.gui.utils.testutils import TestCaseQt
33
33
  from silx.gui import qt
34
- from tomwer.test.utils import skip_gui_test
34
+ from tomwer.tests.utils import skip_gui_test
35
35
  import pytest
36
36
 
37
37
 
@@ -54,7 +54,7 @@ class TestSlurmWidget(TestCaseQt):
54
54
  "cpu-per-task": SlurmSettings.N_CORES_PER_TASK,
55
55
  "n_tasks": SlurmSettings.N_TASKS,
56
56
  "memory": SlurmSettings.MEMORY_PER_WORKER,
57
- "partition": SlurmSettings.PARTIION,
57
+ "partition": SlurmSettings.PARTITION,
58
58
  "n_gpus": SlurmSettings.N_GPUS_PER_WORKER,
59
59
  }
60
60
  assert dict_res == expected_dict, f"{dict_res} vs {expected_dict}"
@@ -66,6 +66,7 @@ from nxtomomill.io.config import TomoHDF5Config as HDF5Config
66
66
  from nxtomomill.io.config import TomoEDFConfig as EDFConfig
67
67
  from nxtomomill.io import generate_default_edf_config
68
68
  from nxtomomill.io import generate_default_h5_config
69
+ from tomwer.gui.utils.qt_utils import block_signals
69
70
 
70
71
  logger = logging.getLogger(__name__)
71
72
 
@@ -222,9 +223,6 @@ class _NXtomomillConfigFileDialog(qt.QDialog):
222
223
  def accept(self):
223
224
  self.hide()
224
225
 
225
- def _createDefaultConfigFile(self):
226
- raise NotImplementedError
227
-
228
226
  def _createConfigFileSelector(self):
229
227
  raise NotImplementedError
230
228
 
@@ -245,7 +243,7 @@ class BlissHDF5DataListDialog(_DataListDialog):
245
243
  def createDataList(self):
246
244
  return BlissScanList(self)
247
245
 
248
- def _callbackAddPath(self):
246
+ def _callbackAddPath(self): # pragma: no cover
249
247
  """ """
250
248
  dialog = qt.QFileDialog(self)
251
249
  dialog.setNameFilters(["HDF5 file *.h5 *.hdf5 *.nx *.nexus", "nxs"])
@@ -280,7 +278,7 @@ class EDFDataListDialog(_DataListDialog):
280
278
  def createDataList(self):
281
279
  return EDFDataList(self)
282
280
 
283
- def _callbackAddPath(self):
281
+ def _callbackAddPath(self): # pragma: no cover
284
282
  """ """
285
283
  dialog = qt.QFileDialog(self)
286
284
  dialog.setFileMode(qt.QFileDialog.DirectoryOnly)
@@ -408,14 +406,14 @@ class _RawDataListMainWindow(qt.QMainWindow):
408
406
  else:
409
407
  return config
410
408
 
411
- def createNXtomomillConfigFileDialog(self):
409
+ def createNXtomomillConfigFileDialog(self): # pragma: no cover
412
410
  file_dialog = qt.QFileDialog()
413
411
  file_dialog.setAcceptMode(qt.QFileDialog.AcceptSave)
414
412
  file_dialog.setWindowTitle("Select file path to save configuration file")
415
413
  file_dialog.setNameFilters(
416
414
  [
417
415
  "Any file (*)",
418
- "Configuration file (*.txt *.cfg *.conf)",
416
+ "Configuration file (*.txt *.cfg *.conf *.config)",
419
417
  ]
420
418
  )
421
419
  file_dialog.setFileMode(qt.QFileDialog.AnyFile)
@@ -442,7 +440,7 @@ class BlissHDF5DataListMainWindow(_RawDataListMainWindow):
442
440
  **kwargs,
443
441
  )
444
442
 
445
- def _createNewConfigFile(self):
443
+ def _createNewConfigFile(self): # pragma: no cover
446
444
  file_dialog = self.createNXtomomillConfigFileDialog()
447
445
  if file_dialog.exec_():
448
446
  files_selected = file_dialog.selectedFiles()
@@ -477,7 +475,7 @@ class EDFDataListMainWindow(_RawDataListMainWindow):
477
475
  **kwargs,
478
476
  )
479
477
 
480
- def _createNewConfigFile(self):
478
+ def _createNewConfigFile(self): # pragma: no cover
481
479
  file_dialog = self.createNXtomomillConfigFileDialog()
482
480
  if file_dialog.exec_():
483
481
  files_selected = file_dialog.selectedFiles()
@@ -498,7 +496,7 @@ class GenericScanListDialog(_DataListDialog):
498
496
  def createDataList(self):
499
497
  return GenericScanList(self)
500
498
 
501
- def _callbackAddPath(self):
499
+ def _callbackAddPath(self): # pragma: no cover
502
500
  """ """
503
501
  dialog = QDataDialog(self, multiSelection=True)
504
502
  dialog.setNameFilters(
@@ -527,7 +525,7 @@ class VolumeListDialog(_DataListDialog):
527
525
  def createDataList(self):
528
526
  return VolumeList(self)
529
527
 
530
- def _callbackAddPath(self):
528
+ def _callbackAddPath(self): # pragma: no cover
531
529
  """ """
532
530
  dialog = QVolumeDialog(self)
533
531
 
@@ -547,8 +545,11 @@ class _TomwerObjectList(qt.QTableWidget):
547
545
 
548
546
  dataReady = qt.Signal(TomwerObject)
549
547
 
548
+ listChanged = qt.Signal()
549
+ """emit when containt of the list changed"""
550
+
550
551
  def __init__(self, parent):
551
- self._target = None
552
+ self._copy_target = None
552
553
  qt.QTableWidget.__init__(self, parent)
553
554
  self.setRowCount(0)
554
555
  self.setColumnCount(1)
@@ -562,12 +563,17 @@ class _TomwerObjectList(qt.QTableWidget):
562
563
  self.setAcceptDrops(True)
563
564
 
564
565
  self._myitems = OrderedDict()
566
+ # key is the TomoObject identifier and value is the QTableWidgetItem.
567
+ # Text is the identifier, QTableWidgetItem data under UserRole is the TomoObject object
565
568
 
566
569
  # QMenu
567
570
  self.menu = qt.QMenu(self)
568
571
  self._copyAction = qt.QAction("copy")
569
572
  self._copyAction.triggered.connect(self._copyRequested)
573
+ self._pasteAction = qt.QAction("paste")
574
+ self._pasteAction.triggered.connect(self._pasteRequested)
570
575
  self.menu.addAction(self._copyAction)
576
+ self.menu.addAction(self._pasteAction)
571
577
 
572
578
  def n_data(self):
573
579
  return len(self._myitems)
@@ -608,6 +614,7 @@ class _TomwerObjectList(qt.QTableWidget):
608
614
  else:
609
615
  item = self._myitems[identifier_as_str]
610
616
  self.remove_item(item)
617
+ self.listChanged.emit()
611
618
 
612
619
  def _update(self):
613
620
  tomwer_objects = [
@@ -615,9 +622,11 @@ class _TomwerObjectList(qt.QTableWidget):
615
622
  for identifier_as_str in self._myitems.keys()
616
623
  ]
617
624
  self.clear()
618
- for tomwer_object in tomwer_objects:
619
- self.add(tomwer_object)
625
+ with block_signals(self):
626
+ for tomwer_object in tomwer_objects:
627
+ self.add(tomwer_object)
620
628
  self.sortByColumn(0, self.horizontalHeader().sortIndicatorOrder())
629
+ self.listChanged.emit()
621
630
 
622
631
  def _getTomoObject(self, obj: str):
623
632
  """
@@ -665,7 +674,8 @@ class _TomwerObjectList(qt.QTableWidget):
665
674
  # in this case we will update the value. Because if the same identifier already exists at launch time
666
675
  # it will create two different objects that could be an issue.
667
676
  # this is better to have it unified
668
- self.remove(self._myitems[identifier_as_str].data(qt.Qt.UserRole))
677
+ with block_signals(self):
678
+ self.remove(self._myitems[identifier_as_str].data(qt.Qt.UserRole))
669
679
 
670
680
  item = qt.QTableWidgetItem()
671
681
  item.setText(identifier_as_str)
@@ -677,6 +687,7 @@ class _TomwerObjectList(qt.QTableWidget):
677
687
  self.setRowCount(row + 1)
678
688
  self.setItem(row, 0, item)
679
689
  self._myitems[identifier_as_str] = item
690
+ self.listChanged.emit()
680
691
  return (obj,)
681
692
 
682
693
  def setMySelection(self, datasets: tuple):
@@ -699,16 +710,18 @@ class _TomwerObjectList(qt.QTableWidget):
699
710
  def dropEvent(self, event):
700
711
  if event.mimeData().hasFormat("text/uri-list"):
701
712
  added_scans = set()
702
- for url in event.mimeData().urls():
703
- new_scans = self.add(str(url.path()))
704
- if new_scans is not None:
705
- for new_scan in new_scans:
706
- if not isinstance(new_scan, TomwerObject):
707
- raise ValueError(
708
- f"new_scan should be an instance of {TomwerObject} and not {type(new_scan)}"
709
- )
710
- added_scans.update(new_scans)
713
+ with block_signals(self):
714
+ for url in event.mimeData().urls():
715
+ new_scans = self.add(str(url.path()))
716
+ if new_scans is not None:
717
+ for new_scan in new_scans:
718
+ if not isinstance(new_scan, TomwerObject):
719
+ raise ValueError(
720
+ f"new_scan should be an instance of {TomwerObject} and not {type(new_scan)}"
721
+ )
722
+ added_scans.update(new_scans)
711
723
  self.setMySelection(added_scans)
724
+ self.listChanged.emit()
712
725
 
713
726
  def supportedDropActions(self):
714
727
  """Inherited method to redefine supported drop actions."""
@@ -742,13 +755,24 @@ class _TomwerObjectList(qt.QTableWidget):
742
755
  return item.data(qt.Qt.UserRole)
743
756
 
744
757
  def contextMenuEvent(self, event):
745
- self._target = self._datasetAt(event.pos())
746
- if self._target is not None:
747
- self.menu.exec_(event.globalPos())
758
+ self._copy_target = self._datasetAt(event.pos())
759
+ self._copyAction.setVisible(self._copy_target is not None)
760
+ self.menu.exec_(event.globalPos())
748
761
 
749
762
  def _copyRequested(self):
750
763
  clipboard = qt.QGuiApplication.clipboard()
751
- clipboard.setText(str(self._target))
764
+ if isinstance(self._copy_target, TomwerObject):
765
+ clipboard.setText(self._copy_target.get_identifier().to_str())
766
+ else:
767
+ clipboard.setText(str(self._copy_target))
768
+
769
+ def _pasteRequested(self):
770
+ clipboard = qt.QGuiApplication.clipboard()
771
+ identifier = clipboard.text()
772
+ try:
773
+ self.add(identifier)
774
+ except Exception as e:
775
+ logger.error(f"Failed to add {identifier}. Error is {e}")
752
776
 
753
777
 
754
778
  class GenericScanList(_TomwerObjectList):
@@ -760,8 +784,12 @@ class GenericScanList(_TomwerObjectList):
760
784
  """
761
785
  some rules to return a TomwerObject from an object (probably a path) from children class
762
786
  """
787
+ return self.getScanObject(obj)
788
+
789
+ @staticmethod
790
+ def getScanObject(obj):
763
791
  if not isinstance(obj, str):
764
- raise TypeError
792
+ raise TypeError(f"obj is an instance of {type(obj)} when {str} expected")
765
793
  try:
766
794
  scan_obj = ScanFactory.create_tomo_object_from_identifier(obj)
767
795
  except ValueError as e1:
@@ -913,29 +941,76 @@ class VolumeList(_TomwerObjectList):
913
941
  }
914
942
 
915
943
  def _getTomoObject(self, obj: str):
944
+ return self.getVolumeObject(obj=obj)
945
+
946
+ @staticmethod
947
+ def getVolumeObject(obj, warn=True):
948
+ """
949
+ get volume from identifier... even if not contained in the list of items (using factory)
950
+ """
916
951
  try:
917
952
  tomo_obj = VolumeFactory.create_tomo_object_from_identifier(obj)
918
953
  except Exception as e:
919
954
  try:
920
- tomo_obj = guess_volumes(obj, scheme_to_vol=self.DEFAULT_SCHEME_TO_VOL)
955
+ tomo_obj = guess_volumes(
956
+ obj, scheme_to_vol=VolumeList.DEFAULT_SCHEME_TO_VOL
957
+ )
921
958
  except Exception:
922
- logger.warning(f"Unable to create a volume from {obj}. Error is {e}")
959
+ if warn:
960
+ logger.warning(
961
+ f"Unable to create a volume from {obj}. Error is {e}"
962
+ )
923
963
  return None
924
964
  else:
965
+ # filter potential 'nabu histogram'
966
+ if tomo_obj is not None:
967
+
968
+ def is_not_histogram(vol_identifier):
969
+ return not (
970
+ hasattr(vol_identifier, "data_path")
971
+ and vol_identifier.data_path.endswith("histogram")
972
+ )
973
+
974
+ tomo_obj = tuple(filter(is_not_histogram, tomo_obj))
975
+
925
976
  if tomo_obj is None or len(tomo_obj) == 0:
926
977
  logger.warning(f"Unable to create a volume from {obj}.")
927
978
  return None
928
979
  else:
929
980
  if len(tomo_obj) > 1:
930
- logger.warning(
931
- f"more than one volume deduce from {obj}. Will only take the first one ({tomo_obj[0]})"
932
- )
981
+ if warn:
982
+ logger.warning(
983
+ f"more than one volume deduce from {obj}. Will only take the first one ({tomo_obj[0]})"
984
+ )
933
985
  return tomo_obj[0]
934
986
  else:
935
987
  return tomo_obj
936
988
 
937
989
  def getVolume(self, volume_id: str, default=None):
990
+ """
991
+ get volume from id contained in the current items, else default
992
+ """
938
993
  if volume_id in self._myitems:
939
994
  return self._myitems[volume_id].data(qt.Qt.UserRole)
940
995
  else:
941
996
  return default
997
+
998
+
999
+ class TomoObjList(_TomwerObjectList):
1000
+ HEADER_NAMES = ("tomo object",)
1001
+
1002
+ def _getTomoObject(self, obj: str):
1003
+ try:
1004
+ tomo_obj = VolumeList.getVolumeObject(obj=obj)
1005
+ except Exception:
1006
+ pass
1007
+ else:
1008
+ if tomo_obj not in (None, tuple()):
1009
+ return tomo_obj
1010
+
1011
+ try:
1012
+ tomo_obj = GenericScanList.getScanObject(obj=obj)
1013
+ except Exception:
1014
+ return None
1015
+ else:
1016
+ return tomo_obj
@@ -111,7 +111,7 @@ class DataTransfertSelector(qt.QWidget):
111
111
  if b is True:
112
112
  self.__updateFolder(None)
113
113
 
114
- def _changeFolder(self):
114
+ def _changeFolder(self): # pragma: no cover
115
115
  """Callback when folder selection is choose"""
116
116
  defaultDirectory = self._folderSelection.text()
117
117
  if os.path.isdir(defaultDirectory):