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
@@ -46,7 +46,7 @@ from tomwer.core.process.reconstruction.nabu.utils import (
46
46
  retrieve_lst_of_value_from_str,
47
47
  )
48
48
  from tomwer.core.process.reconstruction.sadeltabeta.sadeltabeta import (
49
- SADeltaBetaProcess,
49
+ SADeltaBetaTask,
50
50
  )
51
51
  from tomwer.core.process.reconstruction.scores.params import ScoreMethod
52
52
  from tomwer.core.scan.scanbase import TomwerScanBase
@@ -74,7 +74,7 @@ class ScorePlot(_ScorePlot, constructor=DelaBetaSelection):
74
74
  if scan.sa_delta_beta_params:
75
75
  scan.sa_delta_beta_params.score_method = self.getScoreMethod()
76
76
  # update autofocus
77
- SADeltaBetaProcess.autofocus(scan)
77
+ SADeltaBetaTask.autofocus(scan)
78
78
  self.setVarScores(
79
79
  scores=self._scores,
80
80
  score_method=self.getScoreMethod(),
@@ -95,7 +95,7 @@ class _SADeltaBetaTabWidget(qt.QTabWidget):
95
95
  sigConfigurationChanged = qt.Signal()
96
96
  """Signal emit when the configuration changes"""
97
97
 
98
- def __init__(self, parent=None):
98
+ def __init__(self, parent=None, backend=None):
99
99
  qt.QTabWidget.__init__(self, parent=parent)
100
100
 
101
101
  self._deltaBetaSelectionWidget = DeltaBetaSelectionWidget(self)
@@ -111,7 +111,7 @@ class _SADeltaBetaTabWidget(qt.QTabWidget):
111
111
  nabu_icon = icons.getQIcon("nabu")
112
112
  self.addTab(self._nabuSettings, nabu_icon, "reconstruction settings")
113
113
  # results
114
- self._resultsViewer = ScorePlot(self, variable_name="db")
114
+ self._resultsViewer = ScorePlot(self, variable_name="db", backend=backend)
115
115
  results_icon = icons.getQIcon("results")
116
116
  self.addTab(self._resultsViewer, results_icon, "reconstructed slices")
117
117
 
@@ -235,7 +235,7 @@ class SADeltaBetaWindow(qt.QMainWindow):
235
235
  Widget used to determine half-automatically the better delta / beta value
236
236
  """
237
237
 
238
- def __init__(self, parent=None):
238
+ def __init__(self, parent=None, backend=None):
239
239
  qt.QMainWindow.__init__(self, parent)
240
240
  self._db_values = []
241
241
  self._urls = []
@@ -248,7 +248,7 @@ class SADeltaBetaWindow(qt.QMainWindow):
248
248
  self._mainWidget.setLayout(qt.QVBoxLayout())
249
249
  self._scanInfo = ScanNameLabelAndShape(self)
250
250
  self._mainWidget.layout().addWidget(self._scanInfo)
251
- self._tabWidget = _SADeltaBetaTabWidget(self)
251
+ self._tabWidget = _SADeltaBetaTabWidget(self, backend=backend)
252
252
  self._mainWidget.layout().addWidget(self._tabWidget)
253
253
  self.setCentralWidget(self._mainWidget)
254
254
  # next and previous buttons for browsing the tab widget
@@ -64,8 +64,8 @@ class VariableScorePlot(PlotWidget):
64
64
 
65
65
  MARKER_COLOR = "#ff292199"
66
66
 
67
- def __init__(self, parent):
68
- PlotWidget.__init__(self, parent)
67
+ def __init__(self, parent, backend=None):
68
+ PlotWidget.__init__(self, parent, backend=backend)
69
69
  self._scores = {}
70
70
  self.setAxesDisplayed(False)
71
71
  self.setMaximumHeight(150)
@@ -293,6 +293,7 @@ class SingleValueSelection(VariableSelection):
293
293
 
294
294
 
295
295
  class DelaBetaSelection(SingleValueSelection):
296
+ # FIXME: fix typo Dela vs Delta
296
297
  def __init__(self, *args, **kwargs):
297
298
  super().__init__(*args, **kwargs)
298
299
  idx = self._scoreMethodCB.findText(ScoreMethod.TOMO_CONSISTENCY.value)
@@ -488,6 +489,7 @@ class ScorePlot(qt.QWidget):
488
489
  parent=None,
489
490
  variable_name=None,
490
491
  dims_colors=("#ffff5a", "#62efff", "#ff5bff"),
492
+ backend=None,
491
493
  ):
492
494
  if not isinstance(variable_name, str):
493
495
  raise TypeError("a variable name should be provided as a string")
@@ -500,7 +502,7 @@ class ScorePlot(qt.QWidget):
500
502
  # define GUI
501
503
  self.setLayout(qt.QGridLayout())
502
504
  # main plot
503
- self._plot = ImageStack(self, show_overview=False)
505
+ self._plot = ImageStack(self, show_overview=False, backend=backend)
504
506
  self._plot.getPlotWidget().setYAxisInverted(settings.Y_AXIS_DOWNWARD)
505
507
 
506
508
  self._plot.getPlotWidget().setKeepDataAspectRatio(True)
@@ -878,7 +880,7 @@ class _VarSlider(qt.QWidget):
878
880
  value = where[0]
879
881
  if isinstance(value, numpy.ndarray):
880
882
  value = value[0]
881
- self._slider.setValue(value)
883
+ self._slider.setValue(int(value))
882
884
 
883
885
  def value(self):
884
886
  return self._slider.value()
@@ -70,14 +70,14 @@ class SampleMovedWidget(qt.QMainWindow):
70
70
  two projections
71
71
  """
72
72
 
73
- def __init__(self, parent=None):
73
+ def __init__(self, parent=None, backend=None):
74
74
  qt.QMainWindow.__init__(self, parent)
75
75
  self._scan = None
76
76
  self.setWindowFlags(qt.Qt.Widget)
77
77
  self._isConnected = False
78
78
  self._images = {}
79
79
  self._symmetricalStates = {"first": False, "second": False}
80
- self._plot = CompareImages(parent=self)
80
+ self._plot = CompareImages(parent=self, backend=backend)
81
81
  self._plot.setWindowFlags(qt.Qt.Widget)
82
82
  self._plot.getPlot().setYAxisInverted(Y_AXIS_DOWNWARD)
83
83
  self._on_load_callback = []
tomwer/gui/stackplot.py CHANGED
@@ -67,7 +67,7 @@ class _QImageStackPlot(qt.QWidget):
67
67
  )
