tomwer 1.2.0a1__py3-none-any.whl → 1.2.0a3__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 (219) hide show
  1. orangecontrib/tomwer/tutorials/append_raw_darks_and_flats_frames_to_NXtomos.ows +44 -0
  2. orangecontrib/tomwer/tutorials/copy_reduced_darks_and_flats_meth1.ows +55 -0
  3. orangecontrib/tomwer/tutorials/copy_reduced_darks_and_flats_meth2.ows +48 -0
  4. orangecontrib/tomwer/tutorials/default_cor_search.ows +40 -0
  5. orangecontrib/tomwer/tutorials/hello_world_python_script.ows +50 -0
  6. orangecontrib/tomwer/tutorials/simple_slice_reconstruction_on_slurm.ows +50 -0
  7. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +8 -8
  8. orangecontrib/tomwer/widgets/__init__.py +1 -1
  9. orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +0 -1
  10. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +8 -6
  11. orangecontrib/tomwer/widgets/control/AdvancementOW.py +0 -1
  12. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +1 -6
  13. orangecontrib/tomwer/widgets/control/DataListOW.py +0 -1
  14. orangecontrib/tomwer/widgets/control/DataListenerOW.py +4 -4
  15. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +0 -1
  16. orangecontrib/tomwer/widgets/control/DataTransfertOW.py +7 -7
  17. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +0 -1
  18. orangecontrib/tomwer/widgets/control/DataWatcherOW.py +0 -3
  19. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +3 -2
  20. orangecontrib/tomwer/widgets/control/EmailOW.py +82 -0
  21. orangecontrib/tomwer/widgets/control/FilterOW.py +3 -3
  22. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +1 -1
  23. orangecontrib/tomwer/widgets/control/NotifierOW.py +0 -1
  24. orangecontrib/tomwer/widgets/control/ReduceDarkFlatSelectorOW.py +93 -0
  25. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +29 -5
  26. orangecontrib/tomwer/widgets/control/TimerOW.py +1 -2
  27. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +0 -1
  28. orangecontrib/tomwer/widgets/control/VolumeSelector.py +0 -1
  29. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +4 -10
  30. orangecontrib/tomwer/widgets/control/icons/email.png +0 -0
  31. orangecontrib/tomwer/widgets/control/icons/email.svg +58 -0
  32. orangecontrib/tomwer/widgets/control/icons/reduced_darkflat_selector.png +0 -0
  33. orangecontrib/tomwer/widgets/control/icons/reduced_darkflat_selector.svg +199 -0
  34. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +0 -1
  35. orangecontrib/tomwer/widgets/debugtools/ObjectInspectorOW.py +0 -1
  36. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +1 -2
  37. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +1 -2
  38. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +0 -1
  39. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +0 -1
  40. orangecontrib/tomwer/widgets/other/PythonScriptOW.py +29 -1
  41. orangecontrib/tomwer/widgets/other/TomoObjsHub.py +28 -0
  42. orangecontrib/tomwer/widgets/other/icons/hub.png +0 -0
  43. orangecontrib/tomwer/widgets/other/icons/hub.svg +113 -0
  44. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +18 -12
  45. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +0 -2
  46. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +21 -6
  47. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +29 -7
  48. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +18 -5
  49. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +40 -13
  50. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +37 -10
  51. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +2 -3
  52. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +5 -4
  53. orangecontrib/tomwer/widgets/stitching/StitcherOW.py +0 -1
  54. orangecontrib/tomwer/widgets/stitching/ZStitchingConfigOW.py +0 -1
  55. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +10 -4
  56. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  57. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +0 -1
  58. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +0 -1
  59. orangecontrib/tomwer/widgets/visualization/RadioStackOW.py +7 -5
  60. orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +1 -1
  61. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -3
  62. orangecontrib/tomwer/widgets/visualization/SliceStackOW.py +7 -5
  63. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +4 -4
  64. tomwer/__main__.py +139 -5
  65. tomwer/app/axis.py +16 -5
  66. tomwer/app/canvas_launcher/config.py +1 -1
  67. tomwer/app/canvas_launcher/mainwindow.py +164 -6
  68. tomwer/app/darkref.py +10 -181
  69. tomwer/app/darkrefpatch.py +10 -131
  70. tomwer/app/diffframe.py +11 -0
  71. tomwer/app/imagekeyeditor.py +12 -19
  72. tomwer/app/intensitynormalization.py +1 -0
  73. tomwer/app/lamino.py +7 -2
  74. tomwer/app/patchrawdarkflat.py +131 -0
  75. tomwer/app/radiostack.py +10 -0
  76. tomwer/app/reducedarkflat.py +205 -0
  77. tomwer/app/saaxis.py +27 -8
  78. tomwer/app/sadeltabeta.py +29 -8
  79. tomwer/app/samplemoved.py +11 -0
  80. tomwer/app/scanviewer.py +12 -0
  81. tomwer/app/sinogramviewer.py +11 -0
  82. tomwer/app/slicestack.py +11 -0
  83. tomwer/app/zstitching.py +12 -0
  84. tomwer/core/futureobject.py +4 -2
  85. tomwer/core/process/conditions/filters.py +26 -4
  86. tomwer/core/process/control/datadiscovery.py +4 -0
  87. tomwer/core/process/control/datawatcher/datawatcher.py +5 -1
  88. tomwer/core/process/control/email.py +148 -0
  89. tomwer/core/process/control/nxtomoconcatenate.py +9 -2
  90. tomwer/core/process/control/nxtomomill.py +58 -16
  91. tomwer/core/process/control/scanselector.py +4 -0
  92. tomwer/core/process/control/scantransfer.py +52 -23
  93. tomwer/core/process/control/test/test_concatenate_nxtomos.py +1 -0
  94. tomwer/core/process/control/test/test_email.py +52 -0
  95. tomwer/core/process/control/test/test_h52nx_process.py +106 -0
  96. tomwer/core/process/control/test/test_volume_link.py +5 -4
  97. tomwer/core/process/control/timer.py +27 -6
  98. tomwer/core/process/control/tomoobjserie.py +4 -0
  99. tomwer/core/process/control/volumeselector.py +4 -0
  100. tomwer/core/process/control/volumesymlink.py +47 -8
  101. tomwer/core/process/edit/darkflatpatch.py +49 -8
  102. tomwer/core/process/edit/imagekeyeditor.py +63 -13
  103. tomwer/core/process/reconstruction/axis/__init__.py +1 -1
  104. tomwer/core/process/reconstruction/axis/axis.py +61 -41
  105. tomwer/core/process/reconstruction/axis/params.py +4 -6
  106. tomwer/core/process/reconstruction/darkref/darkrefs.py +53 -16
  107. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +12 -2
  108. tomwer/core/process/reconstruction/lamino/__init__.py +1 -1
  109. tomwer/core/process/reconstruction/lamino/tofu.py +22 -2
  110. tomwer/core/process/reconstruction/nabu/nabucommon.py +93 -14
  111. tomwer/core/process/reconstruction/nabu/nabuscores.py +70 -33
  112. tomwer/core/process/reconstruction/nabu/nabuslices.py +219 -41
  113. tomwer/core/process/reconstruction/nabu/nabuvolume.py +240 -108
  114. tomwer/core/process/reconstruction/nabu/utils.py +10 -36
  115. tomwer/core/process/reconstruction/normalization/normalization.py +10 -3
  116. tomwer/core/process/reconstruction/saaxis/__init__.py +1 -0
  117. tomwer/core/process/reconstruction/saaxis/saaxis.py +564 -376
  118. tomwer/core/process/reconstruction/sadeltabeta/__init__.py +1 -0
  119. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +481 -268
  120. tomwer/core/process/reconstruction/scores/params.py +21 -8
  121. tomwer/core/process/reconstruction/test/test_darkref_copy.py +2 -0
  122. tomwer/core/process/reconstruction/test/test_saaxis.py +21 -8
  123. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +8 -5
  124. tomwer/core/process/script/python.py +7 -2
  125. tomwer/core/process/stitching/nabustitcher.py +10 -3
  126. tomwer/core/process/task.py +2 -9
  127. tomwer/core/process/test/test_axis.py +25 -15
  128. tomwer/core/process/test/test_conditions.py +6 -6
  129. tomwer/core/process/test/test_dark_and_flat.py +20 -15
  130. tomwer/core/process/test/test_data_transfer.py +8 -8
  131. tomwer/core/process/test/test_data_watcher.py +1 -1
  132. tomwer/core/process/test/test_lamino.py +6 -6
  133. tomwer/core/process/test/test_nabu.py +13 -8
  134. tomwer/core/process/test/test_normalization.py +1 -0
  135. tomwer/core/process/test/test_timer.py +6 -6
  136. tomwer/core/process/visualization/dataviewer.py +4 -0
  137. tomwer/core/process/visualization/diffviewer.py +4 -0
  138. tomwer/core/process/visualization/imagestackviewer.py +4 -0
  139. tomwer/core/process/visualization/radiostack.py +4 -0
  140. tomwer/core/process/visualization/samplemoved.py +4 -0
  141. tomwer/core/process/visualization/sinogramviewer.py +4 -0
  142. tomwer/core/process/visualization/slicestack.py +4 -0
  143. tomwer/core/process/visualization/volumeviewer.py +4 -0
  144. tomwer/core/scan/hdf5scan.py +4 -4
  145. tomwer/core/scan/scanbase.py +5 -1
  146. tomwer/core/scan/test/test_process_registration.py +9 -9
  147. tomwer/core/settings.py +59 -1
  148. tomwer/core/test/test_lamino.py +2 -1
  149. tomwer/core/utils/__init__.py +16 -0
  150. tomwer/core/utils/locker.py +0 -1
  151. tomwer/core/utils/resource.py +6 -11
  152. tomwer/core/utils/scanutils.py +2 -0
  153. tomwer/gui/cluster/slurm.py +91 -7
  154. tomwer/gui/cluster/supervisor.py +16 -11
  155. tomwer/gui/cluster/test/test_cluster.py +16 -1
  156. tomwer/gui/conditions/filter.py +3 -3
  157. tomwer/gui/control/datalist.py +24 -11
  158. tomwer/gui/control/email.py +183 -0
  159. tomwer/gui/control/reducedarkflatselector.py +545 -0
  160. tomwer/gui/control/singletomoobj.py +23 -1
  161. tomwer/gui/control/test/test_email.py +35 -0
  162. tomwer/gui/control/test/test_reducedarkflat_selector.py +280 -0
  163. tomwer/gui/reconstruction/axis/CompareImages.py +1 -1
  164. tomwer/gui/reconstruction/axis/axis.py +10 -6
  165. tomwer/gui/reconstruction/axis/radioaxis.py +14 -6
  166. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +2 -0
  167. tomwer/gui/reconstruction/darkref/darkrefwidget.py +4 -4
  168. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +3 -1
  169. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +34 -33
  170. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +1 -1
  171. tomwer/gui/reconstruction/normalization/intensity.py +5 -5
  172. tomwer/gui/reconstruction/saaxis/corrangeselector.py +1 -0
  173. tomwer/gui/reconstruction/saaxis/saaxis.py +6 -6
  174. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +6 -6
  175. tomwer/gui/reconstruction/scores/scoreplot.py +6 -4
  176. tomwer/gui/samplemoved/__init__.py +2 -2
  177. tomwer/gui/stackplot.py +18 -7
  178. tomwer/gui/stacks.py +2 -2
  179. tomwer/gui/stitching/stitchandbackground.py +2 -2
  180. tomwer/gui/stitching/stitching.py +1 -1
  181. tomwer/gui/stitching/stitching_raw.py +1 -1
  182. tomwer/gui/utils/__init__.py +1 -85
  183. tomwer/gui/utils/illustrations.py +1 -1
  184. tomwer/gui/utils/inputwidget.py +41 -36
  185. tomwer/gui/utils/slider.py +2 -2
  186. tomwer/gui/utils/utils.py +93 -0
  187. tomwer/gui/visualization/dataviewer.py +8 -5
  188. tomwer/gui/visualization/diffviewer/diffviewer.py +4 -4
  189. tomwer/gui/visualization/reconstructionparameters.py +26 -6
  190. tomwer/gui/visualization/sinogramviewer.py +7 -1
  191. tomwer/gui/visualization/test/test_reconstruction_parameters.py +2 -4
  192. tomwer/gui/visualization/volumeviewer.py +2 -0
  193. tomwer/resources/__init__.py +55 -43
  194. tomwer/resources/gui/icons/compose.png +0 -0
  195. tomwer/resources/gui/icons/compose.svg +75 -0
  196. tomwer/synctools/datatransfert.py +3 -1
  197. tomwer/synctools/stacks/edit/darkflatpatch.py +39 -34
  198. tomwer/synctools/stacks/edit/imagekeyeditor.py +8 -27
  199. tomwer/synctools/stacks/processingstack.py +45 -9
  200. tomwer/synctools/stacks/reconstruction/axis.py +6 -5
  201. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -0
  202. tomwer/synctools/stacks/reconstruction/lamino.py +3 -3
  203. tomwer/synctools/stacks/reconstruction/nabu.py +49 -140
  204. tomwer/synctools/stacks/reconstruction/normalization.py +1 -0
  205. tomwer/synctools/stacks/reconstruction/saaxis.py +19 -33
  206. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +16 -32
  207. tomwer/synctools/test/test_darkRefs.py +19 -10
  208. tomwer/synctools/test/test_foldertransfer.py +7 -7
  209. tomwer/third_party/nabu/preproc/phase.py +6 -8
  210. tomwer/third_party/nabu/utils.py +2 -3
  211. tomwer/version.py +1 -1
  212. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/METADATA +15 -54
  213. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/RECORD +219 -192
  214. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/WHEEL +1 -1
  215. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/entry_points.txt +3 -3
  216. /tomwer-1.2.0a1-py3.9-nspkg.pth → /tomwer-1.2.0a3-py3.11-nspkg.pth +0 -0
  217. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/LICENSE +0 -0
  218. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/namespace_packages.txt +0 -0
  219. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/top_level.txt +0 -0
