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
@@ -0,0 +1,1048 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ DataLab HDF5 browser module
5
+ """
6
+
7
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
8
+
9
+ from __future__ import annotations
10
+
11
+ import abc
12
+ import os
13
+ import os.path as osp
14
+ from typing import TYPE_CHECKING, Any, Callable
15
+
16
+ from guidata.qthelpers import (
17
+ add_actions,
18
+ create_action,
19
+ create_toolbutton,
20
+ exec_dialog,
21
+ get_icon,
22
+ get_std_icon,
23
+ win32_fix_title_bar_background,
24
+ )
25
+ from guidata.utils.misc import to_string
26
+ from guidata.widgets.arrayeditor import ArrayEditor
27
+ from plotpy.builder import make
28
+ from plotpy.plot import PlotOptions, PlotWidget
29
+ from qtpy import QtCore as QC
30
+ from qtpy import QtGui as QG
31
+ from qtpy import QtWidgets as QW
32
+ from qtpy.compat import getopenfilename
33
+ from sigima import ImageObj, SignalObj
34
+
35
+ from datalab.adapters_plotpy import CURVESTYLES, create_adapter_from_object
36
+ from datalab.config import Conf, _
37
+ from datalab.h5 import H5Importer
38
+ from datalab.utils.qthelpers import block_signals, qt_handle_error_message
39
+
40
+ if TYPE_CHECKING:
41
+ from plotpy.plot import BasePlot
42
+
43
+ from datalab.h5.common import BaseNode
44
+
45
+
46
+ class AbstractTreeWidgetMeta(type(QW.QTreeWidget), abc.ABCMeta):
47
+ """Mixed metaclass to avoid conflicts"""
48
+
49
+
50
+ class AbstractTreeWidget(QW.QTreeWidget, metaclass=AbstractTreeWidgetMeta):
51
+ """One-column tree widget with context menu, ..."""
52
+
53
+ def __init__(self, parent: QW.QWidget) -> None:
54
+ super().__init__(parent)
55
+ self.setItemsExpandable(True)
56
+ self.itemActivated.connect(self.activated)
57
+ self.itemClicked.connect(self.clicked)
58
+ # Setup context menu
59
+ self.menu = QW.QMenu(self)
60
+ self.collapse_all_action = None
61
+ self.collapse_selection_action = None
62
+ self.expand_all_action = None
63
+ self.expand_selection_action = None
64
+ self.common_actions = self.setup_common_actions()
65
+
66
+ self.itemSelectionChanged.connect(self.item_selection_changed)
67
+ self.item_selection_changed()
68
+
69
+ @abc.abstractmethod
70
+ def activated(self, item: QW.QTreeWidgetItem) -> None:
71
+ """Double-click event"""
72
+
73
+ @abc.abstractmethod
74
+ def clicked(self, item: QW.QTreeWidgetItem) -> None:
75
+ """Item was clicked"""
76
+
77
+ @abc.abstractmethod
78
+ def get_actions_from_items(
79
+ self, items: list[QW.QTreeWidgetItem]
80
+ ) -> list[QW.QAction]:
81
+ """Get actions from item"""
82
+ # Right here: add other actions if necessary (reimplement this method)
83
+ return []
84
+
85
+ def setup_common_actions(self) -> list[QW.QAction]:
86
+ """Setup context menu common actions"""
87
+ self.collapse_all_action = create_action(
88
+ self,
89
+ _("Collapse all"),
90
+ icon=get_icon("collapse.svg"),
91
+ triggered=self.collapseAll,
92
+ )
93
+ self.expand_all_action = create_action(
94
+ self, _("Expand all"), icon=get_icon("expand.svg"), triggered=self.expandAll
95
+ )
96
+ self.restore_action = create_action(
97
+ self,
98
+ _("Restore"),
99
+ tip=_("Restore original tree layout"),
100
+ icon=get_icon("restore.svg"),
101
+ triggered=self.restore,
102
+ )
103
+ self.collapse_selection_action = create_action(
104
+ self,
105
+ _("Collapse selection"),
106
+ icon=get_icon("collapse_selection.svg"),
107
+ triggered=self.collapse_selection,
108
+ )
109
+ self.expand_selection_action = create_action(
110
+ self,
111
+ _("Expand selection"),
112
+ icon=get_icon("expand_selection.svg"),
113
+ triggered=self.expand_selection,
114
+ )
115
+ return [
116
+ self.collapse_all_action,
117
+ self.expand_all_action,
118
+ self.restore_action,
119
+ None,
120
+ self.collapse_selection_action,
121
+ self.expand_selection_action,
122
+ ]
123
+
124
+ def update_menu(self) -> None:
125
+ """Update context menu"""
126
+ self.menu.clear()
127
+ items = self.selectedItems()
128
+ actions = self.get_actions_from_items(items)
129
+ if actions:
130
+ actions.append(None)
131
+ actions += self.common_actions
132
+ add_actions(self.menu, actions)
133
+
134
+ def restore(self) -> None:
135
+ """Restore tree state"""
136
+ self.collapseAll()
137
+ for item in self.get_top_level_items():
138
+ self.expandItem(item)
139
+
140
+ def __expand_item(self, item: QW.QTreeWidgetItem) -> None: # pragma: no cover
141
+ """Expand item tree branch"""
142
+ self.expandItem(item)
143
+ for index in range(item.childCount()):
144
+ child = item.child(index)
145
+ self.__expand_item(child)
146
+
147
+ def expand_selection(self) -> None: # pragma: no cover
148
+ """Expand selection"""
149
+ items = self.selectedItems()
150
+ if not items:
151
+ items = self.get_top_level_items()
152
+ for item in items:
153
+ self.__expand_item(item)
154
+ if items:
155
+ self.scrollToItem(items[0])
156
+
157
+ def __collapse_item(self, item: QW.QTreeWidgetItem) -> None: # pragma: no cover
158
+ """Collapse item tree branch"""
159
+ self.collapseItem(item)
160
+ for index in range(item.childCount()):
161
+ child = item.child(index)
162
+ self.__collapse_item(child)
163
+
164
+ def collapse_selection(self) -> None: # pragma: no cover
165
+ """Collapse selection"""
166
+ items = self.selectedItems()
167
+ if not items:
168
+ items = self.get_top_level_items()
169
+ for item in items:
170
+ self.__collapse_item(item)
171
+ if items:
172
+ self.scrollToItem(items[0])
173
+
174
+ def item_selection_changed(self) -> None:
175
+ """Item selection has changed"""
176
+ is_selection = len(self.selectedItems()) > 0
177
+ self.expand_selection_action.setEnabled(is_selection)
178
+ self.collapse_selection_action.setEnabled(is_selection)
179
+
180
+ def get_top_level_items(self) -> list[QW.QTreeWidgetItem]:
181
+ """Iterate over top level items"""
182
+ return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())]
183
+
184
+ def find_all_items(self):
185
+ """Find all items"""
186
+ return self.findItems("", QC.Qt.MatchContains | QC.Qt.MatchRecursive)
187
+
188
+ def contextMenuEvent(self, event: QG.QContextMenuEvent) -> None:
189
+ """Override Qt method"""
190
+ self.update_menu()
191
+ self.menu.popup(event.globalPos())
192
+
193
+
194
+ class H5TreeWidget(AbstractTreeWidget):
195
+ """HDF5 Browser Tree Widget
196
+
197
+ Args:
198
+ parent: Parent widget
199
+ """
200
+
201
+ SIG_SELECTED = QC.Signal(QW.QTreeWidgetItem)
202
+
203
+ def __init__(self, parent: QW.QWidget) -> None:
204
+ super().__init__(parent)
205
+ title = _("HDF5 Browser")
206
+ self.setColumnCount(4)
207
+ self.setWindowTitle(title)
208
+ self.setHeaderLabels([_("Name"), _("Size"), _("Type"), _("Value")])
209
+ self.header().setSectionResizeMode(0, QW.QHeaderView.Stretch)
210
+ self.header().setStretchLastSection(False)
211
+ self.fnames: list[str] = []
212
+ self.h5importers: list[H5Importer] = []
213
+
214
+ def add_root(self, fname: str) -> None:
215
+ """Add HDF5 root (new file)
216
+
217
+ Args:
218
+ fname: HDF5 file name
219
+ """
220
+ self.fnames.append(osp.abspath(fname))
221
+ importer = H5Importer(fname)
222
+ self.h5importers.append(importer)
223
+ self.add_root_to_tree(importer)
224
+ # Temporarily expand all items to calculate proper column widths
225
+ rootitem = self.topLevelItem(self.topLevelItemCount() - 1)
226
+ self.expand_all_children(rootitem)
227
+ for col in range(4):
228
+ self.resizeColumnToContents(col)
229
+ # Restore to default state (only root and its immediate children expanded)
230
+ for index in range(rootitem.childCount()):
231
+ child = rootitem.child(index)
232
+ self.collapseItem(child)
233
+
234
+ def remove_root(self, fname: str) -> None:
235
+ """Remove HDF5 root
236
+
237
+ Args:
238
+ fname: HDF5 file name
239
+ """
240
+ index = self.fnames.index(osp.abspath(fname))
241
+ self.fnames.pop(index)
242
+ importer = self.h5importers.pop(index)
243
+ importer.close()
244
+ # Remove root item associated with file
245
+ item = self.topLevelItem(index)
246
+ self.takeTopLevelItem(index)
247
+ del item
248
+
249
+ def cleanup(self) -> None:
250
+ """Clean up widget"""
251
+ for importer in self.h5importers:
252
+ importer.close()
253
+ self.fnames: list[str] = []
254
+ self.h5importers: list[H5Importer] = []
255
+ self.clear()
256
+
257
+ def __get_top_level_item(self, item: QW.QTreeWidgetItem) -> QW.QTreeWidgetItem:
258
+ """Get top level item associated to item
259
+
260
+ Args:
261
+ item: Tree item
262
+
263
+ Returns:
264
+ Top level item
265
+ """
266
+ while item.parent():
267
+ item = item.parent()
268
+ return item
269
+
270
+ def get_node(self, item: QW.QTreeWidgetItem) -> BaseNode:
271
+ """Get HDF5 dataset associated to item
272
+
273
+ Args:
274
+ item: Tree item
275
+
276
+ Returns:
277
+ HDF5 node
278
+ """
279
+ toplevel_item = self.__get_top_level_item(item)
280
+ toplevel_index = self.indexOfTopLevelItem(toplevel_item)
281
+ node_id = item.data(0, QC.Qt.UserRole)
282
+ if node_id:
283
+ importer = self.h5importers[toplevel_index]
284
+ return importer.get(node_id)
285
+ return None
286
+
287
+ def get_nodes(self, only_checked_items: bool = True) -> list[BaseNode]:
288
+ """Get all nodes associated to checked items
289
+
290
+ Args:
291
+ only_checked_items: If True, only checked items are returned
292
+
293
+ Returns:
294
+ List of HDF5 nodes
295
+ """
296
+ datasets = []
297
+ for item in self.find_all_items():
298
+ if item.flags() & QC.Qt.ItemIsUserCheckable:
299
+ if only_checked_items and item.checkState(0) == 0:
300
+ continue
301
+ if item is not self.topLevelItem(0):
302
+ node = self.get_node(item)
303
+ datasets.append(node)
304
+ return datasets
305
+
306
+ def activated(self, item: QW.QTreeWidgetItem) -> None:
307
+ """Double-click event"""
308
+ if item is not self.topLevelItem(0):
309
+ self.SIG_SELECTED.emit(item)
310
+
311
+ def clicked(self, item: QW.QTreeWidgetItem) -> None:
312
+ """Click event"""
313
+ self.activated(item)
314
+
315
+ def get_actions_from_items(self, items): # pylint: disable=W0613
316
+ """Get actions from item"""
317
+ return []
318
+
319
+ def is_empty(self) -> bool:
320
+ """Return True if tree is empty"""
321
+ return len(self.find_all_items()) == 1
322
+
323
+ def is_any_item_checked(self) -> bool:
324
+ """Return True if any item is checked"""
325
+ for item in self.find_all_items():
326
+ if item.checkState(0) > 0:
327
+ return True
328
+ return False
329
+
330
+ def select_all(self, state: bool) -> None:
331
+ """Select all items
332
+
333
+ Args:
334
+ state: If True, all items are selected
335
+ """
336
+ for item in self.find_all_items():
337
+ if item.flags() & QC.Qt.ItemIsUserCheckable:
338
+ item.setSelected(state)
339
+ if state:
340
+ self.clicked(item)
341
+
342
+ def toggle_all(self, state: bool) -> None:
343
+ """Toggle all item state from 'unchecked' to 'checked'
344
+ (or vice-versa)
345
+
346
+ Args:
347
+ state: If True, all items are checked
348
+ """
349
+ for item in self.find_all_items():
350
+ if item.flags() & QC.Qt.ItemIsUserCheckable:
351
+ item.setCheckState(0, QC.Qt.Checked if state else QC.Qt.Unchecked)
352
+
353
+ @staticmethod
354
+ def __create_node(node: BaseNode) -> QW.QTreeWidgetItem:
355
+ """Create tree node from HDF5 node
356
+
357
+ Args:
358
+ node: HDF5 node
359
+
360
+ Returns:
361
+ Tree widget node
362
+ """
363
+ text = to_string(node.text)
364
+ if len(text) > 30:
365
+ text = text[:30] + "..."
366
+ treeitem = QW.QTreeWidgetItem([node.name, node.shape_str, node.dtype_str, text])
367
+ treeitem.setData(0, QC.Qt.UserRole, node.id)
368
+ if node.description:
369
+ for col in range(treeitem.columnCount()):
370
+ treeitem.setToolTip(col, node.description)
371
+ return treeitem
372
+
373
+ @staticmethod
374
+ def __recursive_popfunc(parent_item: QW.QTreeWidgetItem, node: BaseNode) -> None:
375
+ """Recursive HDF5 analysis
376
+
377
+ Args:
378
+ parent_item: Parent tree item
379
+ node: HDF5 node
380
+ """
381
+ tree_item = H5TreeWidget.__create_node(node)
382
+ if node.is_supported():
383
+ tree_item.setCheckState(0, QC.Qt.Unchecked)
384
+ else:
385
+ tree_item.setFlags(QC.Qt.ItemIsEnabled)
386
+ tree_item.setIcon(0, get_icon(node.icon_name))
387
+ parent_item.addChild(tree_item)
388
+ for child in node.children:
389
+ H5TreeWidget.__recursive_popfunc(tree_item, child)
390
+
391
+ def expand_all_children(self, item: QW.QTreeWidgetItem) -> None:
392
+ """Expand all children (recursively)
393
+
394
+ Args:
395
+ item: Tree item
396
+ """
397
+ self.expandItem(item)
398
+ for index in range(item.childCount()):
399
+ child = item.child(index)
400
+ self.expand_all_children(child)
401
+
402
+ def add_root_to_tree(self, importer: H5Importer) -> None:
403
+ """Add root to tree
404
+
405
+ Args:
406
+ importer: HDF5 importer
407
+ """
408
+ root = importer.root
409
+ rootitem = QW.QTreeWidgetItem([root.name])
410
+ rootitem.setToolTip(0, root.description)
411
+ rootitem.setData(0, QC.Qt.UserRole, root.id)
412
+ rootitem.setFlags(QC.Qt.ItemIsEnabled)
413
+ rootitem.setIcon(0, get_icon(root.icon_name))
414
+ self.addTopLevelItem(rootitem)
415
+ for node in root.children:
416
+ self.__recursive_popfunc(rootitem, node)
417
+ self.expandItem(rootitem)
418
+
419
+ def toggle_show_only_checkable_items(self, state: bool) -> None:
420
+ """Show only checkable items
421
+
422
+ Args:
423
+ state: If True, only checkable items are shown
424
+ """
425
+ for item in self.find_all_items():
426
+ item.setHidden(state)
427
+ if state:
428
+ # Iterate over checkable items and show them (and their parents)
429
+ for item in self.find_all_items():
430
+ if item.flags() & QC.Qt.ItemIsUserCheckable:
431
+ item.setHidden(False)
432
+ parent = item.parent()
433
+ while parent:
434
+ parent.setHidden(False)
435
+ parent = parent.parent()
436
+
437
+ def toggle_show_values(self, state: bool) -> None:
438
+ """Show values
439
+
440
+ Args:
441
+ state: If True, values are shown
442
+ """
443
+ # Hide or show the "Value" column
444
+ self.setColumnHidden(3, not state)
445
+
446
+ def set_current_file(self, fname: str) -> None:
447
+ """Set current file
448
+
449
+ Args:
450
+ fname: HDF5 file name
451
+ """
452
+ index = self.fnames.index(osp.abspath(fname))
453
+ item = self.topLevelItem(index)
454
+ self.setCurrentItem(item)
455
+ self.scrollToItem(item, QW.QAbstractItemView.PositionAtTop)
456
+
457
+
458
+ class PlotPreview(QW.QStackedWidget):
459
+ """Plot preview"""
460
+
461
+ def __init__(self, parent: QW.QWidget) -> None:
462
+ super().__init__(parent)
463
+ self.curvewidget = PlotWidget(
464
+ self,
465
+ options=PlotOptions(
466
+ type="curve",
467
+ curve_antialiasing=True,
468
+ show_axes_tab=False,
469
+ autoscale_margin_percent=Conf.view.sig_autoscale_margin_percent.get(
470
+ 0.2
471
+ ),
472
+ ),
473
+ )
474
+ self.addWidget(self.curvewidget)
475
+ self.imagewidget = PlotWidget(
476
+ self,
477
+ options=PlotOptions(
478
+ type="image",
479
+ show_contrast=True,
480
+ show_axes_tab=False,
481
+ autoscale_margin_percent=Conf.view.ima_autoscale_margin_percent.get(
482
+ 0.2
483
+ ),
484
+ ),
485
+ )
486
+ self.addWidget(self.imagewidget)
487
+
488
+ def cleanup(self) -> None:
489
+ """Clean up widget"""
490
+ for widget in (self.imagewidget, self.curvewidget):
491
+ widget.get_plot().del_all_items()
492
+
493
+ def update_plot_preview(self, node: BaseNode) -> None:
494
+ """Update plot preview widget"""
495
+ try:
496
+ obj = node.get_native_object()
497
+ except Exception as msg: # pylint: disable=broad-except
498
+ qt_handle_error_message(self, msg)
499
+ return
500
+ if obj is None:
501
+ # An error occurred while creating the object (invalid data, ...)
502
+ label = make.label(_("Unsupported data"), "C", (0, 0), "C")
503
+ plot: BasePlot = self.currentWidget().get_plot()
504
+ plot.del_all_items()
505
+ plot.add_item(label)
506
+ plot.replot()
507
+ return
508
+ if isinstance(obj, SignalObj):
509
+ obj: SignalObj
510
+ widget = self.curvewidget
511
+ else:
512
+ obj: ImageObj
513
+ widget = self.imagewidget
514
+ with CURVESTYLES.suspend():
515
+ item = create_adapter_from_object(obj).make_item()
516
+ plot = widget.get_plot()
517
+ plot.del_all_items()
518
+ plot.add_item(item)
519
+ plot.set_active_item(item)
520
+ item.unselect()
521
+ plot.do_autoscale()
522
+ self.setCurrentWidget(widget)
523
+
524
+
525
+ class TablePreview(QW.QWidget):
526
+ """Table preview
527
+
528
+ Args:
529
+ title: Group title
530
+ parent: Parent widget
531
+ """
532
+
533
+ def __init__(self, parent: QW.QWidget) -> None:
534
+ super().__init__(parent)
535
+ self.setLayout(QW.QVBoxLayout())
536
+ self.table = QW.QTableWidget(self)
537
+ self.table.setEditTriggers(QW.QAbstractItemView.NoEditTriggers)
538
+ self.table.horizontalHeader().setStretchLastSection(True)
539
+ self.layout().addWidget(self.table)
540
+
541
+ def clear(self) -> None:
542
+ """Clear table"""
543
+ self.table.clear()
544
+
545
+ def update_table_preview(self, data: dict[str, Any]) -> None:
546
+ """Update table preview widget
547
+
548
+ Args:
549
+ node: HDF5 node
550
+ """
551
+ self.clear()
552
+ self.table.setRowCount(len(data))
553
+ self.table.setColumnCount(1)
554
+ self.table.setHorizontalHeaderLabels([_("Value")])
555
+ self.table.setVerticalHeaderLabels(list(data.keys()))
556
+ for row, value in enumerate(data.values()):
557
+ self.table.setItem(row, 0, QW.QTableWidgetItem(str(value)))
558
+ self.table.resizeRowsToContents()
559
+
560
+
561
+ class GroupAndAttributes(QW.QTabWidget):
562
+ """Group and attributes
563
+
564
+ Args:
565
+ parent: Parent widget
566
+ show_array_callback: Callback to show array
567
+ """
568
+
569
+ def __init__(self, parent: QW.QWidget, show_array_callback: Callable) -> None:
570
+ super().__init__(parent)
571
+ self.group = TablePreview(self)
572
+ self.addTab(self.group, get_icon("h5group.svg"), _("Group"))
573
+ self.attrs = TablePreview(self)
574
+ self.addTab(self.attrs, get_icon("h5attrs.svg"), _("Attributes"))
575
+ # Add a button as corner widget to show the array (if any):
576
+ self.__show_array_btn = create_toolbutton(
577
+ self,
578
+ icon=get_icon("show_results.svg"),
579
+ text=_("Show array"),
580
+ autoraise=False,
581
+ triggered=show_array_callback,
582
+ )
583
+ self.__show_array_btn.setEnabled(False)
584
+ self.setCornerWidget(self.__show_array_btn, QC.Qt.TopRightCorner)
585
+
586
+ def cleanup(self) -> None:
587
+ """Clean up widget"""
588
+ self.group.clear()
589
+ self.attrs.clear()
590
+
591
+ def update_from_node(self, node: BaseNode) -> None:
592
+ """Update widget from node
593
+
594
+ Args:
595
+ node: HDF5 node
596
+ """
597
+ # Update group =================================================================
598
+ text = to_string(node.text)
599
+ if text:
600
+ lines = text.splitlines()[:5]
601
+ if len(lines) == 5:
602
+ lines += ["[...]"]
603
+ text = os.linesep.join(lines)
604
+ data = {
605
+ _("Path"): node.id,
606
+ _("Name"): node.name,
607
+ _("Description"): node.description,
608
+ _("Textual preview"): text,
609
+ # "Raw": repr(node.data),
610
+ }
611
+ self.group.update_table_preview(data)
612
+
613
+ # Update attributes ============================================================
614
+ self.attrs.update_table_preview(node.metadata)
615
+
616
+ # Update show array button =====================================================
617
+ self.__show_array_btn.setEnabled(node.IS_ARRAY)
618
+
619
+
620
+ class H5FileSelector(QW.QWidget):
621
+ """HDF5 file selector
622
+
623
+ Args:
624
+ parent: Parent widget
625
+ """
626
+
627
+ SIG_ADD_FILENAME = QC.Signal(str)
628
+ SIG_REMOVE_FILENAME = QC.Signal(str)
629
+ SIG_CURRENT_CHANGED = QC.Signal(str)
630
+
631
+ def __init__(self, parent: QW.QWidget) -> None:
632
+ super().__init__(parent)
633
+ self.setLayout(QW.QHBoxLayout())
634
+ self.layout().setContentsMargins(0, 0, 0, 0)
635
+ self.combo = QW.QComboBox(self)
636
+ self.combo.currentTextChanged.connect(self.current_file_changed)
637
+ self.layout().addWidget(self.combo)
638
+ self.btn_add = create_toolbutton(
639
+ self,
640
+ icon=get_std_icon("DirOpenIcon"),
641
+ text=_("Open") + " ...",
642
+ autoraise=False,
643
+ triggered=lambda _checked=False: self.add_file(),
644
+ )
645
+ self.btn_add.setSizePolicy(QW.QSizePolicy.Fixed, QW.QSizePolicy.Fixed)
646
+ self.layout().addWidget(self.btn_add)
647
+ self.btn_rmv = create_toolbutton(
648
+ self,
649
+ icon=get_std_icon("DialogCloseButton"),
650
+ text=_("Close"),
651
+ autoraise=False,
652
+ triggered=self.remove_file,
653
+ )
654
+ self.btn_rmv.setSizePolicy(QW.QSizePolicy.Fixed, QW.QSizePolicy.Fixed)
655
+ self.layout().addWidget(self.btn_rmv)
656
+ self.btn_rmv.setEnabled(False)
657
+
658
+ def set_current_fname(self, fname: str) -> None:
659
+ """Set current file name
660
+
661
+ Args:
662
+ fname: HDF5 file name
663
+ """
664
+ index = self.combo.findText(fname)
665
+ if index >= 0:
666
+ self.combo.setCurrentIndex(index)
667
+
668
+ def get_current_fname(self) -> str:
669
+ """Return current file name
670
+
671
+ Returns:
672
+ HDF5 file name
673
+ """
674
+ return self.combo.currentText()
675
+
676
+ def current_file_changed(self, fname: str) -> None:
677
+ """Current file changed
678
+
679
+ Args:
680
+ fname: HDF5 file name
681
+ """
682
+ self.SIG_CURRENT_CHANGED.emit(fname)
683
+
684
+ def add_fname(self, fname: str) -> None:
685
+ """Add file name
686
+
687
+ Args:
688
+ fname: HDF5 file name
689
+ """
690
+ self.combo.addItem(get_icon("h5file.svg"), fname)
691
+ self.btn_rmv.setEnabled(True)
692
+
693
+ def remove_fname(self, fname: str) -> None:
694
+ """Remove file name
695
+
696
+ Args:
697
+ fname: HDF5 file name
698
+ """
699
+ index = self.combo.findText(fname)
700
+ if index >= 0:
701
+ self.combo.removeItem(index)
702
+ if self.combo.count() == 0:
703
+ self.btn_rmv.setEnabled(False)
704
+
705
+ def add_file(self, fname: str | None = None) -> None:
706
+ """Browse file
707
+
708
+ Args:
709
+ fname: HDF5 file name. Default is None.
710
+ (this is used for testing only)
711
+ """
712
+ if fname is None:
713
+ fname = getopenfilename(
714
+ self,
715
+ _("Select HDF5 file"),
716
+ "",
717
+ _("HDF5 files (*.h5 *.hdf5 *.hdf *.he5);;All files (*)"),
718
+ )[0]
719
+ if fname:
720
+ self.SIG_ADD_FILENAME.emit(osp.abspath(fname))
721
+
722
+ def remove_file(self, fname: str | None = None) -> None:
723
+ """Remove file name
724
+
725
+ Args:
726
+ fname: HDF5 file name
727
+ """
728
+ if fname is None:
729
+ fname = self.combo.currentText()
730
+ self.SIG_REMOVE_FILENAME.emit(fname)
731
+
732
+
733
+ class H5Browser(QW.QSplitter):
734
+ """HDF5 Browser Widget
735
+
736
+ Args:
737
+ parent: Parent widget
738
+ """
739
+
740
+ SIG_SELECT_NEW_FILE = QC.Signal(str)
741
+ SIG_REMOVE_FILE = QC.Signal(str)
742
+
743
+ def __init__(self, parent: QW.QWidget | None = None) -> None:
744
+ super().__init__(parent)
745
+ self.selector = H5FileSelector(self)
746
+ self.selector.SIG_ADD_FILENAME.connect(self.__add_new_file)
747
+ self.selector.SIG_REMOVE_FILENAME.connect(self.__remove_file)
748
+ self.selector.SIG_CURRENT_CHANGED.connect(self.__selector_current_file_changed)
749
+ self.tree = H5TreeWidget(self)
750
+ self.tree.SIG_SELECTED.connect(self.__item_selected_on_tree)
751
+ selectorandtree = QW.QFrame(self)
752
+ selectorandtree.setLayout(QW.QVBoxLayout())
753
+ selectorandtree.layout().addWidget(self.selector)
754
+ # Add toolbar with tree actions
755
+ toolbar = self.__create_toolbar()
756
+ selectorandtree.layout().addWidget(toolbar)
757
+ selectorandtree.layout().addWidget(self.tree)
758
+ selectorandtree.layout().setContentsMargins(0, 0, 0, 0)
759
+ self.addWidget(selectorandtree)
760
+ preview = QW.QSplitter(self)
761
+ preview.setOrientation(QC.Qt.Vertical)
762
+ self.addWidget(preview)
763
+ self.plotpreview = PlotPreview(self)
764
+ preview.addWidget(self.plotpreview)
765
+ self.groupandattrs = GroupAndAttributes(self, self.show_array)
766
+ preview.addWidget(self.groupandattrs)
767
+ preview.setSizes([int(self.size().height() / 2)] * 2)
768
+
769
+ def __create_toolbar(self) -> QW.QToolBar:
770
+ """Create toolbar with tree actions
771
+
772
+ Returns:
773
+ Toolbar widget
774
+ """
775
+ toolbar = QW.QToolBar(self)
776
+ toolbar.setToolButtonStyle(QC.Qt.ToolButtonTextBesideIcon)
777
+ toolbar.setIconSize(QC.QSize(16, 16))
778
+ toolbar.setStyleSheet("QToolBar { padding: 2px; spacing: 2px; }")
779
+ toolbar.addAction(self.tree.expand_all_action)
780
+ toolbar.addAction(self.tree.collapse_all_action)
781
+ toolbar.addAction(self.tree.restore_action)
782
+ toolbar.addSeparator()
783
+ toolbar.addAction(self.tree.expand_selection_action)
784
+ toolbar.addAction(self.tree.collapse_selection_action)
785
+ return toolbar
786
+
787
+ def open_file(self, fname: str) -> None:
788
+ """Open HDF5 file
789
+
790
+ Args:
791
+ fname: HDF5 file name
792
+ """
793
+ self.tree.add_root(fname)
794
+ self.selector.add_fname(fname)
795
+
796
+ def close_file(self, fname: str) -> None:
797
+ """Close HDF5 file
798
+
799
+ Args:
800
+ fname: HDF5 file name
801
+ """
802
+ self.tree.remove_root(fname)
803
+ self.selector.remove_fname(fname)
804
+
805
+ def __add_new_file(self, fname: str) -> None:
806
+ """Add new file
807
+
808
+ Args:
809
+ fname: HDF5 file name
810
+ """
811
+ self.open_file(fname)
812
+ self.selector.set_current_fname(fname)
813
+ self.SIG_SELECT_NEW_FILE.emit(fname)
814
+
815
+ def __remove_file(self, fname: str) -> None:
816
+ """Remove file
817
+
818
+ Args:
819
+ fname: HDF5 file name
820
+ """
821
+ self.close_file(fname)
822
+ self.SIG_REMOVE_FILE.emit(fname)
823
+
824
+ def cleanup(self) -> None:
825
+ """Clean up widget"""
826
+ self.tree.cleanup()
827
+ self.plotpreview.cleanup()
828
+
829
+ def get_node(self, item: QW.QTreeWidgetItem | None = None) -> BaseNode:
830
+ """Return (selected) dataset
831
+
832
+ Args:
833
+ item: Tree item
834
+
835
+ Returns:
836
+ HDF5 node
837
+ """
838
+ if item is None:
839
+ item = self.tree.currentItem()
840
+ return self.tree.get_node(item)
841
+
842
+ def __item_selected_on_tree(self, item: QW.QTreeWidgetItem) -> None:
843
+ """Item selected on tree
844
+
845
+ Args:
846
+ item: Tree item
847
+ """
848
+ # View the selected item
849
+ node = self.get_node(item)
850
+ if node.is_supported():
851
+ self.plotpreview.update_plot_preview(node)
852
+ self.groupandattrs.update_from_node(node)
853
+ # Update the file selector combo box
854
+ with block_signals(self.selector.combo):
855
+ # Avoid triggering current file changed signal, which would result in
856
+ # loosing the current selection on the tree (side effect: "Show array"
857
+ # button would still be enabled if the previous node was an array, except
858
+ # that now the current node is not an array, thus causing an error if
859
+ # the user clicks on the button).
860
+ self.selector.set_current_fname(node.h5file.filename)
861
+
862
+ def __selector_current_file_changed(self, fname: str) -> None:
863
+ """Selector current file changed
864
+
865
+ Args:
866
+ fname: HDF5 file name
867
+ """
868
+ if fname:
869
+ self.tree.set_current_file(fname)
870
+
871
+ def show_array(self) -> None:
872
+ """Show array"""
873
+ node = self.get_node()
874
+ assert node.IS_ARRAY
875
+ arrayeditor = ArrayEditor(self)
876
+ arrayeditor.setup_and_check(
877
+ node.data, title=node.name, readonly=True, add_title_suffix=False
878
+ )
879
+ exec_dialog(arrayeditor)
880
+
881
+
882
+ class H5BrowserDialog(QW.QDialog):
883
+ """HDF5 Browser Dialog
884
+
885
+ Args:
886
+ parent: Parent widget
887
+ size: Dialog size
888
+ """
889
+
890
+ def __init__(
891
+ self, parent: QW.QWidget | None = None, size: tuple[int, int] = (1150, 700)
892
+ ) -> None:
893
+ super().__init__(parent)
894
+ self.setWindowFlags(QC.Qt.Window)
895
+ self.setObjectName("h5browser")
896
+ self.setWindowTitle(_("HDF5 Browser"))
897
+ self.setWindowIcon(get_icon("h5browser.svg"))
898
+ win32_fix_title_bar_background(self)
899
+ vlayout = QW.QVBoxLayout()
900
+ self.setLayout(vlayout)
901
+ self.button_layout: QW.QHBoxLayout | None = None
902
+ self.bbox: QW.QDialogButtonBox | None = None
903
+ self.nodes: list[BaseNode] = []
904
+ self.checkbox_show_only: QW.QCheckBox | None = None
905
+ self.checkbox_show_values: QW.QCheckBox | None = None
906
+
907
+ self.browser = H5Browser(self)
908
+ self.browser.SIG_SELECT_NEW_FILE.connect(self.select_new_file)
909
+ self.browser.SIG_REMOVE_FILE.connect(self.remove_file)
910
+ vlayout.addWidget(self.browser)
911
+
912
+ self.browser.tree.itemChanged.connect(lambda item: self.refresh_buttons())
913
+
914
+ self.install_button_layout()
915
+
916
+ self.setMinimumSize(QC.QSize(900, 500))
917
+ self.resize(QC.QSize(*size))
918
+ self.browser.setSizes([int(self.size().height() / 2)] * 2)
919
+ self.refresh_buttons()
920
+
921
+ def accept(self) -> None:
922
+ """Accept changes"""
923
+ self.nodes = self.browser.tree.get_nodes()
924
+ QW.QDialog.accept(self)
925
+
926
+ def is_empty(self) -> bool:
927
+ """Return True if tree is empty"""
928
+ return self.browser.tree.is_empty()
929
+
930
+ def cleanup(self) -> None:
931
+ """Cleanup dialog"""
932
+ self.browser.cleanup()
933
+
934
+ def refresh_buttons(self) -> None:
935
+ """Refresh buttons"""
936
+ state = self.browser.tree.is_any_item_checked()
937
+ self.bbox.button(QW.QDialogButtonBox.Ok).setEnabled(state)
938
+
939
+ def show_only_checkable_items(self, state: int) -> None:
940
+ """Show only checkable items
941
+
942
+ Args:
943
+ state: If True, only checkable items are shown
944
+ """
945
+ self.browser.tree.toggle_show_only_checkable_items(state)
946
+ fname = self.browser.selector.get_current_fname()
947
+ if fname:
948
+ self.browser.tree.set_current_file(fname)
949
+
950
+ def __finalize_setup(self) -> None:
951
+ """Finalize setup"""
952
+ tree = self.browser.tree
953
+ tree.toggle_show_only_checkable_items(self.checkbox_show_only.isChecked())
954
+ tree.toggle_show_values(self.checkbox_show_values.isChecked())
955
+
956
+ def open_file(self, fname: str) -> None:
957
+ """Open file
958
+
959
+ Args:
960
+ fname: HDF5 file name
961
+ """
962
+ self.browser.open_file(fname)
963
+ self.__finalize_setup()
964
+
965
+ def open_files(self, fnames: list[str]) -> None:
966
+ """Open files
967
+
968
+ Args:
969
+ fnames: HDF5 file names
970
+ """
971
+ for fname in fnames:
972
+ self.browser.open_file(fname)
973
+ self.__finalize_setup()
974
+
975
+ def select_new_file(self, fname: str) -> None: # pylint:disable=unused-argument
976
+ """Select new file
977
+
978
+ Args:
979
+ fname: HDF5 file name
980
+ """
981
+ self.__finalize_setup()
982
+ self.refresh_buttons()
983
+
984
+ def remove_file(self, fname: str) -> None: # pylint:disable=unused-argument
985
+ """Remove file
986
+
987
+ Args:
988
+ fname: HDF5 file name
989
+ """
990
+ self.refresh_buttons()
991
+
992
+ def get_all_nodes(self) -> list[BaseNode]:
993
+ """Return all supported datasets
994
+
995
+ Returns:
996
+ List of HDF5 nodes
997
+ """
998
+ return self.browser.tree.get_nodes(only_checked_items=False)
999
+
1000
+ def get_nodes(self) -> list[BaseNode]:
1001
+ """Return datasets
1002
+
1003
+ Returns:
1004
+ List of HDF5 nodes
1005
+ """
1006
+ return self.nodes
1007
+
1008
+ def install_button_layout(self) -> None:
1009
+ """Install button layout"""
1010
+ bbox = QW.QDialogButtonBox(QW.QDialogButtonBox.Ok | QW.QDialogButtonBox.Cancel)
1011
+ bbox.accepted.connect(self.accept)
1012
+ bbox.rejected.connect(self.reject)
1013
+
1014
+ btn_check_all = create_toolbutton(
1015
+ self,
1016
+ icon=get_icon("check_all.svg"),
1017
+ text=_("Check all"),
1018
+ autoraise=False,
1019
+ shortcut=QG.QKeySequence.SelectAll,
1020
+ triggered=lambda checked=True: self.browser.tree.toggle_all(checked),
1021
+ )
1022
+ btn_uncheck_all = create_toolbutton(
1023
+ self,
1024
+ icon=get_icon("uncheck_all.svg"),
1025
+ text=_("Uncheck all"),
1026
+ autoraise=False,
1027
+ triggered=lambda checked=False: self.browser.tree.toggle_all(checked),
1028
+ )
1029
+ self.checkbox_show_only = QW.QCheckBox(_("Show only supported data"))
1030
+ self.checkbox_show_only.stateChanged.connect(self.show_only_checkable_items)
1031
+ self.checkbox_show_values = QW.QCheckBox(_("Show values"))
1032
+ self.checkbox_show_values.stateChanged.connect(
1033
+ self.browser.tree.toggle_show_values
1034
+ )
1035
+
1036
+ self.button_layout = QW.QHBoxLayout()
1037
+ self.button_layout.addWidget(self.checkbox_show_only)
1038
+ self.button_layout.addWidget(self.checkbox_show_values)
1039
+ self.button_layout.addSpacing(10)
1040
+ self.button_layout.addWidget(btn_check_all)
1041
+ self.button_layout.addWidget(btn_uncheck_all)
1042
+ self.button_layout.addStretch()
1043
+ self.button_layout.addWidget(bbox)
1044
+ self.bbox = bbox
1045
+
1046
+ vlayout: QW.QVBoxLayout = self.layout()
1047
+ vlayout.addSpacing(10)
1048
+ vlayout.addLayout(self.button_layout)