datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (496) hide show
  1. datalab/__init__.py +35 -2
  2. datalab/adapters_metadata/__init__.py +31 -0
  3. datalab/adapters_metadata/base_adapter.py +316 -0
  4. datalab/adapters_metadata/common.py +422 -0
  5. datalab/adapters_metadata/geometry_adapter.py +98 -0
  6. datalab/adapters_metadata/table_adapter.py +84 -0
  7. datalab/adapters_plotpy/__init__.py +54 -0
  8. datalab/adapters_plotpy/annotations.py +124 -0
  9. datalab/adapters_plotpy/base.py +110 -0
  10. datalab/adapters_plotpy/converters.py +86 -0
  11. datalab/adapters_plotpy/factories.py +80 -0
  12. datalab/adapters_plotpy/objects/__init__.py +0 -0
  13. datalab/adapters_plotpy/objects/base.py +197 -0
  14. datalab/adapters_plotpy/objects/image.py +157 -0
  15. datalab/adapters_plotpy/objects/scalar.py +565 -0
  16. datalab/adapters_plotpy/objects/signal.py +264 -0
  17. datalab/adapters_plotpy/roi/__init__.py +0 -0
  18. datalab/adapters_plotpy/roi/base.py +146 -0
  19. datalab/adapters_plotpy/roi/factory.py +93 -0
  20. datalab/adapters_plotpy/roi/image.py +207 -0
  21. datalab/adapters_plotpy/roi/signal.py +72 -0
  22. datalab/app.py +98 -0
  23. datalab/config.py +817 -0
  24. datalab/control/__init__.py +0 -0
  25. datalab/control/baseproxy.py +776 -0
  26. datalab/control/proxy.py +343 -0
  27. datalab/control/remote.py +1005 -0
  28. datalab/data/doc/DataLab_en.pdf +0 -0
  29. datalab/data/doc/DataLab_fr.pdf +0 -0
  30. datalab/data/icons/analysis/delete_results.svg +109 -0
  31. datalab/data/icons/analysis/fw1e2.svg +156 -0
  32. datalab/data/icons/analysis/fwhm.svg +156 -0
  33. datalab/data/icons/analysis/histogram.svg +49 -0
  34. datalab/data/icons/analysis/peak_detect.svg +160 -0
  35. datalab/data/icons/analysis/plot_results.svg +151 -0
  36. datalab/data/icons/analysis/show_results.svg +83 -0
  37. datalab/data/icons/analysis/stats.svg +49 -0
  38. datalab/data/icons/analysis.svg +120 -0
  39. datalab/data/icons/apply.svg +3 -0
  40. datalab/data/icons/check_all.svg +15 -0
  41. datalab/data/icons/collapse.svg +44 -0
  42. datalab/data/icons/collapse_selection.svg +63 -0
  43. datalab/data/icons/console.svg +101 -0
  44. datalab/data/icons/create/1d-normal.svg +8 -0
  45. datalab/data/icons/create/1d-poisson.svg +9 -0
  46. datalab/data/icons/create/1d-uniform.svg +8 -0
  47. datalab/data/icons/create/1d-zero.svg +57 -0
  48. datalab/data/icons/create/2d-gaussian.svg +56 -0
  49. datalab/data/icons/create/2d-normal.svg +38 -0
  50. datalab/data/icons/create/2d-poisson.svg +38 -0
  51. datalab/data/icons/create/2d-ramp.svg +90 -0
  52. datalab/data/icons/create/2d-sinc.svg +62 -0
  53. datalab/data/icons/create/2d-uniform.svg +38 -0
  54. datalab/data/icons/create/2d-zero.svg +13 -0
  55. datalab/data/icons/create/checkerboard.svg +39 -0
  56. datalab/data/icons/create/cosine.svg +12 -0
  57. datalab/data/icons/create/exponential.svg +55 -0
  58. datalab/data/icons/create/gaussian.svg +12 -0
  59. datalab/data/icons/create/grating.svg +29 -0
  60. datalab/data/icons/create/linear_chirp.svg +7 -0
  61. datalab/data/icons/create/logistic.svg +7 -0
  62. datalab/data/icons/create/lorentzian.svg +12 -0
  63. datalab/data/icons/create/planck.svg +12 -0
  64. datalab/data/icons/create/polynomial.svg +7 -0
  65. datalab/data/icons/create/pulse.svg +12 -0
  66. datalab/data/icons/create/ring.svg +18 -0
  67. datalab/data/icons/create/sawtooth.svg +7 -0
  68. datalab/data/icons/create/siemens.svg +35 -0
  69. datalab/data/icons/create/sinc.svg +12 -0
  70. datalab/data/icons/create/sine.svg +7 -0
  71. datalab/data/icons/create/square.svg +7 -0
  72. datalab/data/icons/create/square_pulse.svg +7 -0
  73. datalab/data/icons/create/step.svg +7 -0
  74. datalab/data/icons/create/step_pulse.svg +12 -0
  75. datalab/data/icons/create/triangle.svg +7 -0
  76. datalab/data/icons/create/voigt.svg +12 -0
  77. datalab/data/icons/edit/annotations.svg +72 -0
  78. datalab/data/icons/edit/annotations_copy.svg +114 -0
  79. datalab/data/icons/edit/annotations_delete.svg +83 -0
  80. datalab/data/icons/edit/annotations_edit.svg +98 -0
  81. datalab/data/icons/edit/annotations_export.svg +85 -0
  82. datalab/data/icons/edit/annotations_import.svg +85 -0
  83. datalab/data/icons/edit/annotations_paste.svg +100 -0
  84. datalab/data/icons/edit/copy_titles.svg +109 -0
  85. datalab/data/icons/edit/delete.svg +84 -0
  86. datalab/data/icons/edit/delete_all.svg +214 -0
  87. datalab/data/icons/edit/duplicate.svg +64 -0
  88. datalab/data/icons/edit/goto_source.svg +60 -0
  89. datalab/data/icons/edit/metadata.svg +60 -0
  90. datalab/data/icons/edit/metadata_add.svg +80 -0
  91. datalab/data/icons/edit/metadata_copy.svg +96 -0
  92. datalab/data/icons/edit/metadata_delete.svg +62 -0
  93. datalab/data/icons/edit/metadata_export.svg +68 -0
  94. datalab/data/icons/edit/metadata_import.svg +68 -0
  95. datalab/data/icons/edit/metadata_paste.svg +79 -0
  96. datalab/data/icons/edit/move_down.svg +55 -0
  97. datalab/data/icons/edit/move_up.svg +54 -0
  98. datalab/data/icons/edit/new_group.svg +76 -0
  99. datalab/data/icons/edit/recompute.svg +60 -0
  100. datalab/data/icons/edit/rename.svg +49 -0
  101. datalab/data/icons/edit.svg +16 -0
  102. datalab/data/icons/expand.svg +44 -0
  103. datalab/data/icons/expand_selection.svg +63 -0
  104. datalab/data/icons/fit/cdf_fit.svg +56 -0
  105. datalab/data/icons/fit/exponential_fit.svg +55 -0
  106. datalab/data/icons/fit/gaussian_fit.svg +62 -0
  107. datalab/data/icons/fit/interactive_fit.svg +101 -0
  108. datalab/data/icons/fit/linear_fit.svg +57 -0
  109. datalab/data/icons/fit/lorentzian_fit.svg +209 -0
  110. datalab/data/icons/fit/multigaussian_fit.svg +85 -0
  111. datalab/data/icons/fit/multilorentzian_fit.svg +85 -0
  112. datalab/data/icons/fit/piecewiseexponential_fit.svg +209 -0
  113. datalab/data/icons/fit/planckian_fit.svg +62 -0
  114. datalab/data/icons/fit/polynomial_fit.svg +59 -0
  115. datalab/data/icons/fit/sigmoid_fit.svg +56 -0
  116. datalab/data/icons/fit/sinusoidal_fit.svg +72 -0
  117. datalab/data/icons/fit/twohalfgaussian_fit.svg +63 -0
  118. datalab/data/icons/fit/voigt_fit.svg +57 -0
  119. datalab/data/icons/group.svg +56 -0
  120. datalab/data/icons/h5/h5array.svg +59 -0
  121. datalab/data/icons/h5/h5attrs.svg +75 -0
  122. datalab/data/icons/h5/h5browser.svg +133 -0
  123. datalab/data/icons/h5/h5file.svg +69 -0
  124. datalab/data/icons/h5/h5group.svg +49 -0
  125. datalab/data/icons/h5/h5scalar.svg +1 -0
  126. datalab/data/icons/help_pdf.svg +46 -0
  127. datalab/data/icons/history.svg +7 -0
  128. datalab/data/icons/image.svg +135 -0
  129. datalab/data/icons/io/fileopen_directory.svg +60 -0
  130. datalab/data/icons/io/fileopen_h5.svg +84 -0
  131. datalab/data/icons/io/fileopen_ima.svg +187 -0
  132. datalab/data/icons/io/fileopen_py.svg +123 -0
  133. datalab/data/icons/io/fileopen_sig.svg +138 -0
  134. datalab/data/icons/io/filesave_h5.svg +97 -0
  135. datalab/data/icons/io/filesave_ima.svg +200 -0
  136. datalab/data/icons/io/filesave_py.svg +136 -0
  137. datalab/data/icons/io/filesave_sig.svg +151 -0
  138. datalab/data/icons/io/import_text.svg +144 -0
  139. datalab/data/icons/io/save_to_directory.svg +134 -0
  140. datalab/data/icons/io.svg +84 -0
  141. datalab/data/icons/libre-camera-flash-off.svg +1 -0
  142. datalab/data/icons/libre-camera-flash-on.svg +1 -0
  143. datalab/data/icons/libre-gui-about.svg +1 -0
  144. datalab/data/icons/libre-gui-action-delete.svg +1 -0
  145. datalab/data/icons/libre-gui-add.svg +1 -0
  146. datalab/data/icons/libre-gui-arrow-down.svg +1 -0
  147. datalab/data/icons/libre-gui-arrow-left.svg +1 -0
  148. datalab/data/icons/libre-gui-arrow-right.svg +1 -0
  149. datalab/data/icons/libre-gui-arrow-up.svg +1 -0
  150. datalab/data/icons/libre-gui-close.svg +40 -0
  151. datalab/data/icons/libre-gui-cogs.svg +1 -0
  152. datalab/data/icons/libre-gui-globe.svg +1 -0
  153. datalab/data/icons/libre-gui-help.svg +1 -0
  154. datalab/data/icons/libre-gui-link.svg +1 -0
  155. datalab/data/icons/libre-gui-menu.svg +1 -0
  156. datalab/data/icons/libre-gui-pencil.svg +1 -0
  157. datalab/data/icons/libre-gui-plugin.svg +1 -0
  158. datalab/data/icons/libre-gui-questions.svg +1 -0
  159. datalab/data/icons/libre-gui-settings.svg +1 -0
  160. datalab/data/icons/libre-gui-unlink.svg +1 -0
  161. datalab/data/icons/libre-tech-ram.svg +1 -0
  162. datalab/data/icons/libre-toolbox.svg +1 -0
  163. datalab/data/icons/logs.svg +1 -0
  164. datalab/data/icons/markers.svg +74 -0
  165. datalab/data/icons/menu.svg +13 -0
  166. datalab/data/icons/new_ima.svg +148 -0
  167. datalab/data/icons/new_sig.svg +123 -0
  168. datalab/data/icons/operations/abs.svg +116 -0
  169. datalab/data/icons/operations/arithmetic.svg +123 -0
  170. datalab/data/icons/operations/average.svg +124 -0
  171. datalab/data/icons/operations/complex_from_magnitude_phase.svg +116 -0
  172. datalab/data/icons/operations/complex_from_real_imag.svg +124 -0
  173. datalab/data/icons/operations/constant.svg +116 -0
  174. datalab/data/icons/operations/constant_add.svg +109 -0
  175. datalab/data/icons/operations/constant_divide.svg +109 -0
  176. datalab/data/icons/operations/constant_multiply.svg +109 -0
  177. datalab/data/icons/operations/constant_subtract.svg +109 -0
  178. datalab/data/icons/operations/convert_dtype.svg +117 -0
  179. datalab/data/icons/operations/convolution.svg +46 -0
  180. datalab/data/icons/operations/deconvolution.svg +57 -0
  181. datalab/data/icons/operations/derivative.svg +127 -0
  182. datalab/data/icons/operations/difference.svg +52 -0
  183. datalab/data/icons/operations/division.svg +139 -0
  184. datalab/data/icons/operations/exp.svg +116 -0
  185. datalab/data/icons/operations/flip_horizontally.svg +69 -0
  186. datalab/data/icons/operations/flip_vertically.svg +74 -0
  187. datalab/data/icons/operations/im.svg +124 -0
  188. datalab/data/icons/operations/integral.svg +50 -0
  189. datalab/data/icons/operations/inverse.svg +143 -0
  190. datalab/data/icons/operations/log10.svg +109 -0
  191. datalab/data/icons/operations/phase.svg +116 -0
  192. datalab/data/icons/operations/power.svg +118 -0
  193. datalab/data/icons/operations/product.svg +124 -0
  194. datalab/data/icons/operations/profile.svg +379 -0
  195. datalab/data/icons/operations/profile_average.svg +399 -0
  196. datalab/data/icons/operations/profile_radial.svg +261 -0
  197. datalab/data/icons/operations/profile_segment.svg +262 -0
  198. datalab/data/icons/operations/quadratic_difference.svg +84 -0
  199. datalab/data/icons/operations/re.svg +124 -0
  200. datalab/data/icons/operations/rotate_left.svg +72 -0
  201. datalab/data/icons/operations/rotate_right.svg +72 -0
  202. datalab/data/icons/operations/signals_to_image.svg +314 -0
  203. datalab/data/icons/operations/sqrt.svg +110 -0
  204. datalab/data/icons/operations/std.svg +124 -0
  205. datalab/data/icons/operations/sum.svg +102 -0
  206. datalab/data/icons/play_demo.svg +9 -0
  207. datalab/data/icons/processing/axis_transform.svg +62 -0
  208. datalab/data/icons/processing/bandpass.svg +79 -0
  209. datalab/data/icons/processing/bandstop.svg +71 -0
  210. datalab/data/icons/processing/binning.svg +126 -0
  211. datalab/data/icons/processing/clip.svg +119 -0
  212. datalab/data/icons/processing/detrending.svg +173 -0
  213. datalab/data/icons/processing/distribute_on_grid.svg +769 -0
  214. datalab/data/icons/processing/edge_detection.svg +46 -0
  215. datalab/data/icons/processing/erase.svg +1 -0
  216. datalab/data/icons/processing/exposure.svg +143 -0
  217. datalab/data/icons/processing/fourier.svg +104 -0
  218. datalab/data/icons/processing/highpass.svg +59 -0
  219. datalab/data/icons/processing/interpolation.svg +71 -0
  220. datalab/data/icons/processing/level_adjustment.svg +70 -0
  221. datalab/data/icons/processing/lowpass.svg +60 -0
  222. datalab/data/icons/processing/morphology.svg +49 -0
  223. datalab/data/icons/processing/noise_addition.svg +114 -0
  224. datalab/data/icons/processing/noise_reduction.svg +38 -0
  225. datalab/data/icons/processing/normalize.svg +84 -0
  226. datalab/data/icons/processing/offset_correction.svg +131 -0
  227. datalab/data/icons/processing/resampling1d.svg +101 -0
  228. datalab/data/icons/processing/resampling2d.svg +240 -0
  229. datalab/data/icons/processing/reset_positions.svg +185 -0
  230. datalab/data/icons/processing/resize.svg +9 -0
  231. datalab/data/icons/processing/reverse_signal_x.svg +171 -0
  232. datalab/data/icons/processing/stability.svg +11 -0
  233. datalab/data/icons/processing/swap_x_y.svg +65 -0
  234. datalab/data/icons/processing/thresholding.svg +63 -0
  235. datalab/data/icons/processing/windowing.svg +45 -0
  236. datalab/data/icons/properties.svg +26 -0
  237. datalab/data/icons/reset.svg +9 -0
  238. datalab/data/icons/restore.svg +40 -0
  239. datalab/data/icons/roi/roi.svg +76 -0
  240. datalab/data/icons/roi/roi_coordinate.svg +78 -0
  241. datalab/data/icons/roi/roi_copy.svg +112 -0
  242. datalab/data/icons/roi/roi_delete.svg +81 -0
  243. datalab/data/icons/roi/roi_export.svg +87 -0
  244. datalab/data/icons/roi/roi_graphical.svg +78 -0
  245. datalab/data/icons/roi/roi_grid.svg +67 -0
  246. datalab/data/icons/roi/roi_ima.svg +188 -0
  247. datalab/data/icons/roi/roi_import.svg +87 -0
  248. datalab/data/icons/roi/roi_new.svg +81 -0
  249. datalab/data/icons/roi/roi_new_circle.svg +95 -0
  250. datalab/data/icons/roi/roi_new_polygon.svg +110 -0
  251. datalab/data/icons/roi/roi_new_rectangle.svg +70 -0
  252. datalab/data/icons/roi/roi_paste.svg +98 -0
  253. datalab/data/icons/roi/roi_sig.svg +124 -0
  254. datalab/data/icons/shapes.svg +134 -0
  255. datalab/data/icons/signal.svg +103 -0
  256. datalab/data/icons/table.svg +85 -0
  257. datalab/data/icons/table_unavailable.svg +102 -0
  258. datalab/data/icons/to_signal.svg +124 -0
  259. datalab/data/icons/tour/next.svg +44 -0
  260. datalab/data/icons/tour/previous.svg +44 -0
  261. datalab/data/icons/tour/rewind.svg +51 -0
  262. datalab/data/icons/tour/stop.svg +47 -0
  263. datalab/data/icons/tour/tour.svg +16 -0
  264. datalab/data/icons/uncheck_all.svg +78 -0
  265. datalab/data/icons/view/curve_antialiasing.svg +50 -0
  266. datalab/data/icons/view/new_window.svg +98 -0
  267. datalab/data/icons/view/refresh-auto.svg +57 -0
  268. datalab/data/icons/view/refresh-manual.svg +51 -0
  269. datalab/data/icons/view/reset_curve_styles.svg +96 -0
  270. datalab/data/icons/view/show_first.svg +55 -0
  271. datalab/data/icons/view/show_titles.svg +46 -0
  272. datalab/data/icons/visualization.svg +51 -0
  273. datalab/data/logo/DataLab-Banner-150.png +0 -0
  274. datalab/data/logo/DataLab-Banner-200.png +0 -0
  275. datalab/data/logo/DataLab-Banner2-100.png +0 -0
  276. datalab/data/logo/DataLab-Splash.png +0 -0
  277. datalab/data/logo/DataLab-watermark.png +0 -0
  278. datalab/data/logo/DataLab.svg +83 -0
  279. datalab/data/tests/reordering_test.h5 +0 -0
  280. datalab/data/tutorials/fabry_perot/fabry-perot1.jpg +0 -0
  281. datalab/data/tutorials/fabry_perot/fabry-perot2.jpg +0 -0
  282. datalab/data/tutorials/laser_beam/TEM00_z_13.jpg +0 -0
  283. datalab/data/tutorials/laser_beam/TEM00_z_18.jpg +0 -0
  284. datalab/data/tutorials/laser_beam/TEM00_z_23.jpg +0 -0
  285. datalab/data/tutorials/laser_beam/TEM00_z_30.jpg +0 -0
  286. datalab/data/tutorials/laser_beam/TEM00_z_35.jpg +0 -0
  287. datalab/data/tutorials/laser_beam/TEM00_z_40.jpg +0 -0
  288. datalab/data/tutorials/laser_beam/TEM00_z_45.jpg +0 -0
  289. datalab/data/tutorials/laser_beam/TEM00_z_50.jpg +0 -0
  290. datalab/data/tutorials/laser_beam/TEM00_z_55.jpg +0 -0
  291. datalab/data/tutorials/laser_beam/TEM00_z_60.jpg +0 -0
  292. datalab/data/tutorials/laser_beam/TEM00_z_65.jpg +0 -0
  293. datalab/data/tutorials/laser_beam/TEM00_z_70.jpg +0 -0
  294. datalab/data/tutorials/laser_beam/TEM00_z_75.jpg +0 -0
  295. datalab/data/tutorials/laser_beam/TEM00_z_80.jpg +0 -0
  296. datalab/env.py +542 -0
  297. datalab/gui/__init__.py +89 -0
  298. datalab/gui/actionhandler.py +1701 -0
  299. datalab/gui/docks.py +473 -0
  300. datalab/gui/h5io.py +150 -0
  301. datalab/gui/macroeditor.py +310 -0
  302. datalab/gui/main.py +2081 -0
  303. datalab/gui/newobject.py +217 -0
  304. datalab/gui/objectview.py +766 -0
  305. datalab/gui/panel/__init__.py +48 -0
  306. datalab/gui/panel/base.py +3254 -0
  307. datalab/gui/panel/image.py +157 -0
  308. datalab/gui/panel/macro.py +607 -0
  309. datalab/gui/panel/signal.py +164 -0
  310. datalab/gui/plothandler.py +800 -0
  311. datalab/gui/processor/__init__.py +84 -0
  312. datalab/gui/processor/base.py +2456 -0
  313. datalab/gui/processor/catcher.py +75 -0
  314. datalab/gui/processor/image.py +1214 -0
  315. datalab/gui/processor/signal.py +755 -0
  316. datalab/gui/profiledialog.py +333 -0
  317. datalab/gui/roieditor.py +633 -0
  318. datalab/gui/roigrideditor.py +208 -0
  319. datalab/gui/settings.py +612 -0
  320. datalab/gui/tour.py +908 -0
  321. datalab/h5/__init__.py +12 -0
  322. datalab/h5/common.py +314 -0
  323. datalab/h5/generic.py +580 -0
  324. datalab/h5/native.py +39 -0
  325. datalab/h5/utils.py +95 -0
  326. datalab/objectmodel.py +640 -0
  327. datalab/plugins/_readme_.txt +9 -0
  328. datalab/plugins/datalab_imageformats.py +175 -0
  329. datalab/plugins/datalab_testdata.py +190 -0
  330. datalab/plugins.py +355 -0
  331. datalab/tests/__init__.py +199 -0
  332. datalab/tests/backbone/__init__.py +1 -0
  333. datalab/tests/backbone/config_unit_test.py +170 -0
  334. datalab/tests/backbone/config_versioning_unit_test.py +34 -0
  335. datalab/tests/backbone/dictlistserial_app_test.py +38 -0
  336. datalab/tests/backbone/errorcatcher_unit_test.py +69 -0
  337. datalab/tests/backbone/errormsgbox_unit_test.py +50 -0
  338. datalab/tests/backbone/execenv_unit.py +262 -0
  339. datalab/tests/backbone/loadtest_gdi.py +147 -0
  340. datalab/tests/backbone/long_callback.py +96 -0
  341. datalab/tests/backbone/main_app_test.py +137 -0
  342. datalab/tests/backbone/memory_leak.py +43 -0
  343. datalab/tests/backbone/procisolation1_unit.py +128 -0
  344. datalab/tests/backbone/procisolation2_unit.py +171 -0
  345. datalab/tests/backbone/procisolation_unit_test.py +22 -0
  346. datalab/tests/backbone/profiling_app.py +27 -0
  347. datalab/tests/backbone/strings_unit_test.py +65 -0
  348. datalab/tests/backbone/title_formatting_unit_test.py +82 -0
  349. datalab/tests/conftest.py +131 -0
  350. datalab/tests/features/__init__.py +1 -0
  351. datalab/tests/features/applauncher/__init__.py +1 -0
  352. datalab/tests/features/applauncher/launcher1_app_test.py +28 -0
  353. datalab/tests/features/applauncher/launcher2_app_test.py +30 -0
  354. datalab/tests/features/common/__init__.py +1 -0
  355. datalab/tests/features/common/add_metadata_app_test.py +134 -0
  356. datalab/tests/features/common/add_metadata_unit_test.py +267 -0
  357. datalab/tests/features/common/annotations_management_unit_test.py +152 -0
  358. datalab/tests/features/common/auto_analysis_recompute_unit_test.py +240 -0
  359. datalab/tests/features/common/createobject_unit_test.py +50 -0
  360. datalab/tests/features/common/geometry_results_app_test.py +135 -0
  361. datalab/tests/features/common/interactive_processing_test.py +1109 -0
  362. datalab/tests/features/common/io_app_test.py +75 -0
  363. datalab/tests/features/common/large_results_app_test.py +187 -0
  364. datalab/tests/features/common/metadata_all_patterns_test.py +103 -0
  365. datalab/tests/features/common/metadata_app_test.py +139 -0
  366. datalab/tests/features/common/metadata_io_unit_test.py +60 -0
  367. datalab/tests/features/common/misc_app_test.py +236 -0
  368. datalab/tests/features/common/multiple_geometry_results_unit_test.py +122 -0
  369. datalab/tests/features/common/multiple_table_results_unit_test.py +64 -0
  370. datalab/tests/features/common/operation_modes_app_test.py +392 -0
  371. datalab/tests/features/common/plot_results_app_test.py +278 -0
  372. datalab/tests/features/common/reorder_app_test.py +75 -0
  373. datalab/tests/features/common/result_deletion_unit_test.py +96 -0
  374. datalab/tests/features/common/result_merged_label_unit_test.py +154 -0
  375. datalab/tests/features/common/result_shape_settings_unit_test.py +223 -0
  376. datalab/tests/features/common/roi_plotitem_unit_test.py +64 -0
  377. datalab/tests/features/common/roieditor_unit_test.py +102 -0
  378. datalab/tests/features/common/save_to_dir_app_test.py +163 -0
  379. datalab/tests/features/common/save_to_dir_unit_test.py +474 -0
  380. datalab/tests/features/common/stat_app_test.py +40 -0
  381. datalab/tests/features/common/stats_tools_unit_test.py +77 -0
  382. datalab/tests/features/common/table_results_app_test.py +52 -0
  383. datalab/tests/features/common/textimport_unit_test.py +131 -0
  384. datalab/tests/features/common/uuid_preservation_test.py +281 -0
  385. datalab/tests/features/common/worker_unit_test.py +402 -0
  386. datalab/tests/features/control/__init__.py +1 -0
  387. datalab/tests/features/control/connect_dialog.py +28 -0
  388. datalab/tests/features/control/embedded1_unit_test.py +304 -0
  389. datalab/tests/features/control/embedded2_unit_test.py +52 -0
  390. datalab/tests/features/control/remoteclient_app_test.py +219 -0
  391. datalab/tests/features/control/remoteclient_unit.py +75 -0
  392. datalab/tests/features/control/simpleclient_unit_test.py +321 -0
  393. datalab/tests/features/hdf5/__init__.py +1 -0
  394. datalab/tests/features/hdf5/h5browser1_unit_test.py +31 -0
  395. datalab/tests/features/hdf5/h5browser2_unit.py +55 -0
  396. datalab/tests/features/hdf5/h5browser_app_test.py +77 -0
  397. datalab/tests/features/hdf5/h5import_app_test.py +25 -0
  398. datalab/tests/features/hdf5/h5importer_app_test.py +34 -0
  399. datalab/tests/features/image/__init__.py +1 -0
  400. datalab/tests/features/image/annotations_app_test.py +28 -0
  401. datalab/tests/features/image/annotations_unit_test.py +80 -0
  402. datalab/tests/features/image/average_app_test.py +46 -0
  403. datalab/tests/features/image/background_dialog_test.py +70 -0
  404. datalab/tests/features/image/blobs_app_test.py +50 -0
  405. datalab/tests/features/image/contour_app_test.py +42 -0
  406. datalab/tests/features/image/contour_fabryperot_app_test.py +51 -0
  407. datalab/tests/features/image/denoise_app_test.py +31 -0
  408. datalab/tests/features/image/distribute_on_grid_app_test.py +95 -0
  409. datalab/tests/features/image/edges_app_test.py +31 -0
  410. datalab/tests/features/image/erase_app_test.py +21 -0
  411. datalab/tests/features/image/fft2d_app_test.py +27 -0
  412. datalab/tests/features/image/flatfield_app_test.py +40 -0
  413. datalab/tests/features/image/geometry_transform_unit_test.py +396 -0
  414. datalab/tests/features/image/imagetools_app_test.py +51 -0
  415. datalab/tests/features/image/imagetools_unit_test.py +27 -0
  416. datalab/tests/features/image/load_app_test.py +73 -0
  417. datalab/tests/features/image/morph_app_test.py +32 -0
  418. datalab/tests/features/image/offsetcorrection_app_test.py +30 -0
  419. datalab/tests/features/image/peak2d_app_test.py +53 -0
  420. datalab/tests/features/image/profile_app_test.py +73 -0
  421. datalab/tests/features/image/profile_dialog_test.py +56 -0
  422. datalab/tests/features/image/roi_app_test.py +98 -0
  423. datalab/tests/features/image/roi_circ_app_test.py +62 -0
  424. datalab/tests/features/image/roi_manipulation_app_test.py +268 -0
  425. datalab/tests/features/image/roigrid_unit_test.py +60 -0
  426. datalab/tests/features/image/side_by_side_app_test.py +52 -0
  427. datalab/tests/features/macro/__init__.py +1 -0
  428. datalab/tests/features/macro/macro_app_test.py +28 -0
  429. datalab/tests/features/macro/macroeditor_unit_test.py +102 -0
  430. datalab/tests/features/signal/__init__.py +1 -0
  431. datalab/tests/features/signal/baseline_dialog_test.py +53 -0
  432. datalab/tests/features/signal/deltax_dialog_unit_test.py +34 -0
  433. datalab/tests/features/signal/fft1d_app_test.py +26 -0
  434. datalab/tests/features/signal/filter_app_test.py +44 -0
  435. datalab/tests/features/signal/fitdialog_unit_test.py +50 -0
  436. datalab/tests/features/signal/interpolation_app_test.py +110 -0
  437. datalab/tests/features/signal/loadbigsignal_app_test.py +80 -0
  438. datalab/tests/features/signal/multiple_rois_unit_test.py +132 -0
  439. datalab/tests/features/signal/pulse_features_app_test.py +118 -0
  440. datalab/tests/features/signal/pulse_features_roi_app_test.py +55 -0
  441. datalab/tests/features/signal/roi_app_test.py +78 -0
  442. datalab/tests/features/signal/roi_manipulation_app_test.py +261 -0
  443. datalab/tests/features/signal/select_xy_cursor_unit_test.py +46 -0
  444. datalab/tests/features/signal/signalpeakdetection_dialog_test.py +33 -0
  445. datalab/tests/features/signal/signals_to_image_app_test.py +98 -0
  446. datalab/tests/features/signal/xarray_compat_app_test.py +128 -0
  447. datalab/tests/features/tour_unit_test.py +22 -0
  448. datalab/tests/features/utilities/__init__.py +1 -0
  449. datalab/tests/features/utilities/installconf_unit_test.py +21 -0
  450. datalab/tests/features/utilities/logview_app_test.py +21 -0
  451. datalab/tests/features/utilities/logview_error.py +24 -0
  452. datalab/tests/features/utilities/logview_unit_test.py +21 -0
  453. datalab/tests/features/utilities/memstatus_app_test.py +42 -0
  454. datalab/tests/features/utilities/settings_unit_test.py +88 -0
  455. datalab/tests/scenarios/__init__.py +1 -0
  456. datalab/tests/scenarios/beautiful_app.py +121 -0
  457. datalab/tests/scenarios/common.py +463 -0
  458. datalab/tests/scenarios/demo.py +212 -0
  459. datalab/tests/scenarios/example_app_test.py +47 -0
  460. datalab/tests/scenarios/scenario_h5_app_test.py +75 -0
  461. datalab/tests/scenarios/scenario_ima1_app_test.py +34 -0
  462. datalab/tests/scenarios/scenario_ima2_app_test.py +34 -0
  463. datalab/tests/scenarios/scenario_mac_app_test.py +58 -0
  464. datalab/tests/scenarios/scenario_sig1_app_test.py +36 -0
  465. datalab/tests/scenarios/scenario_sig2_app_test.py +35 -0
  466. datalab/utils/__init__.py +1 -0
  467. datalab/utils/conf.py +304 -0
  468. datalab/utils/dephash.py +105 -0
  469. datalab/utils/qthelpers.py +633 -0
  470. datalab/utils/strings.py +34 -0
  471. datalab/utils/tests.py +0 -0
  472. datalab/widgets/__init__.py +1 -0
  473. datalab/widgets/connection.py +138 -0
  474. datalab/widgets/filedialog.py +91 -0
  475. datalab/widgets/fileviewer.py +84 -0
  476. datalab/widgets/fitdialog.py +788 -0
  477. datalab/widgets/h5browser.py +1048 -0
  478. datalab/widgets/imagebackground.py +111 -0
  479. datalab/widgets/instconfviewer.py +175 -0
  480. datalab/widgets/logviewer.py +80 -0
  481. datalab/widgets/signalbaseline.py +90 -0
  482. datalab/widgets/signalcursor.py +208 -0
  483. datalab/widgets/signaldeltax.py +151 -0
  484. datalab/widgets/signalpeak.py +199 -0
  485. datalab/widgets/status.py +249 -0
  486. datalab/widgets/textimport.py +786 -0
  487. datalab/widgets/warningerror.py +223 -0
  488. datalab/widgets/wizard.py +286 -0
  489. datalab_platform-1.0.0.dist-info/METADATA +121 -0
  490. datalab_platform-1.0.0.dist-info/RECORD +494 -0
  491. datalab_platform-0.0.1.dev0.dist-info/METADATA +0 -67
  492. datalab_platform-0.0.1.dev0.dist-info/RECORD +0 -7
  493. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/WHEEL +0 -0
  494. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/entry_points.txt +0 -0
  495. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/licenses/LICENSE +0 -0
  496. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/top_level.txt +0 -0
