tomwer 1.0.3__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. orangecontrib/tomwer/tutorials/EBS_tomo_listener.ows +39 -0
  2. orangecontrib/tomwer/tutorials/cast_volume.ows +34 -0
  3. orangecontrib/tomwer/tutorials/simple_slice_reconstruction.ows +39 -0
  4. orangecontrib/tomwer/tutorials/simple_volume_local_reconstruction.ows +49 -0
  5. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +59 -0
  6. orangecontrib/tomwer/tutorials/using_saaxis_to_find_cor.ows +44 -0
  7. orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +1 -1
  8. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +14 -4
  9. orangecontrib/tomwer/widgets/cluster/__init__.py +1 -1
  10. orangecontrib/tomwer/widgets/control/DataListOW.py +12 -5
  11. orangecontrib/tomwer/widgets/control/DataListenerOW.py +18 -9
  12. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +13 -6
  13. orangecontrib/tomwer/widgets/control/DataTransfertOW.py +3 -5
  14. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +8 -4
  15. orangecontrib/tomwer/widgets/control/DataWatcherOW.py +4 -6
  16. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +49 -62
  17. orangecontrib/tomwer/widgets/control/FilterOW.py +2 -4
  18. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +93 -0
  19. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +135 -129
  20. orangecontrib/tomwer/widgets/control/NotifierOW.py +34 -9
  21. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +3 -5
  22. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +19 -13
  23. orangecontrib/tomwer/widgets/control/VolumeSelector.py +12 -4
  24. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +11 -7
  25. orangecontrib/tomwer/widgets/control/icons/notification.svg +4 -4
  26. orangecontrib/tomwer/widgets/control/icons/nxtomomill.png +0 -0
  27. orangecontrib/tomwer/widgets/control/icons/nxtomomill.svg +8 -5
  28. orangecontrib/tomwer/widgets/control/icons/tomoobjserie.png +0 -0
  29. orangecontrib/tomwer/widgets/control/icons/tomoobjserie.svg +73 -78
  30. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +16 -4
  31. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +100 -0
  32. orangecontrib/tomwer/widgets/edit/icons/image_key_editor.png +0 -0
  33. orangecontrib/tomwer/widgets/edit/icons/image_key_upgrader.png +0 -0
  34. orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.png +0 -0
  35. orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.svg +123 -0
  36. orangecontrib/tomwer/widgets/edit/test/test_dark_flat_patch.py +21 -1
  37. orangecontrib/tomwer/widgets/edit/test/test_image_key_editor.py +1 -1
  38. orangecontrib/tomwer/widgets/edit/test/test_image_key_upgrader.py +1 -1
  39. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +25 -0
  40. orangecontrib/tomwer/widgets/other/PythonScriptOW.py +19 -11
  41. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +20 -14
  42. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +24 -10
  43. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +26 -21
  44. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +29 -12
  45. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +44 -17
  46. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +28 -20
  47. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +24 -18
  48. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +6 -6
  49. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +4 -2
  50. orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.png +0 -0
  51. orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.svg +11 -8
  52. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +10 -4
  53. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  54. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +69 -0
  55. orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +2 -4
  56. orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.png +0 -0
  57. orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.svg +105 -0
  58. tomwer/__main__.py +10 -5
  59. tomwer/app/canvas_launcher/config.py +10 -10
  60. tomwer/app/canvas_launcher/mainwindow.py +68 -6
  61. tomwer/app/canvas_launcher/widgetsscheme.py +1 -3
  62. tomwer/app/darkref.py +16 -12
  63. tomwer/app/imagekeyeditor.py +2 -2
  64. tomwer/app/imagekeyupgrader.py +104 -0
  65. tomwer/app/intensitynormalization.py +0 -1
  66. tomwer/app/nxtomoeditor.py +103 -0
  67. tomwer/app/rsync.py +1 -1
  68. tomwer/core/cluster/cluster.py +1 -1
  69. tomwer/core/futureobject.py +1 -0
  70. tomwer/core/process/control/datalistener/datalistener.py +7 -1
  71. tomwer/core/process/control/datalistener/rpcserver.py +3 -4
  72. tomwer/core/process/control/datawatcher/datawatcher.py +18 -18
  73. tomwer/core/process/control/datawatcher/datawatcherobserver.py +5 -8
  74. tomwer/core/process/control/datawatcher/datawatcherprocess.py +2 -3
  75. tomwer/core/process/control/datawatcher/edfdwprocess.py +2 -2
  76. tomwer/core/process/control/nxtomomill.py +33 -58
  77. tomwer/core/process/control/scanlist.py +2 -1
  78. tomwer/core/process/control/scanselector.py +7 -0
  79. tomwer/core/process/control/scantransfer.py +2 -2
  80. tomwer/core/process/control/scanvalidator.py +6 -5
  81. tomwer/core/process/control/singletomoobj.py +2 -1
  82. tomwer/core/process/control/timer.py +2 -1
  83. tomwer/core/process/control/tomoobjserie.py +8 -2
  84. tomwer/core/process/control/volumeselector.py +2 -1
  85. tomwer/core/process/control/volumesymlink.py +2 -1
  86. tomwer/core/process/edit/darkflatpatch.py +2 -1
  87. tomwer/core/process/edit/imagekeyeditor.py +4 -3
  88. tomwer/core/process/reconstruction/axis/axis.py +29 -32
  89. tomwer/core/process/reconstruction/axis/mode.py +3 -2
  90. tomwer/core/process/reconstruction/axis/params.py +35 -16
  91. tomwer/core/process/reconstruction/darkref/darkrefs.py +90 -707
  92. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +44 -16
  93. tomwer/core/process/reconstruction/darkref/params.py +62 -67
  94. tomwer/core/process/reconstruction/lamino/tofu.py +1 -2
  95. tomwer/core/process/reconstruction/nabu/castvolume.py +21 -26
  96. tomwer/core/process/reconstruction/nabu/nabucommon.py +36 -38
  97. tomwer/core/process/reconstruction/nabu/nabuscores.py +28 -13
  98. tomwer/core/process/reconstruction/nabu/nabuslices.py +41 -14
  99. tomwer/core/process/reconstruction/nabu/nabuvolume.py +21 -12
  100. tomwer/core/process/reconstruction/nabu/utils.py +32 -3
  101. tomwer/core/process/reconstruction/normalization/normalization.py +9 -8
  102. tomwer/core/process/reconstruction/saaxis/saaxis.py +46 -20
  103. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +38 -12
  104. tomwer/core/process/reconstruction/test/__init__.py +0 -39
  105. tomwer/core/process/reconstruction/test/test_axis_params.py +25 -3
  106. tomwer/core/process/reconstruction/test/test_darkref_copy.py +117 -1
  107. tomwer/core/process/script/python.py +16 -12
  108. tomwer/core/process/task.py +3 -7
  109. tomwer/core/process/test/test_axis.py +1 -1
  110. tomwer/core/process/test/test_dark_and_flat.py +41 -111
  111. tomwer/core/process/test/test_data_listener.py +0 -29
  112. tomwer/core/process/test/test_data_transfer.py +10 -14
  113. tomwer/core/process/test/test_nabu.py +1 -1
  114. tomwer/core/process/test/test_normalization.py +1 -1
  115. tomwer/core/process/visualization/liveslice.py +6 -0
  116. tomwer/core/scan/blissscan.py +37 -2
  117. tomwer/core/scan/edfscan.py +19 -8
  118. tomwer/core/scan/hdf5scan.py +10 -4
  119. tomwer/core/scan/scanbase.py +35 -29
  120. tomwer/core/scan/scanfactory.py +3 -17
  121. tomwer/core/scan/test/test_h5.py +1 -1
  122. tomwer/core/scan/test/test_process_registration.py +0 -11
  123. tomwer/core/scan/test/test_scan.py +32 -30
  124. tomwer/core/settings.py +2 -2
  125. tomwer/core/test/test_utils.py +1 -1
  126. tomwer/core/tomwer_object.py +19 -0
  127. tomwer/core/utils/__init__.py +0 -45
  128. tomwer/core/utils/char.py +2 -0
  129. tomwer/core/utils/gpu.py +5 -5
  130. tomwer/core/utils/nxtomoutils.py +2 -2
  131. tomwer/core/utils/scanutils.py +50 -0
  132. tomwer/core/utils/volumeutils.py +13 -0
  133. tomwer/core/volume/edfvolume.py +4 -0
  134. tomwer/core/volume/hdf5volume.py +4 -0
  135. tomwer/core/volume/jp2kvolume.py +4 -0
  136. tomwer/core/volume/rawvolume.py +22 -5
  137. tomwer/core/volume/tiffvolume.py +4 -0
  138. tomwer/core/volume/volumebase.py +19 -12
  139. tomwer/core/volume/volumefactory.py +20 -1
  140. tomwer/gui/cluster/slurm.py +1 -1
  141. tomwer/gui/cluster/supervisor.py +0 -2
  142. tomwer/gui/cluster/test/test_cluster.py +2 -2
  143. tomwer/gui/control/datalist.py +109 -36
  144. tomwer/gui/control/datatransfert.py +1 -1
  145. tomwer/gui/control/datawatcher/configuration.py +0 -2
  146. tomwer/gui/control/datawatcher/datawatcher.py +23 -13
  147. tomwer/gui/control/datawatcher/datawatcherobserver.py +1 -1
  148. tomwer/gui/control/observations.py +0 -3
  149. tomwer/gui/control/selectorwidgetbase.py +42 -12
  150. tomwer/gui/control/serie/seriecreator.py +967 -0
  151. tomwer/{web/__init__.py → gui/control/serie/seriewaiter.py} +5 -7
  152. tomwer/gui/control/singletomoobj.py +15 -4
  153. tomwer/gui/control/test/test_datalist.py +1 -1
  154. tomwer/gui/control/test/test_datalistener.py +1 -1
  155. tomwer/gui/control/test/test_inputwidget.py +1 -1
  156. tomwer/gui/control/test/test_process_manager.py +1 -13
  157. tomwer/gui/control/test/test_scanselector.py +1 -1
  158. tomwer/gui/control/test/test_scanvalidator.py +1 -1
  159. tomwer/gui/control/test/test_single_tomo_obj.py +1 -1
  160. tomwer/gui/control/test/test_volume_dialog.py +19 -7
  161. tomwer/gui/control/test/test_volumeselector.py +4 -4
  162. tomwer/gui/debugtools/datasetgenerator.py +1 -9
  163. tomwer/gui/edit/dkrfpatch.py +2 -3
  164. tomwer/gui/edit/imagekeyeditor.py +12 -11
  165. tomwer/gui/edit/nxtomoeditor.py +475 -0
  166. tomwer/gui/edit/test/test_dkrf_patch.py +2 -14
  167. tomwer/gui/edit/test/test_image_key_editor.py +2 -2
  168. tomwer/gui/edit/test/test_nx_editor.py +155 -0
  169. tomwer/gui/icons.py +0 -1
  170. tomwer/gui/qfolderdialog.py +11 -0
  171. tomwer/gui/reconstruction/axis/CompareImages.py +27 -29
  172. tomwer/gui/reconstruction/axis/axis.py +2 -0
  173. tomwer/gui/reconstruction/axis/radioaxis.py +70 -14
  174. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +7 -9
  175. tomwer/gui/reconstruction/darkref/darkrefwidget.py +22 -24
  176. tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
  177. tomwer/gui/reconstruction/lamino/tofu/tofu.py +3 -3
  178. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +4 -4
  179. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +10 -5
  180. tomwer/gui/reconstruction/nabu/castvolume.py +103 -24
  181. tomwer/gui/reconstruction/nabu/check.py +1 -1
  182. tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +352 -0
  183. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +0 -9
  184. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +1 -1
  185. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +18 -19
  186. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +30 -7
  187. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +26 -15
  188. tomwer/gui/reconstruction/nabu/slices.py +10 -4
  189. tomwer/gui/reconstruction/nabu/slurm.py +1 -1
  190. tomwer/gui/reconstruction/nabu/volume.py +13 -7
  191. tomwer/gui/reconstruction/normalization/intensity.py +1 -5
  192. tomwer/gui/reconstruction/saaxis/corrangeselector.py +10 -37
  193. tomwer/gui/reconstruction/saaxis/saaxis.py +11 -7
  194. tomwer/gui/reconstruction/saaxis/sliceselector.py +11 -26
  195. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +13 -8
  196. tomwer/gui/reconstruction/scores/scoreplot.py +67 -62
  197. tomwer/gui/reconstruction/test/test_axis.py +2 -2
  198. tomwer/gui/reconstruction/test/test_lamino.py +2 -2
  199. tomwer/gui/reconstruction/test/test_nabu.py +14 -1
  200. tomwer/gui/reconstruction/test/test_saaxis.py +8 -17
  201. tomwer/gui/reconstruction/test/test_sadeltabeta.py +7 -13
  202. tomwer/gui/stackplot.py +11 -28
  203. tomwer/gui/test/test_axis_gui.py +4 -4
  204. tomwer/gui/test/test_qfolder_dialog.py +12 -0
  205. tomwer/gui/utils/inputwidget.py +42 -22
  206. tomwer/gui/utils/lineselector/lineselector.py +13 -21
  207. tomwer/gui/utils/scandescription.py +2 -4
  208. tomwer/gui/utils/slider.py +1 -102
  209. tomwer/gui/utils/unitsystem.py +48 -11
  210. tomwer/gui/visualization/dataviewer.py +24 -17
  211. tomwer/gui/visualization/diffviewer/diffviewer.py +2 -11
  212. tomwer/gui/visualization/nxtomometadata.py +21 -0
  213. tomwer/gui/visualization/scanoverview.py +0 -1
  214. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +72 -0
  215. tomwer/gui/visualization/test/test_stacks.py +1 -1
  216. tomwer/gui/visualization/tomoobjoverview.py +49 -0
  217. tomwer/gui/visualization/volumeoverview.py +64 -0
  218. tomwer/gui/visualization/volumeviewer.py +1 -1
  219. tomwer/io/utils/utils.py +2 -2
  220. tomwer/resources/gui/icons/multi-document-save.png +0 -0
  221. tomwer/resources/gui/icons/multi-document-save.svg +101 -0
  222. tomwer/resources/gui/illustrations/ctf_z1.png +0 -0
  223. tomwer/resources/gui/illustrations/ctf_z1.svg +471 -0
  224. tomwer/synctools/axis.py +0 -1
  225. tomwer/synctools/darkref.py +0 -1
  226. tomwer/synctools/datalistener.py +5 -1
  227. tomwer/synctools/imageloaderthread.py +2 -2
  228. tomwer/synctools/saaxis.py +0 -1
  229. tomwer/synctools/sadeltabeta.py +0 -1
  230. tomwer/synctools/stacks/edit/imagekeyeditor.py +1 -1
  231. tomwer/synctools/stacks/processingstack.py +2 -2
  232. tomwer/synctools/stacks/reconstruction/castvolume.py +1 -0
  233. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
  234. tomwer/synctools/stacks/reconstruction/lamino.py +1 -3
  235. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +0 -2
  236. tomwer/synctools/test/test_darkRefs.py +32 -149
  237. tomwer/synctools/test/test_foldertransfer.py +1 -1
  238. tomwer/synctools/test/test_scanstages.py +2 -2
  239. tomwer/tests/conftest.py +51 -0
  240. tomwer/{test → tests}/test_scripts.py +1 -1
  241. tomwer/tests/test_utils.py +10 -0
  242. tomwer/{test → tests}/utils/utilstest.py +0 -11
  243. tomwer/version.py +3 -3
  244. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/METADATA +14 -16
  245. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/RECORD +255 -235
  246. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/WHEEL +1 -1
  247. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/entry_points.txt +6 -0
  248. orangecontrib/tomwer/setup.py +0 -45
  249. orangecontrib/tomwer/widgets/setup.py +0 -49
  250. tomwer/app/process.py +0 -153
  251. tomwer/core/process/reconstruction/nabu/slurm.py +0 -36
  252. tomwer/core/process/reconstruction/utils/nabu_slice_exec.py +0 -10
  253. tomwer/core/utils/laminoutils.py +0 -80
  254. tomwer/gui/utils/lineselector/lineselection.py +0 -76
  255. tomwer/setup.py +0 -52
  256. tomwer/slurm/executor.py +0 -36
  257. tomwer/slurm/job.py +0 -349
  258. tomwer/slurm/utils.py +0 -44
  259. tomwer/web/client.py +0 -43
  260. tomwer/web/config.py +0 -36
  261. tomwer/web/test/test_graylog_connection.py +0 -59
  262. {tomwer/slurm → orangecontrib/tomwer/tutorials}/__init__.py +0 -0
  263. /tomwer/{test → gui/control/serie}/__init__.py +0 -0
  264. /tomwer/{web/test → tests}/__init__.py +0 -0
  265. /tomwer/{test → tests}/utils/__init__.py +0 -0
  266. /tomwer-1.0.3-py3.8-nspkg.pth → /tomwer-1.1.0-py3.9-nspkg.pth +0 -0
  267. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/LICENSE +0 -0
  268. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/namespace_packages.txt +0 -0
  269. {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/top_level.txt +0 -0
@@ -414,6 +414,17 @@ class QVolumeDialog(qt.QDialog):
414
414
  guessed_volumes = tomoscan_guess_volumes(
415
415
  file_path, scheme_to_vol=VolumeList.DEFAULT_SCHEME_TO_VOL
416
416
  )
417
+ # filter potential 'nabu histogram'
418
+ if guessed_volumes is not None:
419
+
420
+ def is_not_histogram(vol_identifier):
421
+ return not (
422
+ hasattr(vol_identifier, "data_path")
423
+ and vol_identifier.data_path.endswith("histogram")
424
+ )
425
+
426
+ guessed_volumes = tuple(filter(is_not_histogram, guessed_volumes))
427
+
417
428
  if guessed_volumes is None or len(guessed_volumes) == 0:
418
429
  data_path = ""
419
430
  data_extension = ""
@@ -70,7 +70,6 @@ class CompareImagesToolBar(qt.QToolBar):
70
70
  action.setShortcut(qt.QKeySequence(qt.Qt.Key_A))
71
71
  action.setProperty("mode", CompareImages.VisualizationMode.ONLY_A)
72
72
  menu.addAction(action)
73
- self.__aModeAction = action
74
73
  self.__visualizationGroup.addAction(action)
75
74
 
76
75
  icon = silx_icons.getQIcon("compare-mode-b")
@@ -80,7 +79,6 @@ class CompareImagesToolBar(qt.QToolBar):
80
79
  action.setShortcut(qt.QKeySequence(qt.Qt.Key_B))
81
80
  action.setProperty("mode", CompareImages.VisualizationMode.ONLY_B)
82
81
  menu.addAction(action)
83
- self.__bModeAction = action
84
82
  self.__visualizationGroup.addAction(action)
85
83
 
86
84
  icon = silx_icons.getQIcon("compare-mode-rbneg-channel")
@@ -92,7 +90,6 @@ class CompareImagesToolBar(qt.QToolBar):
92
90
  "mode", _CompareImages.VisualizationMode.COMPOSITE_RED_BLUE_GRAY_NEG
93
91
  )