68
68
  )
69
69
 
70
- def __init__(self, parent, sliderVertical=False):
70
+ def __init__(self, parent, sliderVertical=False, backend=None):
71
71
  qt.QWidget.__init__(self, parent)
72
72
  self._transpose = False
73
73
  """If we want to transpose the image like imagj by default"""
@@ -82,7 +82,7 @@ class _QImageStackPlot(qt.QWidget):
82
82
  mainwidget.setLayout(mainLayout)
83
83
  layout.addWidget(mainwidget)
84
84
 
85
- self._plot = Plot2D(self)
85
+ self._plot = Plot2D(self, backend=backend)
86
86
 
87
87
  def find_y_inverted(actions):
88
88
  for act in actions:
@@ -259,13 +259,15 @@ class _QImageFileStackPlot(_QImageStackPlot):
259
259
  file
260
260
  """
261
261
 
262
- def __init__(self, parent):
262
+ def __init__(self, parent, backend=None):
263
263
  """
264
264
  Constructor
265
265
 
266
266
  :param parent: the Qt parent widget
267
267
  """
268
- _QImageStackPlot.__init__(self, parent)
268
+ _QImageStackPlot.__init__(self, parent, backend=backend)
269
+ self._imagejThreads = []
270
+ # threads used to open frames with image j
269
271
 
270
272
  self._loadingMode = _LoadingModeToolButton(parent=self)
271
273
  self.getControlWidget().layout().addWidget(self._loadingMode)
@@ -393,7 +395,9 @@ class _QImageFileStackPlot(_QImageStackPlot):
393
395
  logger.warning("No active image defined")
394
396
  else:
395
397
  try:
396
- utils.open_url_with_image_j(active_image.url)
398
+ self._imagejThreads.append(
399
+ utils.open_url_with_image_j(active_image.url)
400
+ )
397
401
  except OSError as e:
398
402
  msg = qt.QMessageBox(self)
399
403
  msg.setIcon(qt.QMessageBox.Warning)
@@ -474,6 +478,10 @@ class _QImageFileStackPlot(_QImageStackPlot):
474
478
  self._qlFileName.setText("")
475
479
  super(_QImageFileStackPlot, self).clear()
476
480
 
481
+ def close(self):
482
+ [thread.quit() for thread in self._imagejThreads]
483
+ super.close()
484
+
477
485
 
478
486
  class _StackImage(object):
479
487
  def __init__(self, images: Optional[Iterable]):
@@ -601,8 +609,11 @@ class _StackImageToLoad(_StackImage, qt.QObject):
601
609
  return self._mode
602
610
 
603
611
  def _loadAllImages(self):
604
- for imgIndex, img in self._images.items():
605
- img.load_recons_params(self._force_sync)
612
+ for _, img in self._images.items():
613
+ try:
614
+ img.load(sync=self._force_sync)
615
+ except Exception as e:
616
+ logger.error(e)
606
617
 
607
618
  def _loaded(self, url_path):
608
619
  self.sigImageLoaded.emit(url_path)
tomwer/gui/stacks.py CHANGED
@@ -67,11 +67,11 @@ class _ImageStack(qt.QMainWindow):
67
67
  copy.
68
68
  """
