datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.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 (496) hide show
  1. datalab/__init__.py +35 -2
  2. datalab/adapters_metadata/__init__.py +31 -0
  3. datalab/adapters_metadata/base_adapter.py +316 -0
  4. datalab/adapters_metadata/common.py +422 -0
  5. datalab/adapters_metadata/geometry_adapter.py +98 -0
  6. datalab/adapters_metadata/table_adapter.py +84 -0
  7. datalab/adapters_plotpy/__init__.py +54 -0
  8. datalab/adapters_plotpy/annotations.py +124 -0
  9. datalab/adapters_plotpy/base.py +110 -0
  10. datalab/adapters_plotpy/converters.py +86 -0
  11. datalab/adapters_plotpy/factories.py +80 -0
  12. datalab/adapters_plotpy/objects/__init__.py +0 -0
  13. datalab/adapters_plotpy/objects/base.py +197 -0
  14. datalab/adapters_plotpy/objects/image.py +157 -0
  15. datalab/adapters_plotpy/objects/scalar.py +565 -0
  16. datalab/adapters_plotpy/objects/signal.py +264 -0
  17. datalab/adapters_plotpy/roi/__init__.py +0 -0
  18. datalab/adapters_plotpy/roi/base.py +146 -0
  19. datalab/adapters_plotpy/roi/factory.py +93 -0
  20. datalab/adapters_plotpy/roi/image.py +207 -0
  21. datalab/adapters_plotpy/roi/signal.py +72 -0
  22. datalab/app.py +98 -0
  23. datalab/config.py +817 -0
  24. datalab/control/__init__.py +0 -0
  25. datalab/control/baseproxy.py +776 -0
  26. datalab/control/proxy.py +343 -0
  27. datalab/control/remote.py +1005 -0
  28. datalab/data/doc/DataLab_en.pdf +0 -0
  29. datalab/data/doc/DataLab_fr.pdf +0 -0
  30. datalab/data/icons/analysis/delete_results.svg +109 -0
  31. datalab/data/icons/analysis/fw1e2.svg +156 -0
  32. datalab/data/icons/analysis/fwhm.svg +156 -0
  33. datalab/data/icons/analysis/histogram.svg +49 -0
  34. datalab/data/icons/analysis/peak_detect.svg +160 -0
  35. datalab/data/icons/analysis/plot_results.svg +151 -0
  36. datalab/data/icons/analysis/show_results.svg +83 -0
  37. datalab/data/icons/analysis/stats.svg +49 -0
  38. datalab/data/icons/analysis.svg +120 -0
  39. datalab/data/icons/apply.svg +3 -0
  40. datalab/data/icons/check_all.svg +15 -0
  41. datalab/data/icons/collapse.svg +44 -0
  42. datalab/data/icons/collapse_selection.svg +63 -0
  43. datalab/data/icons/console.svg +101 -0
  44. datalab/data/icons/create/1d-normal.svg +8 -0
  45. datalab/data/icons/create/1d-poisson.svg +9 -0
  46. datalab/data/icons/create/1d-uniform.svg +8 -0
  47. datalab/data/icons/create/1d-zero.svg +57 -0
  48. datalab/data/icons/create/2d-gaussian.svg +56 -0
  49. datalab/data/icons/create/2d-normal.svg +38 -0
  50. datalab/data/icons/create/2d-poisson.svg +38 -0
  51. datalab/data/icons/create/2d-ramp.svg +90 -0
  52. datalab/data/icons/create/2d-sinc.svg +62 -0
  53. datalab/data/icons/create/2d-uniform.svg +38 -0
  54. datalab/data/icons/create/2d-zero.svg +13 -0
  55. datalab/data/icons/create/checkerboard.svg +39 -0
  56. datalab/data/icons/create/cosine.svg +12 -0
  57. datalab/data/icons/create/exponential.svg +55 -0
  58. datalab/data/icons/create/gaussian.svg +12 -0
  59. datalab/data/icons/create/grating.svg +29 -0
  60. datalab/data/icons/create/linear_chirp.svg +7 -0
  61. datalab/data/icons/create/logistic.svg +7 -0
  62. datalab/data/icons/create/lorentzian.svg +12 -0
  63. datalab/data/icons/create/planck.svg +12 -0
  64. datalab/data/icons/create/polynomial.svg +7 -0
  65. datalab/data/icons/create/pulse.svg +12 -0
  66. datalab/data/icons/create/ring.svg +18 -0
  67. datalab/data/icons/create/sawtooth.svg +7 -0
  68. datalab/data/icons/create/siemens.svg +35 -0
  69. datalab/data/icons/create/sinc.svg +12 -0
  70. datalab/data/icons/create/sine.svg +7 -0
  71. datalab/data/icons/create/square.svg +7 -0
  72. datalab/data/icons/create/square_pulse.svg +7 -0
  73. datalab/data/icons/create/step.svg +7 -0
  74. datalab/data/icons/create/step_pulse.svg +12 -0
  75. datalab/data/icons/create/triangle.svg +7 -0
  76. datalab/data/icons/create/voigt.svg +12 -0
  77. datalab/data/icons/edit/annotations.svg +72 -0
  78. datalab/data/icons/edit/annotations_copy.svg +114 -0
  79. datalab/data/icons/edit/annotations_delete.svg +83 -0
  80. datalab/data/icons/edit/annotations_edit.svg +98 -0
  81. datalab/data/icons/edit/annotations_export.svg +85 -0
  82. datalab/data/icons/edit/annotations_import.svg +85 -0
  83. datalab/data/icons/edit/annotations_paste.svg +100 -0
  84. datalab/data/icons/edit/copy_titles.svg +109 -0
  85. datalab/data/icons/edit/delete.svg +84 -0
  86. datalab/data/icons/edit/delete_all.svg +214 -0
  87. datalab/data/icons/edit/duplicate.svg +64 -0
  88. datalab/data/icons/edit/goto_source.svg +60 -0
  89. datalab/data/icons/edit/metadata.svg +60 -0
  90. datalab/data/icons/edit/metadata_add.svg +80 -0
  91. datalab/data/icons/edit/metadata_copy.svg +96 -0
  92. datalab/data/icons/edit/metadata_delete.svg +62 -0
  93. datalab/data/icons/edit/metadata_export.svg +68 -0
  94. datalab/data/icons/edit/metadata_import.svg +68 -0
  95. datalab/data/icons/edit/metadata_paste.svg +79 -0
  96. datalab/data/icons/edit/move_down.svg +55 -0
  97. datalab/data/icons/edit/move_up.svg +54 -0
  98. datalab/data/icons/edit/new_group.svg +76 -0
  99. datalab/data/icons/edit/recompute.svg +60 -0
  100. datalab/data/icons/edit/rename.svg +49 -0
  101. datalab/data/icons/edit.svg +16 -0
  102. datalab/data/icons/expand.svg +44 -0
  103. datalab/data/icons/expand_selection.svg +63 -0
  104. datalab/data/icons/fit/cdf_fit.svg +56 -0
  105. datalab/data/icons/fit/exponential_fit.svg +55 -0
  106. datalab/data/icons/fit/gaussian_fit.svg +62 -0
  107. datalab/data/icons/fit/interactive_fit.svg +101 -0
  108. datalab/data/icons/fit/linear_fit.svg +57 -0
  109. datalab/data/icons/fit/lorentzian_fit.svg +209 -0
  110. datalab/data/icons/fit/multigaussian_fit.svg +85 -0
  111. datalab/data/icons/fit/multilorentzian_fit.svg +85 -0
  112. datalab/data/icons/fit/piecewiseexponential_fit.svg +209 -0
  113. datalab/data/icons/fit/planckian_fit.svg +62 -0
  114. datalab/data/icons/fit/polynomial_fit.svg +59 -0
  115. datalab/data/icons/fit/sigmoid_fit.svg +56 -0
  116. datalab/data/icons/fit/sinusoidal_fit.svg +72 -0
  117. datalab/data/icons/fit/twohalfgaussian_fit.svg +63 -0
  118. datalab/data/icons/fit/voigt_fit.svg +57 -0
  119. datalab/data/icons/group.svg +56 -0
  120. datalab/data/icons/h5/h5array.svg +59 -0
  121. datalab/data/icons/h5/h5attrs.svg +75 -0
  122. datalab/data/icons/h5/h5browser.svg +133 -0
  123. datalab/data/icons/h5/h5file.svg +69 -0
  124. datalab/data/icons/h5/h5group.svg +49 -0
  125. datalab/data/icons/h5/h5scalar.svg +1 -0
  126. datalab/data/icons/help_pdf.svg +46 -0
  127. datalab/data/icons/history.svg +7 -0
  128. datalab/data/icons/image.svg +135 -0
  129. datalab/data/icons/io/fileopen_directory.svg +60 -0
  130. datalab/data/icons/io/fileopen_h5.svg +84 -0
  131. datalab/data/icons/io/fileopen_ima.svg +187 -0
  132. datalab/data/icons/io/fileopen_py.svg +123 -0
  133. datalab/data/icons/io/fileopen_sig.svg +138 -0
  134. datalab/data/icons/io/filesave_h5.svg +97 -0
  135. datalab/data/icons/io/filesave_ima.svg +200 -0
  136. datalab/data/icons/io/filesave_py.svg +136 -0
  137. datalab/data/icons/io/filesave_sig.svg +151 -0
  138. datalab/data/icons/io/import_text.svg +144 -0
  139. datalab/data/icons/io/save_to_directory.svg +134 -0
  140. datalab/data/icons/io.svg +84 -0
  141. datalab/data/icons/libre-camera-flash-off.svg +1 -0
  142. datalab/data/icons/libre-camera-flash-on.svg +1 -0
  143. datalab/data/icons/libre-gui-about.svg +1 -0
  144. datalab/data/icons/libre-gui-action-delete.svg +1 -0
  145. datalab/data/icons/libre-gui-add.svg +1 -0
  146. datalab/data/icons/libre-gui-arrow-down.svg +1 -0
  147. datalab/data/icons/libre-gui-arrow-left.svg +1 -0
  148. datalab/data/icons/libre-gui-arrow-right.svg +1 -0
  149. datalab/data/icons/libre-gui-arrow-up.svg +1 -0
  150. datalab/data/icons/libre-gui-close.svg +40 -0
  151. datalab/data/icons/libre-gui-cogs.svg +1 -0
  152. datalab/data/icons/libre-gui-globe.svg +1 -0
  153. datalab/data/icons/libre-gui-help.svg +1 -0
  154. datalab/data/icons/libre-gui-link.svg +1 -0
  155. datalab/data/icons/libre-gui-menu.svg +1 -0
  156. datalab/data/icons/libre-gui-pencil.svg +1 -0
  157. datalab/data/icons/libre-gui-plugin.svg +1 -0
  158. datalab/data/icons/libre-gui-questions.svg +1 -0
  159. datalab/data/icons/libre-gui-settings.svg +1 -0
  160. datalab/data/icons/libre-gui-unlink.svg +1 -0
  161. datalab/data/icons/libre-tech-ram.svg +1 -0
  162. datalab/data/icons/libre-toolbox.svg +1 -0
  163. datalab/data/icons/logs.svg +1 -0
  164. datalab/data/icons/markers.svg +74 -0
  165. datalab/data/icons/menu.svg +13 -0
  166. datalab/data/icons/new_ima.svg +148 -0
  167. datalab/data/icons/new_sig.svg +123 -0
  168. datalab/data/icons/operations/abs.svg +116 -0
  169. datalab/data/icons/operations/arithmetic.svg +123 -0
  170. datalab/data/icons/operations/average.svg +124 -0
  171. datalab/data/icons/operations/complex_from_magnitude_phase.svg +116 -0
  172. datalab/data/icons/operations/complex_from_real_imag.svg +124 -0
  173. datalab/data/icons/operations/constant.svg +116 -0
  174. datalab/data/icons/operations/constant_add.svg +109 -0
  175. datalab/data/icons/operations/constant_divide.svg +109 -0
  176. datalab/data/icons/operations/constant_multiply.svg +109 -0
  177. datalab/data/icons/operations/constant_subtract.svg +109 -0
  178. datalab/data/icons/operations/convert_dtype.svg +117 -0
  179. datalab/data/icons/operations/convolution.svg +46 -0
  180. datalab/data/icons/operations/deconvolution.svg +57 -0
  181. datalab/data/icons/operations/derivative.svg +127 -0
  182. datalab/data/icons/operations/difference.svg +52 -0
  183. datalab/data/icons/operations/division.svg +139 -0
  184. datalab/data/icons/operations/exp.svg +116 -0
  185. datalab/data/icons/operations/flip_horizontally.svg +69 -0
  186. datalab/data/icons/operations/flip_vertically.svg +74 -0
  187. datalab/data/icons/operations/im.svg +124 -0
  188. datalab/data/icons/operations/integral.svg +50 -0
  189. datalab/data/icons/operations/inverse.svg +143 -0
  190. datalab/data/icons/operations/log10.svg +109 -0
  191. datalab/data/icons/operations/phase.svg +116 -0
  192. datalab/data/icons/operations/power.svg +118 -0
  193. datalab/data/icons/operations/product.svg +124 -0
  194. datalab/data/icons/operations/profile.svg +379 -0
  195. datalab/data/icons/operations/profile_average.svg +399 -0
  196. datalab/data/icons/operations/profile_radial.svg +261 -0
  197. datalab/data/icons/operations/profile_segment.svg +262 -0
  198. datalab/data/icons/operations/quadratic_difference.svg +84 -0
  199. datalab/data/icons/operations/re.svg +124 -0
  200. datalab/data/icons/operations/rotate_left.svg +72 -0
  201. datalab/data/icons/operations/rotate_right.svg +72 -0
  202. datalab/data/icons/operations/signals_to_image.svg +314 -0
  203. datalab/data/icons/operations/sqrt.svg +110 -0
  204. datalab/data/icons/operations/std.svg +124 -0
  205. datalab/data/icons/operations/sum.svg +102 -0
  206. datalab/data/icons/play_demo.svg +9 -0
  207. datalab/data/icons/processing/axis_transform.svg +62 -0
  208. datalab/data/icons/processing/bandpass.svg +79 -0
  209. datalab/data/icons/processing/bandstop.svg +71 -0
  210. datalab/data/icons/processing/binning.svg +126 -0
  211. datalab/data/icons/processing/clip.svg +119 -0
  212. datalab/data/icons/processing/detrending.svg +173 -0
  213. datalab/data/icons/processing/distribute_on_grid.svg +769 -0
  214. datalab/data/icons/processing/edge_detection.svg +46 -0
  215. datalab/data/icons/processing/erase.svg +1 -0
  216. datalab/data/icons/processing/exposure.svg +143 -0
  217. datalab/data/icons/processing/fourier.svg +104 -0
  218. datalab/data/icons/processing/highpass.svg +59 -0
  219. datalab/data/icons/processing/interpolation.svg +71 -0
  220. datalab/data/icons/processing/level_adjustment.svg +70 -0
  221. datalab/data/icons/processing/lowpass.svg +60 -0
  222. datalab/data/icons/processing/morphology.svg +49 -0
  223. datalab/data/icons/processing/noise_addition.svg +114 -0
  224. datalab/data/icons/processing/noise_reduction.svg +38 -0
  225. datalab/data/icons/processing/normalize.svg +84 -0
  226. datalab/data/icons/processing/offset_correction.svg +131 -0
  227. datalab/data/icons/processing/resampling1d.svg +101 -0
  228. datalab/data/icons/processing/resampling2d.svg +240 -0
  229. datalab/data/icons/processing/reset_positions.svg +185 -0
  230. datalab/data/icons/processing/resize.svg +9 -0
  231. datalab/data/icons/processing/reverse_signal_x.svg +171 -0
  232. datalab/data/icons/processing/stability.svg +11 -0
  233. datalab/data/icons/processing/swap_x_y.svg +65 -0
  234. datalab/data/icons/processing/thresholding.svg +63 -0
  235. datalab/data/icons/processing/windowing.svg +45 -0
  236. datalab/data/icons/properties.svg +26 -0
  237. datalab/data/icons/reset.svg +9 -0
  238. datalab/data/icons/restore.svg +40 -0
  239. datalab/data/icons/roi/roi.svg +76 -0
  240. datalab/data/icons/roi/roi_coordinate.svg +78 -0
  241. datalab/data/icons/roi/roi_copy.svg +112 -0
  242. datalab/data/icons/roi/roi_delete.svg +81 -0
  243. datalab/data/icons/roi/roi_export.svg +87 -0
  244. datalab/data/icons/roi/roi_graphical.svg +78 -0
  245. datalab/data/icons/roi/roi_grid.svg +67 -0
  246. datalab/data/icons/roi/roi_ima.svg +188 -0
  247. datalab/data/icons/roi/roi_import.svg +87 -0
  248. datalab/data/icons/roi/roi_new.svg +81 -0
  249. datalab/data/icons/roi/roi_new_circle.svg +95 -0
  250. datalab/data/icons/roi/roi_new_polygon.svg +110 -0
  251. datalab/data/icons/roi/roi_new_rectangle.svg +70 -0
  252. datalab/data/icons/roi/roi_paste.svg +98 -0
  253. datalab/data/icons/roi/roi_sig.svg +124 -0
  254. datalab/data/icons/shapes.svg +134 -0
  255. datalab/data/icons/signal.svg +103 -0
  256. datalab/data/icons/table.svg +85 -0
  257. datalab/data/icons/table_unavailable.svg +102 -0
  258. datalab/data/icons/to_signal.svg +124 -0
  259. datalab/data/icons/tour/next.svg +44 -0
  260. datalab/data/icons/tour/previous.svg +44 -0
  261. datalab/data/icons/tour/rewind.svg +51 -0
  262. datalab/data/icons/tour/stop.svg +47 -0
  263. datalab/data/icons/tour/tour.svg +16 -0
  264. datalab/data/icons/uncheck_all.svg +78 -0
  265. datalab/data/icons/view/curve_antialiasing.svg +50 -0
  266. datalab/data/icons/view/new_window.svg +98 -0
  267. datalab/data/icons/view/refresh-auto.svg +57 -0
  268. datalab/data/icons/view/refresh-manual.svg +51 -0
  269. datalab/data/icons/view/reset_curve_styles.svg +96 -0
  270. datalab/data/icons/view/show_first.svg +55 -0
  271. datalab/data/icons/view/show_titles.svg +46 -0
  272. datalab/data/icons/visualization.svg +51 -0
  273. datalab/data/logo/DataLab-Banner-150.png +0 -0
  274. datalab/data/logo/DataLab-Banner-200.png +0 -0
  275. datalab/data/logo/DataLab-Banner2-100.png +0 -0
  276. datalab/data/logo/DataLab-Splash.png +0 -0
  277. datalab/data/logo/DataLab-watermark.png +0 -0
  278. datalab/data/logo/DataLab.svg +83 -0
  279. datalab/data/tests/reordering_test.h5 +0 -0
  280. datalab/data/tutorials/fabry_perot/fabry-perot1.jpg +0 -0
  281. datalab/data/tutorials/fabry_perot/fabry-perot2.jpg +0 -0
  282. datalab/data/tutorials/laser_beam/TEM00_z_13.jpg +0 -0
  283. datalab/data/tutorials/laser_beam/TEM00_z_18.jpg +0 -0
  284. datalab/data/tutorials/laser_beam/TEM00_z_23.jpg +0 -0
  285. datalab/data/tutorials/laser_beam/TEM00_z_30.jpg +0 -0
  286. datalab/data/tutorials/laser_beam/TEM00_z_35.jpg +0 -0
  287. datalab/data/tutorials/laser_beam/TEM00_z_40.jpg +0 -0
  288. datalab/data/tutorials/laser_beam/TEM00_z_45.jpg +0 -0
  289. datalab/data/tutorials/laser_beam/TEM00_z_50.jpg +0 -0
  290. datalab/data/tutorials/laser_beam/TEM00_z_55.jpg +0 -0
  291. datalab/data/tutorials/laser_beam/TEM00_z_60.jpg +0 -0
  292. datalab/data/tutorials/laser_beam/TEM00_z_65.jpg +0 -0
  293. datalab/data/tutorials/laser_beam/TEM00_z_70.jpg +0 -0
  294. datalab/data/tutorials/laser_beam/TEM00_z_75.jpg +0 -0
  295. datalab/data/tutorials/laser_beam/TEM00_z_80.jpg +0 -0
  296. datalab/env.py +542 -0
  297. datalab/gui/__init__.py +89 -0
  298. datalab/gui/actionhandler.py +1701 -0
  299. datalab/gui/docks.py +473 -0
  300. datalab/gui/h5io.py +150 -0
  301. datalab/gui/macroeditor.py +310 -0
  302. datalab/gui/main.py +2081 -0
  303. datalab/gui/newobject.py +217 -0
  304. datalab/gui/objectview.py +766 -0
  305. datalab/gui/panel/__init__.py +48 -0
  306. datalab/gui/panel/base.py +3254 -0
  307. datalab/gui/panel/image.py +157 -0
  308. datalab/gui/panel/macro.py +607 -0
  309. datalab/gui/panel/signal.py +164 -0
  310. datalab/gui/plothandler.py +800 -0
  311. datalab/gui/processor/__init__.py +84 -0
  312. datalab/gui/processor/base.py +2456 -0
  313. datalab/gui/processor/catcher.py +75 -0
  314. datalab/gui/processor/image.py +1214 -0
  315. datalab/gui/processor/signal.py +755 -0
  316. datalab/gui/profiledialog.py +333 -0
  317. datalab/gui/roieditor.py +633 -0
  318. datalab/gui/roigrideditor.py +208 -0
  319. datalab/gui/settings.py +612 -0
  320. datalab/gui/tour.py +908 -0
  321. datalab/h5/__init__.py +12 -0
  322. datalab/h5/common.py +314 -0
  323. datalab/h5/generic.py +580 -0
  324. datalab/h5/native.py +39 -0
  325. datalab/h5/utils.py +95 -0
  326. datalab/objectmodel.py +640 -0
  327. datalab/plugins/_readme_.txt +9 -0
  328. datalab/plugins/datalab_imageformats.py +175 -0
  329. datalab/plugins/datalab_testdata.py +190 -0
  330. datalab/plugins.py +355 -0
  331. datalab/tests/__init__.py +199 -0
  332. datalab/tests/backbone/__init__.py +1 -0
  333. datalab/tests/backbone/config_unit_test.py +170 -0
  334. datalab/tests/backbone/config_versioning_unit_test.py +34 -0
  335. datalab/tests/backbone/dictlistserial_app_test.py +38 -0
  336. datalab/tests/backbone/errorcatcher_unit_test.py +69 -0
  337. datalab/tests/backbone/errormsgbox_unit_test.py +50 -0
  338. datalab/tests/backbone/execenv_unit.py +262 -0
  339. datalab/tests/backbone/loadtest_gdi.py +147 -0
  340. datalab/tests/backbone/long_callback.py +96 -0
  341. datalab/tests/backbone/main_app_test.py +137 -0
  342. datalab/tests/backbone/memory_leak.py +43 -0
  343. datalab/tests/backbone/procisolation1_unit.py +128 -0
  344. datalab/tests/backbone/procisolation2_unit.py +171 -0
  345. datalab/tests/backbone/procisolation_unit_test.py +22 -0
  346. datalab/tests/backbone/profiling_app.py +27 -0
  347. datalab/tests/backbone/strings_unit_test.py +65 -0
  348. datalab/tests/backbone/title_formatting_unit_test.py +82 -0
  349. datalab/tests/conftest.py +131 -0
  350. datalab/tests/features/__init__.py +1 -0
  351. datalab/tests/features/applauncher/__init__.py +1 -0
  352. datalab/tests/features/applauncher/launcher1_app_test.py +28 -0
  353. datalab/tests/features/applauncher/launcher2_app_test.py +30 -0
  354. datalab/tests/features/common/__init__.py +1 -0
  355. datalab/tests/features/common/add_metadata_app_test.py +134 -0
  356. datalab/tests/features/common/add_metadata_unit_test.py +267 -0
  357. datalab/tests/features/common/annotations_management_unit_test.py +152 -0
  358. datalab/tests/features/common/auto_analysis_recompute_unit_test.py +240 -0
  359. datalab/tests/features/common/createobject_unit_test.py +50 -0
  360. datalab/tests/features/common/geometry_results_app_test.py +135 -0
  361. datalab/tests/features/common/interactive_processing_test.py +1109 -0
  362. datalab/tests/features/common/io_app_test.py +75 -0
  363. datalab/tests/features/common/large_results_app_test.py +187 -0
  364. datalab/tests/features/common/metadata_all_patterns_test.py +103 -0
  365. datalab/tests/features/common/metadata_app_test.py +139 -0
  366. datalab/tests/features/common/metadata_io_unit_test.py +60 -0
  367. datalab/tests/features/common/misc_app_test.py +236 -0
  368. datalab/tests/features/common/multiple_geometry_results_unit_test.py +122 -0
  369. datalab/tests/features/common/multiple_table_results_unit_test.py +64 -0
  370. datalab/tests/features/common/operation_modes_app_test.py +392 -0
  371. datalab/tests/features/common/plot_results_app_test.py +278 -0
  372. datalab/tests/features/common/reorder_app_test.py +75 -0
  373. datalab/tests/features/common/result_deletion_unit_test.py +96 -0
  374. datalab/tests/features/common/result_merged_label_unit_test.py +154 -0
  375. datalab/tests/features/common/result_shape_settings_unit_test.py +223 -0
  376. datalab/tests/features/common/roi_plotitem_unit_test.py +64 -0
  377. datalab/tests/features/common/roieditor_unit_test.py +102 -0
  378. datalab/tests/features/common/save_to_dir_app_test.py +163 -0
  379. datalab/tests/features/common/save_to_dir_unit_test.py +474 -0
  380. datalab/tests/features/common/stat_app_test.py +40 -0
  381. datalab/tests/features/common/stats_tools_unit_test.py +77 -0
  382. datalab/tests/features/common/table_results_app_test.py +52 -0
  383. datalab/tests/features/common/textimport_unit_test.py +131 -0
  384. datalab/tests/features/common/uuid_preservation_test.py +281 -0
  385. datalab/tests/features/common/worker_unit_test.py +402 -0
  386. datalab/tests/features/control/__init__.py +1 -0
  387. datalab/tests/features/control/connect_dialog.py +28 -0
  388. datalab/tests/features/control/embedded1_unit_test.py +304 -0
  389. datalab/tests/features/control/embedded2_unit_test.py +52 -0
  390. datalab/tests/features/control/remoteclient_app_test.py +219 -0
  391. datalab/tests/features/control/remoteclient_unit.py +75 -0
  392. datalab/tests/features/control/simpleclient_unit_test.py +321 -0
  393. datalab/tests/features/hdf5/__init__.py +1 -0
  394. datalab/tests/features/hdf5/h5browser1_unit_test.py +31 -0
  395. datalab/tests/features/hdf5/h5browser2_unit.py +55 -0
  396. datalab/tests/features/hdf5/h5browser_app_test.py +77 -0
  397. datalab/tests/features/hdf5/h5import_app_test.py +25 -0
  398. datalab/tests/features/hdf5/h5importer_app_test.py +34 -0
  399. datalab/tests/features/image/__init__.py +1 -0
  400. datalab/tests/features/image/annotations_app_test.py +28 -0
  401. datalab/tests/features/image/annotations_unit_test.py +80 -0
  402. datalab/tests/features/image/average_app_test.py +46 -0
  403. datalab/tests/features/image/background_dialog_test.py +70 -0
  404. datalab/tests/features/image/blobs_app_test.py +50 -0
  405. datalab/tests/features/image/contour_app_test.py +42 -0
  406. datalab/tests/features/image/contour_fabryperot_app_test.py +51 -0
  407. datalab/tests/features/image/denoise_app_test.py +31 -0
  408. datalab/tests/features/image/distribute_on_grid_app_test.py +95 -0
  409. datalab/tests/features/image/edges_app_test.py +31 -0
  410. datalab/tests/features/image/erase_app_test.py +21 -0
  411. datalab/tests/features/image/fft2d_app_test.py +27 -0
  412. datalab/tests/features/image/flatfield_app_test.py +40 -0
  413. datalab/tests/features/image/geometry_transform_unit_test.py +396 -0
  414. datalab/tests/features/image/imagetools_app_test.py +51 -0
  415. datalab/tests/features/image/imagetools_unit_test.py +27 -0
  416. datalab/tests/features/image/load_app_test.py +73 -0
  417. datalab/tests/features/image/morph_app_test.py +32 -0
  418. datalab/tests/features/image/offsetcorrection_app_test.py +30 -0
  419. datalab/tests/features/image/peak2d_app_test.py +53 -0
  420. datalab/tests/features/image/profile_app_test.py +73 -0
  421. datalab/tests/features/image/profile_dialog_test.py +56 -0
  422. datalab/tests/features/image/roi_app_test.py +98 -0
  423. datalab/tests/features/image/roi_circ_app_test.py +62 -0
  424. datalab/tests/features/image/roi_manipulation_app_test.py +268 -0
  425. datalab/tests/features/image/roigrid_unit_test.py +60 -0
  426. datalab/tests/features/image/side_by_side_app_test.py +52 -0
  427. datalab/tests/features/macro/__init__.py +1 -0
  428. datalab/tests/features/macro/macro_app_test.py +28 -0
  429. datalab/tests/features/macro/macroeditor_unit_test.py +102 -0
  430. datalab/tests/features/signal/__init__.py +1 -0
  431. datalab/tests/features/signal/baseline_dialog_test.py +53 -0
  432. datalab/tests/features/signal/deltax_dialog_unit_test.py +34 -0
  433. datalab/tests/features/signal/fft1d_app_test.py +26 -0
  434. datalab/tests/features/signal/filter_app_test.py +44 -0
  435. datalab/tests/features/signal/fitdialog_unit_test.py +50 -0
  436. datalab/tests/features/signal/interpolation_app_test.py +110 -0
  437. datalab/tests/features/signal/loadbigsignal_app_test.py +80 -0
  438. datalab/tests/features/signal/multiple_rois_unit_test.py +132 -0
  439. datalab/tests/features/signal/pulse_features_app_test.py +118 -0
  440. datalab/tests/features/signal/pulse_features_roi_app_test.py +55 -0
  441. datalab/tests/features/signal/roi_app_test.py +78 -0
  442. datalab/tests/features/signal/roi_manipulation_app_test.py +261 -0
  443. datalab/tests/features/signal/select_xy_cursor_unit_test.py +46 -0
  444. datalab/tests/features/signal/signalpeakdetection_dialog_test.py +33 -0
  445. datalab/tests/features/signal/signals_to_image_app_test.py +98 -0
  446. datalab/tests/features/signal/xarray_compat_app_test.py +128 -0
  447. datalab/tests/features/tour_unit_test.py +22 -0
  448. datalab/tests/features/utilities/__init__.py +1 -0
  449. datalab/tests/features/utilities/installconf_unit_test.py +21 -0
  450. datalab/tests/features/utilities/logview_app_test.py +21 -0
  451. datalab/tests/features/utilities/logview_error.py +24 -0
  452. datalab/tests/features/utilities/logview_unit_test.py +21 -0
  453. datalab/tests/features/utilities/memstatus_app_test.py +42 -0
  454. datalab/tests/features/utilities/settings_unit_test.py +88 -0
  455. datalab/tests/scenarios/__init__.py +1 -0
  456. datalab/tests/scenarios/beautiful_app.py +121 -0
  457. datalab/tests/scenarios/common.py +463 -0
  458. datalab/tests/scenarios/demo.py +212 -0
  459. datalab/tests/scenarios/example_app_test.py +47 -0
  460. datalab/tests/scenarios/scenario_h5_app_test.py +75 -0
  461. datalab/tests/scenarios/scenario_ima1_app_test.py +34 -0
  462. datalab/tests/scenarios/scenario_ima2_app_test.py +34 -0
  463. datalab/tests/scenarios/scenario_mac_app_test.py +58 -0
  464. datalab/tests/scenarios/scenario_sig1_app_test.py +36 -0
  465. datalab/tests/scenarios/scenario_sig2_app_test.py +35 -0
  466. datalab/utils/__init__.py +1 -0
  467. datalab/utils/conf.py +304 -0
  468. datalab/utils/dephash.py +105 -0
  469. datalab/utils/qthelpers.py +633 -0
  470. datalab/utils/strings.py +34 -0
  471. datalab/utils/tests.py +0 -0
  472. datalab/widgets/__init__.py +1 -0
  473. datalab/widgets/connection.py +138 -0
  474. datalab/widgets/filedialog.py +91 -0
  475. datalab/widgets/fileviewer.py +84 -0
  476. datalab/widgets/fitdialog.py +788 -0
  477. datalab/widgets/h5browser.py +1048 -0
  478. datalab/widgets/imagebackground.py +111 -0
  479. datalab/widgets/instconfviewer.py +175 -0
  480. datalab/widgets/logviewer.py +80 -0
  481. datalab/widgets/signalbaseline.py +90 -0
  482. datalab/widgets/signalcursor.py +208 -0
  483. datalab/widgets/signaldeltax.py +151 -0
  484. datalab/widgets/signalpeak.py +199 -0
  485. datalab/widgets/status.py +249 -0
  486. datalab/widgets/textimport.py +786 -0
  487. datalab/widgets/warningerror.py +223 -0
  488. datalab/widgets/wizard.py +286 -0
  489. datalab_platform-1.0.0.dist-info/METADATA +121 -0
  490. datalab_platform-1.0.0.dist-info/RECORD +494 -0
  491. datalab_platform-0.0.1.dev0.dist-info/METADATA +0 -67
  492. datalab_platform-0.0.1.dev0.dist-info/RECORD +0 -7
  493. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/WHEEL +0 -0
  494. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/entry_points.txt +0 -0
  495. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/licenses/LICENSE +0 -0
  496. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/top_level.txt +0 -0