datalab/objectmodel.py ADDED
@@ -0,0 +1,640 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ Object model
5
+ ============
6
+
7
+ The :mod:`datalab.gui.objectmodel` module defines the object data model used by the
8
+ GUI to store signals, images and groups.
9
+
10
+ The model is based on a hierarchical tree of objects, with two levels:
11
+
12
+ - The top level is a list of groups (`ObjectGroup` instances)
13
+ - The second level is a list of objects (`SignalObj` or `ImageObj` instances)
14
+
15
+ The model is implemented by the `ObjectModel` class.
16
+
17
+ Object group
18
+ ------------
19
+
20
+ The `ObjectGroup` class represents a group of objects. It is a container for
21
+ `SignalObj` and `ImageObj` instances.
22
+
23
+ .. autoclass:: ObjectGroup
24
+
25
+ Object model
26
+ ------------
27
+
28
+ The `ObjectModel` class is a container for ObjectGroup instances, as well as
29
+ a container for `SignalObj` and `ImageObj` instances.
30
+
31
+ .. autoclass:: ObjectModel
32
+ """
33
+
34
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
35
+
36
+ from __future__ import annotations
37
+
38
+ import re
39
+ from collections.abc import Iterator
40
+ from typing import Callable
41
+ from uuid import uuid4
42
+
43
+ from sigima import ImageObj, SignalObj
44
+
45
+
46
+ def get_number(obj: SignalObj | ImageObj | ObjectGroup) -> int:
47
+ """Get object number from metadata"""
48
+ if isinstance(obj, ObjectGroup):
49
+ return obj.number
50
+ number = obj.get_metadata_option("number")
51
+ assert isinstance(number, int)
52
+ return number
53
+
54
+
55
+ def set_number(obj: SignalObj | ImageObj | ObjectGroup, number: int) -> None:
56
+ """Set object number in metadata"""
57
+ assert isinstance(number, int)
58
+ if isinstance(obj, ObjectGroup):
59
+ obj.number = number
60
+ else:
61
+ obj.set_metadata_option("number", number)
62
+
63
+
64
+ def get_uuid(obj: SignalObj | ImageObj | ObjectGroup) -> str:
65
+ """Get object UUID"""
66
+ if isinstance(obj, ObjectGroup):
67
+ return obj.uuid
68
+ return obj.get_metadata_option("uuid", str(uuid4()))
69
+
70
+
71
+ def set_uuid(obj: SignalObj | ImageObj | ObjectGroup) -> None:
72
+ """Set object UUID"""
73
+ if isinstance(obj, ObjectGroup):
74
+ obj.uuid = str(uuid4())
75
+ else:
76
+ obj.set_metadata_option("uuid", str(uuid4()))
77
+
78
+
79
+ def get_short_id(obj: SignalObj | ImageObj | ObjectGroup) -> str:
80
+ """Short object ID"""
81
+ if isinstance(obj, ObjectGroup):
82
+ return f"{obj.prefix}{get_number(obj):03d}"
83
+ return f"{obj.PREFIX}{get_number(obj):03d}"
84
+
85
+
86
+ def patch_title_with_ids(
87
+ dst_obj: SignalObj | ImageObj,
88
+ src_objs: list[SignalObj] | list[ImageObj],
89
+ id_func: Callable,
90
+ ) -> None:
91
+ """Patch object title with short IDs of source objects
92
+
93
+ Destination object's title has been set to a string containing placeholders
94
+ (e.g. "integral({0})"), by `sigima` computation function using a generic mecanism
95
+ (see `sigima.base.dst_1_to_1` for example).
96
+
97
+ Args:
98
+ dst_obj: destination object
99
+ src_objs: list of source objects
100
+ id_func: function to get ID from object (e.g. `short_id` or `get_uuid`)
101
+ """
102
+ ids = [id_func(obj) for obj in src_objs]
103
+ title = dst_obj.title
104
+ assert isinstance(title, str), "Title must be a string"
105
+ try:
106
+ dst_obj.title = title.format(*ids)
107
+ except IndexError as exc:
108
+ raise ValueError(
109
+ f"Not enough source objects to fill title placeholders: {title}"
110
+ ) from exc
111
+
112
+
113
+ class ObjectGroup:
114
+ """Represents a DataLab object group
115
+
116
+ Args:
117
+ title: group title
118
+ model: object model
119
+ prefix: prefix for short ID ("gs" for signal groups, "gi" for image groups)
120
+ """
121
+
122
+ def __init__(self, title: str, model: ObjectModel, prefix: str) -> None:
123
+ self.model = model
124
+ self.prefix = prefix # Instance-specific prefix
125
+ self.uuid: str = str(uuid4()) # Group uuid
126
+ self.number: int = 0 # Group number (used for short ID)
127
+ self.__objects: list[str] = [] # list of object uuids
128
+ self.__title: str = title
129
+
130
+ @property
131
+ def title(self) -> str:
132
+ """Return group title"""
133
+ return self.__title
134
+
135
+ @title.setter
136
+ def title(self, title: str) -> None:
137
+ """Set group title"""
138
+ self.__title = title
139
+
140
+ def __iter__(self) -> Iterator[SignalObj | ImageObj]:
141
+ """Iterate over objects in group"""
142
+ return iter(self.model.get_objects(self.__objects))
143
+
144
+ def __len__(self) -> int:
145
+ """Return number of objects in group"""
146
+ return len(self.__objects)
147
+
148
+ def __getitem__(
149
+ self, index: int | slice
150
+ ) -> SignalObj | ImageObj | list[SignalObj | ImageObj]:
151
+ """Return object at index"""
152
+ if isinstance(index, slice):
153
+ return [
154
+ self.model[self.__objects[i]]
155
+ for i in range(*index.indices(len(self)))
156
+ if i < len(self)
157
+ ]
158
+ return self.model[self.__objects[index]]
159
+
160
+ def __contains__(self, obj: SignalObj | ImageObj) -> bool:
161
+ """Return True if obj is in group"""
162
+ return get_uuid(obj) in self.__objects
163
+
164
+ def append(self, obj: SignalObj | ImageObj) -> None:
165
+ """Append object to group"""
166
+ self.__objects.append(get_uuid(obj))
167
+
168
+ def insert(self, index: int, obj: SignalObj | ImageObj) -> None:
169
+ """Insert object at index"""
170
+ self.model.replace_short_ids_by_uuids_in_titles([obj])
171
+ self.__objects.insert(index, get_uuid(obj))
172
+ self.model.reset_short_ids()
173
+ self.model.replace_uuids_by_short_ids_in_titles()
174
+
175
+ def remove(self, obj: SignalObj | ImageObj) -> None:
176
+ """Remove object from group"""
177
+ self.model.replace_short_ids_by_uuids_in_titles()
178
+ self.__objects.remove(get_uuid(obj))
179
+ self.model.reset_short_ids()
180
+ self.model.replace_uuids_by_short_ids_in_titles()
181
+
182
+ def clear(self) -> None:
183
+ """Clear group"""
184
+ self.__objects.clear()
185
+
186
+ def get_objects(self) -> list[SignalObj | ImageObj]:
187
+ """Return objects in group"""
188
+ return self.model.get_objects(self.__objects)
189
+
190
+ def get_object_ids(self) -> list[str]:
191
+ """Return object ids in group"""
192
+ return self.__objects
193
+
194
+
195
+ class ObjectModel:
196
+ """Represents a DataLab object model (groups of signals/images)"""
197
+
198
+ def __init__(self, group_prefix: str) -> None:
199
+ """Initialize object model
200
+
201
+ Args:
202
+ group_prefix: prefix for group short IDs ("gs" for signal, "gi" for image)
203
+ """
204
+ self._group_prefix = group_prefix
205
+ # dict of objects, key is object uuid:
206
+ self._objects: dict[str, SignalObj | ImageObj] = {}
207
+ # list of groups:
208
+ self._groups: list[ObjectGroup] = []
209
+
210
+ def reset_short_ids(self) -> None:
211
+ """Reset short IDs (used for object numbering)
212
+
213
+ This method is called when an object was removed from a group."""
214
+ gnb = onb = 1
215
+ for group in self._groups:
216
+ set_number(group, gnb)
217
+ gnb += 1
218
+ for obj in group:
219
+ set_number(obj, onb)
220
+ onb += 1
221
+
222
+ def __len__(self) -> int:
223
+ """Return number of objects"""
224
+ return len(self._objects)
225
+
226
+ def __getitem__(self, uuid: str) -> SignalObj | ImageObj:
227
+ """Return object with uuid"""
228
+ return self._objects[uuid]
229
+
230
+ def __iter__(self) -> Iterator[SignalObj | ImageObj]:
231
+ """Iterate over objects"""
232
+ return iter(self._objects.values())
233
+
234
+ def __repr__(self) -> str:
235
+ """Return object representation"""
236
+ return repr(self._objects)
237
+
238
+ def __str__(self) -> str:
239
+ """Return object string representation"""
240
+ return str(self._objects)
241
+
242
+ def __bool__(self) -> bool:
243
+ """Return True if model is not empty"""
244
+ return bool(self._objects)
245
+
246
+ def __contains__(self, obj: SignalObj | ImageObj) -> bool:
247
+ """Return True if obj is in model"""
248
+ return get_uuid(obj) in self._objects
249
+
250
+ def has_uuid(self, uuid: str) -> bool:
251
+ """Check if an object with the given UUID exists in the model
252
+
253
+ Args:
254
+ uuid: UUID string to check
255
+
256
+ Returns:
257
+ True if an object with this UUID exists, False otherwise
258
+ """
259
+ return uuid in self._objects
260
+
261
+ def clear(self) -> None:
262
+ """Clear model"""
263
+ self._objects.clear()
264
+ self._groups.clear()
265
+
266
+ def get_all_objects(
267
+ self, flatten: bool = True
268
+ ) -> list[SignalObj | ImageObj] | list[list[SignalObj | ImageObj]]:
269
+ """Return all objects, in order of appearance in groups
270
+
271
+ Args:
272
+ flatten: if True, return a flat list of objects, otherwise return
273
+ a list of lists (one list per group)
274
+
275
+ Returns:
276
+ List of objects in order of appearance in groups.
277
+ """
278
+ objects = []
279
+ for group in self._groups:
280
+ if flatten:
281
+ objects.extend(group.get_objects())
282
+ else:
283
+ objects.append(group.get_objects())
284
+ return objects
285
+
286
+ def get_object_or_group(self, uuid: str) -> SignalObj | ImageObj | ObjectGroup:
287
+ """Return object or group with uuid"""
288
+ if uuid in self._objects:
289
+ return self._objects[uuid]
290
+ for group in self._groups:
291
+ if get_uuid(group) == uuid:
292
+ return group
293
+ raise KeyError(f"Object or group with uuid {uuid} not found")
294
+
295
+ def get_group(self, uuid: str) -> ObjectGroup:
296
+ """Return group with uuid"""
297
+ for group in self._groups:
298
+ if get_uuid(group) == uuid:
299
+ return group
300
+ raise KeyError(f"Group with uuid {uuid} not found")
301
+
302
+ def get_number(self, obj_or_group: SignalObj | ImageObj | ObjectGroup) -> int:
303
+ """Return number of object or group"""
304
+ if isinstance(obj_or_group, ObjectGroup):
305
+ try:
306
+ return self._groups.index(obj_or_group) + 1
307
+ except ValueError as exc:
308
+ raise KeyError(
309
+ f"Group {get_uuid(obj_or_group)} not found in model"
310
+ ) from exc
311
+ if isinstance(obj_or_group, (SignalObj, ImageObj)):
312
+ objs = self.get_all_objects()
313
+ try:
314
+ return objs.index(obj_or_group) + 1
315
+ except ValueError as exc:
316
+ raise KeyError(
317
+ f"Object {get_uuid(obj_or_group)} not found in model"
318
+ ) from exc
319
+ raise KeyError(f"Object or group {get_uuid(obj_or_group)} not found in model")
320
+
321
+ def get_group_from_number(self, number: int) -> ObjectGroup:
322
+ """Return group from its number.
323
+
324
+ Args:
325
+ number: group number (starts with 1)
326
+
327
+ Returns:
328
+ Group
329
+
330
+ Raises:
331
+ IndexError: if group with number not found
332
+ """
333
+ if number < 1:
334
+ raise IndexError(f"Group number {number} is out of range (must be >= 1)")
335
+ if number > len(self._groups):
336
+ raise IndexError(
337
+ f"Group number {number} is out of range (max is {len(self._groups)})"
338
+ )
339
+ return self._groups[number - 1]
340
+
341
+ def get_group_from_title(self, title: str) -> ObjectGroup:
342
+ """Return group from its title.
343
+
344
+ Args:
345
+ title: group title
346
+
347
+ Returns:
348
+ Group
349
+
350
+ Raises:
351
+ KeyError: if group with title not found
352
+ """
353
+ for group in self._groups:
354
+ if group.title == title:
355
+ return group
356
+ raise KeyError(f"Group with title '{title}' not found")
357
+
358
+ def get_group_from_object(self, obj: SignalObj | ImageObj) -> ObjectGroup:
359
+ """Return group containing object
360
+
361
+ Args:
362
+ obj: object to find group for
363
+
364
+ Returns:
365
+ Group
366
+
367
+ Raises:
368
+ KeyError: if object not found in any group
369
+ """
370
+ for group in self._groups:
371
+ if obj in group:
372
+ return group
373
+ raise KeyError(f"Object with uuid '{get_uuid(obj)}' not found in any group")
374
+
375
+ def get_groups(self, uuids: list[str] | None = None) -> list[ObjectGroup]:
376
+ """Return groups"""
377
+ if uuids is None:
378
+ return self._groups
379
+ return [group for group in self._groups if get_uuid(group) in uuids]
380
+
381
+ def add_group(self, title: str) -> ObjectGroup:
382
+ """Add group to model
383
+
384
+ Args:
385
+ title: group title
386
+
387
+ Returns:
388
+ Created group object
389
+ """
390
+ group = ObjectGroup(title, self, self._group_prefix)
391
+ self._groups.append(group)
392
+ self.reset_short_ids()
393
+ return group
394
+
395
+ def get_object_group_id(self, obj: SignalObj | ImageObj) -> str | None:
396
+ """Return group id of object
397
+
398
+ Args:
399
+ obj: object to get group id from
400
+
401
+ Returns:
402
+ group id or None if object is not in any group
403
+ """
404
+ try:
405
+ return get_uuid(self.get_group_from_object(obj))
406
+ except KeyError:
407
+ return None
408
+
409
+ def get_group_object_ids(self, group_id: str) -> list[str]:
410
+ """Return object ids in group"""
411
+ for group in self._groups:
412
+ if get_uuid(group) == group_id:
413
+ return group.get_object_ids()
414
+ raise KeyError(f"Group with uuid '{group_id}' not found")
415
+
416
+ def remove_group(self, group: ObjectGroup) -> None:
417
+ """Remove group from model"""
418
+ self.replace_short_ids_by_uuids_in_titles()
419
+ self._groups.remove(group)
420
+ for obj in group:
421
+ remove_obj = True
422
+ for other_group in self._groups:
423
+ if obj in other_group:
424
+ remove_obj = False
425
+ break
426
+ if remove_obj:
427
+ del self._objects[get_uuid(obj)]
428
+ self.reset_short_ids()
429
+ self.replace_uuids_by_short_ids_in_titles()
430
+
431
+ def add_object(self, obj: SignalObj | ImageObj, group_id: str) -> None:
432
+ """Add object to model"""
433
+ self.replace_short_ids_by_uuids_in_titles([obj])
434
+ self._objects[get_uuid(obj)] = obj
435
+ onb = 0
436
+ for group in self._groups:
437
+ onb += len(group)
438
+ if get_uuid(group) == group_id:
439
+ set_number(obj, onb + 1)
440
+ group.append(obj)
441
+ break
442
+ else:
443
+ raise KeyError(f"Group with uuid '{group_id}' not found")
444
+ self.reset_short_ids()
445
+ self.replace_uuids_by_short_ids_in_titles()
446
+
447
+ def remove_object(self, obj: SignalObj | ImageObj) -> None:
448
+ """Remove object from model"""
449
+ for group in self._groups:
450
+ if obj in group:
451
+ group.remove(obj)
452
+ del self._objects[get_uuid(obj)]
453
+ self.reset_short_ids()
454
+
455
+ def get_object_from_number(self, number: int) -> SignalObj | ImageObj:
456
+ """Return object from its number.
457
+
458
+ Args:
459
+ number: object number (unique in model)
460
+
461
+ Returns:
462
+ Object
463
+
464
+ Raises:
465
+ IndexError: if object with number not found
466
+ """
467
+ if number < 1:
468
+ raise IndexError(f"Object number {number} is out of range (must be >= 1)")
469
+ objs = self.get_all_objects()
470
+ if number > len(objs):
471
+ raise IndexError(
472
+ f"Object number {number} is out of range (max is {len(objs)})"
473
+ )
474
+ return objs[number - 1]
475
+
476
+ def get_objects(self, uuids: list[str]) -> list[SignalObj | ImageObj]:
477
+ """Return objects with uuids"""
478
+ return [self._objects[uuid] for uuid in uuids]
479
+
480
+ def get_object_ids(self, flatten: bool = True) -> list[str] | list[list[str]]:
481
+ """Return object ids, in order of appearance in groups
482
+
483
+ Args:
484
+ flatten: if True, return a flat list of object ids, otherwise return
485
+ a list of lists (one list per group)
486
+
487
+ Returns:
488
+ List of object ids in order of appearance in groups.
489
+ """
490
+ ids = []
491
+ for group in self._groups:
492
+ if flatten:
493
+ ids.extend(group.get_object_ids())
494
+ else:
495
+ ids.append(group.get_object_ids())
496
+ return ids
497
+
498
+ def get_group_titles_with_object_info(
499
+ self,
500
+ ) -> tuple[list[str], list[list[str]], list[list[str]]]:
501
+ """Return groups titles and lists of inner objects uuids and titles.
502
+
503
+ Returns:
504
+ Tuple: groups titles, lists of inner objects uuids and titles
505
+ """
506
+ grp_titles = []
507
+ obj_uuids = []
508
+ obj_titles = []
509
+ for group in self._groups:
510
+ grp_titles.append(group.title)
511
+ obj_uuids.append(group.get_object_ids())
512
+ obj_titles.append([obj.title for obj in group])
513
+ return grp_titles, obj_uuids, obj_titles
514
+
515
+ def get_object_titles(self, flatten: bool = True) -> list[str] | list[list[str]]:
516
+ """Return object titles, in order of appearance in groups
517
+
518
+ Args:
519
+ flatten: if True, return a flat list of object titles, otherwise return
520
+ a list of lists (one list per group)
521
+
522
+ Returns:
523
+ List of object titles in order of appearance in groups.
524
+ """
525
+ if flatten:
526
+ return [obj.title for obj in self.get_all_objects()]
527
+ return [[obj.title for obj in group] for group in self._groups]
528
+
529
+ def get_object_from_title(self, title: str) -> SignalObj | ImageObj:
530
+ """Return object with title.
531
+
532
+ Args:
533
+ title: object title
534
+
535
+ Returns:
536
+ object with title
537
+
538
+ Raises:
539
+ KeyError: if object with title not found
540
+ """
541
+ for obj in self._objects.values():
542
+ if obj.title == title:
543
+ return obj
544
+ raise KeyError(f"Object with title '{title}' not found")
545
+
546
+ def __get_group_object_mapping_to_shortid(self) -> dict[str, str]:
547
+ """Return dictionary mapping group/object uuids to their short ID"""
548
+ mapping = {}
549
+ for group in self._groups:
550
+ mapping[get_uuid(group)] = get_short_id(group)
551
+ for obj in group:
552
+ mapping[get_uuid(obj)] = get_short_id(obj)
553
+ return mapping
554
+
555
+ def replace_short_ids_by_uuids_in_titles(
556
+ self, other_objects: tuple[SignalObj | ImageObj] | None = None
557
+ ) -> None:
558
+ """Replace short IDs by uuids in titles
559
+
560
+ Args:
561
+ other_objects: tuple of other objects to consider for short ID replacement
562
+
563
+ .. note::
564
+
565
+ This method is called before reorganizing groups or objects. It replaces the
566
+ short IDs in titles by the uuids. This is needed because the short IDs are
567
+ used to reflect in the title the operation performed on the object/group,
568
+ e.g. "fft(s001)" or "g001 + g002". But when reorganizing groups or objects,
569
+ the short IDs may change, so we need to replace them by the uuids, which are
570
+ stable. Once the reorganization is done, we will replace the uuids by the
571
+ new short IDs thanks to the `__replace_uuids_by_short_ids_in_titles` method.
572
+ """
573
+ mapping = self.__get_group_object_mapping_to_shortid()
574
+ objs = self._objects.values()
575
+ if other_objects is not None:
576
+ objs = list(objs) + list(other_objects)
577
+ for obj in objs:
578
+ for obj_uuid, short_id in mapping.items():
579
+ obj.title = obj.title.replace(short_id, obj_uuid)
580
+ for group in self._groups:
581
+ for grp_uuid, short_id in mapping.items():
582
+ group.title = group.title.replace(short_id, grp_uuid)
583
+
584
+ def replace_uuids_by_short_ids_in_titles(self) -> None:
585
+ """Replace uuids by short IDs in titles
586
+
587
+ .. note::
588
+
589
+ This method is called after reorganizing groups or objects. It replaces
590
+ the uuids in titles by the short IDs.
591
+ """
592
+ mapping = self.__get_group_object_mapping_to_shortid()
593
+ for obj in self._objects.values():
594
+ for obj_uuid, short_id in mapping.items():
595
+ obj.title = obj.title.replace(obj_uuid, short_id)
596
+ for group in self._groups:
597
+ for grp_uuid, short_id in mapping.items():
598
+ group.title = group.title.replace(grp_uuid, short_id)
599
+ # Replace remaining UUIDs with f"{obj.PREFIX}xxx"
600
+ # (this may happen if groups or objects were removed in the meantime):
601
+ pattern = re.compile(r"\b[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\b")
602
+ for obj in self._objects.values():
603
+ obj.title = pattern.sub(f"{obj.PREFIX}xxx", obj.title)
604
+ for group in self._groups:
605
+ for obj in group:
606
+ obj.title = pattern.sub(f"{obj.PREFIX}xxx", obj.title)
607
+
608
+ def reorder_groups(self, group_ids: list[str]) -> None:
609
+ """Reorder groups.
610
+
611
+ Args:
612
+ group_ids: list of group uuids
613
+ """
614
+ # Replace short IDs by uuids in titles:
615
+ self.replace_short_ids_by_uuids_in_titles()
616
+ # Reordering groups:
617
+ self._groups = [self.get_group(group_id) for group_id in group_ids]
618
+ # Reset short IDs:
619
+ self.reset_short_ids()
620
+ # Replace uuids by short IDs in titles:
621
+ self.replace_uuids_by_short_ids_in_titles()
622
+
623
+ def reorder_objects(self, obj_ids: dict[str, list[str]]) -> None:
624
+ """Reorder objects in groups.
625
+
626
+ Args:
627
+ obj_ids: dict of group uuids and list of object uuids
628
+ """
629
+ # Replace short IDs by uuids in titles:
630
+ self.replace_short_ids_by_uuids_in_titles()
631
+ # Reordering objects in groups:
632
+ for group_id, obj_uuids in obj_ids.items():
633
+ group = self.get_group(group_id)
634
+ group.clear()
635
+ for obj_uuid in obj_uuids:
636
+ group.append(self._objects[obj_uuid])
637
+ # Reset short IDs:
638
+ self.reset_short_ids()
639
+ # Replace uuids by short IDs in titles:
640
+ self.replace_uuids_by_short_ids_in_titles()
@@ -0,0 +1,9 @@
1
+ Warning:
2
+ --------
3
+
4
+ This folder is not a Python package.
5
+ That is intentional, because it is not meant to be imported.
6
+ It is distributed as data folder from a packaging point of view.
7
+
8
+ It contains a collection of plugins that are automatically loaded by the
9
+ plugin system.