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.
- datalab/__init__.py +35 -2
- datalab/adapters_metadata/__init__.py +31 -0
- datalab/adapters_metadata/base_adapter.py +316 -0
- datalab/adapters_metadata/common.py +422 -0
- datalab/adapters_metadata/geometry_adapter.py +98 -0
- datalab/adapters_metadata/table_adapter.py +84 -0
- datalab/adapters_plotpy/__init__.py +54 -0
- datalab/adapters_plotpy/annotations.py +124 -0
- datalab/adapters_plotpy/base.py +110 -0
- datalab/adapters_plotpy/converters.py +86 -0
- datalab/adapters_plotpy/factories.py +80 -0
- datalab/adapters_plotpy/objects/__init__.py +0 -0
- datalab/adapters_plotpy/objects/base.py +197 -0
- datalab/adapters_plotpy/objects/image.py +157 -0
- datalab/adapters_plotpy/objects/scalar.py +565 -0
- datalab/adapters_plotpy/objects/signal.py +264 -0
- datalab/adapters_plotpy/roi/__init__.py +0 -0
- datalab/adapters_plotpy/roi/base.py +146 -0
- datalab/adapters_plotpy/roi/factory.py +93 -0
- datalab/adapters_plotpy/roi/image.py +207 -0
- datalab/adapters_plotpy/roi/signal.py +72 -0
- datalab/app.py +98 -0
- datalab/config.py +817 -0
- datalab/control/__init__.py +0 -0
- datalab/control/baseproxy.py +776 -0
- datalab/control/proxy.py +343 -0
- datalab/control/remote.py +1005 -0
- datalab/data/doc/DataLab_en.pdf +0 -0
- datalab/data/doc/DataLab_fr.pdf +0 -0
- datalab/data/icons/analysis/delete_results.svg +109 -0
- datalab/data/icons/analysis/fw1e2.svg +156 -0
- datalab/data/icons/analysis/fwhm.svg +156 -0
- datalab/data/icons/analysis/histogram.svg +49 -0
- datalab/data/icons/analysis/peak_detect.svg +160 -0
- datalab/data/icons/analysis/plot_results.svg +151 -0
- datalab/data/icons/analysis/show_results.svg +83 -0
- datalab/data/icons/analysis/stats.svg +49 -0
- datalab/data/icons/analysis.svg +120 -0
- datalab/data/icons/apply.svg +3 -0
- datalab/data/icons/check_all.svg +15 -0
- datalab/data/icons/collapse.svg +44 -0
- datalab/data/icons/collapse_selection.svg +63 -0
- datalab/data/icons/console.svg +101 -0
- datalab/data/icons/create/1d-normal.svg +8 -0
- datalab/data/icons/create/1d-poisson.svg +9 -0
- datalab/data/icons/create/1d-uniform.svg +8 -0
- datalab/data/icons/create/1d-zero.svg +57 -0
- datalab/data/icons/create/2d-gaussian.svg +56 -0
- datalab/data/icons/create/2d-normal.svg +38 -0
- datalab/data/icons/create/2d-poisson.svg +38 -0
- datalab/data/icons/create/2d-ramp.svg +90 -0
- datalab/data/icons/create/2d-sinc.svg +62 -0
- datalab/data/icons/create/2d-uniform.svg +38 -0
- datalab/data/icons/create/2d-zero.svg +13 -0
- datalab/data/icons/create/checkerboard.svg +39 -0
- datalab/data/icons/create/cosine.svg +12 -0
- datalab/data/icons/create/exponential.svg +55 -0
- datalab/data/icons/create/gaussian.svg +12 -0
- datalab/data/icons/create/grating.svg +29 -0
- datalab/data/icons/create/linear_chirp.svg +7 -0
- datalab/data/icons/create/logistic.svg +7 -0
- datalab/data/icons/create/lorentzian.svg +12 -0
- datalab/data/icons/create/planck.svg +12 -0
- datalab/data/icons/create/polynomial.svg +7 -0
- datalab/data/icons/create/pulse.svg +12 -0
- datalab/data/icons/create/ring.svg +18 -0
- datalab/data/icons/create/sawtooth.svg +7 -0
- datalab/data/icons/create/siemens.svg +35 -0
- datalab/data/icons/create/sinc.svg +12 -0
- datalab/data/icons/create/sine.svg +7 -0
- datalab/data/icons/create/square.svg +7 -0
- datalab/data/icons/create/square_pulse.svg +7 -0
- datalab/data/icons/create/step.svg +7 -0
- datalab/data/icons/create/step_pulse.svg +12 -0
- datalab/data/icons/create/triangle.svg +7 -0
- datalab/data/icons/create/voigt.svg +12 -0
- datalab/data/icons/edit/annotations.svg +72 -0
- datalab/data/icons/edit/annotations_copy.svg +114 -0
- datalab/data/icons/edit/annotations_delete.svg +83 -0
- datalab/data/icons/edit/annotations_edit.svg +98 -0
- datalab/data/icons/edit/annotations_export.svg +85 -0
- datalab/data/icons/edit/annotations_import.svg +85 -0
- datalab/data/icons/edit/annotations_paste.svg +100 -0
- datalab/data/icons/edit/copy_titles.svg +109 -0
- datalab/data/icons/edit/delete.svg +84 -0
- datalab/data/icons/edit/delete_all.svg +214 -0
- datalab/data/icons/edit/duplicate.svg +64 -0
- datalab/data/icons/edit/goto_source.svg +60 -0
- datalab/data/icons/edit/metadata.svg +60 -0
- datalab/data/icons/edit/metadata_add.svg +80 -0
- datalab/data/icons/edit/metadata_copy.svg +96 -0
- datalab/data/icons/edit/metadata_delete.svg +62 -0
- datalab/data/icons/edit/metadata_export.svg +68 -0
- datalab/data/icons/edit/metadata_import.svg +68 -0
- datalab/data/icons/edit/metadata_paste.svg +79 -0
- datalab/data/icons/edit/move_down.svg +55 -0
- datalab/data/icons/edit/move_up.svg +54 -0
- datalab/data/icons/edit/new_group.svg +76 -0
- datalab/data/icons/edit/recompute.svg +60 -0
- datalab/data/icons/edit/rename.svg +49 -0
- datalab/data/icons/edit.svg +16 -0
- datalab/data/icons/expand.svg +44 -0
- datalab/data/icons/expand_selection.svg +63 -0
- datalab/data/icons/fit/cdf_fit.svg +56 -0
- datalab/data/icons/fit/exponential_fit.svg +55 -0
- datalab/data/icons/fit/gaussian_fit.svg +62 -0
- datalab/data/icons/fit/interactive_fit.svg +101 -0
- datalab/data/icons/fit/linear_fit.svg +57 -0
- datalab/data/icons/fit/lorentzian_fit.svg +209 -0
- datalab/data/icons/fit/multigaussian_fit.svg +85 -0
- datalab/data/icons/fit/multilorentzian_fit.svg +85 -0
- datalab/data/icons/fit/piecewiseexponential_fit.svg +209 -0
- datalab/data/icons/fit/planckian_fit.svg +62 -0
- datalab/data/icons/fit/polynomial_fit.svg +59 -0
- datalab/data/icons/fit/sigmoid_fit.svg +56 -0
- datalab/data/icons/fit/sinusoidal_fit.svg +72 -0
- datalab/data/icons/fit/twohalfgaussian_fit.svg +63 -0
- datalab/data/icons/fit/voigt_fit.svg +57 -0
- datalab/data/icons/group.svg +56 -0
- datalab/data/icons/h5/h5array.svg +59 -0
- datalab/data/icons/h5/h5attrs.svg +75 -0
- datalab/data/icons/h5/h5browser.svg +133 -0
- datalab/data/icons/h5/h5file.svg +69 -0
- datalab/data/icons/h5/h5group.svg +49 -0
- datalab/data/icons/h5/h5scalar.svg +1 -0
- datalab/data/icons/help_pdf.svg +46 -0
- datalab/data/icons/history.svg +7 -0
- datalab/data/icons/image.svg +135 -0
- datalab/data/icons/io/fileopen_directory.svg +60 -0
- datalab/data/icons/io/fileopen_h5.svg +84 -0
- datalab/data/icons/io/fileopen_ima.svg +187 -0
- datalab/data/icons/io/fileopen_py.svg +123 -0
- datalab/data/icons/io/fileopen_sig.svg +138 -0
- datalab/data/icons/io/filesave_h5.svg +97 -0
- datalab/data/icons/io/filesave_ima.svg +200 -0
- datalab/data/icons/io/filesave_py.svg +136 -0
- datalab/data/icons/io/filesave_sig.svg +151 -0
- datalab/data/icons/io/import_text.svg +144 -0
- datalab/data/icons/io/save_to_directory.svg +134 -0
- datalab/data/icons/io.svg +84 -0
- datalab/data/icons/libre-camera-flash-off.svg +1 -0
- datalab/data/icons/libre-camera-flash-on.svg +1 -0
- datalab/data/icons/libre-gui-about.svg +1 -0
- datalab/data/icons/libre-gui-action-delete.svg +1 -0
- datalab/data/icons/libre-gui-add.svg +1 -0
- datalab/data/icons/libre-gui-arrow-down.svg +1 -0
- datalab/data/icons/libre-gui-arrow-left.svg +1 -0
- datalab/data/icons/libre-gui-arrow-right.svg +1 -0
- datalab/data/icons/libre-gui-arrow-up.svg +1 -0
- datalab/data/icons/libre-gui-close.svg +40 -0
- datalab/data/icons/libre-gui-cogs.svg +1 -0
- datalab/data/icons/libre-gui-globe.svg +1 -0
- datalab/data/icons/libre-gui-help.svg +1 -0
- datalab/data/icons/libre-gui-link.svg +1 -0
- datalab/data/icons/libre-gui-menu.svg +1 -0
- datalab/data/icons/libre-gui-pencil.svg +1 -0
- datalab/data/icons/libre-gui-plugin.svg +1 -0
- datalab/data/icons/libre-gui-questions.svg +1 -0
- datalab/data/icons/libre-gui-settings.svg +1 -0
- datalab/data/icons/libre-gui-unlink.svg +1 -0
- datalab/data/icons/libre-tech-ram.svg +1 -0
- datalab/data/icons/libre-toolbox.svg +1 -0
- datalab/data/icons/logs.svg +1 -0
- datalab/data/icons/markers.svg +74 -0
- datalab/data/icons/menu.svg +13 -0
- datalab/data/icons/new_ima.svg +148 -0
- datalab/data/icons/new_sig.svg +123 -0
- datalab/data/icons/operations/abs.svg +116 -0
- datalab/data/icons/operations/arithmetic.svg +123 -0
- datalab/data/icons/operations/average.svg +124 -0
- datalab/data/icons/operations/complex_from_magnitude_phase.svg +116 -0
- datalab/data/icons/operations/complex_from_real_imag.svg +124 -0
- datalab/data/icons/operations/constant.svg +116 -0
- datalab/data/icons/operations/constant_add.svg +109 -0
- datalab/data/icons/operations/constant_divide.svg +109 -0
- datalab/data/icons/operations/constant_multiply.svg +109 -0
- datalab/data/icons/operations/constant_subtract.svg +109 -0
- datalab/data/icons/operations/convert_dtype.svg +117 -0
- datalab/data/icons/operations/convolution.svg +46 -0
- datalab/data/icons/operations/deconvolution.svg +57 -0
- datalab/data/icons/operations/derivative.svg +127 -0
- datalab/data/icons/operations/difference.svg +52 -0
- datalab/data/icons/operations/division.svg +139 -0
- datalab/data/icons/operations/exp.svg +116 -0
- datalab/data/icons/operations/flip_horizontally.svg +69 -0
- datalab/data/icons/operations/flip_vertically.svg +74 -0
- datalab/data/icons/operations/im.svg +124 -0
- datalab/data/icons/operations/integral.svg +50 -0
- datalab/data/icons/operations/inverse.svg +143 -0
- datalab/data/icons/operations/log10.svg +109 -0
- datalab/data/icons/operations/phase.svg +116 -0
- datalab/data/icons/operations/power.svg +118 -0
- datalab/data/icons/operations/product.svg +124 -0
- datalab/data/icons/operations/profile.svg +379 -0
- datalab/data/icons/operations/profile_average.svg +399 -0
- datalab/data/icons/operations/profile_radial.svg +261 -0
- datalab/data/icons/operations/profile_segment.svg +262 -0
- datalab/data/icons/operations/quadratic_difference.svg +84 -0
- datalab/data/icons/operations/re.svg +124 -0
- datalab/data/icons/operations/rotate_left.svg +72 -0
- datalab/data/icons/operations/rotate_right.svg +72 -0
- datalab/data/icons/operations/signals_to_image.svg +314 -0
- datalab/data/icons/operations/sqrt.svg +110 -0
- datalab/data/icons/operations/std.svg +124 -0
- datalab/data/icons/operations/sum.svg +102 -0
- datalab/data/icons/play_demo.svg +9 -0
- datalab/data/icons/processing/axis_transform.svg +62 -0
- datalab/data/icons/processing/bandpass.svg +79 -0
- datalab/data/icons/processing/bandstop.svg +71 -0
- datalab/data/icons/processing/binning.svg +126 -0
- datalab/data/icons/processing/clip.svg +119 -0
- datalab/data/icons/processing/detrending.svg +173 -0
- datalab/data/icons/processing/distribute_on_grid.svg +769 -0
- datalab/data/icons/processing/edge_detection.svg +46 -0
- datalab/data/icons/processing/erase.svg +1 -0
- datalab/data/icons/processing/exposure.svg +143 -0
- datalab/data/icons/processing/fourier.svg +104 -0
- datalab/data/icons/processing/highpass.svg +59 -0
- datalab/data/icons/processing/interpolation.svg +71 -0
- datalab/data/icons/processing/level_adjustment.svg +70 -0
- datalab/data/icons/processing/lowpass.svg +60 -0
- datalab/data/icons/processing/morphology.svg +49 -0
- datalab/data/icons/processing/noise_addition.svg +114 -0
- datalab/data/icons/processing/noise_reduction.svg +38 -0
- datalab/data/icons/processing/normalize.svg +84 -0
- datalab/data/icons/processing/offset_correction.svg +131 -0
- datalab/data/icons/processing/resampling1d.svg +101 -0
- datalab/data/icons/processing/resampling2d.svg +240 -0
- datalab/data/icons/processing/reset_positions.svg +185 -0
- datalab/data/icons/processing/resize.svg +9 -0
- datalab/data/icons/processing/reverse_signal_x.svg +171 -0
- datalab/data/icons/processing/stability.svg +11 -0
- datalab/data/icons/processing/swap_x_y.svg +65 -0
- datalab/data/icons/processing/thresholding.svg +63 -0
- datalab/data/icons/processing/windowing.svg +45 -0
- datalab/data/icons/properties.svg +26 -0
- datalab/data/icons/reset.svg +9 -0
- datalab/data/icons/restore.svg +40 -0
- datalab/data/icons/roi/roi.svg +76 -0
- datalab/data/icons/roi/roi_coordinate.svg +78 -0
- datalab/data/icons/roi/roi_copy.svg +112 -0
- datalab/data/icons/roi/roi_delete.svg +81 -0
- datalab/data/icons/roi/roi_export.svg +87 -0
- datalab/data/icons/roi/roi_graphical.svg +78 -0
- datalab/data/icons/roi/roi_grid.svg +67 -0
- datalab/data/icons/roi/roi_ima.svg +188 -0
- datalab/data/icons/roi/roi_import.svg +87 -0
- datalab/data/icons/roi/roi_new.svg +81 -0
- datalab/data/icons/roi/roi_new_circle.svg +95 -0
- datalab/data/icons/roi/roi_new_polygon.svg +110 -0
- datalab/data/icons/roi/roi_new_rectangle.svg +70 -0
- datalab/data/icons/roi/roi_paste.svg +98 -0
- datalab/data/icons/roi/roi_sig.svg +124 -0
- datalab/data/icons/shapes.svg +134 -0
- datalab/data/icons/signal.svg +103 -0
- datalab/data/icons/table.svg +85 -0
- datalab/data/icons/table_unavailable.svg +102 -0
- datalab/data/icons/to_signal.svg +124 -0
- datalab/data/icons/tour/next.svg +44 -0
- datalab/data/icons/tour/previous.svg +44 -0
- datalab/data/icons/tour/rewind.svg +51 -0
- datalab/data/icons/tour/stop.svg +47 -0
- datalab/data/icons/tour/tour.svg +16 -0
- datalab/data/icons/uncheck_all.svg +78 -0
- datalab/data/icons/view/curve_antialiasing.svg +50 -0
- datalab/data/icons/view/new_window.svg +98 -0
- datalab/data/icons/view/refresh-auto.svg +57 -0
- datalab/data/icons/view/refresh-manual.svg +51 -0
- datalab/data/icons/view/reset_curve_styles.svg +96 -0
- datalab/data/icons/view/show_first.svg +55 -0
- datalab/data/icons/view/show_titles.svg +46 -0
- datalab/data/icons/visualization.svg +51 -0
- datalab/data/logo/DataLab-Banner-150.png +0 -0
- datalab/data/logo/DataLab-Banner-200.png +0 -0
- datalab/data/logo/DataLab-Banner2-100.png +0 -0
- datalab/data/logo/DataLab-Splash.png +0 -0
- datalab/data/logo/DataLab-watermark.png +0 -0
- datalab/data/logo/DataLab.svg +83 -0
- datalab/data/tests/reordering_test.h5 +0 -0
- datalab/data/tutorials/fabry_perot/fabry-perot1.jpg +0 -0
- datalab/data/tutorials/fabry_perot/fabry-perot2.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_13.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_18.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_23.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_30.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_35.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_40.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_45.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_50.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_55.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_60.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_65.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_70.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_75.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_80.jpg +0 -0
- datalab/env.py +542 -0
- datalab/gui/__init__.py +89 -0
- datalab/gui/actionhandler.py +1701 -0
- datalab/gui/docks.py +473 -0
- datalab/gui/h5io.py +150 -0
- datalab/gui/macroeditor.py +310 -0
- datalab/gui/main.py +2081 -0
- datalab/gui/newobject.py +217 -0
- datalab/gui/objectview.py +766 -0
- datalab/gui/panel/__init__.py +48 -0
- datalab/gui/panel/base.py +3254 -0
- datalab/gui/panel/image.py +157 -0
- datalab/gui/panel/macro.py +607 -0
- datalab/gui/panel/signal.py +164 -0
- datalab/gui/plothandler.py +800 -0
- datalab/gui/processor/__init__.py +84 -0
- datalab/gui/processor/base.py +2456 -0
- datalab/gui/processor/catcher.py +75 -0
- datalab/gui/processor/image.py +1214 -0
- datalab/gui/processor/signal.py +755 -0
- datalab/gui/profiledialog.py +333 -0
- datalab/gui/roieditor.py +633 -0
- datalab/gui/roigrideditor.py +208 -0
- datalab/gui/settings.py +612 -0
- datalab/gui/tour.py +908 -0
- datalab/h5/__init__.py +12 -0
- datalab/h5/common.py +314 -0
- datalab/h5/generic.py +580 -0
- datalab/h5/native.py +39 -0
- datalab/h5/utils.py +95 -0
- datalab/objectmodel.py +640 -0
- datalab/plugins/_readme_.txt +9 -0
- datalab/plugins/datalab_imageformats.py +175 -0
- datalab/plugins/datalab_testdata.py +190 -0
- datalab/plugins.py +355 -0
- datalab/tests/__init__.py +199 -0
- datalab/tests/backbone/__init__.py +1 -0
- datalab/tests/backbone/config_unit_test.py +170 -0
- datalab/tests/backbone/config_versioning_unit_test.py +34 -0
- datalab/tests/backbone/dictlistserial_app_test.py +38 -0
- datalab/tests/backbone/errorcatcher_unit_test.py +69 -0
- datalab/tests/backbone/errormsgbox_unit_test.py +50 -0
- datalab/tests/backbone/execenv_unit.py +262 -0
- datalab/tests/backbone/loadtest_gdi.py +147 -0
- datalab/tests/backbone/long_callback.py +96 -0
- datalab/tests/backbone/main_app_test.py +137 -0
- datalab/tests/backbone/memory_leak.py +43 -0
- datalab/tests/backbone/procisolation1_unit.py +128 -0
- datalab/tests/backbone/procisolation2_unit.py +171 -0
- datalab/tests/backbone/procisolation_unit_test.py +22 -0
- datalab/tests/backbone/profiling_app.py +27 -0
- datalab/tests/backbone/strings_unit_test.py +65 -0
- datalab/tests/backbone/title_formatting_unit_test.py +82 -0
- datalab/tests/conftest.py +131 -0
- datalab/tests/features/__init__.py +1 -0
- datalab/tests/features/applauncher/__init__.py +1 -0
- datalab/tests/features/applauncher/launcher1_app_test.py +28 -0
- datalab/tests/features/applauncher/launcher2_app_test.py +30 -0
- datalab/tests/features/common/__init__.py +1 -0
- datalab/tests/features/common/add_metadata_app_test.py +134 -0
- datalab/tests/features/common/add_metadata_unit_test.py +267 -0
- datalab/tests/features/common/annotations_management_unit_test.py +152 -0
- datalab/tests/features/common/auto_analysis_recompute_unit_test.py +240 -0
- datalab/tests/features/common/createobject_unit_test.py +50 -0
- datalab/tests/features/common/geometry_results_app_test.py +135 -0
- datalab/tests/features/common/interactive_processing_test.py +1109 -0
- datalab/tests/features/common/io_app_test.py +75 -0
- datalab/tests/features/common/large_results_app_test.py +187 -0
- datalab/tests/features/common/metadata_all_patterns_test.py +103 -0
- datalab/tests/features/common/metadata_app_test.py +139 -0
- datalab/tests/features/common/metadata_io_unit_test.py +60 -0
- datalab/tests/features/common/misc_app_test.py +236 -0
- datalab/tests/features/common/multiple_geometry_results_unit_test.py +122 -0
- datalab/tests/features/common/multiple_table_results_unit_test.py +64 -0
- datalab/tests/features/common/operation_modes_app_test.py +392 -0
- datalab/tests/features/common/plot_results_app_test.py +278 -0
- datalab/tests/features/common/reorder_app_test.py +75 -0
- datalab/tests/features/common/result_deletion_unit_test.py +96 -0
- datalab/tests/features/common/result_merged_label_unit_test.py +154 -0
- datalab/tests/features/common/result_shape_settings_unit_test.py +223 -0
- datalab/tests/features/common/roi_plotitem_unit_test.py +64 -0
- datalab/tests/features/common/roieditor_unit_test.py +102 -0
- datalab/tests/features/common/save_to_dir_app_test.py +163 -0
- datalab/tests/features/common/save_to_dir_unit_test.py +474 -0
- datalab/tests/features/common/stat_app_test.py +40 -0
- datalab/tests/features/common/stats_tools_unit_test.py +77 -0
- datalab/tests/features/common/table_results_app_test.py +52 -0
- datalab/tests/features/common/textimport_unit_test.py +131 -0
- datalab/tests/features/common/uuid_preservation_test.py +281 -0
- datalab/tests/features/common/worker_unit_test.py +402 -0
- datalab/tests/features/control/__init__.py +1 -0
- datalab/tests/features/control/connect_dialog.py +28 -0
- datalab/tests/features/control/embedded1_unit_test.py +304 -0
- datalab/tests/features/control/embedded2_unit_test.py +52 -0
- datalab/tests/features/control/remoteclient_app_test.py +219 -0
- datalab/tests/features/control/remoteclient_unit.py +75 -0
- datalab/tests/features/control/simpleclient_unit_test.py +321 -0
- datalab/tests/features/hdf5/__init__.py +1 -0
- datalab/tests/features/hdf5/h5browser1_unit_test.py +31 -0
- datalab/tests/features/hdf5/h5browser2_unit.py +55 -0
- datalab/tests/features/hdf5/h5browser_app_test.py +77 -0
- datalab/tests/features/hdf5/h5import_app_test.py +25 -0
- datalab/tests/features/hdf5/h5importer_app_test.py +34 -0
- datalab/tests/features/image/__init__.py +1 -0
- datalab/tests/features/image/annotations_app_test.py +28 -0
- datalab/tests/features/image/annotations_unit_test.py +80 -0
- datalab/tests/features/image/average_app_test.py +46 -0
- datalab/tests/features/image/background_dialog_test.py +70 -0
- datalab/tests/features/image/blobs_app_test.py +50 -0
- datalab/tests/features/image/contour_app_test.py +42 -0
- datalab/tests/features/image/contour_fabryperot_app_test.py +51 -0
- datalab/tests/features/image/denoise_app_test.py +31 -0
- datalab/tests/features/image/distribute_on_grid_app_test.py +95 -0
- datalab/tests/features/image/edges_app_test.py +31 -0
- datalab/tests/features/image/erase_app_test.py +21 -0
- datalab/tests/features/image/fft2d_app_test.py +27 -0
- datalab/tests/features/image/flatfield_app_test.py +40 -0
- datalab/tests/features/image/geometry_transform_unit_test.py +396 -0
- datalab/tests/features/image/imagetools_app_test.py +51 -0
- datalab/tests/features/image/imagetools_unit_test.py +27 -0
- datalab/tests/features/image/load_app_test.py +73 -0
- datalab/tests/features/image/morph_app_test.py +32 -0
- datalab/tests/features/image/offsetcorrection_app_test.py +30 -0
- datalab/tests/features/image/peak2d_app_test.py +53 -0
- datalab/tests/features/image/profile_app_test.py +73 -0
- datalab/tests/features/image/profile_dialog_test.py +56 -0
- datalab/tests/features/image/roi_app_test.py +98 -0
- datalab/tests/features/image/roi_circ_app_test.py +62 -0
- datalab/tests/features/image/roi_manipulation_app_test.py +268 -0
- datalab/tests/features/image/roigrid_unit_test.py +60 -0
- datalab/tests/features/image/side_by_side_app_test.py +52 -0
- datalab/tests/features/macro/__init__.py +1 -0
- datalab/tests/features/macro/macro_app_test.py +28 -0
- datalab/tests/features/macro/macroeditor_unit_test.py +102 -0
- datalab/tests/features/signal/__init__.py +1 -0
- datalab/tests/features/signal/baseline_dialog_test.py +53 -0
- datalab/tests/features/signal/deltax_dialog_unit_test.py +34 -0
- datalab/tests/features/signal/fft1d_app_test.py +26 -0
- datalab/tests/features/signal/filter_app_test.py +44 -0
- datalab/tests/features/signal/fitdialog_unit_test.py +50 -0
- datalab/tests/features/signal/interpolation_app_test.py +110 -0
- datalab/tests/features/signal/loadbigsignal_app_test.py +80 -0
- datalab/tests/features/signal/multiple_rois_unit_test.py +132 -0
- datalab/tests/features/signal/pulse_features_app_test.py +118 -0
- datalab/tests/features/signal/pulse_features_roi_app_test.py +55 -0
- datalab/tests/features/signal/roi_app_test.py +78 -0
- datalab/tests/features/signal/roi_manipulation_app_test.py +261 -0
- datalab/tests/features/signal/select_xy_cursor_unit_test.py +46 -0
- datalab/tests/features/signal/signalpeakdetection_dialog_test.py +33 -0
- datalab/tests/features/signal/signals_to_image_app_test.py +98 -0
- datalab/tests/features/signal/xarray_compat_app_test.py +128 -0
- datalab/tests/features/tour_unit_test.py +22 -0
- datalab/tests/features/utilities/__init__.py +1 -0
- datalab/tests/features/utilities/installconf_unit_test.py +21 -0
- datalab/tests/features/utilities/logview_app_test.py +21 -0
- datalab/tests/features/utilities/logview_error.py +24 -0
- datalab/tests/features/utilities/logview_unit_test.py +21 -0
- datalab/tests/features/utilities/memstatus_app_test.py +42 -0
- datalab/tests/features/utilities/settings_unit_test.py +88 -0
- datalab/tests/scenarios/__init__.py +1 -0
- datalab/tests/scenarios/beautiful_app.py +121 -0
- datalab/tests/scenarios/common.py +463 -0
- datalab/tests/scenarios/demo.py +212 -0
- datalab/tests/scenarios/example_app_test.py +47 -0
- datalab/tests/scenarios/scenario_h5_app_test.py +75 -0
- datalab/tests/scenarios/scenario_ima1_app_test.py +34 -0
- datalab/tests/scenarios/scenario_ima2_app_test.py +34 -0
- datalab/tests/scenarios/scenario_mac_app_test.py +58 -0
- datalab/tests/scenarios/scenario_sig1_app_test.py +36 -0
- datalab/tests/scenarios/scenario_sig2_app_test.py +35 -0
- datalab/utils/__init__.py +1 -0
- datalab/utils/conf.py +304 -0
- datalab/utils/dephash.py +105 -0
- datalab/utils/qthelpers.py +633 -0
- datalab/utils/strings.py +34 -0
- datalab/utils/tests.py +0 -0
- datalab/widgets/__init__.py +1 -0
- datalab/widgets/connection.py +138 -0
- datalab/widgets/filedialog.py +91 -0
- datalab/widgets/fileviewer.py +84 -0
- datalab/widgets/fitdialog.py +788 -0
- datalab/widgets/h5browser.py +1048 -0
- datalab/widgets/imagebackground.py +111 -0
- datalab/widgets/instconfviewer.py +175 -0
- datalab/widgets/logviewer.py +80 -0
- datalab/widgets/signalbaseline.py +90 -0
- datalab/widgets/signalcursor.py +208 -0
- datalab/widgets/signaldeltax.py +151 -0
- datalab/widgets/signalpeak.py +199 -0
- datalab/widgets/status.py +249 -0
- datalab/widgets/textimport.py +786 -0
- datalab/widgets/warningerror.py +223 -0
- datalab/widgets/wizard.py +286 -0
- datalab_platform-1.0.1.dist-info/METADATA +121 -0
- datalab_platform-1.0.1.dist-info/RECORD +494 -0
- datalab_platform-0.0.1.dev0.dist-info/METADATA +0 -67
- datalab_platform-0.0.1.dev0.dist-info/RECORD +0 -7
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/WHEEL +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/entry_points.txt +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/top_level.txt +0 -0
datalab/plugins.py
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
DataLab plugin system
|
|
5
|
+
---------------------
|
|
6
|
+
|
|
7
|
+
DataLab plugin system provides a way to extend the application with new
|
|
8
|
+
functionalities.
|
|
9
|
+
|
|
10
|
+
Plugins are Python modules that relies on two classes:
|
|
11
|
+
|
|
12
|
+
- :class:`PluginInfo`, which stores information about the plugin
|
|
13
|
+
- :class:`PluginBase`, which is the base class for all plugins
|
|
14
|
+
|
|
15
|
+
Plugins may also extends DataLab I/O features by providing new image or
|
|
16
|
+
signal formats. To do so, they must provide a subclass of :class:`ImageFormatBase`
|
|
17
|
+
or :class:`SignalFormatBase`, in which format information is defined using the
|
|
18
|
+
:class:`FormatInfo` class.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import abc
|
|
24
|
+
import dataclasses
|
|
25
|
+
import importlib
|
|
26
|
+
import os
|
|
27
|
+
import os.path as osp
|
|
28
|
+
import pkgutil
|
|
29
|
+
import sys
|
|
30
|
+
from typing import TYPE_CHECKING
|
|
31
|
+
|
|
32
|
+
from qtpy import QtWidgets as QW
|
|
33
|
+
|
|
34
|
+
# pylint: disable=unused-import
|
|
35
|
+
from sigima.io.base import FormatInfo # noqa: F401
|
|
36
|
+
from sigima.io.image.base import ImageFormatBase # noqa: F401
|
|
37
|
+
from sigima.io.image.formats import ClassicsImageFormat # noqa: F401
|
|
38
|
+
from sigima.io.signal.base import SignalFormatBase # noqa: F401
|
|
39
|
+
|
|
40
|
+
from datalab.config import MOD_NAME, OTHER_PLUGINS_PATHLIST, Conf, _
|
|
41
|
+
from datalab.control.proxy import LocalProxy
|
|
42
|
+
from datalab.env import execenv
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from sigima.objects import NewImageParam, NewSignalParam
|
|
46
|
+
|
|
47
|
+
from datalab.gui import main
|
|
48
|
+
from datalab.gui.panel.image import ImagePanel
|
|
49
|
+
from datalab.gui.panel.signal import SignalPanel
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
PLUGINS_DEFAULT_PATH = Conf.get_path("plugins")
|
|
53
|
+
|
|
54
|
+
if not osp.isdir(PLUGINS_DEFAULT_PATH):
|
|
55
|
+
os.makedirs(PLUGINS_DEFAULT_PATH)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# pylint: disable=bad-mcs-classmethod-argument
|
|
59
|
+
class PluginRegistry(type):
|
|
60
|
+
"""Metaclass for registering plugins"""
|
|
61
|
+
|
|
62
|
+
_plugin_classes: list[type[PluginBase]] = []
|
|
63
|
+
_plugin_instances: list[PluginBase] = []
|
|
64
|
+
|
|
65
|
+
def __init__(cls, name, bases, attrs):
|
|
66
|
+
super().__init__(name, bases, attrs)
|
|
67
|
+
if name != "PluginBase":
|
|
68
|
+
cls._plugin_classes.append(cls)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def get_plugin_classes(cls) -> list[type[PluginBase]]:
|
|
72
|
+
"""Return plugin classes"""
|
|
73
|
+
return cls._plugin_classes
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def get_plugins(cls) -> list[PluginBase]:
|
|
77
|
+
"""Return plugin instances"""
|
|
78
|
+
return cls._plugin_instances
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def get_plugin(cls, name_or_class: str | type[PluginBase]) -> PluginBase | None:
|
|
82
|
+
"""Return plugin instance"""
|
|
83
|
+
for plugin in cls._plugin_instances:
|
|
84
|
+
if name_or_class in (plugin.info.name, plugin.__class__):
|
|
85
|
+
return plugin
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def register_plugin(cls, plugin: PluginBase):
|
|
90
|
+
"""Register plugin"""
|
|
91
|
+
if plugin.info.name in [plug.info.name for plug in cls._plugin_instances]:
|
|
92
|
+
raise ValueError(f"Plugin {plugin.info.name} already registered")
|
|
93
|
+
cls._plugin_instances.append(plugin)
|
|
94
|
+
execenv.log(cls, f"Plugin {plugin.info.name} registered")
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def unregister_plugin(cls, plugin: PluginBase):
|
|
98
|
+
"""Unregister plugin"""
|
|
99
|
+
cls._plugin_instances.remove(plugin)
|
|
100
|
+
execenv.log(cls, f"Plugin {plugin.info.name} unregistered")
|
|
101
|
+
execenv.log(cls, f"{len(cls._plugin_instances)} plugins left")
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def unregister_all_plugins(cls):
|
|
105
|
+
"""Unregister all plugins"""
|
|
106
|
+
for plugin in cls._plugin_instances:
|
|
107
|
+
execenv.log(cls, f"Unregistering plugin {plugin.info.name}")
|
|
108
|
+
plugin.unregister()
|
|
109
|
+
cls._plugin_instances.clear()
|
|
110
|
+
execenv.log(cls, "All plugins unregistered")
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def get_plugin_info(cls, html: bool = True) -> str:
|
|
114
|
+
"""Return plugin information (names, versions, descriptions) in html format
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
html: return html formatted text (default: True)
|
|
118
|
+
"""
|
|
119
|
+
linesep = "<br>" if html else os.linesep
|
|
120
|
+
bullet = "• " if html else " " * 4
|
|
121
|
+
|
|
122
|
+
def italic(text: str) -> str:
|
|
123
|
+
"""Return italic text"""
|
|
124
|
+
return f"<i>{text}</i>" if html else text
|
|
125
|
+
|
|
126
|
+
if Conf.main.plugins_enabled.get():
|
|
127
|
+
plugins = cls.get_plugins()
|
|
128
|
+
if plugins:
|
|
129
|
+
text = italic(_("Registered plugins:"))
|
|
130
|
+
text += linesep
|
|
131
|
+
for plugin in plugins:
|
|
132
|
+
text += f"{bullet}{plugin.info.name} ({plugin.info.version})"
|
|
133
|
+
if plugin.info.description:
|
|
134
|
+
text += f": {plugin.info.description}"
|
|
135
|
+
text += linesep
|
|
136
|
+
else:
|
|
137
|
+
text = italic(_("No plugins available"))
|
|
138
|
+
else:
|
|
139
|
+
text = italic(_("Plugins are disabled (see DataLab settings)"))
|
|
140
|
+
return text
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclasses.dataclass
|
|
144
|
+
class PluginInfo:
|
|
145
|
+
"""Plugin info"""
|
|
146
|
+
|
|
147
|
+
name: str = None
|
|
148
|
+
version: str = "0.0.0"
|
|
149
|
+
description: str = ""
|
|
150
|
+
icon: str = None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class PluginBaseMeta(PluginRegistry, abc.ABCMeta):
|
|
154
|
+
"""Mixed metaclass to avoid conflicts"""
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class PluginBase(abc.ABC, metaclass=PluginBaseMeta):
|
|
158
|
+
"""Plugin base class"""
|
|
159
|
+
|
|
160
|
+
PLUGIN_INFO: PluginInfo = None
|
|
161
|
+
|
|
162
|
+
def __init__(self):
|
|
163
|
+
self.main: main.DLMainWindow = None
|
|
164
|
+
self.proxy: LocalProxy = None
|
|
165
|
+
self._is_registered = False
|
|
166
|
+
self.info = self.PLUGIN_INFO
|
|
167
|
+
if self.info is None:
|
|
168
|
+
raise ValueError(f"Plugin info not set for {self.__class__.__name__}")
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def signalpanel(self) -> SignalPanel:
|
|
172
|
+
"""Return signal panel"""
|
|
173
|
+
return self.main.signalpanel
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def imagepanel(self) -> ImagePanel:
|
|
177
|
+
"""Return image panel"""
|
|
178
|
+
return self.main.imagepanel
|
|
179
|
+
|
|
180
|
+
def show_warning(self, message: str):
|
|
181
|
+
"""Show warning message"""
|
|
182
|
+
QW.QMessageBox.warning(self.main, _("Warning"), message)
|
|
183
|
+
|
|
184
|
+
def show_error(self, message: str):
|
|
185
|
+
"""Show error message"""
|
|
186
|
+
QW.QMessageBox.critical(self.main, _("Error"), message)
|
|
187
|
+
|
|
188
|
+
def show_info(self, message: str):
|
|
189
|
+
"""Show info message"""
|
|
190
|
+
QW.QMessageBox.information(self.main, _("Information"), message)
|
|
191
|
+
|
|
192
|
+
def ask_yesno(
|
|
193
|
+
self, message: str, title: str | None = None, cancelable: bool = False
|
|
194
|
+
) -> bool:
|
|
195
|
+
"""Ask yes/no question"""
|
|
196
|
+
if title is None:
|
|
197
|
+
title = _("Question")
|
|
198
|
+
buttons = QW.QMessageBox.Yes | QW.QMessageBox.No
|
|
199
|
+
if cancelable:
|
|
200
|
+
buttons |= QW.QMessageBox.Cancel
|
|
201
|
+
answer = QW.QMessageBox.question(self.main, title, message, buttons)
|
|
202
|
+
if answer == QW.QMessageBox.Yes:
|
|
203
|
+
return True
|
|
204
|
+
if answer == QW.QMessageBox.No:
|
|
205
|
+
return False
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
def edit_new_signal_parameters(
|
|
209
|
+
self,
|
|
210
|
+
title: str | None = None,
|
|
211
|
+
size: int | None = None,
|
|
212
|
+
) -> NewSignalParam:
|
|
213
|
+
"""Create and edit new signal parameter dataset
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
title: title of the new signal
|
|
217
|
+
size: size of the new signal (default: None, get from current signal)
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
New signal parameter dataset (or None if canceled)
|
|
221
|
+
"""
|
|
222
|
+
newparam = self.signalpanel.get_newparam_from_current(title=title)
|
|
223
|
+
if size is not None:
|
|
224
|
+
newparam.size = size
|
|
225
|
+
if newparam.edit(self.main):
|
|
226
|
+
return newparam
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
def edit_new_image_parameters(
|
|
230
|
+
self,
|
|
231
|
+
title: str | None = None,
|
|
232
|
+
shape: tuple[int, int] | None = None,
|
|
233
|
+
hide_height: bool = False,
|
|
234
|
+
hide_width: bool = False,
|
|
235
|
+
hide_type: bool = True,
|
|
236
|
+
hide_dtype: bool = False,
|
|
237
|
+
) -> NewImageParam | None:
|
|
238
|
+
"""Create and edit new image parameter dataset
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
title: title of the new image
|
|
242
|
+
shape: shape of the new image (default: None, get from current image)
|
|
243
|
+
hide_height: hide image heigth parameter (default: False)
|
|
244
|
+
hide_width: hide image width parameter (default: False)
|
|
245
|
+
hide_type: hide image type parameter (default: True)
|
|
246
|
+
hide_dtype: hide image data type parameter (default: False)
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
New image parameter dataset (or None if canceled)
|
|
250
|
+
"""
|
|
251
|
+
newparam = self.imagepanel.get_newparam_from_current(title=title)
|
|
252
|
+
if shape is not None:
|
|
253
|
+
newparam.height, newparam.width = shape
|
|
254
|
+
newparam.hide_height = hide_height
|
|
255
|
+
newparam.hide_width = hide_width
|
|
256
|
+
newparam.hide_type = hide_type
|
|
257
|
+
newparam.hide_dtype = hide_dtype
|
|
258
|
+
if newparam.edit(self.main):
|
|
259
|
+
return newparam
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
def is_registered(self):
|
|
263
|
+
"""Return True if plugin is registered"""
|
|
264
|
+
return self._is_registered
|
|
265
|
+
|
|
266
|
+
def register(self, main: main.DLMainWindow) -> None:
|
|
267
|
+
"""Register plugin"""
|
|
268
|
+
if self._is_registered:
|
|
269
|
+
return
|
|
270
|
+
PluginRegistry.register_plugin(self)
|
|
271
|
+
self._is_registered = True
|
|
272
|
+
self.main = main
|
|
273
|
+
self.proxy = LocalProxy(main)
|
|
274
|
+
self.register_hooks()
|
|
275
|
+
|
|
276
|
+
def unregister(self):
|
|
277
|
+
"""Unregister plugin"""
|
|
278
|
+
if not self._is_registered:
|
|
279
|
+
return
|
|
280
|
+
PluginRegistry.unregister_plugin(self)
|
|
281
|
+
self._is_registered = False
|
|
282
|
+
self.unregister_hooks()
|
|
283
|
+
self.main = None
|
|
284
|
+
self.proxy = None
|
|
285
|
+
|
|
286
|
+
def register_hooks(self):
|
|
287
|
+
"""Register plugin hooks"""
|
|
288
|
+
|
|
289
|
+
def unregister_hooks(self):
|
|
290
|
+
"""Unregister plugin hooks"""
|
|
291
|
+
|
|
292
|
+
@abc.abstractmethod
|
|
293
|
+
def create_actions(self):
|
|
294
|
+
"""Create actions"""
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def discover_plugins() -> list[type[PluginBase]]:
|
|
298
|
+
"""Discover plugins using naming convention
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
List of discovered plugins (as classes)
|
|
302
|
+
"""
|
|
303
|
+
if Conf.main.plugins_enabled.get():
|
|
304
|
+
for path in [
|
|
305
|
+
Conf.main.plugins_path.get(),
|
|
306
|
+
PLUGINS_DEFAULT_PATH,
|
|
307
|
+
] + OTHER_PLUGINS_PATHLIST:
|
|
308
|
+
rpath = osp.realpath(path)
|
|
309
|
+
if rpath not in sys.path:
|
|
310
|
+
sys.path.append(rpath)
|
|
311
|
+
return [
|
|
312
|
+
importlib.import_module(name)
|
|
313
|
+
for _finder, name, _ispkg in pkgutil.iter_modules()
|
|
314
|
+
if name.startswith(f"{MOD_NAME}_")
|
|
315
|
+
]
|
|
316
|
+
return []
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def discover_v020_plugins() -> list[tuple[str, str]]:
|
|
320
|
+
"""Discover v0.20 plugins (with ``cdl_`` prefix) without importing them
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List of tuples (plugin_name, directory_path) for discovered v0.20 plugins
|
|
324
|
+
"""
|
|
325
|
+
v020_plugins = []
|
|
326
|
+
if Conf.main.plugins_enabled.get():
|
|
327
|
+
for path in [
|
|
328
|
+
Conf.main.plugins_path.get(),
|
|
329
|
+
PLUGINS_DEFAULT_PATH,
|
|
330
|
+
] + OTHER_PLUGINS_PATHLIST:
|
|
331
|
+
rpath = osp.realpath(path)
|
|
332
|
+
if rpath not in sys.path:
|
|
333
|
+
sys.path.append(rpath)
|
|
334
|
+
for finder, name, _ispkg in pkgutil.iter_modules():
|
|
335
|
+
if name.startswith("cdl_"):
|
|
336
|
+
# Get the directory path from the module finder
|
|
337
|
+
if hasattr(finder, "path"):
|
|
338
|
+
directory_path = finder.path
|
|
339
|
+
v020_plugins.append((name, directory_path))
|
|
340
|
+
else:
|
|
341
|
+
# Fallback if path is not available
|
|
342
|
+
v020_plugins.append((name, ""))
|
|
343
|
+
return v020_plugins
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def get_available_plugins() -> list[PluginBase]:
|
|
347
|
+
"""Instantiate and get available plugins
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
List of available plugins (as instances)
|
|
351
|
+
"""
|
|
352
|
+
# Note: this function is not used by DataLab itself, but it is used by the
|
|
353
|
+
# test suite to get a list of available plugins
|
|
354
|
+
discover_plugins()
|
|
355
|
+
return [plugin_class() for plugin_class in PluginRegistry.get_plugin_classes()]
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Tests (:mod:`datalab.tests`)
|
|
5
|
+
------------------------
|
|
6
|
+
|
|
7
|
+
The DataLab test suite is based on the `pytest <https://pytest.org>`_ framework.
|
|
8
|
+
|
|
9
|
+
The test suite modules are organized in subpackages according to their purpose.
|
|
10
|
+
The following subpackages are available:
|
|
11
|
+
|
|
12
|
+
- :mod:`datalab.tests.backbone`: backbone tests
|
|
13
|
+
- :mod:`datalab.tests.features`: feature tests (unit tests and application tests)
|
|
14
|
+
- :mod:`datalab.tests.scenarios`: high-level scenarios tests
|
|
15
|
+
|
|
16
|
+
.. seealso::
|
|
17
|
+
|
|
18
|
+
:ref:`validation` for more information about DataLab's testing strategy.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import os
|
|
24
|
+
import os.path as osp
|
|
25
|
+
import sys
|
|
26
|
+
import time
|
|
27
|
+
from contextlib import contextmanager
|
|
28
|
+
from typing import Generator
|
|
29
|
+
|
|
30
|
+
import psutil
|
|
31
|
+
import pytest
|
|
32
|
+
from guidata.guitest import run_testlauncher
|
|
33
|
+
from sigima.tests import helpers
|
|
34
|
+
|
|
35
|
+
import datalab.config # Loading icons
|
|
36
|
+
from datalab.config import MOD_NAME, SHOTPATH
|
|
37
|
+
from datalab.control.proxy import RemoteProxy
|
|
38
|
+
from datalab.env import execenv
|
|
39
|
+
from datalab.gui.main import DLMainWindow
|
|
40
|
+
from datalab.gui.panel.image import ImagePanel
|
|
41
|
+
from datalab.gui.panel.signal import SignalPanel
|
|
42
|
+
from datalab.utils import qthelpers as qth
|
|
43
|
+
|
|
44
|
+
# Add test data files and folders pointed by `DATALAB_DATA` environment variable:
|
|
45
|
+
helpers.add_test_path_from_env("DATALAB_DATA")
|
|
46
|
+
|
|
47
|
+
# Add test data files and folders for the DataLab module:
|
|
48
|
+
helpers.add_test_module_path(MOD_NAME, osp.join("data", "tests"))
|
|
49
|
+
|
|
50
|
+
# Set default screenshot path for tests
|
|
51
|
+
execenv.screenshot_path = SHOTPATH
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@contextmanager
|
|
55
|
+
def datalab_test_app_context(
|
|
56
|
+
size: tuple[int, int] = None,
|
|
57
|
+
maximized: bool = False,
|
|
58
|
+
save: bool = False,
|
|
59
|
+
console: bool | None = None,
|
|
60
|
+
exec_loop: bool = True,
|
|
61
|
+
) -> Generator[DLMainWindow, None, None]:
|
|
62
|
+
"""Context manager handling DataLab mainwindow creation and Qt event loop
|
|
63
|
+
with optional HDF5 file save and other options for testing purposes
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
size: mainwindow size (default: (950, 600))
|
|
67
|
+
maximized: whether to maximize mainwindow (default: False)
|
|
68
|
+
save: whether to save HDF5 file (default: False)
|
|
69
|
+
console: whether to show console (default: None)
|
|
70
|
+
exec_loop: whether to execute Qt event loop (default: True)
|
|
71
|
+
"""
|
|
72
|
+
if size is None:
|
|
73
|
+
size = 1200, 700
|
|
74
|
+
with qth.datalab_app_context(exec_loop=exec_loop):
|
|
75
|
+
win: DLMainWindow | None = None
|
|
76
|
+
try:
|
|
77
|
+
win = DLMainWindow(console=console)
|
|
78
|
+
if maximized:
|
|
79
|
+
win.showMaximized()
|
|
80
|
+
else:
|
|
81
|
+
width, height = size
|
|
82
|
+
win.resize(width, height)
|
|
83
|
+
win.showNormal()
|
|
84
|
+
win.show()
|
|
85
|
+
win.setObjectName(helpers.get_default_test_name()) # screenshot name
|
|
86
|
+
yield win
|
|
87
|
+
finally:
|
|
88
|
+
if save:
|
|
89
|
+
path = helpers.get_output_data_path("h5")
|
|
90
|
+
try:
|
|
91
|
+
os.remove(path)
|
|
92
|
+
win.save_to_h5_file(path)
|
|
93
|
+
except (FileNotFoundError, PermissionError):
|
|
94
|
+
pass
|
|
95
|
+
has_exception_occurred = sys.exc_info()[0] is not None
|
|
96
|
+
if not exec_loop or has_exception_occurred and win is not None:
|
|
97
|
+
# Closing main window properly
|
|
98
|
+
win.set_modified(False)
|
|
99
|
+
win.close()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def is_pid_alive(pid: int) -> bool:
|
|
103
|
+
"""Check if a process with the given PID is alive
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
Process ID to check
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
True if the process is alive, False otherwise
|
|
110
|
+
"""
|
|
111
|
+
return psutil.pid_exists(pid) and psutil.Process(pid).is_running()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def run_datalab_in_background(wait_until_ready: bool = True) -> None:
|
|
115
|
+
"""Run DataLab application as a service.
|
|
116
|
+
|
|
117
|
+
This function starts the DataLab application in a separate process, ensuring that
|
|
118
|
+
it runs independently of the current script. It sets the necessary environment
|
|
119
|
+
variables to prevent the application from quitting automatically (since the script
|
|
120
|
+
is executed in a non-interactive mode - the so-called "unattended" mode) and to
|
|
121
|
+
avoid port conflicts. After starting the application, it waits for a short period
|
|
122
|
+
to allow the application to initialize and then checks if the process is alive.
|
|
123
|
+
|
|
124
|
+
The main use case for this function is in testing scenarios where the DataLab
|
|
125
|
+
application needs to be running in the background while a client connects to it
|
|
126
|
+
and performs various operations.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
wait_until_ready: If True, waits until the DataLab application is ready to
|
|
130
|
+
accept connections (default: True). Uses RemoteProxy's built-in retry logic
|
|
131
|
+
with extended timeout to handle DataLab startup time.
|
|
132
|
+
|
|
133
|
+
Raises:
|
|
134
|
+
RuntimeError: If the DataLab application fails to start
|
|
135
|
+
"""
|
|
136
|
+
env = os.environ.copy()
|
|
137
|
+
env[execenv.DO_NOT_QUIT_ENV] = "1"
|
|
138
|
+
if execenv.XMLRPCPORT_ENV in env:
|
|
139
|
+
# May happen when executing other tests before
|
|
140
|
+
env.pop(execenv.XMLRPCPORT_ENV)
|
|
141
|
+
|
|
142
|
+
proc = helpers.exec_script(
|
|
143
|
+
"-m", args=["datalab.app"], wait=False, env=env, verbose=False
|
|
144
|
+
)
|
|
145
|
+
# If the process fails to start, it will raise the `AssertionError` exception
|
|
146
|
+
# with the message "Unable to start DataLab application".
|
|
147
|
+
# In that case, it might be useful to set `wait=True` and `verbose=True` in the
|
|
148
|
+
# `exec_script` call above, so that the script waits for the DataLab application
|
|
149
|
+
# to start and prints the output to the console. This way, you can see any
|
|
150
|
+
# error messages or logs that might help you understand why the application failed
|
|
151
|
+
# to start.
|
|
152
|
+
# If the script is executed within a pytest session, add the `-s` option to pytest.
|
|
153
|
+
|
|
154
|
+
# Give the process a moment to actually start
|
|
155
|
+
time.sleep(1)
|
|
156
|
+
if not is_pid_alive(proc.pid):
|
|
157
|
+
raise RuntimeError("DataLab process terminated immediately after start")
|
|
158
|
+
|
|
159
|
+
if wait_until_ready:
|
|
160
|
+
# Use RemoteProxy's built-in retry mechanism with extended timeout
|
|
161
|
+
# DataLab startup: Python imports, Qt init, GUI creation, XML-RPC server
|
|
162
|
+
try:
|
|
163
|
+
proxy = RemoteProxy(autoconnect=False)
|
|
164
|
+
proxy.connect(timeout=30.0) # 30 seconds max for DataLab to be ready
|
|
165
|
+
proxy.disconnect()
|
|
166
|
+
except ConnectionRefusedError as exc:
|
|
167
|
+
if is_pid_alive(proc.pid):
|
|
168
|
+
proc.kill()
|
|
169
|
+
raise RuntimeError(
|
|
170
|
+
"Failed to connect to DataLab application. "
|
|
171
|
+
"Process may have started but XML-RPC server is not responding."
|
|
172
|
+
) from exc
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@contextmanager
|
|
176
|
+
def skip_if_opencv_missing() -> Generator[None, None, None]:
|
|
177
|
+
"""Skip test if OpenCV is not available"""
|
|
178
|
+
try:
|
|
179
|
+
yield
|
|
180
|
+
except ImportError as exc:
|
|
181
|
+
if "cv2" in str(exc).lower():
|
|
182
|
+
pytest.skip("OpenCV not available, skipping test")
|
|
183
|
+
raise exc
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def take_plotwidget_screenshot(panel: SignalPanel | ImagePanel, name: str) -> None:
|
|
187
|
+
"""Eventually takes plotwidget screenshot (only in screenshot mode)"""
|
|
188
|
+
if execenv.screenshot:
|
|
189
|
+
prefix = panel.PARAMCLASS.PREFIX
|
|
190
|
+
qth.grab_save_window(panel.plothandler.plotwidget, f"{prefix}_{name}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def run() -> None:
|
|
194
|
+
"""Run DataLab test launcher"""
|
|
195
|
+
run_testlauncher(datalab)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
run()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#
|