94
92
  menu.addAction(action)
95
- self.__ycChannelModeAction = action
96
93
  self.__visualizationGroup.addAction(action)
97
94
 
98
95
  icon = tomwer_icons.getQIcon("compare_mode_a_minus_b")
@@ -102,9 +99,17 @@ class CompareImagesToolBar(qt.QToolBar):
102
99
  action.setShortcut(qt.QKeySequence(qt.Qt.Key_S))
103
100
  action.setProperty("mode", _CompareImages.VisualizationMode.COMPOSITE_A_MINUS_B)
104
101
  menu.addAction(action)
105
- self.__ycChannelModeAction = action
106
102
  self.__visualizationGroup.addAction(action)
107
103
 
104
+ # create crop images action
105
+ icon = silx_icons.getQIcon("crop")
106
+ action = qt.QAction(icon, "crop compared images", self)
107
+ action.setCheckable(True)
108
+ action.setChecked(True)
109
+ action.triggered.connect(self.__cropComparedImagesChanged)
110
+ self.__cropComparedImages = action
111
+ self.addAction(action)
112
+
108
113
  def setCompareWidget(self, widget):
109
114
  """
110
115
  Connect this tool bar to a specific :class:`CompareImages` widget.
@@ -171,6 +176,12 @@ class CompareImagesToolBar(qt.QToolBar):
171
176
  mode = selectedAction.property("mode")
172
177
  widget.setVisualizationMode(mode)
173
178
 
179
+ def __cropComparedImagesChanged(self):
180
+ widget = self.getCompareWidget()
181
+ if widget is not None:
182
+ cropCompositeImage = self.__cropComparedImages.isChecked()
183
+ widget.setCropComparedImages(cropCompositeImage)
184
+
174
185
  def __updateVisualizationMenu(self):
175
186
  """Update the state of the action containing visualization menu."""
176
187
  selectedAction = self.__visualizationGroup.checkedAction()
@@ -183,35 +194,22 @@ class CompareImagesToolBar(qt.QToolBar):
183
194
  self.__visualizationAction.setIcon(qt.QIcon())
184
195
  self.__visualizationAction.setToolTip("")
185
196
 
186
- def __alignmentModeChanged(self, selectedAction):
187
- """Called when user requesting changes of the alignment mode."""
188
- self.__updateAlignmentMenu()
189
- widget = self.getCompareWidget()
190
- if widget is not None:
191
- mode = selectedAction.property("mode")
192
- widget.setAlignmentMode(mode)
193
197
 
194
- def __updateAlignmentMenu(self):
195
- """Update the state of the action containing alignment menu."""
196
- selectedAction = self.__alignmentGroup.checkedAction()
197
- if selectedAction is not None:
198
- self.__alignmentAction.setText(selectedAction.text())
199
- self.__alignmentAction.setIcon(selectedAction.icon())
200
- self.__alignmentAction.setToolTip(selectedAction.toolTip())
201
- else:
202
- self.__alignmentAction.setText("")
203
- self.__alignmentAction.setIcon(qt.QIcon())
204
- self.__alignmentAction.setToolTip("")
198
+ class CompareImages(_CompareImages.CompareImages):
199
+ sigCropImagesChanged = qt.Signal()
200
+ """Emit when cropping of the compared images has changed"""
205
201
 
206
- def __keypointVisibilityChanged(self):
207
- """Called when action managing keypoints visibility changes"""
208
- widget = self.getCompareWidget()
209
- if widget is not None:
210
- keypointsVisible = self.__displayKeypoints.isChecked()
211
- widget.setKeypointsVisible(keypointsVisible)
202
+ def __init__(self, parent=None, backend=None):
203
+ super().__init__(parent, backend)
204
+ self.__cropComparedImages = True
212
205
 
206
+ def cropComparedImages(self) -> bool:
207
+ return self.__cropComparedImages
208
+
209
+ def setCropComparedImages(self, crop):
210
+ self.__cropComparedImages = crop
211
+ self.sigCropImagesChanged.emit()
213
212
 
214
- class CompareImages(_CompareImages.CompareImages):
215
213
  def _createToolBars(self, plot):
216
214
  """Create tool bars displayed by the widget"""
217
215
  self._interactiveModeToolBar = tools.InteractiveModeToolBar(
@@ -594,6 +594,8 @@ class _PositionInfoWidget(qt.QWidget):
594
594
  def setPosition(self, relative_cor: Optional[float], abs_cor: Optional[float]):
595
595
  if relative_cor is None:
596
596
  self._relativePositionLabel.setText("?")
597
+ elif isinstance(relative_cor, str):
598
+ self._relativePositionLabel.setText(relative_cor)
597
599
  else:
598
600
  self._relativePositionLabel.setText("{:.3f}".format(relative_cor))
599
601
  if abs_cor is None:
@@ -108,6 +108,7 @@ class RadioAxisWindow(qt.QMainWindow):
108
108
  self._plot.setAutoResetZoom(False)
109
109
  _mode = CompareImages.VisualizationMode.COMPOSITE_A_MINUS_B
110
110
  self._plot.setVisualizationMode(_mode)
111
+ self._plot.setAlignmentMode(CompareImages.AlignmentMode.STRETCH)
111
112
  self.setCentralWidget(self._plot)
112
113
 
113
114
  self._dockWidgetCtrl = qt.QDockWidget(parent=self)
@@ -133,7 +134,6 @@ class RadioAxisWindow(qt.QMainWindow):
133
134
  self.getAxis = self._controlWidget.getAxis
134
135
  self.getMode = self._controlWidget.getMode
135
136
  self.setModeLock = self._controlWidget.setModeLock
136
- self.getEstimatedCor = self._controlWidget.getEstimatedCor
137
137
 
138
138
  # signal / slot connection
139
139
  self._controlWidget.sigShiftChanged.connect(self._updateShift)
@@ -145,9 +145,11 @@ class RadioAxisWindow(qt.QMainWindow):
145
145
  self._controlWidget.sigResetZoomRequested.connect(self._resetZoomPlot)
146
146
  self._controlWidget.sigSubsamplingChanged.connect(self._updateSubSampling)
147
147
  self._controlWidget.sigUrlChanged.connect(self._urlChanged)
148
+ self._plot.sigCropImagesChanged.connect(self._updateShift)
148
149
 
149
150
  # adapt gui to the axis value
150
151
  self.setReconsParams(axis=self.__recons_params)
152
+ self.getPlot().getPlot().setAxesDisplayed(True)
151
153
 
152
154
  def manual_uses_full_image(self, value):
153
155
  self._controlWidget.manual_uses_full_image(value)
@@ -181,6 +183,8 @@ class RadioAxisWindow(qt.QMainWindow):
181
183
  with block_signals(self._controlWidget):
182
184
  with block_signals(self._axis_params):
183
185
  self._controlWidget.setMode(mode)
186
+ if mode is axis_mode.AxisMode.manual:
187
+ self._setModeLockFrmSettings(False)
184
188
 
185
189
  def updateAutomaticallyEstimatedCor(self):
186
190
  return self._controlWidget.updateAutomaticallyEstimatedCor()
@@ -194,7 +198,7 @@ class RadioAxisWindow(qt.QMainWindow):
194
198
  def getEstimatedCor(self):
195
199
  return self._controlWidget.getEstimatedCor()
196
200
 
197
- def _setModeLockFrmSettings(self, lock):
201
+ def _setModeLockFrmSettings(self, lock: bool):
198
202
  # only lock the push button
199
203
  with block_signals(self):
200
204
  self._controlWidget._mainWidget._calculationWidget._lockMethodPB.setLock(
@@ -411,18 +415,71 @@ class RadioAxisWindow(qt.QMainWindow):
411
415
  cval_imgA = 0
412
416
  cval_imgB = 0
413
417
  try:
418
+ x_shift = self.getXShift() / self.getImgSubsampling()
419
+ y_shift = self.getYShift() / self.getImgSubsampling()
414
420
  self._shiftedImgA = image.shift_img(
415
421
  data=_imgA,
416
- dx=-self.getXShift() / self.getImgSubsampling(),
417
- dy=self.getYShift() / self.getImgSubsampling(),
422
+ dx=-x_shift,
423
+ dy=y_shift,
418
424
  cval=cval_imgA,
419
425
  )
420
426
  self._shiftedImgB = image.shift_img(
421
427
  data=_imgB,
422
- dx=self.getXShift() / self.getImgSubsampling(),
423
- dy=self.getYShift() / self.getImgSubsampling(),
428
+ dx=x_shift,
429
+ dy=y_shift,
424
430
  cval=cval_imgB,
425
431
  )
432
+ crop = self.getPlot().cropComparedImages()
433
+
434
+ if not crop:
435
+ # handling of the crop:
436
+ # 1. we will concatenate the shifted array with the unshifted to avoid crop
437
+ # 2. in order to handled properly the shift and overlaps we need to add an empty array
438
+ abs_x_shift = abs(int(x_shift))
439
+ buffer_array_img_A = numpy.full(
440
+ shape=(self._shiftedImgA.shape[0], abs_x_shift),
441
+ fill_value=cval_imgA,
442
+ )
443
+ buffer_array_img_B = numpy.full(
444
+ shape=(self._shiftedImgB.shape[0], abs_x_shift),
445
+ fill_value=cval_imgB,
446
+ )
447
+ if x_shift == 0:
448
+ pass
449
+ elif x_shift > 0:
450
+ self._shiftedImgA = numpy.concatenate(
451
+ (
452
+ _imgA[:, :abs_x_shift],
453
+ self._shiftedImgA,
454
+ buffer_array_img_A,
455
+ ),
456
+ axis=1,
457
+ )
458
+ self._shiftedImgB = numpy.concatenate(
459
+ (
460
+ buffer_array_img_B,
461
+ self._shiftedImgB,
462
+ _imgB[:, -abs_x_shift:],
463
+ ),
464
+ axis=1,
465
+ )
466
+ else:
467
+ self._shiftedImgA = numpy.concatenate(
468
+ (
469
+ buffer_array_img_A,
470
+ self._shiftedImgA,
471
+ _imgA[:, :abs_x_shift],
472
+ ),
473
+ axis=1,
474
+ )
475
+ self._shiftedImgB = numpy.concatenate(
476
+ (
477
+ _imgB[:, :abs_x_shift],
478
+ self._shiftedImgB,
479
+ buffer_array_img_B,
480
+ ),
481
+ axis=1,
482
+ )
426
483
  except ValueError as e:
427
484
  _logger.error(e)
428
485
  self._shiftedImgA = _imgA
@@ -532,7 +589,7 @@ class _AxisRead(qt.QWidget):
532
589
  assert isinstance(axis, QAxisRP)
533
590
  self._axis = axis
534
591
 
535
- def _selectFile(self):
592
+ def _selectFile(self): # pragma: no cover
536
593
  dialog = qt.QFileDialog(self)
537
594
  dialog.setFileMode(qt.QFileDialog.ExistingFile)
538
595
 
@@ -778,7 +835,6 @@ class _AxisManual(qt.QWidget):
778
835
 
779
836
 
780
837
  class _AxisManualSelection(qt.QWidget):
781
-
782
838
  sigResetZoomRequested = qt.Signal()
783
839
  """Signal emitted when a zoom request is necessary (when change to full
784
840
  image)"""
@@ -1073,7 +1129,6 @@ class _ShiftControl(qt.QWidget):
1073
1129
 
1074
1130
 
1075
1131
  class _ImgOpts(qt.QGroupBox):
1076
-
1077
1132
  sigSubsamplingChanged = qt.Signal()
1078
1133
  """Signal emitted when the subsampling change"""
1079
1134
 
@@ -1458,8 +1513,8 @@ class AxisTabWidget(qt.QTabWidget):
1458
1513
  def getNearPos(self):
1459
1514
  return self._calculationWidget.getNearPosition()
1460
1515
 
1461
- def setNearPos(self):
1462
- return self._calculationWidget.setNearPosition()
1516
+ def setNearPos(self, position):
1517
+ return self._calculationWidget.setNearPosition(position=position)
1463
1518
 
1464
1519
  def updateAutomaticallyEstimatedCor(self):
1465
1520
  return self._calculationWidget.updateAutomaticallyEstimatedCor()
@@ -1643,7 +1698,7 @@ class _CalculationWidget(qt.QWidget):
1643
1698
  validator = qt.QDoubleValidator(self._qleNearPosQLE)
1644
1699
  self._qleNearPosQLE.setValidator(validator)
1645
1700
  self._qleValueW.layout().addRow(
1646
- "estimated value (in absolute):", self._qleNearPosQLE
1701
+ "estimated value (in relative):", self._qleNearPosQLE
1647
1702
  )
1648
1703
 
1649
1704
  # cor_options
@@ -1671,6 +1726,7 @@ class _CalculationWidget(qt.QWidget):
1671
1726
  self._sideWidget.setVisible(False)
1672
1727
  self.setAxisParams(axis_params)
1673
1728
  self._nearValueCB.setChecked(True)
1729
+ self._nearOptsWidget.setHidden(True)
1674
1730
 
1675
1731
  def getMethodLockPB(self) -> qt.QPushButton:
1676
1732
  return self._lockMethodPB
@@ -1835,8 +1891,8 @@ class _CalculationWidget(qt.QWidget):
1835
1891
  def lockMode(self, lock):
1836
1892
  with block_signals(self._lockMethodPB):
1837
1893
  self._lockMethodPB.setLock(lock)
1838
- for widget in (self._qcbPosition, self._qleNearPosQLE):
1839
- widget.setEnabled(not lock)
1894
+ self._qcbPosition.setEnabled(not lock)
1895
+
1840
1896
  self.sigLockModeChanged.emit(lock)
1841
1897
 
1842
1898
  def getMode(self):
@@ -251,10 +251,9 @@ class RefCopyWidget(qt.QGroupBox):
251
251
  except Exception as e:
252
252
  _logger.error(f"Fails to define flat url. Error is {e}")
253
253
  return False
254
-
255
- if dialog.exec_():
256
- DarkRefsCopy.save_flats_to_be_copied(self.__save_dir, data=url)
257
- return True
254
+ else:
255
+ DarkRefsCopy.save_flats_to_be_copied(self.__save_dir, data=url)
256
+ return True
258
257
  return False
259
258
 
260
259
  def askForDarksUrl(self):
@@ -267,10 +266,9 @@ class RefCopyWidget(qt.QGroupBox):
267
266
  except Exception as e:
268
267
  _logger.error(f"Fails to define flat url. Error is {e}")
269
268
  return False
270
-
271
- if dialog.exec_():
272
- DarkRefsCopy.save_darks_to_be_copied(self.__save_dir, data=url)
273
- return True
269
+ else:
270
+ DarkRefsCopy.save_darks_to_be_copied(self.__save_dir, data=url)
271
+ return True
274
272
  return False
275
273
 
276
274
  def setModeAuto(self, b):
@@ -289,7 +287,7 @@ class RefCopyWidget(qt.QGroupBox):
289
287
  def copyActivated(self):
290
288
  """