@@ -28,10 +28,13 @@ __license__ = "MIT"
28
28
  __date__ = "31/03/2021"
29
29
 
30
30
 
31
+ import logging
31
32
  from typing import Optional
32
33
  from silx.gui import qt
33
34
  from tomwer.core.utils.char import BETA_CHAR, DELTA_CHAR
34
35
 
36
+ _logger = logging.getLogger(__name__)
37
+
35
38
 
36
39
  class ReconstructionParameters(qt.QWidget):
37
40
  """
@@ -114,7 +117,10 @@ class ReconstructionParameters(qt.QWidget):
114
117
  self._setSinoNormalization,
115
118
  self._setSoftwareVersion,
116
119
  ):
117
- func(metadata)
120
+ try:
121
+ func(metadata)
122
+ except Exception as e:
123
+ _logger.warning(f"Fail update when call {func}. Error is", e)
118
124
 
119
125
  def _setMethod(self, metadata: dict):
120
126
  method = (
@@ -140,8 +146,10 @@ class ReconstructionParameters(qt.QWidget):
140
146
  metadata.get("processing_options", {})
141
147
  .get("phase", {})
142
148
  .get("distance_cm", None)
149
+ ) or metadata.get("processing_options", {}).get("reconstruction", {}).get(
150
+ "sample_detector_dist", None
143
151
  )
144
- if distance_cm not in (None, ""):
152
+ if distance_cm not in (None, "", "None"):
145
153
  distance_cm = float(distance_cm)
146
154
  distance_cm = f"{distance_cm:.2}"
147
155
  else:
@@ -151,12 +159,24 @@ class ReconstructionParameters(qt.QWidget):
151
159
  def _setPixelSize(self, metadata: dict):
152
160
  # voxel size can be stored as pixel size (old version) or voxel size (new version)
153
161
  recons_params = metadata.get("processing_options", {}).get("reconstruction", {})
154
- voxel_size_cm = recons_params.get("voxel_size_cm", None) or recons_params.get(
155
- "pixel_size_cm", None
156
- )
162
+ voxel_size_cm = recons_params.get("voxel_size_cm", None)
163
+
164
+ # now voxel size is expected to be a tuple of three elements
165
+ if voxel_size_cm is not None:
166
+ voxel_size_cm = voxel_size_cm[0]
167
+ # FIXME: load_ini seems to fail to remove some char like '(' or ')'... to be fixed or investigate
168
+ # simplest might be to filter those when dumping it to text file... ??? or to handle those at silx level
169
+ if isinstance(voxel_size_cm, str):
170
+ for char_to_ignore in (" ", "(", ")", "[", "]"):
171
+ voxel_size_cm = voxel_size_cm.replace(char_to_ignore, "")
172
+ else:
173
+ # backward compatibility with old volume
174
+ voxel_size_cm = recons_params.get("pixel_size_cm", None)
157
175
  if voxel_size_cm is not None:
158
176
  voxel_size_cm = f"{float(voxel_size_cm):.8}"
159
- self._pixelSizeQLE.setText(voxel_size_cm if voxel_size_cm is not None else "")
177
+ self._pixelSizeQLE.setText(
178
+ str(voxel_size_cm) if voxel_size_cm is not None else ""
179
+ )
160
180
 
161
181
  def _setCor(self, metadata: dict):
162
182
  cor = (
@@ -59,7 +59,13 @@ class SinogramViewer(qt.QMainWindow):
59
59
  sigSinoLoadEnded = qt.Signal()
60
60
  """Signal emitted when a computation is ended"""
61
61
 
62
- def __init__(self, parent=None, scan=None, opts_orientation=qt.Qt.Vertical):
62
+ def __init__(
63
+ self,
64
+ parent=None,
65
+ scan=None,
66
+ opts_orientation=qt.Qt.Vertical,
67
+ backend=None,
68
+ ):
63
69
  qt.QMainWindow.__init__(self, parent)
64
70
  self._scan = None
65
71
  self._sinoInfoCache = None
@@ -53,14 +53,12 @@ def test_ReconstructionParameters(qtapp, phase_method): # noqa F401
53
53
  },
54
54
  },
55
55
  "processing_options": {
56
- "phase": {
57
- "distance_cm": 0.4,
58
- },
59
56
  "reconstruction": {
60
- "voxel_size_cm": 0.2,
57
+ "voxel_size_cm": (0.2, 0.2, 0.2),
61
58
  "rotation_axis_position": 104,
62
59
  "enable_halftomo": True,
63
60
  "fbp_filter_type": "Hilbert",
61
+ "sample_detector_dist": 0.4,
64
62
  },
65
63
  "take_log": {
66
64
  "log_min_clip": 1.0,
@@ -357,6 +357,8 @@ class VolumeViewer(qt.QMainWindow):
357
357
  self._set_volume(data)
358
358
  # set reconstruction parameters
359
359
  try:
360
+ # warning: limitation expected for .vol as it gets two configuration file. The default one is vol.info and does not contains
361
+ # any of the metadata 'distance', 'pixel size'... but it is here for backward compatiblity
360
362
  self._reconsWidget.setVolumeMetadata(
361
363
  volume.metadata or volume.load_metadata()
362
364
  )
@@ -24,17 +24,17 @@
24
24
  # ###########################################################################*/
25
25
 
26
26
 
27
- import importlib
27
+ from __future__ import annotations
28
28
  import os
29
29
  import sys
30
+ import contextlib
31
+ import atexit
32
+ from typing import NamedTuple, Optional
33
+ import importlib
34
+ import functools
30
35
 
31
- # pkg_resources is useful when this package is stored in a zip
32
- # When pkg_resources is not available, the resources dir defaults to the
33
- # directory containing this module.
34
- try:
36
+ if sys.version_info < (3, 9):
35
37
  import pkg_resources
36
- except ImportError:
37
- pkg_resources = None
38
38
 
39
39
  # For packaging purpose, patch this variable to use an alternative directory
40
40
  # E.g., replace with _RESOURCES_DIR = '/usr/share/silx/data'
@@ -56,25 +56,20 @@ if getattr(sys, "frozen", False):
56
56
  _RESOURCES_DIR = _dir
57
57
 
58
58
 
59
- class _ResourceDirectory(object):
59
+ # Manage resource files life-cycle
60
+ _file_manager = contextlib.ExitStack()
61
+ atexit.register(_file_manager.close)
62
+
63
+
64
+ class _ResourceDirectory(NamedTuple):
60
65
  """Store a source of resources"""
61
66
 
62
- def __init__(self, package_name, package_path=None, forced_path=None):
63
- if forced_path is None:
64
- if package_path is None:
65
- if pkg_resources is None:
66
- # In this case we have to compute the package path
67
- # Else it will not be used
68
- module = importlib.import_module(package_name)
69
- package_path = os.path.abspath(os.path.dirname(module.__file__))
70
- self.package_name = package_name
71
- self.package_path = package_path
72
- self.forced_path = forced_path
67
+ package_name: str
68
+ forced_path: Optional[str] = None
73
69
 
74
70
 
75
71
  _TOMWER_DIRECTORY = _ResourceDirectory(
76
72
  package_name=__name__,
77
- package_path=os.path.abspath(os.path.dirname(__file__)),
78
73
  forced_path=_RESOURCES_DIR,
79
74
  )
80
75
 
@@ -100,45 +95,62 @@ def _get_package_and_resource(resource, default_directory=None):
100
95
  else:
101
96
  prefix = "tomwer"
102
97
  if default_directory is not None:
103
- resource = os.path.join(default_directory, resource)
98
+ resource = f"{default_directory}/{resource}"
104
99
  if prefix not in _RESOURCE_DIRECTORIES:
105
100
  raise ValueError("Resource '%s' uses an unregistred prefix", resource)
106
101
  resource_directory = _RESOURCE_DIRECTORIES[prefix]
107
102
  return resource_directory, resource
108
103
 
109
104
 
110
- def _resource_filename(resource, default_directory=None):
105
+ # Manage resource files life-cycle
106
+ _file_manager = contextlib.ExitStack()
107
+ atexit.register(_file_manager.close)
108
+
109
+
110
+ @functools.lru_cache(maxsize=None)
111
+ def _get_resource_filename(package: str, resource: str) -> str:
112
+ """Returns path to requested resource in package
113
+
114
+ :param package: Name of the package in which to look for the resource
115
+ :param resource: Resource path relative to package using '/' path separator
116
+ :return: Abolute resource path in the file system
117
+ """
118
+ if sys.version_info < (3, 9):
119
+ return pkg_resources.resource_filename(package, resource)
120
+
121
+ # Caching prevents extracting the resource twice
122
+ file_context = importlib.resources.as_file(
123
+ importlib.resources.files(package) / resource
124
+ )
125
+ path = _file_manager.enter_context(file_context)
126
+ return str(path.absolute())
127
+
128
+
129
+ def _resource_filename(resource: str, default_directory: Optional[str] = None) -> str:
111
130
  """Return filename corresponding to resource.