69
69
 
70
- def __init__(self, parent=None):
70
+ def __init__(self, parent=None, backend=None):
71
71
  qt.QMainWindow.__init__(self, parent)
72
72
  self.setWindowFlags(qt.Qt.Widget)
73
73
  self._scans = set()
74
- self._viewer = _QImageFileStackPlot(parent=self)
74
+ self._viewer = _QImageFileStackPlot(parent=self, backend=backend)
75
75
  self._viewer.addFolderName(True)
76
76
  self.setCentralWidget(self._viewer)
77
77
 
@@ -38,13 +38,13 @@ class AlphaChannelWidget(qt.QWidget):
38
38
  self.sigAlphaBackgroundChanged.emit()
39
39
 
40
40
  def setAlphaStitchedImg(self, alpha: float):
41
- self._alphaImgSlider.setValue(alpha * 100.0)
41
+ self._alphaImgSlider.setValue(int(alpha * 100.0))
42
42
 
43
43
  def getAlphaStitchedImg(self) -> float:
44
44
  return self._alphaImgSlider.value() / 100.0
45
45
 
46
46
  def setAlphaBackgroundImg(self, alpha: float):
47
- self._alphaBackgroundSlider.setValue(alpha * 100.0)
47
+ self._alphaBackgroundSlider.setValue(int(alpha * 100.0))
48
48
 
49
49
  def getAlphaBackgroundImg(self) -> float:
50
50
  return self._alphaBackgroundSlider.value() / 100.0
@@ -703,7 +703,7 @@ class ZStitchingWindow(qt.QMainWindow):
703
703
  2: self._updateAxis2PosFromPreviewCalc.isChecked(),
704
704
  }
705
705
 