291
289
 
292
- :return bool: Return True if the user want to aply the dark ref copy
290
+ :return bool: Return True if the user want to copy reduced darks and flats
293
291
  """
294
292
  return self.isChecked()
295
293
 
@@ -101,7 +101,7 @@ class _WhatCheckBox(qt.QWidget):
101
101
  (None, median, average) for a what (ref, dark)
102
102
  """
103
103
 
104
- sigChanged = qt.Signal(dkrf.Method)
104
+ sigChanged = qt.Signal(dkrf.ReduceMethod)
105
105
  """Signal emitted when the calculation mode change"""
106
106
 
107
107
  def __init__(self, parent, text):
@@ -110,27 +110,27 @@ class _WhatCheckBox(qt.QWidget):
110
110
  self._checkbox = qt.QCheckBox(text=text, parent=self)
111
111
  self.layout().addWidget(self._checkbox)
112
112
  self._modeCB = qt.QComboBox(parent=self)
113
- for mode in dkrf.Method:
114
- if mode is dkrf.Method.none:
113
+ for mode in dkrf.ReduceMethod:
114
+ if mode is dkrf.ReduceMethod.NONE:
115
115
  continue
116
- self._modeCB.addItem(mode.name.title())
116
+ self._modeCB.addItem(mode.value)
117
117
  self._modeCB.setItemData(
118
- self._modeCB.findText(dkrf.Method.median.name.title()),
118
+ self._modeCB.findText(dkrf.ReduceMethod.MEDIAN.value),
119
119
  "Compute the median for each serie",
120
120
  qt.Qt.ToolTipRole,
121
121
  )