112
131
 
113
- The existence of the resource is not checked.
132
+ The existence of the resource is not checked.
114
133
 
115
- The resource name can be prefixed by the name of a resource directory. For
134
+ The resource name can be prefixed by the name of a resource directory. For
116
135
  example "silx:foo.png" identify the resource "foo.png" from the resource
117
- directory "silx". See also :func:`register_resource_directory`.
118
-
119
- :param str resource: Resource path relative to resource directory
120
- using '/' path separator. It can be either a file or
121
- a directory.
122
- :param str default_directory: If the resource is not prefixed, the resource
123
- will be searched on this default directory of the silx resource
124
- directory. It should only be used internally by silx.
125
- :return: Absolute resource path in the file system
126
- :rtype: str
136
+ directory "silx". See also :func:`register_resource_directory`.
137
+
138
+ :param resource: Resource path relative to resource directory
139
+ using '/' path separator. It can be either a file or
140
+ a directory.
141
+ :param default_directory: If the resource is not prefixed, the resource
142
+ will be searched on this default directory of the silx resource
143
+ directory. It should only be used internally by silx.
144
+ :return: Absolute resource path in the file system
127
145
  """
128
146
  resource_directory, resource_name = _get_package_and_resource(
129
147
  resource, default_directory=default_directory
130
148
  )
149
+
131
150
  if resource_directory.forced_path is not None:
132
151
  # if set, use this directory
133
152
  base_dir = resource_directory.forced_path
134
153
  resource_path = os.path.join(base_dir, *resource_name.split("/"))
135
154
  return resource_path
136
- elif pkg_resources is None:
137
- # Fallback if pkg_resources is not available
138
- base_dir = resource_directory.package_path
139
- resource_path = os.path.join(base_dir, *resource_name.split("/"))
140
- return resource_path
141
- else:
142
- # Preferred way to get resources as it supports zipfile package
143
- package_name = resource_directory.package_name
144
- return pkg_resources.resource_filename(package_name, resource_name)
155
+
156
+ return _get_resource_filename(resource_directory.package_name, resource_name)
Binary file
@@ -0,0 +1,75 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="48"
6
+ height="48"
7
+ viewBox="0 0 12.7 12.7"
8
+ version="1.1"
9
+ id="svg286"
10
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
11
+ sodipodi:docname="compose.svg"
12
+ inkscape:export-filename="compose.png"
13
+ inkscape:export-xdpi="96"
14
+ inkscape:export-ydpi="96"
15
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
16
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ xmlns:svg="http://www.w3.org/2000/svg">
19
+ <sodipodi:namedview
20
+ id="namedview288"
21
+ pagecolor="#ffffff"
22
+ bordercolor="#000000"
23
+ borderopacity="0.25"
24
+ inkscape:showpageshadow="2"
25
+ inkscape:pageopacity="0.0"
26
+ inkscape:pagecheckerboard="0"
27
+ inkscape:deskcolor="#d1d1d1"
28
+ inkscape:document-units="mm"
29
+ showgrid="false"
30
+ inkscape:zoom="4.7572176"
31
+ inkscape:cx="23.858484"
32
+ inkscape:cy="53.182348"
33
+ inkscape:window-width="1920"
34
+ inkscape:window-height="1131"
35
+ inkscape:window-x="0"
36
+ inkscape:window-y="32"
37
+ inkscape:window-maximized="1"
38
+ inkscape:current-layer="g872" />
39
+ <defs
40
+ id="defs283" />
41
+ <g
42
+ inkscape:label="Layer 1"
43
+ inkscape:groupmode="layer"
44
+ id="layer1">
45
+ <g
46
+ id="g872"
47
+ transform="matrix(2.7603153,0,0,2.1174268,-0.61582227,-584.86385)">
48
+ <g
49
+ id="g877"
50
+ transform="matrix(0.69394884,-0.91617956,0.56586616,0.69394884,-154.75708,81.317197)"
51
+ inkscape:transform-center-x="6.3041843"
52
+ inkscape:transform-center-y="-5.6562159"
53
+ style="fill:none">
54
+ <rect
55
+ style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.233763;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
56
+ id="rect825"
57
+ width="3.7308216"
58
+ height="0.62381279"
59
+ x="-4.3877668"
60
+ y="281.05283" />
61
+ <path
62
+ style="fill:none;stroke:#000000;stroke-width:0.168986;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
63
+ d="m -4.3877668,281.67664 c -0.2793163,0.009 -0.5577288,-0.0435 -0.8324456,-0.35142 0.2750181,-0.18258 0.5523208,-0.28005 0.8324456,-0.27239"
64
+ id="path827"
65
+ inkscape:connector-curvature="0"
66
+ sodipodi:nodetypes="ccc" />
67
+ <path
68
+ style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
69
+ d="m -1.110839,281.0557 0.00835,0.60971"
70
+ id="path848"
71
+ inkscape:connector-curvature="0" />
72
+ </g>
73
+ </g>
74
+ </g>
75
+ </svg>
@@ -30,7 +30,9 @@ __date__ = "28/01/2019"
30
30
 
31
31
  from silx.gui import qt
32
32
 
33
- from tomwer.core.process.control.scantransfer import ScanTransfer as FolderTransfertP
33
+ from tomwer.core.process.control.scantransfer import (
34
+ ScanTransferTask as FolderTransfertP,
35
+ )
34
36
  from tomwer.core.scan.scanbase import TomwerScanBase
35
37
 
36
38
 
@@ -109,12 +109,18 @@ class _DarkFlatPatchProcessingThread(ProcessingThread, SuperviseProcess):
109
109
  self._configuration = configuration
110
110
 
111
111
  def run(self):
112
+ # TODO: must be replace by calling the 'DarkFlatPatch' task directly
112
113
  self.sigComputationStarted.emit()
113
114
  _logger.processStarted(f"{self._scan} Start dark-flat patch")
115
+ process = darkflatpatch.DarkFlatPatchTask(
116
+ inputs={
117
+ "data": self._scan,
118
+ "configuration": self._configuration,
119
+ "serialize_output_data": False,
120
+ }
121
+ )
114
122
  try:
115
- darkflatpatch.apply_dark_flat_patch(
116
- config=self._configuration, scan=self._scan
117
- )
123
+ process.run()
118
124
  except Exception as e:
119
125
  info = f"Fail to patch dark-flat for {self._scan}. Reason is {e}"
120
126
  _logger.processFailed(info)
@@ -124,36 +130,35 @@ class _DarkFlatPatchProcessingThread(ProcessingThread, SuperviseProcess):
124
130
  state=DatasetState.FAILED,
125
131
  details=info,
126
132
  )
133
+ return
134
+
135
+ index = self._scan.pop_process_index()
136
+ if isinstance(self._scan, EDFTomoScan):
137
+ entry = None
127
138
  else:
128
- index = self._scan.pop_process_index()
129
- if isinstance(self._scan, EDFTomoScan):
130
- entry = None
131
- else:
132
- entry = self._scan.entry
133
- configuration_to_dump = self._configuration
134
- keys = list(configuration_to_dump.keys())
135
- for key in keys:
136
- if isinstance(configuration_to_dump[key], DataUrl):
137
- configuration_to_dump[key] = configuration_to_dump[key].path()
138
- try:
139
- darkflatpatch.DarkFlatPatch._register_process(
140
- process_file=self._scan.process_file,
141
- entry=entry,
142
- configuration=configuration_to_dump,
143
- results={},
144
- process=darkflatpatch.DarkFlatPatch,
145
- process_index=index,
146
- overwrite=True,
147
- )
148
- except Exception as e:
149
- _logger.warning(
150
- f"Fail to register DarkFlatPatch process. Reason is {e}"
151
- )
152
- info = f"Dark-flat patched for {self._scan}."
153
- _logger.processSucceed(info)
154
- ProcessManager().notify_dataset_state(
155
- dataset=self._scan,
156
- process=self,
157
- state=DatasetState.SUCCEED,
158
- details=info,
139
+ entry = self._scan.entry
140
+ configuration_to_dump = self._configuration
141
+ keys = list(configuration_to_dump.keys())
142
+ for key in keys:
143
+ if isinstance(configuration_to_dump[key], DataUrl):
144
+ configuration_to_dump[key] = configuration_to_dump[key].path()
145
+ try:
146
+ darkflatpatch.DarkFlatPatchTask._register_process(
147
+ process_file=self._scan.process_file,
148
+ entry=entry,
149
+ configuration=configuration_to_dump,
150
+ results={},
151
+ process=darkflatpatch.DarkFlatPatchTask,
152
+ process_index=index,
153
+ overwrite=True,
159
154
  )
155
+ except Exception as e:
156
+ _logger.warning(f"Fail to register DarkFlatPatch process. Reason is {e}")
157
+ info = f"Dark-flat patched for {self._scan}."
158
+ _logger.processSucceed(info)
159
+ ProcessManager().notify_dataset_state(
160
+ dataset=self._scan,
161
+ process=self,
162
+ state=DatasetState.SUCCEED,
163
+ details=info,
164
+ )
@@ -32,7 +32,6 @@ import logging
32
32
  from processview.core.manager import DatasetState, ProcessManager
33
33
  from processview.core.superviseprocess import SuperviseProcess
34
34
  from silx.gui import qt
35
- from tomoscan.esrf.scan.hdf5scan import ImageKey
36
35
 
37
36
  from tomwer.core.process.edit import imagekeyeditor
38
37
  from tomwer.core.scan.scanbase import TomwerScanBase
@@ -107,10 +106,15 @@ class _ImageKeyEditorProcessingThread(ProcessingThread, SuperviseProcess):
107
106
  def run(self):
108
107
  self.sigComputationStarted.emit()
109
108
  _logger.processStarted(f"Start image key edition of {self._scan}")
109
+ task = imagekeyeditor.ImageKeyEditorTask(
110
+ inputs={
111
+ "data": self._scan,
112
+ "serialize_output_data": False,
113
+ "configuration": self._configuration,
114
+ },
115
+ )
110
116
  try:
111
- imagekeyeditor.change_image_key_control(
112
- config=self._configuration, scan=self._scan
113
- )
117
+ task.run()
114
118
  except Exception as e:
115
119
  info = f"Fail to edit image keys for {self._scan}. Reason is {e}"
116
120
  _logger.processFailed(info)
@@ -121,29 +125,6 @@ class _ImageKeyEditorProcessingThread(ProcessingThread, SuperviseProcess):
121
125
  details=info,
122
126
  )
123
127
  else:
124
- conf_to_dump = {}
125
- if "modifications" in self._configuration:
126
- for frame_index, new_frame_type in self._configuration[
127
- "modifications"
128
- ].items():
129
- conf_to_dump[str(frame_index)] = ImageKey.from_value(
130
- new_frame_type
131
- ).value
132
- index = self._scan.pop_process_index()
133
- try:
134
- imagekeyeditor.ImageKeyEditor._register_process(
135
- process_file=self._scan.process_file,
136
- entry=self._scan.entry,
137
- configuration=conf_to_dump,
138
- results={},
139
- process=imagekeyeditor.ImageKeyEditor,
140
- process_index=index,
141
- overwrite=True,
142
- )
143
- except Exception as e:
144
- _logger.warning(
145
- f"Fail to register ImageKeyEditor process. Reason is {e}"
146
- )
147
128
  info = f"Image key edited for {self._scan.get_identifier()}."
148
129
  _logger.processSucceed(info)
149
130
  ProcessManager().notify_dataset_state(
@@ -28,7 +28,10 @@ __license__ = "MIT"
28
28
  __date__ = "08/01/2020"
29
29
 
30
30
 
31
- from queue import Queue
31
+ from collections import deque
32
+ from typing import Optional
33
+ from tomwer.core.tomwer_object import TomwerObject
34
+ from silx.gui.utils import blockSignals
32
35
 
33
36
  from processview.core.manager import DatasetState, ProcessManager
34
37
  from processview.core.superviseprocess import SuperviseProcess
@@ -42,7 +45,7 @@ class ProcessingThread(qt.QThread):
42
45
  """Signal emitted when a computation is started"""
43
46
 
44
47
 
45
- class FIFO(Queue, SuperviseProcess):
48
+ class FIFO(SuperviseProcess):
46
49
  """Processing Queue with a First In, First Out behavior"""
47
50
 
48
51
  sigComputationStarted = qt.Signal(object)
@@ -53,7 +56,7 @@ class FIFO(Queue, SuperviseProcess):
53
56
 
54
57
  def __init__(self, process_id=None):
55
58
  SuperviseProcess.__init__(self, process_id=process_id)
56
- Queue.__init__(self)
59
+ self._deque = deque()
57
60
  self._computationThread = self._create_processing_thread(process_id=process_id)
58
61
  assert isinstance(self._computationThread, ProcessingThread)
59
62
  self._computationThread.sigComputationStarted.connect(
@@ -64,6 +67,10 @@ class FIFO(Queue, SuperviseProcess):
64
67
  """Scan computed currently"""
65
68
  self._processing = False
66
69
 
70
+ @property
71
+ def data_currently_computed(self) -> Optional[TomwerObject]:
72
+ return self._data_currently_computed
73
+
67
74
  def add(self, data, configuration=None, callback=None):
68
75
  """