706
- if update_requested[0] or update_requested[1]:
706
+ if update_requested[0] or update_requested[2]:
707
707
  existing_tomo_obj = {
708
708
  tomo_obj.get_identifier().to_str(): tomo_obj
709
709
  for tomo_obj in self._widget._mainWidget.getTomoObjs()
@@ -572,4 +572,4 @@ class AlphaValueSlider(qt.QTableWidget):
572
572
  return self._slider.value()
573
573
 
574
574
  def setValue(self, value: int):
575
- self._slider.setValue(value)
575
+ self._slider.setValue(int(value))
@@ -1,85 +1 @@
1
- # coding: utf-8
2
- # /*##########################################################################
3
- # Copyright (C) 2017 European Synchrotron Radiation Facility
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
- #
23
- #############################################################################*/
24
-
25
- """
26
- Some utils GUI associated to illustrations
27
- """
28
-
29
- __authors__ = ["H.Payno"]
30
- __license__ = "MIT"
31
- __date__ = "29/01/2020"
32
-
33
-
34
- import logging
35
- import os
36
- import subprocess
37
-
38
- _logger = logging.getLogger(__name__)
39
-
40
-
41
- __has_image_j = None
42
- __image_j_from = None
43
-
44
- _IMAGE_J_BIN_PATH = "/sware/pub/ImageJ/ImageJ"
45
-
46
-
47
- def has_imagej():
48
- """Return if imagej command is accessible from the computer"""
49
- global __has_image_j
50
- global __image_j_from
51
- if __has_image_j is None:
52
- try:
53
- # use help because there is no information regarding version
54
- subprocess.call(["imagej", "-h"], stdout=subprocess.PIPE)
55
- except OSError:
56
- if os.path.exists(_IMAGE_J_BIN_PATH):
57
- __image_j_from = "binary"
58
- __has_image_j = True
59
- else:
60
- __has_image_j = False
61
- else:
62
- __image_j_from = "command"
63
- __has_image_j = True
64
- return __has_image_j
65
-
66
-
67
- def open_url_with_image_j(url):
68
- """open the url in an imagej subprocess
69
-
70
- :param DataUrl url: url we want to open in imagej
71
- """
72
- global __image_j_from
73
- if not has_imagej():
74
- raise OSError("ImageJ is not installed")
75
- elif __image_j_from == "command":
76
- # for now we only manage the simple case of an edf file
77
- try:
78
- subprocess.call(["imagej", "-o", url.file_path()])
79
- except Exception as e:
80
- _logger.warning(f"Fail to open {url}. Reason is {e}")
81
- elif __image_j_from == "binary":
82
- try:
83
- subprocess.call([_IMAGE_J_BIN_PATH, url.file_path()])
84
- except Exception as e:
85
- _logger.warning(f"Fail to open {url}. Reason is {e}")
1
+ from .utils import has_imagej, open_url_with_image_j # noqa F403
@@ -81,7 +81,7 @@ class _IllustrationWidget(qt.QWidget):
81
81
  height = event.size().height()
82
82
  width = self.widthForHeight(height)
83
83
 
84
- self._display.resize(width, height)
84
+ self._display.resize(int(width), int(height))
85
85
  if self.isUsingSvg() is False:
86
86
  self._updatePixamp()
87
87
 
@@ -28,7 +28,7 @@ __date__ = "16/08/2018"
28
28
 
29
29
  import logging
30
30
  import os
31
- from typing import Optional, Union
31
+ from typing import Optional
32
32
 
33
33
  import numpy
34
34
  from nxtomomill.io.config import TomoEDFConfig as EDFConfig
@@ -41,6 +41,7 @@ from tomwer.gui import icons
41
41
  from tomwer.gui.qlefilesystem import QLFileSystem
42
42
  from tomwer.io.utils import get_default_directory
43
43
  from tomwer.core.volume.volumefactory import VolumeFactory
44
+ from tomwer.core.process.control.nxtomomill import NXtomomillNXDefaultOutput
44
45
 
45
46
  from tomwer.core.volume import (
46
47
  EDFVolume,
@@ -71,11 +72,14 @@ class SelectionLineEdit(qt.QWidget):
71
72
 
72
73
  _DEFAULT_SELECTION = LIST_MODE
73
74
 
74
- def __init__(self, text=None, parent=None):
75
+ def __init__(self, text=None, parent=None, allow_negative_indices=False):
75
76
  qt.QWidget.__init__(self, parent)
76
77
  self.setLayout(qt.QHBoxLayout())
77
78
  self._qLineEdit = qt.QLineEdit(parent=self)
78
- fpm = "\\d*\\.?\\d+" # float or int matching
79
+ if allow_negative_indices:
80
+ fpm = "\\-?\\d*\\.?\\d+" # float or int matching
81
+ else:
82
+ fpm = "\\d*\\.?\\d+" # float or int matching
79
83
  qRegExp = qt.QRegExp(
80
84
  "(" + fpm + "[;]?[,]?[ ]?){1,}" + "|" + ":".join((fpm, fpm, fpm))
81
85
  )
@@ -260,38 +264,33 @@ class NXTomomillOutputDirSelector(qt.QWidget):
260
264
  sigChanged = qt.Signal()
261
265
  """Signal emit when the output directory of the nx file change"""
262
266
 
263
- DEFAULT_PROCESSED_DIR = (
264
- "{scan_parent_dir_basename}/../../PROCESSED_DATA/{scan_dir_name}"
265
- )
266
- """Default pattern to find the 'processed' directory"""
267
-
268
267
  def __init__(self, parent=None):
269
268
  qt.QWidget.__init__(self, parent)
270
269
  self.setLayout(qt.QGridLayout())
271
270
  self.__buttonGroup = qt.QButtonGroup(self)
272
271
  self.__buttonGroup.setExclusive(True)
273
272
 
274
- tooltip = f"""Define the output directory of the nexus (.nx) file. Options are:
273
+ tooltip = """Define the output directory of the nexus (.nx) file. Options are:
275
274
  \n - next to bliss file: create the NXtomos at the same level as the bliss input file
276
- \n - 'PROCESSED_DATA' folder: create NXtomos on the default 'PROCESSED_DATA' folder (bliss default folder, nearby the 'raw' folder). Uses {self.DEFAULT_PROCESSED_DIR} pattern
275
+ \n - 'PROCESSED_DATA' folder: create NXtomos on the default 'PROCESSED_DATA' folder (bliss default folder, nearby the 'raw' folder).
277
276
  \n - user defined folder: users can provide their own folders using keywords for string formatting such as 'scan_dir_name', 'scan_basename' or 'scan_parent_dir_basename'
278
277
  """
279
278
 
280
279
  # output dir is the folder containing the .nx file
281
- self._closeToNxRB = qt.QRadioButton("next to bliss file", self)
282
- self._closeToNxRB.setToolTip(tooltip)
283
- self.layout().addWidget(self._closeToNxRB, 0, 0, 1, 1)
284
- self.__buttonGroup.addButton(self._closeToNxRB)
280
+ self._closeToBlissFileRB = qt.QRadioButton("near input file", self)
281
+ self._closeToBlissFileRB.setToolTip(tooltip)
282
+ self.layout().addWidget(self._closeToBlissFileRB, 0, 0, 1, 1)
283
+ self.__buttonGroup.addButton(self._closeToBlissFileRB)
285
284
  # output dir is the default 'reduced'folder
286
- self._processedFolderRB = qt.QRadioButton("'PROCESSED_DATA' folder", self)
287
- self._processedFolderRB.setToolTip(tooltip)
288
- self.layout().addWidget(self._processedFolderRB, 1, 0, 1, 1)
289
- self.__buttonGroup.addButton(self._processedFolderRB)
285
+ self._processedDataFolderRB = qt.QRadioButton("'PROCESSED_DATA' folder", self)
286
+ self._processedDataFolderRB.setToolTip(tooltip)
287
+ self.layout().addWidget(self._processedDataFolderRB, 1, 0, 1, 1)
288
+ self.__buttonGroup.addButton(self._processedDataFolderRB)
290
289
  # manual
291
290
  self._manualRB = qt.QRadioButton("custom output directory", self)
292
291
  self._manualRB.setToolTip(tooltip)
293
292
  self.layout().addWidget(self._manualRB, 2, 0, 1, 1)
294
- self._outputFolderQLE = qt.QLineEdit("", self)
293
+ self._outputFolderQLE = QLFileSystem("", self)
295
294
  self.layout().addWidget(self._outputFolderQLE, 2, 1, 1, 1)
296
295
  self._selectButton = qt.QPushButton("", self)
297
296
  style = qt.QApplication.style()
@@ -304,13 +303,13 @@ class NXTomomillOutputDirSelector(qt.QWidget):
304
303
  # connect signal / slot
305
304
  self._selectButton.released.connect(self._selectOutpuFolder)
306
305
  self.__buttonGroup.buttonReleased.connect(self._updateVisiblity)
307
- self._closeToNxRB.toggled.connect(self._outputDirChanged)
308
- self._processedFolderRB.toggled.connect(self._outputDirChanged)
306
+ self._closeToBlissFileRB.toggled.connect(self._outputDirChanged)
307
+ self._processedDataFolderRB.toggled.connect(self._outputDirChanged)
309
308
  self._manualRB.toggled.connect(self._outputDirChanged)
310
309
  self._outputFolderQLE.editingFinished.connect(self._outputDirChanged)
311
310
 
312
311
  # set up
313
- self._closeToNxRB.setChecked(True)
312
+ self._processedDataFolderRB.setChecked(True)
314
313
  self._updateVisiblity()
315
314
 
316
315
  def _updateVisiblity(self, *args, **kwargs):
@@ -335,28 +334,34 @@ class NXTomomillOutputDirSelector(qt.QWidget):
335
334
  self._outputFolderQLE.setText(dialog.selectedFiles()[0])
336
335
  self.sigChanged.emit()
337
336
 
338
- def getOutputFolder(self) -> Union[None, str]:
337
+ def getOutputFolder(self) -> str:
339
338
  if self._manualRB.isChecked():
340
339
  return self._outputFolderQLE.text()
341
- elif self._processedFolderRB.isChecked():
342
- return self.DEFAULT_PROCESSED_DIR
340
+ elif self._processedDataFolderRB.isChecked():
341
+ return NXtomomillNXDefaultOutput.PROCESSED_DATA.value
342
+ elif self._closeToBlissFileRB.isChecked():
343
+ return NXtomomillNXDefaultOutput.NEAR_INPUT_FILE.value
343
344
  else:
344
- return None
345
+ raise RuntimeError("Use case - h52nx output dir - not handled")
345
346
 
346
- def setOutputFolder(self, output_folder: Optional[str]):
347
+ def setOutputFolder(self, output_folder: str):
347
348
  old = self.blockSignals(True)
348
349
  self._manualRB.setChecked(output_folder is not None)
349
- if output_folder is None:
350
- self._closeToNxRB.setChecked(True)
350
+ try:
351
+ default_output = NXtomomillNXDefaultOutput.from_value(output_folder)
352
+ except ValueError:
353
+ self._outputFolderQLE.setText(output_folder)
354
+ self._manualRB.setChecked(True)
351
355
  else:
352
- is_default_processed_folder = output_folder == self.DEFAULT_PROCESSED_DIR
353
- if is_default_processed_folder:
354
- self._processedFolderRB.setChecked(True)
356
+ if default_output is NXtomomillNXDefaultOutput.NEAR_INPUT_FILE:
357
+ self._processedDataFolderRB.setChecked(True)
358
+ elif default_output is NXtomomillNXDefaultOutput.PROCESSED_DATA:
359
+ self._closeToBlissFileRB.setChecked(True)
355
360
  else:
356
- self._outputFolderQLE.setText(output_folder)
357
- self._manualRB.setChecked(True)
358
- self._updateVisiblity()
359
- self.blockSignals(old)
361
+ raise ValueError(f"default output not handled ({default_output})")
362
+ finally:
363
+ self._updateVisiblity()
364
+ self.blockSignals(old)
360
365
 
361
366
 
362
367
  class _ConfigFileSelector(qt.QWidget):
@@ -73,7 +73,7 @@ class LogSlider(qt.QWidget):
73
73
  if min_ <= 0.0 or max_ <= 0.0:
74
74
  raise ValueError("LogSlider can only handled positive values")
75
75
  self._valueQBSB.setRange(min_, max_)
76
- self._slider.setRange(numpy.log(min_), numpy.log(max_))
76
+ self._slider.setRange(int(numpy.log(min_)), int(numpy.log(max_)))
77
77
 
78
78
  def _sliderValueChanged(self, *args, **kwargs):
79
79
  old = self._valueQBSB.blockSignals(True)
@@ -83,7 +83,7 @@ class LogSlider(qt.QWidget):
83
83
 
84
84
  def _qdsbValueChanged(self, *args, **kwargs):
85
85
  old = self._slider.blockSignals(True)
86
- self._slider.setValue(numpy.log(self._valueQBSB.value()))
86
+ self._slider.setValue(int(numpy.log(self._valueQBSB.value())))
87
87
  self._slider.blockSignals(old)
88
88
  self.valueChanged.emit(self.value())
89
89
 
@@ -0,0 +1,93 @@
1
+ """
2
+ Some utils GUI associated to illustrations
3
+ """
4
+
5
+ __authors__ = ["H.Payno"]
6
+ __license__ = "MIT"
7
+ __date__ = "29/01/2020"
8
+
9
+
10
+ import logging
11
+ import os
12
+ import subprocess
13
+ import threading
14
+ import platform
15
+
16
+ _logger = logging.getLogger(__name__)
17
+
18
+
19
+ __has_image_j = None
20
+ __image_j_from = None
21
+
22
+ _IMAGE_J_BIN_PATH = "/sware/pub/ImageJ/ImageJ"
23
+
24
+
25
+ def has_imagej():
26
+ """Return if imagej command is accessible from the computer"""
27
+ global __has_image_j
28
+ global __image_j_from
29
+ if __has_image_j is None:
30
+ if platform.machine() in ("x86_64", "AMD64") and os.path.exists(
31
+ _IMAGE_J_BIN_PATH
32
+ ):
33
+ __image_j_from = "binary"
34
+ __has_image_j = True
35
+ else:
36
+ try:
37
+ # use help because there is no information regarding version
38
+ subprocess.call(["imagej", "-h"], stdout=subprocess.PIPE)
39
+ except Exception:
40
+ __has_image_j = False
41
+ else:
42
+ __image_j_from = "command"
43
+ __has_image_j = True
44
+ return __has_image_j
45
+
46
+
47
+ def open_url_with_image_j(url) -> threading.Thread:
48
+ """open the url in an imagej subprocess within a thread.
49
+ It is up to the caller to handle thread life
50
+
51
+ :param DataUrl url: url we want to open in imagej
52
+ """
53
+ global __image_j_from
54
+ if not has_imagej():
55
+ raise OSError("ImageJ is not installed")
56
+ thread = ImageJthread(url=url, image_j_from=__image_j_from)
57
+ thread.start()
58
+ return thread
59
+
60
+
61
+ class ImageJthread(threading.Thread):
62
+ def __init__(self, url, image_j_from, *args, **kwargs) -> None:
63
+ super().__init__(*args, **kwargs)
64
+ self._image_j_from = image_j_from
65
+ self._url = url
66
+ self._process = None
67
+
68
+ def run(self):
69
+ if self._image_j_from == "command":
70
+ # for now we only manage the simple case of an edf file
71
+ try:
72
+ self._process = subprocess.Popen(
73
+ ["imagej", "-o", self._url.file_path()],
74
+ )
75
+ except Exception as e:
76
+ _logger.warning(f"Fail to open {self._url}. Reason is {e}")
77
+ else:
78
+ self._process.communicate()
79
+
80
+ elif self._image_j_from == "binary":
81
+ try:
82
+ self._process = subprocess.Popen(
83
+ [_IMAGE_J_BIN_PATH, self._url.file_path()],
84
+ )
85
+ except Exception as e:
86
+ _logger.warning(f"Fail to open {self._url}. Reason is {e}")
87
+ else:
88
+ self._process.communicate()
89
+
90
+ def quit(self):
91
+ if self._process is not None:
92
+ self._process.kill()
93
+ self._process.wait()
@@ -77,12 +77,14 @@ class DataViewer(qt.QMainWindow):
77
77
  sigConfigChanged = qt.Signal()
78
78
  """Signal emitted when the settings (display mode, options...) changed. """
79
79
 
80
- def __init__(self, parent, show_overview=True):
81
- qt.QMainWindow.__init__(self, parent)
80
+ def __init__(self, parent, show_overview=True, backend=None):
81
+ super().__init__(parent)
82
82
  self.setWindowFlags(qt.Qt.Widget)
83
83
  self._scan = None
84
84
  # viewer
85
- self._viewer = ImageStack(parent=self, show_overview=show_overview)
85
+ self._viewer = ImageStack(
86
+ parent=self, show_overview=show_overview, backend=backend
87
+ )
86
88
  self._viewer.getPlotWidget().setYAxisInverted(Y_AXIS_DOWNWARD)
87
89
  # set an UrlLoader managing .npy and .vol
88
90
  self._viewer.getPlotWidget().setKeepDataAspectRatio(True)
@@ -419,10 +421,11 @@ class DisplayControl(qt.QWidget):
419
421
 
420
422
 
421
423
  class ImageStack(_ImageStack):
422
- def __init__(self, parent, show_overview=True):
424
+ def __init__(self, parent, show_overview=True, backend=None):
423
425
  self._normFct = None
424
426
  self._url_indexes = None
425
- super(ImageStack, self).__init__(parent)
427
+ super().__init__(parent)
428
+ self.getPlotWidget().setBackend(backend)
426
429
  self.setUrlLoaderClass(_TomwerUrlLoader)
427
430
  # hide axis to be display
428
431
  self._plot.getPlotWidget().setAxesDisplayed(False)
@@ -57,8 +57,8 @@ _logger = logging.getLogger(__name__)
57
57
 
58
58
 
59
59
  class CompareImages(_CompareImages):
60
- def __init__(self, parent=None):
61
- super().__init__(parent=parent)
60
+ def __init__(self, parent=None, backend=None):
61
+ super().__init__(parent=parent, backend=backend)
62
62
  # ignore Pan with arrow keys. This should be used by the "arrowControlWidget"
63
63
  self.getPlot().setPanWithArrowKeys(False)
64
64
  self._firstVisible = True
@@ -371,7 +371,7 @@ class DiffFrameViewer(qt.QMainWindow):
371
371
  or dark.
372
372
  """
373
373
 
374
- def __init__(self, parent=None):
374
+ def __init__(self, parent=None, backend=None):
375
375
  qt.QMainWindow.__init__(self, parent)
376
376
  self.setWindowFlags(qt.Qt.Widget)
377
377
  # add frame selector
@@ -393,7 +393,7 @@ class DiffFrameViewer(qt.QMainWindow):
393
393
  self.addDockWidget(qt.Qt.TopDockWidgetArea, self._shiftDW)
394
394
 
395
395
  # define central widget
396
- self._mainWidget = CompareImages(parent=self)
396
+ self._mainWidget = CompareImages(parent=self, backend=backend)
397
397
  self._mainWidget.setVisualizationMode(
398
398
  CompareImages.VisualizationMode.COMPOSITE_A_MINUS_B
399
399
  )