122
122
  self._modeCB.setItemData(
123
- self._modeCB.findText(dkrf.Method.average.name.title()),
123
+ self._modeCB.findText(dkrf.ReduceMethod.MEAN.value),
124
124
  "Compute the average for each serie",
125
125
  qt.Qt.ToolTipRole,
126
126
  )
127
127
  self._modeCB.setItemData(
128
- self._modeCB.findText(dkrf.Method.first.name.title()),
128
+ self._modeCB.findText(dkrf.ReduceMethod.FIRST.value),
129
129
  "Pick the first frame for a serie",
130
130
  qt.Qt.ToolTipRole,
131
131
  )
132
132
  self._modeCB.setItemData(
133
- self._modeCB.findText(dkrf.Method.last.name.title()),
133
+ self._modeCB.findText(dkrf.ReduceMethod.LAST.value),
134
134
  "Pick the last frame for a serie",
135
135
  qt.Qt.ToolTipRole,
136
136
  )
@@ -147,30 +147,28 @@ class _WhatCheckBox(qt.QWidget):
147
147
  or not (in the case we are simply reading structure and to avoid
148
148
  looping in signals with other QObject)"""
149
149
 
150
- def getMode(self):
151
- if self._checkbox.isChecked() is True:
152
- return getattr(dkrf.Method, self._modeCB.currentText().lower())
150
+ def getMode(self) -> dkrf.ReduceMethod:
151
+ if self._checkbox.isChecked():
152
+ return dkrf.ReduceMethod.from_value(self._modeCB.currentText())
153
153
  else:
154
- return dkrf.Method.none
154
+ return dkrf.ReduceMethod.NONE
155
155
 
156
156
  def getModeName(self):
157
- return self.getMode().name.split(".")[-1].title()
157
+ return self.getMode().value
158
158
 
159
159
  def _modeChange(self, *a, **b):
160
160
  self.sigChanged.emit(self.getMode())
161
161
 
162
162
  def setMode(self, mode):
163
- _mode = mode
164
- if type(_mode) is str:
165
- _mode = getattr(dkrf.Method, mode.lower())
166
- assert _mode in dkrf.Method
163
+ mode = dkrf.ReduceMethod.from_value(mode)
164
+ assert mode in dkrf.ReduceMethod
167
165
  self._checkbox.toggled.disconnect(self._modeChange)
168
166
  self._modeCB.currentIndexChanged.disconnect(self._modeChange)
169
- self._checkbox.setChecked(_mode is not dkrf.Method.none)
170
- if _mode is not dkrf.Method.none:
171
- index = self._modeCB.findText(_mode.name.title())
167
+ self._checkbox.setChecked(mode is not dkrf.ReduceMethod.NONE)
168
+ if mode is not dkrf.ReduceMethod.NONE:
169
+ index = self._modeCB.findText(mode.value)
172
170
  if index < 0:
173
- logger.error("index for %s is not recognized" % _mode)
171
+ logger.error(f"index for {mode.value} is not recognized")
174
172
  else:
175
173
  self._modeCB.setCurrentIndex(index)
176
174
  self._checkbox.toggled.connect(self._modeChange)
@@ -303,7 +301,7 @@ class DarkRefTab(qt.QTabWidget):
303
301
  self.recons_params.__class__.dark_calc_method.setter
304
302
  )
305
303
  self.tabGeneral.sigRefChanged.disconnect(
306
- self.recons_params.__class__.ref_calc_method.setter
304
+ self.recons_params.__class__.flat_calc_method.setter
307
305
  )
308
306
  self.tabGeneral.sigRmToggled.disconnect(self.recons_params._set_remove_opt)
309
307
  self.tabGeneral.sigSkipToggled.disconnect(
@@ -326,7 +324,7 @@ class DarkRefTab(qt.QTabWidget):
326
324
  self.recons_params.__class__.dark_calc_method.setter
327
325
  )
328
326
  self.tabGeneral.sigRefChanged.connect(
329
- self.recons_params.__class__.ref_calc_method.setter
327
+ self.recons_params.__class__.flat_calc_method.setter
330
328
  )
331
329
  self.tabGeneral.sigRmToggled.connect(self.recons_params._set_remove_opt)
332
330
  self.tabGeneral.sigSkipToggled.connect(self.recons_params._set_skip_if_exist)
@@ -335,7 +333,7 @@ class DarkRefTab(qt.QTabWidget):
335
333
  self.recons_params.__class__.dark_pattern.setter
336
334
  )
337
335
  self.tabExpert.sigRefPatternEdited.connect(
338
- self.recons_params.__class__.ref_pattern.setter
336
+ self.recons_params.__class__.flat_pattern.setter
339
337
  )
340
338
  self.recons_params.sigChanged.connect(self._updateReconsParams)
341
339
 
@@ -455,7 +455,7 @@ class _FFCWhenWidget(qt.QWidget):
455
455
  # name.split('.')[-1] because name can be given as FFCWhen.on_the_fly...
456
456
  _when_instance = getattr(FFCWhen, _name.lower().replace(" ", "_"))
457
457
  except Exception:
458
- _logger.error('fail to determine "FFCWhen" value from ', name)
458
+ _logger.error(f"fail to determine 'FFCWhen' value from {name}")
459
459
  else:
460
460
  self.setWhen(_when_instance)
461
461
  else:
@@ -198,7 +198,7 @@ class TofuWidget(_TofuOptionLoader, qt.QWidget):
198
198
  scan_type = "psi angle"
199
199
  self._tabs._outputWidget._setScanType(scan_type)
200
200
 
201
- def load(self):
201
+ def load(self): # pragma: no cover
202
202
  dialog = qt.QFileDialog(self)
203
203
  dialog.setNameFilters(["ini (*.ini)"])
204
204
  dialog.setFileMode(qt.QFileDialog.ExistingFile)
@@ -215,7 +215,7 @@ class TofuWidget(_TofuOptionLoader, qt.QWidget):
215
215
  conf = dictdump.load(_file)
216
216
  except Exception as error:
217
217
  _logger.warning(
218
- "Fail to load configuration from ", _file, ". Reason is", error
218
+ f"Fail to load configuration from {_file}. Reason is {error}"
219
219
  )
220
220
  else:
221
221
  if self._OPT_RECO_SECTION_ID not in conf:
@@ -245,7 +245,7 @@ class TofuWidget(_TofuOptionLoader, qt.QWidget):
245
245
  tomwer_config=tomwer_config,
246
246
  )
247
247
 
248
- def save(self):
248
+ def save(self): # pragma: no cover
249
249
  dialog = qt.QFileDialog(self)
250
250
  dialog.setNameFilters(["ini (*.ini)"])
251
251
  dialog.setAcceptMode(qt.QFileDialog.AcceptSave)
@@ -122,7 +122,7 @@ class ExpertTofuWidget(_TofuOptionLoader, qt.QWidget):
122
122
  try:
123
123
  self._retriesSP.setValue(int(value))
124
124
  except Exception as error:
125
- _logger.warning("Fails to set N tries, error is", error)
125
+ _logger.warning(f"Fails to set N tries, error is {error}")
126
126
 
127
127
  def getRetriesTimeout(self):
128
128
  """