69
76
  add a scan to process
@@ -82,7 +89,7 @@ class FIFO(Queue, SuperviseProcess):
82
89
  except Exception:
83
90
  pass
84
91
 
85
- Queue.put(self, (data, configuration, callback))
92
+ self.append((data, configuration, callback))
86
93
  if self.can_process_next():
87
94
  self._process_next()
88
95
 
@@ -90,11 +97,11 @@ class FIFO(Queue, SuperviseProcess):
90
97
  raise NotImplementedError("Virtual class")
91
98
 
92
99
  def _process_next(self):
93
- if Queue.empty(self):
100
+ if len(self) == 0:
94
101
  return
95
102
 
96
103
  self._processing = True
97
- data, configuration, callback = Queue.get(self)
104
+ data, configuration, callback = self.pop()
98
105
  self._process(data=data, configuration=configuration or {}, callback=callback)
99
106
 
100
107
  def can_process_next(self):
@@ -154,8 +161,37 @@ class FIFO(Queue, SuperviseProcess):
154
161
  future_tomo_obj=future_tomo_obj,
155
162
  )
156
163
 
164
+ def cancel(self):
165
+ if self._computationThread.isRunning():
166
+ with blockSignals(self._computationThread):
167
+ self._computationThread.cancel()
168
+ # stop stack
169
+ self.stop()
170
+ # emit next
171
+ self.sigComputationEnded.emit(None, None)
172
+
157
173
  def stop(self):
