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
@@ -0,0 +1,800 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ Plot handler
5
+ ============
6
+
7
+ The :mod:`datalab.gui.plothandler` module provides plot handlers for signal
8
+ and image panels, that is, classes handling `PlotPy` plot items for representing
9
+ signals and images.
10
+
11
+ Signal plot handler
12
+ -------------------
13
+
14
+ .. autoclass:: SignalPlotHandler
15
+ :members:
16
+ :inherited-members:
17
+
18
+ Image plot handler
19
+ ------------------
20
+
21
+ .. autoclass:: ImagePlotHandler
22
+ :members:
23
+ :inherited-members:
24
+ """
25
+
26
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
27
+
28
+ from __future__ import annotations
29
+
30
+ import hashlib
31
+ from collections.abc import Iterator
32
+ from typing import TYPE_CHECKING, Generic
33
+ from weakref import WeakKeyDictionary
34
+
35
+ import numpy as np
36
+ from plotpy.constants import PlotType
37
+ from plotpy.items import CurveItem, GridItem, LegendBoxItem, MaskedXYImageItem
38
+ from plotpy.plot import PlotOptions
39
+ from qtpy import QtWidgets as QW
40
+ from qwt import QwtScaleDraw
41
+ from sigima.objects import ImageObj, SignalObj, TypeObj
42
+
43
+ from datalab.adapters_metadata import GeometryAdapter, TableAdapter
44
+ from datalab.adapters_plotpy import TypePlotItem, create_adapter_from_object
45
+ from datalab.adapters_plotpy.objects.scalar import MergedResultPlotPyAdapter
46
+ from datalab.config import Conf, _
47
+ from datalab.objectmodel import get_uuid
48
+ from datalab.utils.qthelpers import block_signals, create_progress_bar
49
+
50
+ if TYPE_CHECKING:
51
+ from plotpy.items import LabelItem
52
+ from plotpy.plot import PlotWidget
53
+
54
+ from datalab.gui.panel.base import BaseDataPanel
55
+
56
+
57
+ def calc_data_hash(obj: SignalObj | ImageObj) -> str:
58
+ """Calculate a hash for a SignalObj | ImageObj object's data"""
59
+ return hashlib.sha1(np.ascontiguousarray(obj.data)).hexdigest()
60
+
61
+
62
+ class BasePlotHandler(Generic[TypeObj, TypePlotItem]): # type: ignore
63
+ """Object handling plot items associated to objects (signals/images)"""
64
+
65
+ PLOT_TYPE: PlotType | None = None # Replaced in subclasses
66
+
67
+ def __init__(
68
+ self,
69
+ panel: BaseDataPanel,
70
+ plotwidget: PlotWidget,
71
+ ) -> None:
72
+ self.panel = panel
73
+ self.plotwidget = plotwidget
74
+ self.plot = plotwidget.get_plot()
75
+
76
+ # Plot items: key = object uuid, value = plot item
77
+ self.__plotitems: dict[str, TypePlotItem] = {}
78
+
79
+ self.__shapeitems = []
80
+ self.__cached_hashes: WeakKeyDictionary[TypeObj, list[int]] = (
81
+ WeakKeyDictionary()
82
+ )
83
+ self.__auto_refresh = False
84
+ self.__show_first_only = False
85
+ # Mapping from object UUID to merged result adapter
86
+ # The merged adapter consolidates all result labels for an object
87
+ self.__merged_result_adapters: dict[str, MergedResultPlotPyAdapter] = {}
88
+
89
+ def __len__(self) -> int:
90
+ """Return number of items"""
91
+ return len(self.__plotitems)
92
+
93
+ def __getitem__(self, oid: str) -> TypePlotItem:
94
+ """Return item associated to object uuid"""
95
+ try:
96
+ return self.__plotitems[oid]
97
+ except KeyError as exc:
98
+ # Item does not exist: this may happen when "auto refresh" is disabled
99
+ # (object has been added to model but the corresponding plot item has not
100
+ # been created yet)
101
+ if not self.__auto_refresh:
102
+ self.refresh_plot(oid, True, force=True, only_visible=False)
103
+ return self.__plotitems[oid]
104
+ # Item does not exist and auto refresh is enabled: this should not happen
105
+ raise exc
106
+
107
+ def get(self, key: str, default: TypePlotItem | None = None) -> TypePlotItem | None:
108
+ """Return item associated to object uuid.
109
+ If the key is not found, default is returned if given,
110
+ otherwise None is returned."""
111
+ return self.__plotitems.get(key, default)
112
+
113
+ def get_obj_from_item(self, item: TypePlotItem) -> TypeObj | None:
114
+ """Return object associated to plot item
115
+
116
+ Args:
117
+ item: plot item
118
+
119
+ Returns:
120
+ Object associated to plot item
121
+ """
122
+ for obj in self.panel.objmodel:
123
+ if self.get(get_uuid(obj)) is item:
124
+ return obj
125
+ return None
126
+
127
+ def __setitem__(self, oid: str, item: TypePlotItem) -> None:
128
+ """Set item associated to object uuid"""
129
+ self.__plotitems[oid] = item
130
+
131
+ def __iter__(self) -> Iterator[TypePlotItem]:
132
+ """Return an iterator over plothandler values (plot items)"""
133
+ return iter(self.__plotitems.values())
134
+
135
+ def remove_item(self, oid: str) -> None:
136
+ """Remove plot item associated to object uuid"""
137
+ try:
138
+ item = self.__plotitems.pop(oid)
139
+ except KeyError:
140
+ # Item does not exist: this may happen when "auto refresh" is disabled
141
+ # (object has been added to model but the corresponding plot item has not
142
+ # been created yet).
143
+ # This may also happen after opening a project, then immediately selecting
144
+ # a group containing more than one object: plot item would have been created
145
+ # only for the first object in group, and this exception would be raised
146
+ # for the second one (which does not have a plot item yet).
147
+ return
148
+ self.plot.del_item(item)
149
+
150
+ def clear(self) -> None:
151
+ """Clear plot items"""
152
+ self.__plotitems = {}
153
+ self.__merged_result_adapters = {}
154
+ self.cleanup_dataview()
155
+ self.remove_all_shape_items()
156
+
157
+ def add_shapes(self, oid: str, do_autoscale: bool = False) -> None:
158
+ """Add geometric shape items associated to computed results and annotations,
159
+ for the object with the given uuid"""
160
+ obj = self.panel.objmodel[oid]
161
+ if obj.metadata:
162
+ obj_adapter = create_adapter_from_object(obj)
163
+ items = list(obj_adapter.iterate_shape_items(editable=False))
164
+
165
+ # Collect all result adapters
166
+ results = list(TableAdapter.iterate_from_obj(obj)) + list(
167
+ GeometryAdapter.iterate_from_obj(obj)
168
+ )
169
+
170
+ # Create or update merged result adapter
171
+ if results:
172
+ merged_adapter = MergedResultPlotPyAdapter(results, obj)
173
+ self.__merged_result_adapters[oid] = merged_adapter
174
+
175
+ # Get the merged label
176
+ merged_label = merged_adapter.get_merged_label()
177
+ if merged_label is not None:
178
+ # Set initial visibility based on configuration
179
+ merged_label.setVisible(Conf.view.show_result_label.get())
180
+ items.append(merged_label)
181
+
182
+ # Add other items from the merged adapter (e.g., geometric shapes)
183
+ items.extend(merged_adapter.get_other_items())
184
+ else:
185
+ # No results, remove any existing adapter
186
+ self.__merged_result_adapters.pop(oid, None)
187
+
188
+ if items:
189
+ if do_autoscale:
190
+ self.plot.do_autoscale()
191
+ # Performance optimization: block `plotpy.plot.BasePlot` signals, add
192
+ # all items except the last one, unblock signals, then add the last one
193
+ # (this avoids some unnecessary refresh process by PlotPy)
194
+ with block_signals(self.plot):
195
+ with create_progress_bar(
196
+ self.panel, _("Creating geometric shapes"), max_=len(items) - 1
197
+ ) as progress:
198
+ for i_item, item in enumerate(items[:-1]):
199
+ progress.setValue(i_item + 1)
200
+ if progress.wasCanceled():
201
+ break
202
+ self.plot.add_item(item)
203
+ self.__shapeitems.append(item)
204
+ # Only process events every 10 items to keep UI responsive
205
+ # without killing performance
206
+ if (i_item + 1) % 10 == 0:
207
+ QW.QApplication.processEvents()
208
+ self.plot.add_item(items[-1])
209
+ self.__shapeitems.append(items[-1])
210
+
211
+ def update_resultproperty_from_plot_item(self, item: LabelItem) -> None:
212
+ """Update result properties from merged label plot item.
213
+
214
+ When the merged results label is moved, update the stored position
215
+ in the merged result adapter.
216
+
217
+ Args:
218
+ item: Merged results label item
219
+ """
220
+ # Find the merged adapter that owns this label
221
+ for merged_adapter in self.__merged_result_adapters.values():
222
+ if merged_adapter.get_cached_label() is item:
223
+ # Update the adapter's metadata with the new position
224
+ merged_adapter.update_obj_metadata_from_item(item)
225
+ break
226
+
227
+ def remove_all_shape_items(self) -> None:
228
+ """Remove all geometric shapes associated to result items"""
229
+ if set(self.__shapeitems).issubset(set(self.plot.items)):
230
+ self.plot.del_items(self.__shapeitems)
231
+ self.__shapeitems = []
232
+ # Clear cached labels in merged result adapters since they were removed
233
+ for merged_adapter in self.__merged_result_adapters.values():
234
+ merged_adapter.invalidate_cached_label()
235
+
236
+ def refresh_all_shape_items(self) -> None:
237
+ """Refresh all geometric shapes to apply new style parameters.
238
+
239
+ This method is called when shape/marker visualization settings
240
+ are changed in the Settings dialog. It removes and recreates all shape
241
+ items to apply the new parameters.
242
+ """
243
+ # Get all object IDs that have shape items
244
+ oids_with_shapes = list(self.__merged_result_adapters.keys())
245
+
246
+ if not oids_with_shapes:
247
+ return
248
+
249
+ # Remove all existing shape items
250
+ self.remove_all_shape_items()
251
+
252
+ # Recreate shape items for all objects with the new settings
253
+ for oid in oids_with_shapes:
254
+ self.add_shapes(oid, do_autoscale=False)
255
+
256
+ self.plot.replot()
257
+
258
+ def toggle_result_label_visibility(self, show: bool) -> None:
259
+ """Toggle the visibility of all merged result labels on the plot.
260
+
261
+ Args:
262
+ show: True to show the labels, False to hide them
263
+ """
264
+ for merged_adapter in self.__merged_result_adapters.values():
265
+ label = merged_adapter.get_cached_label()
266
+ if label is not None:
267
+ label.setVisible(show)
268
+ self.plot.replot()
269
+
270
+ def __add_item_to_plot(self, oid: str) -> TypePlotItem:
271
+ """Make plot item and add it to plot.
272
+
273
+ Args:
274
+ oid: object uuid
275
+
276
+ Returns:
277
+ Plot item
278
+ """
279
+ obj = self.panel.objmodel[oid]
280
+ self.__cached_hashes[obj] = calc_data_hash(obj)
281
+ item: TypePlotItem = create_adapter_from_object(obj).make_item()
282
+ item.set_readonly(True)
283
+ self[oid] = item
284
+ self.plot.add_item(item)
285
+ return item
286
+
287
+ def __update_item_on_plot(self, oid: str, just_show: bool = False) -> None:
288
+ """Update plot item.
289
+
290
+ Args:
291
+ oid: object uuid
292
+ just_show: if True, only show the item (do not update it).
293
+ Defaults to False.
294
+ """
295
+ if not just_show:
296
+ obj = self.panel.objmodel[oid]
297
+ cached_hash = self.__cached_hashes.get(obj)
298
+ new_hash = calc_data_hash(obj)
299
+ data_changed = cached_hash is None or cached_hash != new_hash
300
+ self.__cached_hashes[obj] = new_hash
301
+ adapter = create_adapter_from_object(obj)
302
+ adapter.update_item(self[oid], data_changed=data_changed)
303
+
304
+ def set_auto_refresh(self, auto_refresh: bool) -> None:
305
+ """Set auto refresh mode.
306
+
307
+ Args:
308
+ auto_refresh: if True, refresh plot items automatically
309
+ """
310
+ self.__auto_refresh = auto_refresh
311
+ if auto_refresh:
312
+ self.refresh_plot("selected")
313
+
314
+ def set_show_first_only(self, show_first_only: bool) -> None:
315
+ """Set show first only mode.
316
+
317
+ Args:
318
+ show_first_only: if True, show only the first selected item
319
+ """
320
+ self.__show_first_only = show_first_only
321
+ if self.__auto_refresh:
322
+ self.refresh_plot("selected")
323
+
324
+ def get_existing_oids(self) -> list[str]:
325
+ """Get existing object uuids.
326
+
327
+ Returns:
328
+ List of object uuids that have a plot item associated to them.
329
+ """
330
+ return list(self.__plotitems.keys())
331
+
332
+ def reduce_shown_oids(self, oids: list[str]) -> list[str]:
333
+ """Reduce the number of shown objects to visible items only. The base
334
+ implementation is to show only the first selected item if the option
335
+ "Show first only" is enabled.
336
+
337
+ Args:
338
+ oids: list of object uuids
339
+
340
+ Returns:
341
+ Reduced list of object uuids
342
+ """
343
+ if self.__show_first_only:
344
+ return oids[:1]
345
+ return oids
346
+
347
+ def refresh_plot(
348
+ self,
349
+ what: str,
350
+ update_items: bool = True,
351
+ force: bool = False,
352
+ only_visible: bool = True,
353
+ only_existing: bool = False,
354
+ ) -> None:
355
+ """Refresh plot.
356
+
357
+ Args:
358
+ what: string describing the objects to refresh.
359
+ Valid values are "selected" (refresh the selected objects),
360
+ "all" (refresh all objects), "existing" (refresh existing plot items),
361
+ or an object uuid.
362
+ update_items: if True, update the items.
363
+ If False, only show the items (do not update them).
364
+ Defaults to True.
365
+ force: if True, force refresh even if auto refresh is disabled.
366
+ Defaults to False.
367
+ only_visible: if True, only refresh visible items. Defaults to True.
368
+ Visible items are the ones that are not hidden by other items or the items
369
+ except the first one if the option "Show first only" is enabled.
370
+ This is useful for images, where the last image is the one that is shown.
371
+ If False, all items are refreshed.
372
+ only_existing: if True, only refresh existing items. Defaults to False.
373
+ Existing items are the ones that have already been created and are
374
+ associated to the object uuid. If False, create new items for the
375
+ objects that do not have an item yet.
376
+
377
+ Raises:
378
+ ValueError: if `what` is not a valid value
379
+ """
380
+ if not self.__auto_refresh and not force:
381
+ return
382
+ if what == "selected":
383
+ # Refresh selected objects
384
+ oids = self.panel.objview.get_sel_object_uuids(include_groups=True)
385
+ if len(oids) == 1:
386
+ self.cleanup_dataview()
387
+ self.remove_all_shape_items()
388
+ for item in self:
389
+ if item is not None:
390
+ item.hide()
391
+ elif what == "existing":
392
+ # Refresh existing objects
393
+ oids = self.get_existing_oids()
394
+ elif what == "all":
395
+ # Refresh all objects
396
+ oids = self.panel.objmodel.get_object_ids()
397
+ else:
398
+ # Refresh a single object defined by its uuid
399
+ oids = [what]
400
+ try:
401
+ # Check if this is a valid object uuid
402
+ self.panel.objmodel.get_objects(oids)
403
+ except KeyError as exc:
404
+ raise ValueError(f"Invalid value for `what`: {what}") from exc
405
+
406
+ # Initialize titles and scales dictionaries
407
+ title_keys = ("title", "xlabel", "ylabel", "zlabel", "xunit", "yunit", "zunit")
408
+ titles_dict = {}
409
+ autoscale = False
410
+ scale_keys = (
411
+ "xscalelog",
412
+ "xscalemin",
413
+ "xscalemax",
414
+ "yscalelog",
415
+ "yscalemin",
416
+ "yscalemax",
417
+ )
418
+ scales_dict = {}
419
+
420
+ if oids:
421
+ if what != "existing" and only_visible:
422
+ # Remove hidden items from the list of objects to refresh
423
+ oids = self.reduce_shown_oids(oids)
424
+ with create_progress_bar(
425
+ self.panel, _("Creating plot items"), max_=len(oids)
426
+ ) as progress:
427
+ # Iterate over objects
428
+ for i_obj, oid in enumerate(oids):
429
+ progress.setValue(i_obj + 1)
430
+ if progress.wasCanceled():
431
+ break
432
+ obj = self.panel.objmodel[oid]
433
+
434
+ # Collecting titles information
435
+ for key in title_keys:
436
+ title = getattr(obj, key, "")
437
+ value = titles_dict.get(key)
438
+ if value is None:
439
+ titles_dict[key] = title
440
+ elif value != title:
441
+ titles_dict[key] = ""
442
+
443
+ # Collecting scales information
444
+ autoscale = autoscale or obj.autoscale
445
+ for key in scale_keys:
446
+ scale = getattr(obj, key, None)
447
+ if scale is not None:
448
+ cmp = min if "min" in key else max
449
+ scales_dict[key] = cmp(scales_dict.get(key, scale), scale)
450
+
451
+ # Update or add item to plot
452
+ item = self.get(oid)
453
+ if item is None:
454
+ if only_existing:
455
+ continue
456
+ item = self.__add_item_to_plot(oid)
457
+ else:
458
+ self.__update_item_on_plot(oid, just_show=not update_items)
459
+ if what != "existing" or item.isVisible():
460
+ self.plot.set_item_visible(item, True, replot=False)
461
+ self.plot.set_active_item(item)
462
+ item.unselect()
463
+
464
+ # Add geometric shapes
465
+ self.add_shapes(oid, do_autoscale=autoscale)
466
+
467
+ self.plot.replot()
468
+
469
+ else:
470
+ # No object to refresh: clean up titles
471
+ for key in title_keys:
472
+ titles_dict[key] = ""
473
+
474
+ # Set titles
475
+ tdict = titles_dict
476
+ tdict["ylabel"] = (tdict["ylabel"], tdict.pop("zlabel"))
477
+ tdict["yunit"] = (tdict["yunit"], tdict.pop("zunit"))
478
+ self.plot.set_titles(**titles_dict)
479
+
480
+ # Set scales
481
+ replot = False
482
+ for axis_name, axis in (("bottom", "x"), ("left", "y")):
483
+ axis_id = self.plot.get_axis_id(axis_name)
484
+ scalelog = scales_dict.get(f"{axis}scalelog")
485
+ if scalelog is not None:
486
+ new_scale = "log" if scalelog else "lin"
487
+ self.plot.set_axis_scale(axis_id, new_scale, autoscale=False)
488
+ replot = True
489
+ if autoscale:
490
+ self.plot.do_autoscale()
491
+ else:
492
+ for axis_name, axis in (("bottom", "x"), ("left", "y")):
493
+ axis_id = self.plot.get_axis_id(axis_name)
494
+ new_vmin = scales_dict.get(f"{axis}scalemin")
495
+ new_vmax = scales_dict.get(f"{axis}scalemax")
496
+ if new_vmin is not None or new_vmax is not None:
497
+ self.plot.do_autoscale(replot=False, axis_id=axis_id)
498
+ vmin, vmax = self.plot.get_axis_limits(axis_id)
499
+ new_vmin = new_vmin if new_vmin is not None else vmin
500
+ new_vmax = new_vmax if new_vmax is not None else vmax
501
+ self.plot.set_axis_limits(axis_id, new_vmin, new_vmax)
502
+ replot = True
503
+ if replot:
504
+ self.plot.replot()
505
+
506
+ def cleanup_dataview(self) -> None:
507
+ """Clean up data view"""
508
+ # Find items to remove (items that are not in the handler and not special items)
509
+ # Skip shape items since they're managed separately and checking membership
510
+ # for many items is expensive
511
+ shape_items_set = set(self.__shapeitems)
512
+ items_to_remove = [
513
+ item
514
+ for item in self.plot.items[:]
515
+ if item not in shape_items_set
516
+ and item not in self
517
+ and not isinstance(item, (LegendBoxItem, GridItem))
518
+ ]
519
+ # Delete items one by one with error handling for items already removed
520
+ for item in items_to_remove:
521
+ try:
522
+ self.plot.del_item(item)
523
+ except ValueError:
524
+ # Item was already removed (e.g., by detach())
525
+ pass
526
+
527
+ def get_plot_options(self) -> PlotOptions:
528
+ """Return standard signal/image plot options"""
529
+ return PlotOptions(
530
+ type=self.PLOT_TYPE,
531
+ xlabel=self.plot.get_axis_title("bottom"),
532
+ ylabel=self.plot.get_axis_title("left"),
533
+ xunit=self.plot.get_axis_unit("bottom"),
534
+ yunit=self.plot.get_axis_unit("left"),
535
+ show_axes_tab=False,
536
+ )
537
+
538
+
539
+ class SignalPlotHandler(BasePlotHandler[SignalObj, CurveItem]):
540
+ """Object handling signal plot items, plot dialogs, plot options"""
541
+
542
+ PLOT_TYPE = PlotType.CURVE
543
+
544
+ def toggle_anti_aliasing(self, state: bool) -> None:
545
+ """Toggle anti-aliasing
546
+
547
+ Args:
548
+ state: if True, enable anti-aliasing
549
+ """
550
+ self.plot.set_antialiasing(state)
551
+ self.plot.replot()
552
+
553
+ def get_plot_options(self) -> PlotOptions:
554
+ """Return standard signal/image plot options"""
555
+ options = super().get_plot_options()
556
+ options.curve_antialiasing = self.plot.antialiased
557
+ return options
558
+
559
+ def refresh_plot(
560
+ self,
561
+ what: str,
562
+ update_items: bool = True,
563
+ force: bool = False,
564
+ only_visible: bool = True,
565
+ only_existing: bool = False,
566
+ ) -> None:
567
+ """Refresh plot and configure datetime axis if needed.
568
+
569
+ This override adds automatic datetime axis configuration when at least one
570
+ of the displayed signals has a datetime X-axis.
571
+
572
+ Args:
573
+ what: string describing the objects to refresh
574
+ update_items: if True, update the items
575
+ force: if True, force refresh even if auto refresh is disabled
576
+ only_visible: if True, only refresh visible items
577
+ only_existing: if True, only refresh existing items
578
+ """
579
+ # Call parent implementation
580
+ super().refresh_plot(what, update_items, force, only_visible, only_existing)
581
+
582
+ # Check if any visible signal has datetime X-axis
583
+ has_datetime = False
584
+ datetime_format = None
585
+ for item in self:
586
+ if item is not None and item.isVisible():
587
+ obj = self.get_obj_from_item(item)
588
+ if obj is not None and obj.is_x_datetime():
589
+ has_datetime = True
590
+ # Get format from signal metadata, or use configured default
591
+ datetime_format = obj.metadata.get("x_datetime_format")
592
+ if datetime_format is None:
593
+ # Use configured format based on time unit
594
+ unit = obj.xunit if obj.xunit else "s"
595
+ if unit in ("ns", "us", "ms"):
596
+ datetime_format = Conf.view.sig_datetime_format_ms.get(
597
+ "%H:%M:%S.%f"
598
+ )
599
+ else:
600
+ datetime_format = Conf.view.sig_datetime_format_s.get(
601
+ "%H:%M:%S"
602
+ )
603
+ break
604
+
605
+ # Configure X-axis for datetime or restore default
606
+ if has_datetime and datetime_format is not None:
607
+ self.plot.set_axis_datetime("bottom", format=datetime_format)
608
+ else:
609
+ # Restore default scale draw (remove datetime formatting)
610
+ self.plot.setAxisScaleDraw(self.plot.get_axis_id("bottom"), QwtScaleDraw())
611
+
612
+
613
+ class ImagePlotHandler(BasePlotHandler[ImageObj, MaskedXYImageItem]):
614
+ """Object handling image plot items, plot dialogs, plot options"""
615
+
616
+ PLOT_TYPE = PlotType.IMAGE
617
+
618
+ def reduce_shown_oids(self, oids: list[str]) -> list[str]:
619
+ """Reduce the number of shown objects to visible items only. The base
620
+ implementation is to show only the first selected item if the option
621
+ "Show first only" is enabled.
622
+
623
+ Args:
624
+ oids: list of object uuids
625
+
626
+ Returns:
627
+ Reduced list of object uuids
628
+ """
629
+ oids = super().reduce_shown_oids(oids)
630
+
631
+ # For Image View, we show only the last image (which is the highest z-order
632
+ # plot item) if more than one image is selected, if last image has no
633
+ # transparency and if the other images are all completely covered by the last
634
+ # image.
635
+ # TODO: [P4] Enhance this algorithm to handle more complex cases
636
+ # (not sure it's worth it)
637
+ if len(oids) > 1:
638
+ # Get objects associated to the oids
639
+ objs = self.panel.objmodel.get_objects(oids)
640
+ # First condition is about the image transparency
641
+ last_obj = objs[-1]
642
+ alpha_cond = (
643
+ last_obj.get_metadata_option("alpha", 1.0) == 1.0
644
+ and last_obj.get_metadata_option("alpha_function", 0) == 0
645
+ )
646
+ if alpha_cond:
647
+ # Second condition is about the image size and position
648
+ geom_cond = True
649
+ for obj in objs[:-1]:
650
+ # Handle both uniform and non-uniform coordinates
651
+ if last_obj.is_uniform_coords and obj.is_uniform_coords:
652
+ # Both have uniform coordinates, use old logic
653
+ geom_cond = (
654
+ geom_cond
655
+ and last_obj.x0 <= obj.x0
656
+ and last_obj.y0 <= obj.y0
657
+ and last_obj.x0 + last_obj.width >= obj.x0 + obj.width
658
+ and last_obj.y0 + last_obj.height >= obj.y0 + obj.height
659
+ )
660
+ else:
661
+ # At least one has non-uniform coordinates,
662
+ # use extent comparison
663
+ last_x0 = (
664
+ last_obj.x0
665
+ if last_obj.is_uniform_coords
666
+ else last_obj.xcoords[0]
667
+ )
668
+ last_y0 = (
669
+ last_obj.y0
670
+ if last_obj.is_uniform_coords
671
+ else last_obj.ycoords[0]
672
+ )
673
+ last_x1 = (
674
+ last_obj.x0 + last_obj.width
675
+ if last_obj.is_uniform_coords
676
+ else last_obj.xcoords[-1]
677
+ )
678
+ last_y1 = (
679
+ last_obj.y0 + last_obj.height
680
+ if last_obj.is_uniform_coords
681
+ else last_obj.ycoords[-1]
682
+ )
683
+
684
+ obj_x0 = obj.x0 if obj.is_uniform_coords else obj.xcoords[0]
685
+ obj_y0 = obj.y0 if obj.is_uniform_coords else obj.ycoords[0]
686
+ obj_x1 = (
687
+ obj.x0 + obj.width
688
+ if obj.is_uniform_coords
689
+ else obj.xcoords[-1]
690
+ )
691
+ obj_y1 = (
692
+ obj.y0 + obj.height
693
+ if obj.is_uniform_coords
694
+ else obj.ycoords[-1]
695
+ )
696
+
697
+ geom_cond = (
698
+ geom_cond
699
+ and last_x0 <= obj_x0
700
+ and last_y0 <= obj_y0
701
+ and last_x1 >= obj_x1
702
+ and last_y1 >= obj_y1
703
+ )
704
+ if not geom_cond:
705
+ break
706
+ if geom_cond:
707
+ oids = oids[-1:]
708
+ return oids
709
+
710
+ def refresh_plot(
711
+ self,
712
+ what: str,
713
+ update_items: bool = True,
714
+ force: bool = False,
715
+ only_visible: bool = True,
716
+ only_existing: bool = False,
717
+ ) -> None:
718
+ """Refresh plot.
719
+
720
+ Args:
721
+ what: string describing the objects to refresh.
722
+ Valid values are "selected" (refresh the selected objects),
723
+ "all" (refresh all objects), "existing" (refresh existing plot items),
724
+ or an object uuid.
725
+ update_items: if True, update the items.
726
+ If False, only show the items (do not update them).
727
+ Defaults to True.
728
+ force: if True, force refresh even if auto refresh is disabled.
729
+ Defaults to False.
730
+ only_visible: if True, only refresh visible items. Defaults to True.
731
+ Visible items are the ones that are not hidden by other items or the items
732
+ except the first one if the option "Show first only" is enabled.
733
+ This is useful for images, where the last image is the one that is shown.
734
+ If False, all items are refreshed.
735
+ only_existing: if True, only refresh existing items. Defaults to False.
736
+ Existing items are the ones that have already been created and are
737
+ associated to the object uuid. If False, create new items for the
738
+ objects that do not have an item yet.
739
+
740
+ Raises:
741
+ ValueError: if `what` is not a valid value
742
+ """
743
+ super().refresh_plot(
744
+ what=what,
745
+ update_items=update_items,
746
+ force=force,
747
+ only_visible=only_visible,
748
+ only_existing=only_existing,
749
+ )
750
+ self.plotwidget.contrast.setVisible(Conf.view.show_contrast.get(True))
751
+ plot = self.plotwidget.get_plot()
752
+ new_aspect_ratio = current_aspect_ratio = plot.get_aspect_ratio()
753
+ new_lock = current_lock = plot.lock_aspect_ratio
754
+ if Conf.view.ima_aspect_ratio_1_1.get():
755
+ # Lock aspect ratio to 1:1
756
+ new_aspect_ratio = 1.0
757
+ new_lock = True
758
+ else:
759
+ # Use physical pixel size to set aspect ratio
760
+ # Determine which objects to check based on the 'what' parameter
761
+ if what == "selected":
762
+ # Use selected objects to determine aspect ratio
763
+ oids = self.panel.objview.get_sel_object_uuids(include_groups=True)
764
+ elif what == "existing":
765
+ # Use existing objects
766
+ oids = self.get_existing_oids()
767
+ elif what == "all":
768
+ # Use all objects
769
+ oids = self.panel.objmodel.get_object_ids()
770
+ else:
771
+ # Single object by uuid
772
+ oids = [what]
773
+
774
+ # Reduce to visible items and check aspect ratio
775
+ for oid in reversed(self.reduce_shown_oids(oids)):
776
+ if oid in self.get_existing_oids() and self.get(oid).isVisible():
777
+ obj: ImageObj = self.panel.objmodel[oid]
778
+ if obj.is_uniform_coords:
779
+ new_aspect_ratio = obj.dx / obj.dy
780
+ new_lock = True
781
+ else:
782
+ new_lock = False
783
+ break
784
+ if new_aspect_ratio != current_aspect_ratio or new_lock != current_lock:
785
+ # Update aspect ratio only if it has changed
786
+ plot.set_aspect_ratio(new_aspect_ratio, lock=new_lock)
787
+ plot.do_autoscale()
788
+
789
+ def cleanup_dataview(self) -> None:
790
+ """Clean up data view"""
791
+ for widget in (self.plotwidget.xcsw, self.plotwidget.ycsw):
792
+ widget.hide()
793
+ super().cleanup_dataview()
794
+
795
+ def get_plot_options(self) -> PlotOptions:
796
+ """Return standard signal/image plot options"""
797
+ options = super().get_plot_options()
798
+ options.zlabel = self.plot.get_axis_title("right")
799
+ options.zunit = self.plot.get_axis_unit("right")
800
+ return options