@@ -139,7 +139,7 @@ class ExpertTofuWidget(_TofuOptionLoader, qt.QWidget):
139
139
  try:
140
140
  self._timeoutLE.setText(str(value))
141
141
  except Exception as error:
142
- _logger.warning("Fails to set N tries, error is", error)
142
+ _logger.warning(f"Fails to set N tries, error is {error}")
143
143
 
144
144
  def getSliceMemoryCoeff(self):
145
145
  """
@@ -543,7 +543,7 @@ class GpuGroupBox(qt.QGroupBox):
543
543
  try:
544
544
  self._slicePerDevice.setValue(int(value))
545
545
  except Exception as error:
546
- _logger.warning("Fails to set slice per device, error is", error)
546
+ _logger.warning(f"Fails to set slice per device, error is {error}")
547
547
 
548
548
  def setSliceMemoryCoeff(self, value):
549
549
  self._sliceMemoryCoeff.setVisible(True)
@@ -551,7 +551,7 @@ class GpuGroupBox(qt.QGroupBox):
551
551
  try:
552
552
  self._sliceMemoryCoeff.setText(value)
553
553
  except Exception as error:
554
- _logger.warning("Fails to set memory coefficient, error is", error)
554
+ _logger.warning(f"Fails to set memory coefficient, error is {error}")
555
555
 
556
556
  def getHighLimit(self):
557
557
  if self._slicePerDeviceRB.isChecked():
@@ -200,7 +200,6 @@ class OutputTofuWidget(_TofuOptionLoader, qt.QWidget):
200
200
 
201
201
 
202
202
  class PlaneGroup(qt.QGroupBox):
203
-
204
203
  sigPlaneChanged = qt.Signal(str)
205
204
  """Signal emitted when the plane selection changed"""
206
205
 
@@ -510,9 +509,15 @@ class StepGroup(qt.QGroupBox):
510
509
  if self._zSB.isVisible() is True:
511
510
  start_from = self.getZ() or 0.0
512
511
  nbCut = self.getNCut()
513
- _from = -nbCut / 2 * self.getStepSize() + start_from
514
- _to = nbCut / 2 * self.getStepSize() + start_from
515
- return _from, _to, self.getStepSize()
512
+ if nbCut is None:
513
+ raise ValueError("couldn't find nb cut")
514
+ else:
515
+ _from = (
516
+ -nbCut / 2 * self.getStepSize() # pylint: disable=E1130
517
+ + start_from
518
+ )
519
+ _to = nbCut / 2 * self.getStepSize() + start_from
520
+ return _from, _to, self.getStepSize()
516
521
  else:
517
522
  start_from = 0.0
518
523
  if self._zSB.isVisible() is True:
@@ -706,7 +711,7 @@ class _OutputPathWidget(qt.QGroupBox):
706
711
  def lock(self, lock=True):
707
712
  self._lockButton.setChecked(lock)
708
713
 
709
- def _setFolderPathFrmDiag(self):
714
+ def _setFolderPathFrmDiag(self): # pragma: no cover
710
715
  dialog = qt.QFileDialog(self)
711
716
  dialog.setFileMode(qt.QFileDialog.DirectoryOnly)
712
717