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
@@ -88,11 +88,6 @@ class FutureTomwerScanObserverWidget(qt.QWidget):
88
88
 
89
89
  # set up
90
90
  self._convertWhenFinished.setChecked(True)
91
- h_header = self.observationTable.horizontalHeader()
92
- for column_index in (0, 2, 3, 4):
93
- h_header.setSectionResizeMode(column_index, qt.QHeaderView.Interactive)
94
- for column_index in (1,):
95
- h_header.setSectionResizeMode(column_index, qt.QHeaderView.Stretch)
96
91
 
97
92
  # connect signal / slot
98
93
  self.filterWidget.sigChanged.connect(self._filterUpdated)
@@ -249,12 +244,12 @@ class _DatasetProcessModel(qt.QAbstractTableModel):
249
244
  )
250
245
  if trigger_update:
251
246
  self._tomoObjStatus[future_tomo_obj] = status
247
+ obj_index = list(self._tomoObjStatus.keys()).index(future_tomo_obj)
252
248
  try:
253
249
  self.sigStatusUpdated.emit(future_tomo_obj, status)
254
250
  except Exception:
255
251
  pass
256
252
  else:
257
- obj_index = list(self._tomoObjStatus.keys()).index(future_tomo_obj)
258
253
  self.dataChanged.emit(
259
254
  self.createIndex(obj_index, 0),
260
255
  self.createIndex(obj_index, len(self._headers) - 1),
@@ -353,11 +348,21 @@ class _DatasetProcessModel(qt.QAbstractTableModel):
353
348
 
354
349
 
355
350
  class ObservationTable(qt.QTableView):
356
- def __init__(self, parent):
357
- qt.QTableView.__init__(self, parent)
358
-
359
- # QMenu
360
- self.menu = qt.QMenu(self)
351
+ def sizeHintForColumn(self, column):
352
+ # warning: to be called we need to call resizeColumnsToContents upper
353
+ if column == 1: # dataset column
354
+ return 400
355
+ elif column in (
356
+ 0,
357
+ 2,
358
+ 3,
359
+ 4,
360
+ ): # start-time, status, requester, slurm-job
361
+ return 55
362
+ elif column == 5:
363
+ return 200
364
+ else:
365
+ return super().sizeHintForColumn(column)
361
366
 
362
367
 
363
368
  class FilterWidget(qt.QGroupBox):
@@ -32,8 +32,13 @@ from silx.gui import qt
32
32
  from silx.gui.utils.testutils import TestCaseQt
33
33
 
34
34
  from tomwer.core.settings import SlurmSettings
35
- from tomwer.gui.cluster.slurm import SlurmSettingsWidget
35
+ from tomwer.gui.cluster.slurm import (
36
+ SlurmSettingsMode,
37
+ SlurmSettingsWidget,
38
+ SlurmSettingsWindow,
39
+ )
36
40
  from tomwer.tests.utils import skip_gui_test
41
+ from tomwer.tests.conftest import qtapp # noqa F401
37
42
 
38
43
 
39
44
  @pytest.mark.skipif(skip_gui_test(), reason="skip gui test")
@@ -78,3 +83,13 @@ class TestSlurmWidget(TestCaseQt):
78
83
  assert self.slurmWidget.getMemory() == 156
79
84
  assert self.slurmWidget.getQueue() == "test-queue"
80
85
  assert self.slurmWidget.getNGPU() == 5
86
+
87
+
88
+ def test_SlurmSettingsWindow(qtapp): # noqa F811
89
+ """
90
+ test that slurm SettingsWindow can load all the different slurm settings modes
91
+ """
92
+ widget = SlurmSettingsWindow()
93
+ for mode in SlurmSettingsMode:
94
+ widget.setCurrentSettingsMode(mode)
95
+ widget.getConfiguration()
@@ -34,7 +34,7 @@ __date__ = "19/07/2018"
34
34
 
35
35
  from silx.gui import qt
36
36
 
37
- from tomwer.core.process.conditions.filters import FileNameFilter
37
+ from tomwer.core.process.conditions.filters import FileNameFilterTask
38
38
  from tomwer.gui import icons
39
39
  from tomwer.gui.utils.sandboxes import (
40
40
  RegularExpressionSandBox,
@@ -66,7 +66,7 @@ class FileNameFilterWidget(qt.QWidget):
66
66
 
67
67
  self.layout().addWidget(qt.QLabel("filter type:", self), 1, 0)
68
68
  self._filterTypeCB = qt.QComboBox(parent=self)
69
- for filter_type in FileNameFilter.FILTER_TYPES:
69
+ for filter_type in FileNameFilterTask.FILTER_TYPES:
70
70
  self._filterTypeCB.addItem(filter_type)
71
71
  self.layout().addWidget(self._filterTypeCB, 1, 1, 1, 2)
72
72
 
@@ -84,7 +84,7 @@ class FileNameFilterWidget(qt.QWidget):
84
84
 
85
85
  # set up
86
86
  self._sandBoxPB.setVisible(False)
87
- self._filterTypeCB.setCurrentText(FileNameFilter._DEFAULT_FILTER_TYPE)
87
+ self._filterTypeCB.setCurrentText(FileNameFilterTask._DEFAULT_FILTER_TYPE)
88
88
 
89
89
  self.sandboxDialog = None
90
90
 
@@ -509,7 +509,9 @@ class GenericScanListDialog(_DataListDialog):
509
509
  files_or_folders = dialog.files_selected()
510
510
  added_scans = []
511
511
  for file_or_folder in files_or_folders:
512
- added_scans.extend(self.add(file_or_folder))
512
+ new_scans = self.add(file_or_folder)
513
+ if new_scans is not None:
514
+ added_scans.extend(new_scans)
513
515
  super()._callbackAddPath()
514
516
  self.datalist.setMySelection(added_scans)
515
517
 
@@ -530,7 +532,9 @@ class VolumeListDialog(_DataListDialog):
530
532
  files_or_folders = dialog.files_selected()
531
533
  added_volumes = []
532
534
  for file_or_folder in files_or_folders:
533
- added_volumes.extend(self.add(file_or_folder))
535
+ new_volumes = self.add(file_or_folder)
536
+ if new_volumes is not None:
537
+ added_volumes.extend(new_volumes)
534
538
  super()._callbackAddPath()
535
539
  self.datalist.setMySelection(added_volumes)
536
540
 
@@ -765,18 +769,27 @@ class _TomwerObjectList(qt.QTableWidget):
765
769
 
766
770
  def _copyRequested(self):
767
771
  clipboard = qt.QGuiApplication.clipboard()
768
- if isinstance(self._copy_target, TomwerObject):
769
- clipboard.setText(self._copy_target.get_identifier().to_str())
770
- else:
771
- clipboard.setText(str(self._copy_target))
772
+
773
+ def get_info(item):
774
+ user_data = item.data(qt.Qt.UserRole)
775
+ if isinstance(user_data, TomwerObject):
776
+ return user_data.get_identifier().to_str()
777
+ else:
778
+ return str(user_data)
779
+
780
+ selection = [get_info(item) for item in self.selectedItems()]
781
+ clipboard.setText("\n".join(selection))
772
782
 
773
783
  def _pasteRequested(self):
774
784
  clipboard = qt.QGuiApplication.clipboard()
775
- identifier = clipboard.text()
776
- try:
777
- self.add(identifier)
778
- except Exception as e:
779
- logger.error(f"Failed to add {identifier}. Error is {e}")
785
+ identifiers = clipboard.text()
786
+ # handle paste of several lines...
787
+ identifiers.replace(";", "\n")
788
+ for identifier in identifiers.split("\n"):
789
+ try:
790
+ self.add(identifier)
791
+ except Exception as e:
792
+ logger.error(f"Failed to add {identifier}. Error is {e}")
780
793
 
781
794
 
782
795
  class GenericScanList(_TomwerObjectList):
@@ -0,0 +1,183 @@
1
+ from typing import Optional
2
+ from silx.gui import qt
3
+ from tomwer.gui import icons as tomwericons
4
+ from tomwer.core.utils import concatenate_dict
5
+
6
+
7
+ class EmailComposition(qt.QWidget):
8
+ """
9
+ Widget used to compose an email
10
+
11
+ Please see https://confluence.esrf.fr/display/SCKB/Rules+about+email for email rules at esrf
12
+ """
13
+
14
+ sigChanged = qt.Signal()
15
+ """emit when the composition changed"""
16
+
17
+ def __init__(self, parent: Optional[qt.QWidget]) -> None:
18
+ super().__init__(parent)
19
+ self.setLayout(qt.QFormLayout())
20
+ # from
21
+ self._fromAddressesQLE = qt.QLineEdit(self)
22
+ singleEmailValidator = qt.QRegularExpressionValidator(
23
+ qt.QRegularExpression(
24
+ r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
25
+ ),
26
+ self._fromAddressesQLE,
27
+ )
28
+ self._fromAddressesQLE.setValidator(singleEmailValidator)
29
+ self.layout().addRow("from", self._fromAddressesQLE)
30
+ # to
31
+ self._toAdressesQLE = qt.QLineEdit(self)
32
+ severalEmailValidator = qt.QRegularExpressionValidator(
33
+ qt.QRegularExpression(
34
+ r"(([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)(\s*;\s*|\s*$))*"
35
+ ),
36
+ self,
37
+ )
38
+ self._toAdressesQLE.setValidator(severalEmailValidator)
39
+ self.layout().addRow("to", self._toAdressesQLE)
40
+ # subject
41
+ self._subjectQLE = qt.QLineEdit("tomwer: {tomo_obj_short_id} received", self)
42
+ tooltip = """
43
+ this section can contain some predefine `keywords` and will be formatting before sending the email.\n
44
+ Reserve keywords are: \n
45
+ - {tomo_obj_short_id}: tomo_obj short identifier
46
+ - {tomo_obj_id}: tomo_obj id
47
+ - {ls_tomo_obj}: ls of the scan folder
48
+ - {timestamp}: current time
49
+ - {footnote}: some footnote defined by tomwer
50
+ - {dataset_processing_states}: list dataset state regarding met processes
51
+ """
52
+ self._subjectQLE.setToolTip(tooltip)
53
+ self.layout().addRow("subject", self._subjectQLE)
54
+ # text
55
+ self._textQLE = qt.QPlainTextEdit(
56
+ "{tomo_obj_id} received at {timestamp} \n\nprocesses:\n{dataset_processing_states}\n\nfile listening:\n{ls_tomo_obj}\n\nInfo: {footnote}"
57
+ )
58
+ self._textQLE.setToolTip(tooltip)
59
+ self.layout().addRow(self._textQLE)
60
+
61
+ # connect signal / slot
62
+ self._fromAddressesQLE.editingFinished.connect(self._changed)
63
+ self._toAdressesQLE.editingFinished.connect(self._changed)
64
+ self._subjectQLE.editingFinished.connect(self._changed)
65
+ self._textQLE.textChanged.connect(self._changed)
66
+
67
+ def _changed(self):
68
+ self.sigChanged.emit()
69
+
70
+ def getToAddrs(self) -> tuple:
71
+ adresses = self._toAdressesQLE.text().replace(" ", "")
72
+ adresses.replace(",", ";")
73
+ return tuple(set(adresses.split(";")))
74
+
75
+ def getConfiguration(self) -> dict:
76
+ return {
77
+ "subject": self._subjectQLE.text(),
78
+ "from_addr": self._fromAddressesQLE.text(),
79
+ "to_addrs": self.getToAddrs(),
80
+ "text": self._textQLE.toPlainText(),
81
+ }
82
+
83
+ def setConfiguration(self, config: dict):
84
+ assert isinstance(config, dict)
85
+ subject = config.get("subject", None)
86
+ if subject is not None:
87
+ self._subjectQLE.setText(str(subject))
88
+ from_addr = config.get("from_addr", None)
89
+ if from_addr is not None:
90
+ self._fromAddressesQLE.setText(str(from_addr))
91
+ to_addrs = config.get("to_addrs", None)
92
+ if to_addrs is not None:
93
+ if not isinstance(to_addrs, str):
94
+ to_addrs = ";".join(to_addrs)
95
+ self._toAdressesQLE.setText(str(to_addrs))
96
+ text = config.get("text", None)
97
+ if text is not None:
98
+ self._textQLE.setPlainText(str(text))
99
+
100
+
101
+ class EmailSettings(qt.QWidget):
102
+ """
103
+ Widget to set up mail server
104
+ """
105
+
106
+ sigChanged = qt.Signal()
107
+ """emit when the settings changed"""
108
+
109
+ def __init__(self, parent: Optional[qt.QWidget]) -> None:
110
+ super().__init__(parent)
111
+ self.setLayout(qt.QFormLayout(self))
112
+ # server
113
+ self._hostQLE = qt.QLineEdit("smtps.esrf.fr", self)
114
+ self.layout().addRow("host", self._hostQLE)
115
+ # port
116
+ self._portQLE = qt.QLineEdit("0", self)
117
+ self._portQLE.setValidator(qt.QIntValidator(self._portQLE))
118
+ self.layout().addRow("port", self._portQLE)
119
+
120
+ # connect signal / slot
121
+ self._hostQLE.editingFinished.connect(self._changed)
122
+ self._portQLE.editingFinished.connect(self._changed)
123
+
124
+ def _changed(self):
125
+ self.sigChanged.emit()
126
+
127
+ def getConfiguration(self) -> dict:
128
+ return {"host": self._hostQLE.text(), "port": int(self._portQLE.text())}
129
+
130
+ def setConfiguration(self, config: dict):
131
+ server = config.get("host", None)
132
+ if server is not None:
133
+ self._hostQLE.setText(str(server))
134
+ port = config.get("port", None)
135
+ if port is not None:
136
+ self._portQLE.setText(str(port))
137
+
138
+
139
+ class Emailwidget(qt.QTabWidget):
140
+ """
141
+ Main widget to send email
142
+ """
143
+
144
+ sigChanged = qt.Signal()
145
+ """emit when the email settings or composition changed"""
146
+
147
+ def __init__(self, parent: Optional[qt.QWidget] = None) -> None:
148
+ super().__init__(parent)
149
+
150
+ self.setTabPosition(qt.QTabWidget.East)
151
+
152
+ self._compositionWidget = EmailComposition(self)
153
+ composeIcon = tomwericons.getQIcon("compose")
154
+ self.addTab(
155
+ self._compositionWidget,
156
+ composeIcon,
157
+ "",
158
+ )
159
+
160
+ self._serverSettingsWidget = EmailSettings(self)
161
+ settingsIcon = tomwericons.getQIcon("parameters")
162
+ self.addTab(
163
+ self._serverSettingsWidget,
164
+ settingsIcon,
165
+ "",
166
+ )
167
+
168
+ # connect signal / slot
169
+ self._compositionWidget.sigChanged.connect(self._changed)
170
+ self._serverSettingsWidget.sigChanged.connect(self._changed)
171
+
172
+ def _changed(self):
173
+ self.sigChanged.emit()
174
+
175
+ def getConfiguration(self) -> dict:
176
+ return concatenate_dict(
177
+ self._serverSettingsWidget.getConfiguration(),
178
+ self._compositionWidget.getConfiguration(),
179
+ )
180
+
181
+ def setConfiguration(self, config: dict):
182
+ self._serverSettingsWidget.setConfiguration(config)
183
+ self._compositionWidget.setConfiguration(config)