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,607 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ .. Macro panel (see parent package :mod:`datalab.gui.panel`)
5
+ """
6
+
7
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
8
+
9
+ from __future__ import annotations
10
+
11
+ import os.path as osp
12
+ import re
13
+ from typing import TYPE_CHECKING
14
+
15
+ from guidata.config import CONF
16
+ from guidata.configtools import get_font, get_icon
17
+ from guidata.qthelpers import add_actions, create_action
18
+ from guidata.widgets.console.shell import PythonShellWidget
19
+ from guidata.widgets.dockable import DockableWidgetMixin
20
+ from qtpy import QtCore as QC
21
+ from qtpy import QtWidgets as QW
22
+ from qtpy.compat import getopenfilename, getsavefilename
23
+
24
+ from datalab.config import Conf, _
25
+ from datalab.env import execenv
26
+ from datalab.gui.macroeditor import Macro
27
+ from datalab.gui.panel.base import AbstractPanel
28
+ from datalab.utils.qthelpers import (
29
+ create_menu_button,
30
+ qt_try_loadsave_file,
31
+ save_restore_stds,
32
+ )
33
+
34
+ if TYPE_CHECKING:
35
+ from guidata.widgets.codeeditor import CodeEditor
36
+
37
+ from datalab.h5.native import NativeH5Reader, NativeH5Writer
38
+
39
+
40
+ class MacroTabs(QW.QTabWidget):
41
+ """Macro tabwidget
42
+
43
+ Args:
44
+ parent (QWidget): Parent widget
45
+ """
46
+
47
+ SIG_CONTEXT_MENU = QC.Signal(QC.QPoint)
48
+ SIG_RENAME = QC.Signal(int)
49
+ SIG_REMOVE = QC.Signal(int)
50
+
51
+ def __init__(self, parent=None) -> None:
52
+ super().__init__(parent)
53
+ self.setTabsClosable(True)
54
+ self.setMovable(True)
55
+ self.tabBarDoubleClicked.connect(self.__rename)
56
+ self.tabCloseRequested.connect(self.__remove)
57
+ self.__titles: list[str] = []
58
+
59
+ def clear(self) -> None:
60
+ """Override Qt method"""
61
+ super().clear()
62
+ self.__titles.clear()
63
+
64
+ def contextMenuEvent(self, event): # pylint: disable=C0103
65
+ """Override Qt method"""
66
+ self.SIG_CONTEXT_MENU.emit(event.globalPos())
67
+
68
+ def __rename(self, index: int) -> None:
69
+ """Rename tab
70
+
71
+ Args:
72
+ index: Index of the tab
73
+ """
74
+ self.SIG_RENAME.emit(index + 1)
75
+
76
+ def __remove(self, index: int) -> None:
77
+ """Remove tab
78
+
79
+ Args:
80
+ index: Index of the tab
81
+ """
82
+ self.SIG_REMOVE.emit(index + 1)
83
+
84
+ def __update_tab_titles(self) -> None:
85
+ """Update tab titles"""
86
+ for number, title in enumerate(self.__titles, 1):
87
+ self.setTabText(number - 1, f"{number:02d}: {title}")
88
+
89
+ def add_tab(self, macro: Macro) -> int:
90
+ """Add tab
91
+
92
+ Args:
93
+ macro: Macro object
94
+
95
+ Returns:
96
+ int: Number of the tab (starting at 1)
97
+ """
98
+ self.__titles.append(macro.title)
99
+ index = self.addTab(macro.editor, "")
100
+ self.__update_tab_titles()
101
+ return index + 1 # Numbering starts at 1
102
+
103
+ def remove_tab(self, number: int) -> None:
104
+ """Remove tab
105
+
106
+ Args:
107
+ number: Number of the tab (starting at 1)
108
+ """
109
+ self.removeTab(number - 1)
110
+ self.__titles.pop(number - 1)
111
+ self.__update_tab_titles()
112
+
113
+ def get_widget(self, number: int) -> CodeEditor:
114
+ """Return macro editor widget at number
115
+
116
+ Args:
117
+ number: Number of the tab (starting at 1)
118
+
119
+ Returns:
120
+ Macro editor widget
121
+ """
122
+ return self.widget(number - 1)
123
+
124
+ def set_current_number(self, number: int) -> None:
125
+ """Set current tab number
126
+
127
+ Args:
128
+ number: Number of the tab (starting at 1)
129
+ """
130
+ self.setCurrentIndex(number - 1)
131
+
132
+ def get_current_number(self) -> int:
133
+ """Return current tab number
134
+
135
+ Returns:
136
+ int: Number of the tab (starting at 1)
137
+ """
138
+ return self.currentIndex() + 1
139
+
140
+ def set_tab_title(self, number: int, name: str) -> None:
141
+ """Set tab title
142
+
143
+ Args:
144
+ number: Number of the tab (starting at 1)
145
+ name: Macro name
146
+ """
147
+ self.__titles[number - 1] = name
148
+ self.__update_tab_titles()
149
+
150
+
151
+ class MacroPanel(AbstractPanel, DockableWidgetMixin):
152
+ """Macro Panel widget
153
+
154
+ Args:
155
+ parent (QWidget): Parent widget
156
+ """
157
+
158
+ LOCATION = QC.Qt.RightDockWidgetArea
159
+ PANEL_STR = _("Macro panel")
160
+
161
+ H5_PREFIX = "DataLab_Mac"
162
+
163
+ SIG_OBJECT_MODIFIED = QC.Signal()
164
+
165
+ FILE_FILTERS = f"{_('Python files')} (*.py)"
166
+
167
+ def __init__(self, parent: QW.QMainWindow) -> None:
168
+ super().__init__(parent)
169
+ self.setWindowTitle(self.PANEL_STR)
170
+ self.setWindowIcon(get_icon("libre-gui-cogs.svg"))
171
+ self.setOrientation(QC.Qt.Vertical)
172
+
173
+ self.context_menu = QW.QMenu()
174
+ self.tabwidget_tb = QW.QToolBar(self)
175
+ self.tabwidget_tb.setOrientation(QC.Qt.Vertical)
176
+
177
+ self.console = PythonShellWidget(self, read_only=True)
178
+ self.console.setMaximumBlockCount(5000)
179
+ font = get_font(CONF, "console")
180
+ font.setPointSize(10)
181
+ self.console.set_font(font)
182
+ self.console.write(_("-***- Macro Console -***-"), prompt=True)
183
+
184
+ self.tabwidget = MacroTabs(self)
185
+ self.tabwidget.SIG_RENAME.connect(self.rename_macro)
186
+ self.tabwidget.SIG_REMOVE.connect(self.remove_macro)
187
+ self.tabwidget.currentChanged.connect(self.__update_actions)
188
+
189
+ tabwidget_with_tb = QW.QWidget(self)
190
+ tabwidget_with_tb.setLayout(QW.QHBoxLayout())
191
+ tabwidget_with_tb.layout().addWidget(self.tabwidget_tb)
192
+ tabwidget_with_tb.layout().addWidget(self.tabwidget)
193
+
194
+ # Put console in a groupbox to have a title
195
+ console_groupbox = QW.QGroupBox(_("Console"), self)
196
+ console_groupbox.setLayout(QW.QHBoxLayout())
197
+ console_groupbox.layout().addWidget(self.console)
198
+ # Put console groupbox in a frame to have a nice margin
199
+ console_frame = QW.QFrame(self)
200
+ console_frame.setLayout(QW.QHBoxLayout())
201
+ console_frame.layout().addWidget(console_groupbox)
202
+
203
+ for widget in (tabwidget_with_tb, console_frame):
204
+ self.addWidget(widget)
205
+ self.setStretchFactor(0, 2)
206
+ self.setStretchFactor(1, 1)
207
+
208
+ self.run_action = None
209
+ self.stop_action = None
210
+ self.obj_actions: list[QW.QAction] = [] # Object-dependent actions
211
+ self.__macros: list[Macro] = []
212
+
213
+ self.setup_actions()
214
+
215
+ def update_color_mode(self) -> None:
216
+ """Update color mode according to the current theme"""
217
+ self.console.update_color_mode()
218
+ for macro in self.__macros:
219
+ macro.editor.update_color_mode()
220
+
221
+ # ------AbstractPanel interface-----------------------------------------------------
222
+ # pylint: disable=unused-argument
223
+ def get_serializable_name(self, obj: Macro) -> str:
224
+ """Return serializable name of object"""
225
+ title = re.sub("[^-a-zA-Z0-9_.() ]+", "", obj.title.replace("/", "_"))
226
+ name = f"{obj.PREFIX}{(self.__macros.index(obj) + 1):03d}: {title}"
227
+ return name
228
+
229
+ def serialize_to_hdf5(self, writer: NativeH5Writer) -> None:
230
+ """Serialize whole panel to a HDF5 file
231
+
232
+ Args:
233
+ writer: HDF5 writer
234
+ """
235
+ with writer.group(self.H5_PREFIX):
236
+ for obj in self.__macros:
237
+ self.serialize_object_to_hdf5(obj, writer)
238
+
239
+ def deserialize_from_hdf5(
240
+ self, reader: NativeH5Reader, reset_all: bool = False
241
+ ) -> None:
242
+ """Deserialize whole panel from a HDF5 file
243
+
244
+ Args:
245
+ reader: HDF5 reader
246
+ reset_all: If True, preserve original UUIDs (workspace reload).
247
+ If False, regenerate UUIDs (importing objects).
248
+ """
249
+ with reader.group(self.H5_PREFIX):
250
+ for name in reader.h5.get(self.H5_PREFIX, []):
251
+ # Contrary to signal or image panels, macros are not stored
252
+ # in a group but directly in the root of the HDF5 file
253
+ obj = self.deserialize_object_from_hdf5(reader, name, reset_all)
254
+ self.add_object(obj)
255
+
256
+ def __len__(self) -> int:
257
+ """Return number of objects"""
258
+ return len(self.__macros)
259
+
260
+ def __getitem__(self, nb: int) -> Macro:
261
+ """Return object from its number (1 to N)"""
262
+ return self.__macros[nb - 1]
263
+
264
+ def __iter__(self):
265
+ """Iterate over objects"""
266
+ return iter(self.__macros)
267
+
268
+ def create_object(self) -> Macro:
269
+ """Create object.
270
+
271
+ Returns:
272
+ Macro object
273
+ """
274
+ macro = Macro(self.console)
275
+ macro.objectNameChanged.connect(self.macro_name_changed)
276
+ macro.STARTED.connect(
277
+ lambda orig_macro=macro: self.macro_state_changed(orig_macro, True)
278
+ )
279
+ macro.FINISHED.connect(
280
+ lambda orig_macro=macro: self.macro_state_changed(orig_macro, False)
281
+ )
282
+ macro.MODIFIED.connect(self.macro_contents_changed)
283
+ return macro
284
+
285
+ def add_object(self, obj: Macro) -> None:
286
+ """Add object.
287
+
288
+ Args:
289
+ obj: Macro object
290
+ """
291
+ self.__macros.append(obj)
292
+ number = self.tabwidget.add_tab(obj)
293
+ self.SIG_OBJECT_ADDED.emit()
294
+ self.tabwidget.set_current_number(number)
295
+
296
+ def remove_all_objects(self) -> None:
297
+ """Remove all objects"""
298
+ self.tabwidget.clear()
299
+ self.__macros.clear()
300
+ super().remove_all_objects()
301
+
302
+ # ---- Macro panel API -------------------------------------------------------------
303
+ def setup_actions(self) -> None:
304
+ """Setup macro menu actions"""
305
+ self.run_action = create_action(
306
+ self,
307
+ _("Run macro"),
308
+ icon=get_icon("libre-camera-flash-on.svg"),
309
+ triggered=self.run_macro,
310
+ shortcut="Ctrl+F5",
311
+ )
312
+ self.stop_action = create_action(
313
+ self,
314
+ _("Stop macro"),
315
+ icon=get_icon("libre-camera-flash-off.svg"),
316
+ triggered=self.stop_macro,
317
+ shortcut="Shift+F5",
318
+ )
319
+ self.stop_action.setDisabled(True)
320
+ add_act = create_action(
321
+ self,
322
+ _("New macro"),
323
+ icon=get_icon("libre-gui-add.svg"),
324
+ triggered=self.add_macro,
325
+ )
326
+ ren_act = create_action(
327
+ self,
328
+ _("Rename macro"),
329
+ icon=get_icon("libre-gui-pencil.svg"),
330
+ triggered=self.rename_macro,
331
+ )
332
+ imp_act = create_action(
333
+ self,
334
+ _("Import macro from file"),
335
+ icon=get_icon("fileopen_py.svg"),
336
+ triggered=self.import_macro_from_file,
337
+ )
338
+ exp_act = create_action(
339
+ self,
340
+ _("Export macro to file"),
341
+ icon=get_icon("filesave_py.svg"),
342
+ triggered=self.export_macro_to_file,
343
+ )
344
+ rem_act = create_action(
345
+ self,
346
+ _("Remove macro"),
347
+ icon=get_icon("libre-gui-action-delete.svg"),
348
+ triggered=self.remove_macro,
349
+ )
350
+ self.obj_actions += [
351
+ self.run_action,
352
+ self.stop_action,
353
+ ren_act,
354
+ exp_act,
355
+ rem_act,
356
+ ]
357
+
358
+ self.tabwidget.SIG_CONTEXT_MENU.connect(self.context_menu.popup)
359
+
360
+ tabwidget_corner = QW.QToolBar(_("Macro editor toolbar"), self)
361
+ self.context_menu.aboutToShow.connect(self.__update_actions)
362
+ tabwidget_menu_btn = create_menu_button(self, self.context_menu)
363
+ tabwidget_corner.addWidget(tabwidget_menu_btn)
364
+ self.tabwidget.setCornerWidget(tabwidget_corner)
365
+
366
+ main_actions = [
367
+ self.run_action,
368
+ self.stop_action,
369
+ None,
370
+ add_act,
371
+ ren_act,
372
+ imp_act,
373
+ exp_act,
374
+ ]
375
+ add_actions(tabwidget_corner, [self.run_action, self.stop_action])
376
+ add_actions(self.tabwidget_tb, main_actions)
377
+ add_actions(self.context_menu, main_actions + [None, rem_act])
378
+
379
+ self.__update_actions()
380
+
381
+ def __update_actions(self) -> None:
382
+ """Update actions"""
383
+ not_empty = self.tabwidget.count() > 0
384
+ for action in self.obj_actions:
385
+ action.setEnabled(not_empty)
386
+ if not_empty:
387
+ macro = self.get_macro()
388
+ if macro is not None:
389
+ macro: Macro
390
+ self.macro_state_changed(macro, macro.is_running())
391
+
392
+ def get_macro(self, number_or_title: int | str | None = None) -> Macro | None:
393
+ """Return macro at number (if number is None, return current macro)
394
+
395
+ Args:
396
+ number: Number of the macro (starting at 1) or title of the macro.
397
+ Defaults to None (current macro).
398
+
399
+ Returns:
400
+ Macro object or None (if not found)
401
+ """
402
+ if number_or_title is None:
403
+ number_or_title = self.tabwidget.get_current_number()
404
+ if isinstance(number_or_title, str):
405
+ return self.get_macro(self.get_number_from_title(number_or_title))
406
+ for macro in self.__macros:
407
+ if self.tabwidget.get_widget(number_or_title) is macro.editor:
408
+ return macro
409
+ return None
410
+
411
+ def get_number_from_title(self, title: str) -> int | None:
412
+ """Return macro number from title
413
+
414
+ Args:
415
+ title: Title of the macro
416
+
417
+ Returns:
418
+ Number of the macro (starting at 1) or None (if not found)
419
+ """
420
+ for number in range(1, self.tabwidget.count() + 1):
421
+ if self.tabwidget.tabText(number - 1).endswith(title):
422
+ return number
423
+ return None
424
+
425
+ def get_number_from_macro(self, macro: Macro) -> int | None:
426
+ """Return macro number from macro object
427
+
428
+ Args:
429
+ macro: Macro object
430
+
431
+ Returns:
432
+ Number of the macro (starting at 1) or None (if not found)
433
+ """
434
+ for number in range(1, self.tabwidget.count() + 1):
435
+ if self.tabwidget.get_widget(number) is macro.editor:
436
+ return number
437
+ return None
438
+
439
+ def get_macro_titles(self) -> list[str]:
440
+ """Return list of macro titles"""
441
+ return [macro.title for macro in self.__macros]
442
+
443
+ def macro_contents_changed(self) -> None:
444
+ """One of the macro contents has changed"""
445
+ self.SIG_OBJECT_MODIFIED.emit()
446
+
447
+ def run_macro(self, number_or_title: int | str | None = None) -> None:
448
+ """Run current macro
449
+
450
+ Args:
451
+ number: Number of the macro (starting at 1). Defaults to None (run
452
+ current macro, or does nothing if there is no macro).
453
+ """
454
+ macro = self.get_macro(number_or_title)
455
+ if macro is not None:
456
+ macro: Macro
457
+ macro.run()
458
+
459
+ def stop_macro(self, number_or_title: int | str | None = None) -> None:
460
+ """Stop current macro
461
+
462
+ Args:
463
+ number: Number of the macro (starting at 1). Defaults to None (run
464
+ current macro, or does nothing if there is no macro).
465
+ """
466
+ macro = self.get_macro(number_or_title)
467
+ if macro is not None:
468
+ macro: Macro
469
+ macro.kill()
470
+
471
+ def macro_state_changed(self, orig_macro: Macro, state: bool) -> None:
472
+ """Macro state has changed (True: started, False: stopped)
473
+
474
+ Args:
475
+ orig_macro: Macro object
476
+ state: State of the macro
477
+ """
478
+ macro = self.get_macro()
479
+ if macro is orig_macro:
480
+ self.run_action.setEnabled(not state)
481
+ self.stop_action.setEnabled(state)
482
+
483
+ def add_macro(self) -> Macro:
484
+ """Add macro, optionally with name
485
+
486
+ Returns:
487
+ Macro object
488
+ """
489
+ macro = self.create_object()
490
+ self.add_object(macro)
491
+ if not macro.title:
492
+ self.rename_macro()
493
+ return macro
494
+
495
+ def macro_name_changed(self, name: str) -> None:
496
+ """Macro name has been changed
497
+
498
+ Args:
499
+ name: New name of the macro
500
+ """
501
+ number = self.get_number_from_macro(self.sender())
502
+ if number is not None:
503
+ self.tabwidget.set_tab_title(number, name)
504
+
505
+ def rename_macro(self, number: int | None = None, title: str | None = None) -> None:
506
+ """Rename macro
507
+
508
+ Args:
509
+ number: Number of the macro (starting at 1). Defaults to None.
510
+ title: Title of the macro. Defaults to None.
511
+ """
512
+ macro = self.get_macro(number)
513
+ assert isinstance(macro, Macro)
514
+ if title is None:
515
+ title, valid = QW.QInputDialog.getText(
516
+ self,
517
+ _("Rename"),
518
+ _("New title:"),
519
+ QW.QLineEdit.Normal,
520
+ macro.title,
521
+ )
522
+ title = title if valid else None
523
+ if title:
524
+ macro.title = title
525
+ if number is not None:
526
+ self.tabwidget.set_current_number(number)
527
+
528
+ def export_macro_to_file(
529
+ self, number_or_title: int | str | None = None, filename: str | None = None
530
+ ) -> None:
531
+ """Export macro to file
532
+
533
+ Args:
534
+ number_or_title: Number of the macro (starting at 1) or title of the macro.
535
+ Defaults to None.
536
+ filename: Filename. Defaults to None.
537
+
538
+ Raises:
539
+ ValueError: If title is not found
540
+ """
541
+ macro = self.get_macro(number_or_title)
542
+ assert isinstance(macro, Macro)
543
+ if filename is None:
544
+ basedir = Conf.main.base_dir.get()
545
+ with save_restore_stds():
546
+ filename, _filt = getsavefilename(
547
+ self, _("Save as"), basedir, self.FILE_FILTERS
548
+ )
549
+ if filename:
550
+ with qt_try_loadsave_file(self.parentWidget(), filename, "save"):
551
+ Conf.main.base_dir.set(filename)
552
+ macro.title = osp.basename(filename)
553
+ macro.to_file(filename)
554
+
555
+ def import_macro_from_file(self, filename: str | None = None) -> int:
556
+ """Import macro from file
557
+
558
+ Args:
559
+ filename: Filename. Defaults to None.
560
+
561
+ Returns:
562
+ Number of the macro (starting at 1)
563
+ """
564
+ if filename is None:
565
+ basedir = Conf.main.base_dir.get()
566
+ with save_restore_stds():
567
+ filename, _filt = getopenfilename(
568
+ self, _("Open"), basedir, self.FILE_FILTERS
569
+ )
570
+ if filename:
571
+ with qt_try_loadsave_file(self.parentWidget(), filename, "load"):
572
+ Conf.main.base_dir.set(filename)
573
+ macro = self.add_macro()
574
+ macro.from_file(filename)
575
+ return self.get_number_from_macro(macro)
576
+ return -1
577
+
578
+ def remove_macro(self, number_or_title: int | str | None = None) -> None:
579
+ """Remove macro
580
+
581
+ Args:
582
+ number_or_title: Number of the macro (starting at 1) or title of the macro.
583
+ Defaults to None.
584
+ """
585
+ if number_or_title is None:
586
+ number_or_title = self.tabwidget.get_current_number()
587
+ if isinstance(number_or_title, str):
588
+ number_or_title = self.get_number_from_title(number_or_title)
589
+ txt = "<br>".join(
590
+ [
591
+ _(
592
+ "When closed, the macro is <u>permanently destroyed</u>, "
593
+ "unless it has been exported first."
594
+ ),
595
+ "",
596
+ _("Do you want to continue?"),
597
+ ]
598
+ )
599
+ btns = QW.QMessageBox.StandardButton.Yes | QW.QMessageBox.StandardButton.No
600
+ if execenv.unattended:
601
+ choice = QW.QMessageBox.StandardButton.Yes
602
+ else:
603
+ choice = QW.QMessageBox.warning(self, self.windowTitle(), txt, btns)
604
+ if choice == QW.QMessageBox.StandardButton.Yes:
605
+ self.tabwidget.remove_tab(number_or_title)
606
+ self.__macros.pop(number_or_title - 1)
607
+ self.SIG_OBJECT_REMOVED.emit()