158
- while not self.empty():
159
- self.get()
160
- self._computationThread.blockSignals(True)
161
174
  self._computationThread.wait()
175
+ self._processing = False
176
+ self._data_currently_computed = None
177
+
178
+ # expose deque API
179
+ def append(self, value):
180
+ self._deque.append(value)
181
+
182
+ def clear(self):
183
+ self._deque.clear()
184
+
185
+ def pop(self):
186
+ return self._deque.pop()
187
+
188
+ def remove(self, value):
189
+ return self._deque.remove(value)
190
+
191
+ def __len__(self):
192
+ return len(self._deque)
193
+
194
+ def __contains__(self, value):
195
+ return value.get_identifier().to_str() in [
196
+ scan.get_identifier().to_str() for scan in self._deque
197
+ ]
@@ -34,7 +34,7 @@ from processview.core.manager import DatasetState, ProcessManager
34
34
  from processview.core.superviseprocess import SuperviseProcess
35
35
  from silx.gui import qt
36
36
 
37
- from tomwer.core.process.reconstruction.axis import AxisProcess
37
+ from tomwer.core.process.reconstruction.axis import AxisTask
38
38
  from tomwer.core.process.reconstruction.axis.axis import NoAxisUrl
39
39
  from tomwer.core.process.reconstruction.axis.mode import AxisMode
