datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.1__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.1.dist-info/METADATA +121 -0
  490. datalab_platform-1.0.1.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.1.dist-info}/WHEEL +0 -0
  494. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/entry_points.txt +0 -0
  495. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/licenses/LICENSE +0 -0
  496. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/top_level.txt +0 -0
datalab/h5/generic.py ADDED
@@ -0,0 +1,580 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ DataLab Generic HDF5 format support
5
+ """
6
+
7
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
8
+
9
+ import h5py
10
+ import numpy as np
11
+ from guidata.utils.misc import to_string
12
+ from sigima.objects import create_image, create_signal
13
+
14
+ from datalab.h5 import common, utils
15
+
16
+ # =============================================================================
17
+ # Encoding and Data Reading Utilities
18
+ # =============================================================================
19
+
20
+
21
+ def safe_decode_bytes(data, fallback="<binary data>"):
22
+ """Safely decode bytes to string using multiple encoding strategies."""
23
+ if isinstance(data, str):
24
+ return data
25
+ if not isinstance(data, bytes):
26
+ return str(data)
27
+
28
+ # Try encodings in order of preference
29
+ for encoding in ["utf-8", "latin1", "cp1252", "iso-8859-1", "ascii"]:
30
+ try:
31
+ decoded = data.decode(encoding)
32
+ # For legacy encodings, validate the result looks reasonable
33
+ if encoding in ["latin1", "cp1252", "iso-8859-1"]:
34
+ if _is_reasonable_text(decoded):
35
+ return decoded
36
+ else:
37
+ return decoded
38
+ except (UnicodeDecodeError, UnicodeError):
39
+ continue
40
+
41
+ # Final fallback with replacement characters
42
+ try:
43
+ return data.decode("utf-8", errors="replace")
44
+ except Exception: # pylint: disable=broad-except
45
+ return fallback
46
+
47
+
48
+ def _is_reasonable_text(text):
49
+ """Check if decoded text looks reasonable (mostly printable)."""
50
+ if not text:
51
+ return True
52
+
53
+ printable_chars = sum(1 for c in text if c.isprintable() or c.isspace())
54
+ ratio = printable_chars / len(text)
55
+
56
+ # Accept if mostly printable or short enough to be likely text
57
+ return ratio >= 0.8 or len(text) < 20
58
+
59
+
60
+ def safe_read_dataset(dset, fallback_data=None):
61
+ """Safely read HDF5 dataset with encoding error handling."""
62
+ try:
63
+ return dset[()]
64
+ except UnicodeDecodeError:
65
+ # Try alternative reading strategies for problematic encodings
66
+ return _try_alternative_read(dset, fallback_data)
67
+ except (TypeError, ValueError, OSError):
68
+ return fallback_data
69
+
70
+
71
+ def _try_alternative_read(dset, fallback_data):
72
+ """Try alternative strategies to read datasets with encoding issues."""
73
+ strategies = [
74
+ lambda d: d.asstr()[()], # Try string conversion
75
+ lambda d: d.astype("S")[()], # Try reading as bytes
76
+ ]
77
+
78
+ for strategy in strategies:
79
+ try:
80
+ return strategy(dset)
81
+ except Exception: # pylint: disable=broad-except
82
+ continue
83
+
84
+ return fallback_data
85
+
86
+
87
+ # =============================================================================
88
+ # Text Formatting Utilities
89
+ # =============================================================================
90
+
91
+
92
+ def format_text_data(data):
93
+ """Format various types of data for text display."""
94
+ if data is None:
95
+ return "<unreadable data>"
96
+
97
+ try:
98
+ return to_string(data)
99
+ except (UnicodeDecodeError, UnicodeError):
100
+ return _handle_encoding_issues(data)
101
+
102
+
103
+ def _handle_encoding_issues(data):
104
+ """Handle data with encoding issues."""
105
+ if isinstance(data, bytes):
106
+ return safe_decode_bytes(data)
107
+
108
+ if isinstance(data, np.ndarray):
109
+ if data.dtype.kind in ["S", "a", "U"]: # String arrays
110
+ return _format_string_array(data)
111
+ if data.dtype.names: # Compound data
112
+ return _format_compound_data(data)
113
+
114
+ return f"<data with encoding issues: {type(data)}>"
115
+
116
+
117
+ def _format_string_array(data):
118
+ """Format string arrays with encoding handling."""
119
+ try:
120
+ if data.size == 1:
121
+ return safe_decode_bytes(data.item())
122
+ # Show first few elements
123
+ items = []
124
+ for i, item in enumerate(data.flat):
125
+ if i >= 5:
126
+ items.append("...")
127
+ break
128
+ items.append(safe_decode_bytes(item))
129
+ return f"[{', '.join(items)}]"
130
+ except Exception: # pylint: disable=broad-except
131
+ return f"<string array: {data.shape} {data.dtype}>"
132
+
133
+
134
+ def _format_compound_data(data):
135
+ """Format compound data with encoding handling."""
136
+ try:
137
+ result_parts = []
138
+ for field_name in data.dtype.names:
139
+ field_data = data[field_name]
140
+ if hasattr(field_data, "item"):
141
+ field_data = field_data.item()
142
+
143
+ if isinstance(field_data, bytes):
144
+ field_value = safe_decode_bytes(field_data)
145
+ else:
146
+ field_value = str(field_data)
147
+
148
+ result_parts.append(f"{field_name}: {field_value}")
149
+
150
+ return f"({', '.join(result_parts)})"
151
+ except Exception: # pylint: disable=broad-except
152
+ return f"<compound data: {data.dtype}>"
153
+
154
+
155
+ # =============================================================================
156
+ # Base Node Class
157
+ # =============================================================================
158
+
159
+
160
+ class BaseGenericNode(common.BaseNode):
161
+ """Base class for generic HDF5 data nodes with encoding support."""
162
+
163
+ @classmethod
164
+ def match(cls, dset):
165
+ """Return True if h5 dataset matches this node pattern."""
166
+ return not isinstance(dset, h5py.Group)
167
+
168
+ @property
169
+ def icon_name(self):
170
+ """Icon name associated to node."""
171
+ return "h5scalar.svg"
172
+
173
+ @property
174
+ def data(self):
175
+ """Data associated to node, if available."""
176
+ return safe_read_dataset(self.dset, fallback_data=None)
177
+
178
+ @property
179
+ def dtype_str(self):
180
+ """Return string representation of node data type."""
181
+ try:
182
+ return str(self.dset.dtype)
183
+ except (UnicodeDecodeError, TypeError, ValueError):
184
+ if self.data is None:
185
+ return "unknown"
186
+ try:
187
+ return str(self.data.dtype)
188
+ except Exception: # pylint: disable=broad-except
189
+ return "unknown"
190
+
191
+ @property
192
+ def text(self):
193
+ """Return node textual representation."""
194
+ return format_text_data(self.data)
195
+
196
+
197
+ # =============================================================================
198
+ # Specialized Node Classes
199
+ # =============================================================================
200
+
201
+
202
+ class GenericScalarNode(BaseGenericNode):
203
+ """Node for scalar HDF5 data."""
204
+
205
+ @classmethod
206
+ def match(cls, dset):
207
+ """Match scalar numeric data."""
208
+ if not super().match(dset):
209
+ return False
210
+ data = safe_read_dataset(dset)
211
+ return (
212
+ data is not None
213
+ and isinstance(data, np.generic)
214
+ and utils.is_supported_num_dtype(data)
215
+ )
216
+
217
+
218
+ class GenericTextNode(BaseGenericNode):
219
+ """Node for text/string HDF5 data."""
220
+
221
+ @classmethod
222
+ def match(cls, dset):
223
+ """Match text or string data."""
224
+ if not super().match(dset):
225
+ return False
226
+ data = safe_read_dataset(dset)
227
+ if data is None:
228
+ # Try to match based on dtype for unreadable data
229
+ try:
230
+ dtype = dset.dtype
231
+ return dtype.kind in ["S", "a", "U"] or "str" in str(dtype)
232
+ except Exception: # pylint: disable=broad-except
233
+ return False
234
+ return isinstance(data, bytes) or utils.is_supported_str_dtype(data)
235
+
236
+ @property
237
+ def dtype_str(self):
238
+ """Return simplified dtype for text data."""
239
+ return "string"
240
+
241
+ @property
242
+ def text(self):
243
+ """Return formatted text with special handling for single arrays."""
244
+ if self.data is None:
245
+ return "<unreadable data>"
246
+
247
+ try:
248
+ if utils.is_single_str_array(self.data):
249
+ item = self.data[0]
250
+ return safe_decode_bytes(item) if isinstance(item, bytes) else str(item)
251
+ return format_text_data(self.data)
252
+ except (UnicodeDecodeError, UnicodeError):
253
+ return _handle_text_encoding_issues(self.data)
254
+
255
+
256
+ def _handle_text_encoding_issues(data):
257
+ """Handle encoding issues specific to text nodes."""
258
+ if isinstance(data, bytes):
259
+ return safe_decode_bytes(data)
260
+ if isinstance(data, np.ndarray) and data.dtype.kind in ["S", "a"]:
261
+ try:
262
+ if data.size == 1:
263
+ return safe_decode_bytes(data.item())
264
+ decoded = [safe_decode_bytes(item) for item in data.flat]
265
+ return str(decoded[:10]) # Show first 10 elements
266
+ except Exception: # pylint: disable=broad-except
267
+ return f"<string data: {data.shape}>"
268
+ return "<text with encoding issues>"
269
+
270
+
271
+ class GenericArrayNode(BaseGenericNode):
272
+ """Node for array HDF5 data, including numeric arrays from compound data."""
273
+
274
+ IS_ARRAY = True
275
+
276
+ @classmethod
277
+ def match(cls, dset):
278
+ """Match numeric array data, including convertible compound data."""
279
+ if not super().match(dset):
280
+ return False
281
+ data = safe_read_dataset(dset)
282
+
283
+ if data is None:
284
+ return False
285
+
286
+ # First check direct numeric arrays
287
+ if (
288
+ utils.is_supported_num_dtype(data)
289
+ and isinstance(data, np.ndarray)
290
+ and len(data.shape) in (1, 2)
291
+ ):
292
+ return True
293
+
294
+ # Then check compound data that can be converted to numeric arrays
295
+ if (
296
+ isinstance(data, np.ndarray)
297
+ and hasattr(data.dtype, "names")
298
+ and data.dtype.names is not None
299
+ ):
300
+ return cls._can_convert_compound_to_numeric(data)
301
+
302
+ return False
303
+
304
+ @classmethod
305
+ def _can_convert_compound_to_numeric(cls, data):
306
+ """Check if compound data can be converted to a supported numeric array."""
307
+ try:
308
+ numeric_array = cls.extract_numeric_from_compound(data)
309
+ return (
310
+ numeric_array is not None
311
+ and utils.is_supported_num_dtype(numeric_array)
312
+ and isinstance(numeric_array, np.ndarray)
313
+ and len(numeric_array.shape) in (1, 2)
314
+ )
315
+ except Exception: # pylint: disable=broad-except
316
+ return False
317
+
318
+ @classmethod
319
+ def extract_numeric_from_compound(cls, data):
320
+ """Extract a numeric array from compound data."""
321
+ if not (hasattr(data.dtype, "names") and data.dtype.names):
322
+ return None
323
+
324
+ # Find ALL fields and check if they are numeric
325
+ all_fields = list(data.dtype.names)
326
+ numeric_fields = []
327
+ for field_name in all_fields:
328
+ field_dtype = data.dtype.fields[field_name][0]
329
+ if np.issubdtype(field_dtype, np.number):
330
+ numeric_fields.append(field_name)
331
+
332
+ # Only convert if ALL fields are numeric (preserve all information)
333
+ # or if there's a single numeric field and no important string data
334
+ if len(numeric_fields) == 0:
335
+ return None
336
+
337
+ if len(numeric_fields) != len(all_fields):
338
+ # Mixed data - check if non-numeric fields contain meaningful data
339
+ for field_name in all_fields:
340
+ if field_name not in numeric_fields:
341
+ field_data = data[field_name]
342
+ # If there's meaningful string data, don't convert
343
+ if cls._has_meaningful_string_data(field_data):
344
+ return None
345
+
346
+ try:
347
+ # If single numeric field, extract it directly
348
+ if len(numeric_fields) == 1:
349
+ return data[numeric_fields[0]]
350
+
351
+ # Multiple numeric fields: stack them if compatible shapes
352
+ field_data = [data[field] for field in numeric_fields]
353
+
354
+ # Check if all fields have the same shape
355
+ shapes = [arr.shape for arr in field_data]
356
+ if len(set(shapes)) == 1:
357
+ # Stack along new axis to create 2D array
358
+ return np.stack(field_data, axis=-1)
359
+
360
+ except Exception: # pylint: disable=broad-except
361
+ pass
362
+ return None
363
+
364
+ @classmethod
365
+ def _has_meaningful_string_data(cls, field_data):
366
+ """Check if string field contains meaningful data worth preserving."""
367
+ try:
368
+ if hasattr(field_data, "flat"):
369
+ # Check if most entries are non-empty and meaningful
370
+ non_empty_count = 0
371
+ for item in field_data.flat:
372
+ if isinstance(item, bytes):
373
+ decoded = item.decode("utf-8", errors="ignore").strip()
374
+ if decoded and len(decoded) > 0:
375
+ non_empty_count += 1
376
+ elif isinstance(item, str) and item.strip():
377
+ non_empty_count += 1
378
+
379
+ # If most entries have meaningful content, preserve it
380
+ return non_empty_count / field_data.size > 0.5
381
+ return True # Default to preserving unknown string data
382
+ except Exception: # pylint: disable=broad-except
383
+ return True # Conservative: preserve if we can't determine
384
+
385
+ @property
386
+ def data(self):
387
+ """Data associated to node, if available."""
388
+ raw_data = safe_read_dataset(self.dset, fallback_data=None)
389
+
390
+ # If this is compound data, try to extract numeric array
391
+ if (
392
+ raw_data is not None
393
+ and isinstance(raw_data, np.ndarray)
394
+ and hasattr(raw_data.dtype, "names")
395
+ and raw_data.dtype.names is not None
396
+ ):
397
+ numeric_data = self.extract_numeric_from_compound(raw_data)
398
+ if numeric_data is not None:
399
+ return numeric_data
400
+
401
+ return raw_data
402
+
403
+ def is_supported(self) -> bool:
404
+ """Return True if node is associated to supported data"""
405
+ return self.data.size > 1
406
+
407
+ @property
408
+ def __is_signal(self):
409
+ """Return True if array represents a signal"""
410
+ shape = self.data.shape
411
+ return len(shape) == 1 or shape[0] in (1, 2) or shape[1] in (1, 2)
412
+
413
+ @property
414
+ def icon_name(self):
415
+ """Icon name associated to node"""
416
+ if self.is_supported():
417
+ return "signal.svg" if self.__is_signal else "image.svg"
418
+ return "h5array.svg"
419
+
420
+ @property
421
+ def shape_str(self):
422
+ """Return string representation of node shape, if any"""
423
+ return " x ".join([str(size) for size in self.data.shape])
424
+
425
+ @property
426
+ def dtype_str(self):
427
+ """Return string representation of node data type, if any"""
428
+ return str(self.data.dtype)
429
+
430
+ @property
431
+ def text(self):
432
+ """Return node textual representation"""
433
+ return str(self.data)
434
+
435
+ def create_native_object(self):
436
+ """Create native object, if supported"""
437
+ if self.__is_signal:
438
+ obj = create_signal(self.object_title)
439
+ try:
440
+ self.set_signal_data(obj)
441
+ except ValueError:
442
+ obj = None
443
+ else:
444
+ obj = create_image(self.object_title)
445
+ try:
446
+ self.set_image_data(obj)
447
+ except ValueError:
448
+ obj = None
449
+ return obj
450
+
451
+
452
+ class GenericCompoundNode(BaseGenericNode):
453
+ """Node for compound/structured HDF5 data that can't convert to numeric arrays."""
454
+
455
+ IS_ARRAY = True
456
+
457
+ @classmethod
458
+ def match(cls, dset):
459
+ """Match compound/structured data that cannot be converted to numeric arrays."""
460
+ if not super().match(dset):
461
+ return False
462
+
463
+ data = safe_read_dataset(dset)
464
+ if data is None:
465
+ # Try to match based on dtype if we can't read the data
466
+ try:
467
+ return dset.dtype.names is not None
468
+ except Exception: # pylint: disable=broad-except
469
+ return False
470
+
471
+ # Check if it's compound data (structured array)
472
+ if not (
473
+ isinstance(data, np.ndarray)
474
+ and hasattr(data.dtype, "names")
475
+ and data.dtype.names is not None
476
+ ):
477
+ return False
478
+
479
+ # IMPORTANT: Only match if GenericArrayNode cannot handle this data
480
+ # Try to convert to a numeric array first
481
+ if cls._can_convert_to_numeric_array(data):
482
+ return False # Let GenericArrayNode handle it
483
+
484
+ return True # We handle compound data that can't be converted
485
+
486
+ @classmethod
487
+ def _can_convert_to_numeric_array(cls, data):
488
+ """Check if compound data can be converted to a numeric array."""
489
+ try:
490
+ # Try to extract numeric fields and create a pure numeric array
491
+ numeric_array = cls.extract_numeric_array(data)
492
+ if numeric_array is None:
493
+ return False
494
+
495
+ # Check if the resulting array would be supported by GenericArrayNode
496
+ return (
497
+ utils.is_supported_num_dtype(numeric_array)
498
+ and isinstance(numeric_array, np.ndarray)
499
+ and len(numeric_array.shape) in (1, 2)
500
+ )
501
+ except Exception: # pylint: disable=broad-except
502
+ return False
503
+
504
+ @classmethod
505
+ def extract_numeric_array(cls, data):
506
+ """Try to extract a pure numeric array from compound data."""
507
+ # Use the same logic as GenericArrayNode
508
+ return GenericArrayNode.extract_numeric_from_compound(data)
509
+
510
+ @property
511
+ def dtype_str(self):
512
+ """Return detailed compound dtype information."""
513
+ try:
514
+ dtype = self.dset.dtype
515
+ if dtype.names:
516
+ field_info = []
517
+ for name in dtype.names:
518
+ field_dtype = dtype.fields[name][0]
519
+ field_info.append(f"{name}: {field_dtype}")
520
+ return f"compound({', '.join(field_info)})"
521
+ return str(dtype)
522
+ except Exception: # pylint: disable=broad-except
523
+ return super().dtype_str
524
+
525
+ @property
526
+ def text(self):
527
+ """Return formatted compound data."""
528
+ if self.data is None:
529
+ return "<unreadable compound data>"
530
+ try:
531
+ return self._format_compound_data()
532
+ except Exception: # pylint: disable=broad-except
533
+ return f"<compound data: {self.data.shape} records>"
534
+
535
+ def _format_compound_data(self):
536
+ """Format compound data for display."""
537
+ if not (hasattr(self.data.dtype, "names") and self.data.dtype.names):
538
+ return super().text
539
+ if self.data.size == 1:
540
+ return self._format_single_record()
541
+ return self._format_multiple_records()
542
+
543
+ def _format_single_record(self):
544
+ """Format a single compound record."""
545
+ parts = []
546
+ for field_name in self.data.dtype.names:
547
+ field_value = self.data[field_name].item()
548
+ if isinstance(field_value, bytes):
549
+ field_value = safe_decode_bytes(field_value)
550
+ parts.append(f"{field_name}: {field_value}")
551
+ return f"({', '.join(parts)})"
552
+
553
+ def _format_multiple_records(self):
554
+ """Format multiple compound records."""
555
+ records = []
556
+ for i, record in enumerate(self.data.flat):
557
+ if i >= 3: # Show max 3 records
558
+ records.append("...")
559
+ break
560
+
561
+ parts = []
562
+ for field_name in self.data.dtype.names:
563
+ field_value = record[field_name]
564
+ if isinstance(field_value, bytes):
565
+ field_value = safe_decode_bytes(field_value)
566
+ parts.append(f"{field_name}: {field_value}")
567
+ records.append(f"({', '.join(parts)})")
568
+
569
+ return f"[{', '.join(records)}]"
570
+
571
+
572
+ # =============================================================================
573
+ # Node Registration
574
+ # =============================================================================
575
+
576
+ # Register all node types with the factory
577
+ common.NODE_FACTORY.register(GenericScalarNode, is_generic=True)
578
+ common.NODE_FACTORY.register(GenericTextNode, is_generic=True)
579
+ common.NODE_FACTORY.register(GenericArrayNode, is_generic=True)
580
+ common.NODE_FACTORY.register(GenericCompoundNode, is_generic=True)
datalab/h5/native.py ADDED
@@ -0,0 +1,39 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ DataLab Native HDF5 I/O module
5
+ """
6
+
7
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
8
+
9
+ from __future__ import annotations
10
+
11
+ from guidata.io import HDF5Reader, HDF5Writer
12
+
13
+ import datalab
14
+
15
+ DATALAB_VERSION_NAME = "DataLab_Version"
16
+
17
+
18
+ class NativeH5Writer(HDF5Writer):
19
+ """DataLab signal/image objects HDF5 guidata Dataset Writer class
20
+
21
+ Args:
22
+ filename (str): HDF5 file name
23
+ """
24
+
25
+ def __init__(self, filename: str) -> None:
26
+ super().__init__(filename)
27
+ self.h5[DATALAB_VERSION_NAME] = datalab.__version__
28
+
29
+
30
+ class NativeH5Reader(HDF5Reader):
31
+ """DataLab signal/image objects HDF5 guidata dataset Writer class
32
+
33
+ Args:
34
+ filename (str): HDF5 file name
35
+ """
36
+
37
+ def __init__(self, filename: str) -> None:
38
+ super().__init__(filename)
39
+ self.version = self.h5[DATALAB_VERSION_NAME]