datalab/plugins.py ADDED
@@ -0,0 +1,355 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ DataLab plugin system
5
+ ---------------------
6
+
7
+ DataLab plugin system provides a way to extend the application with new
8
+ functionalities.
9
+
10
+ Plugins are Python modules that relies on two classes:
11
+
12
+ - :class:`PluginInfo`, which stores information about the plugin
13
+ - :class:`PluginBase`, which is the base class for all plugins
14
+
15
+ Plugins may also extends DataLab I/O features by providing new image or
16
+ signal formats. To do so, they must provide a subclass of :class:`ImageFormatBase`
17
+ or :class:`SignalFormatBase`, in which format information is defined using the
18
+ :class:`FormatInfo` class.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import abc
24
+ import dataclasses
25
+ import importlib
26
+ import os
27
+ import os.path as osp
28
+ import pkgutil
29
+ import sys
30
+ from typing import TYPE_CHECKING
31
+
32
+ from qtpy import QtWidgets as QW
33
+
34
+ # pylint: disable=unused-import
35
+ from sigima.io.base import FormatInfo # noqa: F401
36
+ from sigima.io.image.base import ImageFormatBase # noqa: F401
37
+ from sigima.io.image.formats import ClassicsImageFormat # noqa: F401
38
+ from sigima.io.signal.base import SignalFormatBase # noqa: F401
39
+
40
+ from datalab.config import MOD_NAME, OTHER_PLUGINS_PATHLIST, Conf, _
41
+ from datalab.control.proxy import LocalProxy
42
+ from datalab.env import execenv
43
+
44
+ if TYPE_CHECKING:
45
+ from sigima.objects import NewImageParam, NewSignalParam
46
+
47
+ from datalab.gui import main
48
+ from datalab.gui.panel.image import ImagePanel
49
+ from datalab.gui.panel.signal import SignalPanel
50
+
51
+
52
+ PLUGINS_DEFAULT_PATH = Conf.get_path("plugins")
53
+
54
+ if not osp.isdir(PLUGINS_DEFAULT_PATH):
55
+ os.makedirs(PLUGINS_DEFAULT_PATH)
56
+
57
+
58
+ # pylint: disable=bad-mcs-classmethod-argument
59
+ class PluginRegistry(type):
60
+ """Metaclass for registering plugins"""
61
+
62
+ _plugin_classes: list[type[PluginBase]] = []
63
+ _plugin_instances: list[PluginBase] = []
64
+
65
+ def __init__(cls, name, bases, attrs):
66
+ super().__init__(name, bases, attrs)
67
+ if name != "PluginBase":
68
+ cls._plugin_classes.append(cls)
69
+
70
+ @classmethod
71
+ def get_plugin_classes(cls) -> list[type[PluginBase]]:
72
+ """Return plugin classes"""
73
+ return cls._plugin_classes
74
+
75
+ @classmethod
76
+ def get_plugins(cls) -> list[PluginBase]:
77
+ """Return plugin instances"""
78
+ return cls._plugin_instances
79
+
80
+ @classmethod
81
+ def get_plugin(cls, name_or_class: str | type[PluginBase]) -> PluginBase | None:
82
+ """Return plugin instance"""
83
+ for plugin in cls._plugin_instances:
84
+ if name_or_class in (plugin.info.name, plugin.__class__):
85
+ return plugin
86
+ return None
87
+
88
+ @classmethod
89
+ def register_plugin(cls, plugin: PluginBase):
90
+ """Register plugin"""
91
+ if plugin.info.name in [plug.info.name for plug in cls._plugin_instances]:
92
+ raise ValueError(f"Plugin {plugin.info.name} already registered")
93
+ cls._plugin_instances.append(plugin)
94
+ execenv.log(cls, f"Plugin {plugin.info.name} registered")
95
+
96
+ @classmethod
97
+ def unregister_plugin(cls, plugin: PluginBase):
98
+ """Unregister plugin"""
99
+ cls._plugin_instances.remove(plugin)
100
+ execenv.log(cls, f"Plugin {plugin.info.name} unregistered")
101
+ execenv.log(cls, f"{len(cls._plugin_instances)} plugins left")
102
+
103
+ @classmethod
104
+ def unregister_all_plugins(cls):
105
+ """Unregister all plugins"""
106
+ for plugin in cls._plugin_instances:
107
+ execenv.log(cls, f"Unregistering plugin {plugin.info.name}")
108
+ plugin.unregister()
109
+ cls._plugin_instances.clear()
110
+ execenv.log(cls, "All plugins unregistered")
111
+
112
+ @classmethod
113
+ def get_plugin_info(cls, html: bool = True) -> str:
114
+ """Return plugin information (names, versions, descriptions) in html format
115
+
116
+ Args:
117
+ html: return html formatted text (default: True)
118
+ """
119
+ linesep = "<br>" if html else os.linesep
120
+ bullet = "• " if html else " " * 4
121
+
122
+ def italic(text: str) -> str:
123
+ """Return italic text"""
124
+ return f"<i>{text}</i>" if html else text
125
+
126
+ if Conf.main.plugins_enabled.get():
127
+ plugins = cls.get_plugins()
128
+ if plugins:
129
+ text = italic(_("Registered plugins:"))
130
+ text += linesep
131
+ for plugin in plugins:
132
+ text += f"{bullet}{plugin.info.name} ({plugin.info.version})"
133
+ if plugin.info.description:
134
+ text += f": {plugin.info.description}"
135
+ text += linesep
136
+ else:
137
+ text = italic(_("No plugins available"))
138
+ else:
139
+ text = italic(_("Plugins are disabled (see DataLab settings)"))
140
+ return text
141
+
142
+
143
+ @dataclasses.dataclass
144
+ class PluginInfo:
145
+ """Plugin info"""
146
+
147
+ name: str = None
148
+ version: str = "0.0.0"
149
+ description: str = ""
150
+ icon: str = None
151
+
152
+
153
+ class PluginBaseMeta(PluginRegistry, abc.ABCMeta):
154
+ """Mixed metaclass to avoid conflicts"""
155
+
156
+
157
+ class PluginBase(abc.ABC, metaclass=PluginBaseMeta):
158
+ """Plugin base class"""
159
+
160
+ PLUGIN_INFO: PluginInfo = None
161
+
162
+ def __init__(self):
163
+ self.main: main.DLMainWindow = None
164
+ self.proxy: LocalProxy = None
165
+ self._is_registered = False
166
+ self.info = self.PLUGIN_INFO
167
+ if self.info is None:
168
+ raise ValueError(f"Plugin info not set for {self.__class__.__name__}")
169
+
170
+ @property
171
+ def signalpanel(self) -> SignalPanel:
172
+ """Return signal panel"""
173
+ return self.main.signalpanel
174
+
175
+ @property
176
+ def imagepanel(self) -> ImagePanel:
177
+ """Return image panel"""
178
+ return self.main.imagepanel
179
+
180
+ def show_warning(self, message: str):
181
+ """Show warning message"""
182
+ QW.QMessageBox.warning(self.main, _("Warning"), message)
183
+
184
+ def show_error(self, message: str):
185
+ """Show error message"""
186
+ QW.QMessageBox.critical(self.main, _("Error"), message)
187
+
188
+ def show_info(self, message: str):
189
+ """Show info message"""
190
+ QW.QMessageBox.information(self.main, _("Information"), message)
191
+
192
+ def ask_yesno(
193
+ self, message: str, title: str | None = None, cancelable: bool = False
194
+ ) -> bool:
195
+ """Ask yes/no question"""
196
+ if title is None:
197
+ title = _("Question")
198
+ buttons = QW.QMessageBox.Yes | QW.QMessageBox.No
199
+ if cancelable:
200
+ buttons |= QW.QMessageBox.Cancel
201
+ answer = QW.QMessageBox.question(self.main, title, message, buttons)
202
+ if answer == QW.QMessageBox.Yes:
203
+ return True
204
+ if answer == QW.QMessageBox.No:
205
+ return False
206
+ return None
207
+
208
+ def edit_new_signal_parameters(
209
+ self,
210
+ title: str | None = None,
211
+ size: int | None = None,
212
+ ) -> NewSignalParam:
213
+ """Create and edit new signal parameter dataset
214
+
215
+ Args:
216
+ title: title of the new signal
217
+ size: size of the new signal (default: None, get from current signal)
218
+
219
+ Returns:
220
+ New signal parameter dataset (or None if canceled)
221
+ """
222
+ newparam = self.signalpanel.get_newparam_from_current(title=title)
223
+ if size is not None:
224
+ newparam.size = size
225
+ if newparam.edit(self.main):
226
+ return newparam
227
+ return None
228
+
229
+ def edit_new_image_parameters(
230
+ self,
231
+ title: str | None = None,
232
+ shape: tuple[int, int] | None = None,
233
+ hide_height: bool = False,
234
+ hide_width: bool = False,
235
+ hide_type: bool = True,
236
+ hide_dtype: bool = False,
237
+ ) -> NewImageParam | None:
238
+ """Create and edit new image parameter dataset
239
+
240
+ Args:
241
+ title: title of the new image
242
+ shape: shape of the new image (default: None, get from current image)
243
+ hide_height: hide image heigth parameter (default: False)
244
+ hide_width: hide image width parameter (default: False)
245
+ hide_type: hide image type parameter (default: True)
246
+ hide_dtype: hide image data type parameter (default: False)
247
+
248
+ Returns:
249
+ New image parameter dataset (or None if canceled)
250
+ """
251
+ newparam = self.imagepanel.get_newparam_from_current(title=title)
252
+ if shape is not None:
253
+ newparam.height, newparam.width = shape
254
+ newparam.hide_height = hide_height
255
+ newparam.hide_width = hide_width
256
+ newparam.hide_type = hide_type
257
+ newparam.hide_dtype = hide_dtype
258
+ if newparam.edit(self.main):
259
+ return newparam
260
+ return None
261
+
262
+ def is_registered(self):
263
+ """Return True if plugin is registered"""
264
+ return self._is_registered
265
+
266
+ def register(self, main: main.DLMainWindow) -> None:
267
+ """Register plugin"""
268
+ if self._is_registered:
269
+ return
270
+ PluginRegistry.register_plugin(self)
271
+ self._is_registered = True
272
+ self.main = main
273
+ self.proxy = LocalProxy(main)
274
+ self.register_hooks()
275
+
276
+ def unregister(self):
277
+ """Unregister plugin"""
278
+ if not self._is_registered:
279
+ return
280
+ PluginRegistry.unregister_plugin(self)
281
+ self._is_registered = False
282
+ self.unregister_hooks()
283
+ self.main = None
284
+ self.proxy = None
285
+
286
+ def register_hooks(self):
287
+ """Register plugin hooks"""
288
+
289
+ def unregister_hooks(self):
290
+ """Unregister plugin hooks"""
291
+
292
+ @abc.abstractmethod
293
+ def create_actions(self):
294
+ """Create actions"""
295
+
296
+
297
+ def discover_plugins() -> list[type[PluginBase]]:
298
+ """Discover plugins using naming convention
299
+
300
+ Returns:
301
+ List of discovered plugins (as classes)
302
+ """
303
+ if Conf.main.plugins_enabled.get():
304
+ for path in [
305
+ Conf.main.plugins_path.get(),
306
+ PLUGINS_DEFAULT_PATH,
307
+ ] + OTHER_PLUGINS_PATHLIST:
308
+ rpath = osp.realpath(path)
309
+ if rpath not in sys.path:
310
+ sys.path.append(rpath)
311
+ return [
312
+ importlib.import_module(name)
313
+ for _finder, name, _ispkg in pkgutil.iter_modules()
314
+ if name.startswith(f"{MOD_NAME}_")
315
+ ]
316
+ return []
317
+
318
+
319
+ def discover_v020_plugins() -> list[tuple[str, str]]:
320
+ """Discover v0.20 plugins (with ``cdl_`` prefix) without importing them
321
+
322
+ Returns:
323
+ List of tuples (plugin_name, directory_path) for discovered v0.20 plugins
324
+ """
325
+ v020_plugins = []
326
+ if Conf.main.plugins_enabled.get():
327
+ for path in [
328
+ Conf.main.plugins_path.get(),
329
+ PLUGINS_DEFAULT_PATH,
330
+ ] + OTHER_PLUGINS_PATHLIST:
331
+ rpath = osp.realpath(path)
332
+ if rpath not in sys.path:
333
+ sys.path.append(rpath)
334
+ for finder, name, _ispkg in pkgutil.iter_modules():
335
+ if name.startswith("cdl_"):
336
+ # Get the directory path from the module finder
337
+ if hasattr(finder, "path"):
338
+ directory_path = finder.path
339
+ v020_plugins.append((name, directory_path))
340
+ else:
341
+ # Fallback if path is not available
342
+ v020_plugins.append((name, ""))
343
+ return v020_plugins
344
+
345
+
346
+ def get_available_plugins() -> list[PluginBase]:
347
+ """Instantiate and get available plugins
348
+
349
+ Returns:
350
+ List of available plugins (as instances)
351
+ """
352
+ # Note: this function is not used by DataLab itself, but it is used by the
353
+ # test suite to get a list of available plugins
354
+ discover_plugins()
355
+ return [plugin_class() for plugin_class in PluginRegistry.get_plugin_classes()]
@@ -0,0 +1,199 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ Tests (:mod:`datalab.tests`)
5
+ ------------------------
6
+
7
+ The DataLab test suite is based on the `pytest <https://pytest.org>`_ framework.
8
+
9
+ The test suite modules are organized in subpackages according to their purpose.
10
+ The following subpackages are available:
11
+
12
+ - :mod:`datalab.tests.backbone`: backbone tests
13
+ - :mod:`datalab.tests.features`: feature tests (unit tests and application tests)
14
+ - :mod:`datalab.tests.scenarios`: high-level scenarios tests
15
+
16
+ .. seealso::
17
+
18
+ :ref:`validation` for more information about DataLab's testing strategy.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import os
24
+ import os.path as osp
25
+ import sys
26
+ import time
27
+ from contextlib import contextmanager
28
+ from typing import Generator
29
+
30
+ import psutil
31
+ import pytest
32
+ from guidata.guitest import run_testlauncher
33
+ from sigima.tests import helpers
34
+
35
+ import datalab.config # Loading icons
36
+ from datalab.config import MOD_NAME, SHOTPATH
37
+ from datalab.control.proxy import RemoteProxy
38
+ from datalab.env import execenv
39
+ from datalab.gui.main import DLMainWindow
40
+ from datalab.gui.panel.image import ImagePanel
41
+ from datalab.gui.panel.signal import SignalPanel
42
+ from datalab.utils import qthelpers as qth
43
+
44
+ # Add test data files and folders pointed by `DATALAB_DATA` environment variable:
45
+ helpers.add_test_path_from_env("DATALAB_DATA")
46
+
47
+ # Add test data files and folders for the DataLab module:
48
+ helpers.add_test_module_path(MOD_NAME, osp.join("data", "tests"))
49
+
50
+ # Set default screenshot path for tests
51
+ execenv.screenshot_path = SHOTPATH
52
+
53
+
54
+ @contextmanager
55
+ def datalab_test_app_context(
56
+ size: tuple[int, int] = None,
57
+ maximized: bool = False,
58
+ save: bool = False,
59
+ console: bool | None = None,
60
+ exec_loop: bool = True,
61
+ ) -> Generator[DLMainWindow, None, None]:
62
+ """Context manager handling DataLab mainwindow creation and Qt event loop
63
+ with optional HDF5 file save and other options for testing purposes
64
+
65
+ Args:
66
+ size: mainwindow size (default: (950, 600))
67
+ maximized: whether to maximize mainwindow (default: False)
68
+ save: whether to save HDF5 file (default: False)
69
+ console: whether to show console (default: None)
70
+ exec_loop: whether to execute Qt event loop (default: True)
71
+ """
72
+ if size is None:
73
+ size = 1200, 700
74
+ with qth.datalab_app_context(exec_loop=exec_loop):
75
+ win: DLMainWindow | None = None
76
+ try:
77
+ win = DLMainWindow(console=console)
78
+ if maximized:
79
+ win.showMaximized()
80
+ else:
81
+ width, height = size
82
+ win.resize(width, height)
83
+ win.showNormal()
84
+ win.show()
85
+ win.setObjectName(helpers.get_default_test_name()) # screenshot name
86
+ yield win
87
+ finally:
88
+ if save:
89
+ path = helpers.get_output_data_path("h5")
90
+ try:
91
+ os.remove(path)
92
+ win.save_to_h5_file(path)
93
+ except (FileNotFoundError, PermissionError):
94
+ pass
95
+ has_exception_occurred = sys.exc_info()[0] is not None
96
+ if not exec_loop or has_exception_occurred and win is not None:
97
+ # Closing main window properly
98
+ win.set_modified(False)
99
+ win.close()
100
+
101
+
102
+ def is_pid_alive(pid: int) -> bool:
103
+ """Check if a process with the given PID is alive
104
+
105
+ Args:
106
+ Process ID to check
107
+
108
+ Returns:
109
+ True if the process is alive, False otherwise
110
+ """
111
+ return psutil.pid_exists(pid) and psutil.Process(pid).is_running()
112
+
113
+
114
+ def run_datalab_in_background(wait_until_ready: bool = True) -> None:
115
+ """Run DataLab application as a service.
116
+
117
+ This function starts the DataLab application in a separate process, ensuring that
118
+ it runs independently of the current script. It sets the necessary environment
119
+ variables to prevent the application from quitting automatically (since the script
120
+ is executed in a non-interactive mode - the so-called "unattended" mode) and to
121
+ avoid port conflicts. After starting the application, it waits for a short period
122
+ to allow the application to initialize and then checks if the process is alive.
123
+
124
+ The main use case for this function is in testing scenarios where the DataLab
125
+ application needs to be running in the background while a client connects to it
126
+ and performs various operations.
127
+
128
+ Args:
129
+ wait_until_ready: If True, waits until the DataLab application is ready to
130
+ accept connections (default: True). Uses RemoteProxy's built-in retry logic
131
+ with extended timeout to handle DataLab startup time.
132
+
133
+ Raises:
134
+ RuntimeError: If the DataLab application fails to start
135
+ """
136
+ env = os.environ.copy()
137
+ env[execenv.DO_NOT_QUIT_ENV] = "1"
138
+ if execenv.XMLRPCPORT_ENV in env:
139
+ # May happen when executing other tests before
140
+ env.pop(execenv.XMLRPCPORT_ENV)
141
+
142
+ proc = helpers.exec_script(
143
+ "-m", args=["datalab.app"], wait=False, env=env, verbose=False
144
+ )
145
+ # If the process fails to start, it will raise the `AssertionError` exception
146
+ # with the message "Unable to start DataLab application".
147
+ # In that case, it might be useful to set `wait=True` and `verbose=True` in the
148
+ # `exec_script` call above, so that the script waits for the DataLab application
149
+ # to start and prints the output to the console. This way, you can see any
150
+ # error messages or logs that might help you understand why the application failed
151
+ # to start.
152
+ # If the script is executed within a pytest session, add the `-s` option to pytest.
153
+
154
+ # Give the process a moment to actually start
155
+ time.sleep(1)
156
+ if not is_pid_alive(proc.pid):
157
+ raise RuntimeError("DataLab process terminated immediately after start")
158
+
159
+ if wait_until_ready:
160
+ # Use RemoteProxy's built-in retry mechanism with extended timeout
161
+ # DataLab startup: Python imports, Qt init, GUI creation, XML-RPC server
162
+ try:
163
+ proxy = RemoteProxy(autoconnect=False)
164
+ proxy.connect(timeout=30.0) # 30 seconds max for DataLab to be ready
165
+ proxy.disconnect()
166
+ except ConnectionRefusedError as exc:
167
+ if is_pid_alive(proc.pid):
168
+ proc.kill()
169
+ raise RuntimeError(
170
+ "Failed to connect to DataLab application. "
171
+ "Process may have started but XML-RPC server is not responding."
172
+ ) from exc
173
+
174
+
175
+ @contextmanager
176
+ def skip_if_opencv_missing() -> Generator[None, None, None]:
177
+ """Skip test if OpenCV is not available"""
178
+ try:
179
+ yield
180
+ except ImportError as exc:
181
+ if "cv2" in str(exc).lower():
182
+ pytest.skip("OpenCV not available, skipping test")
183
+ raise exc
184
+
185
+
186
+ def take_plotwidget_screenshot(panel: SignalPanel | ImagePanel, name: str) -> None:
187
+ """Eventually takes plotwidget screenshot (only in screenshot mode)"""
188
+ if execenv.screenshot:
189
+ prefix = panel.PARAMCLASS.PREFIX
190
+ qth.grab_save_window(panel.plothandler.plotwidget, f"{prefix}_{name}")
191
+
192
+
193
+ def run() -> None:
194
+ """Run DataLab test launcher"""
195
+ run_testlauncher(datalab)
196
+
197
+
198
+ if __name__ == "__main__":
199
+ run()
@@ -0,0 +1 @@
1
+ #