40
40
  from tomwer.core.scan.hdf5scan import HDF5TomoScan
@@ -98,10 +98,10 @@ class AxisProcessStack(FIFO, qt.QObject):
98
98
  entry = "entry"
99
99
  try:
100
100
  with data.acquire_process_file_lock():
101
- AxisProcess._register_process(
101
+ AxisTask._register_process(
102
102
  process_file=data.process_file,
103
103
  entry=entry,
104
- process=AxisProcess,
104
+ process=AxisTask,
105
105
  results={"center_of_rotation": cor if cor is not None else "-"},
106
106
  configuration=self._axis_params.to_dict(),
107
107
  process_index=data.pop_process_index(),
@@ -115,7 +115,7 @@ class AxisProcessStack(FIFO, qt.QObject):
115
115
 
116
116
  elif (
117
117
  not self._axis_params.use_sinogram
118
- and mode not in AxisProcess._CALCULATIONS_METHODS
118
+ and mode not in AxisTask._CALCULATIONS_METHODS
119
119
  ):
120
120
  _logger.warning(f"no method defined to compute {mode}")
121
121
  if callback is not None:
@@ -188,10 +188,11 @@ class _ProcessingThread(ProcessingThread, SuperviseProcess):
188
188
 
189
189
  def run(self):
190
190
  self.sigComputationStarted.emit()
191
- axis = AxisProcess(
191
+ axis = AxisTask(
192
192
  inputs={
193
193
  "data": self._scan,
194
194
  "axis_params": self._axis_params,
195
+ "serialize_output_data": False,
195
196
  },
196
197
  process_id=self.process_id,
197
198
  )
@@ -152,6 +152,7 @@ class _ProcessingThread(ProcessingThread, SuperviseProcess):
152
152
  inputs = self._inputs
153
153
  inputs["data"] = self._data
154
154
  inputs["save_dir"] = self._save_dir
155
+ inputs["serialize_output_data"] = False
155
156
  process = DarkRefsCopyWithSig(
156
157
  parent=self.parent(), inputs=inputs, process_id=self.process_id
157
158
  )