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
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
DataLab remote control
|
|
5
|
+
----------------------
|
|
6
|
+
|
|
7
|
+
This module provides utilities to control DataLab from a Python script (e.g. with
|
|
8
|
+
Spyder) or from a Jupyter notebook.
|
|
9
|
+
|
|
10
|
+
The :class:`RemoteClient` class provides the main interface to DataLab XML-RPC server.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import functools
|
|
16
|
+
import sys
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
import warnings
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from typing import TYPE_CHECKING
|
|
22
|
+
from xmlrpc.client import Binary, ServerProxy
|
|
23
|
+
from xmlrpc.server import SimpleXMLRPCServer
|
|
24
|
+
|
|
25
|
+
import guidata.dataset as gds
|
|
26
|
+
import numpy as np
|
|
27
|
+
from packaging.version import Version
|
|
28
|
+
from qtpy import QtCore as QC
|
|
29
|
+
from sigima.client import utils
|
|
30
|
+
from sigima.objects import ImageObj, SignalObj, create_image, create_signal
|
|
31
|
+
|
|
32
|
+
import datalab
|
|
33
|
+
from datalab.adapters_plotpy import items_to_json, json_to_items
|
|
34
|
+
from datalab.config import Conf, initialize
|
|
35
|
+
from datalab.control.baseproxy import AbstractDLControl, BaseProxy
|
|
36
|
+
from datalab.env import execenv
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from datalab.gui.main import DLMainWindow
|
|
40
|
+
|
|
41
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
42
|
+
# pylint: disable=duplicate-code
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def remote_call(func: Callable) -> object:
|
|
46
|
+
"""Decorator for method calling DataLab main window remotely"""
|
|
47
|
+
|
|
48
|
+
@functools.wraps(func)
|
|
49
|
+
def method_wrapper(*args, **kwargs):
|
|
50
|
+
"""Decorator wrapper function"""
|
|
51
|
+
self = args[0] # extracting 'self' from method arguments
|
|
52
|
+
self.is_ready = False
|
|
53
|
+
output = func(*args, **kwargs)
|
|
54
|
+
while not self.is_ready:
|
|
55
|
+
QC.QCoreApplication.processEvents()
|
|
56
|
+
time.sleep(0.05)
|
|
57
|
+
return output
|
|
58
|
+
|
|
59
|
+
return method_wrapper
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Note: RemoteServer can't inherit from AbstractDLControl because it is a QThread
|
|
63
|
+
# and most of the methods are not returning expected data types
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RemoteServer(QC.QThread):
|
|
67
|
+
"""XML-RPC server QThread"""
|
|
68
|
+
|
|
69
|
+
SIG_SERVER_PORT = QC.Signal(int)
|
|
70
|
+
SIG_CLOSE_APP = QC.Signal()
|
|
71
|
+
SIG_RAISE_WINDOW = QC.Signal()
|
|
72
|
+
SIG_ADD_OBJECT = QC.Signal(object, str, bool)
|
|
73
|
+
SIG_ADD_GROUP = QC.Signal(str, str, bool)
|
|
74
|
+
SIG_LOAD_FROM_FILES = QC.Signal(list)
|
|
75
|
+
SIG_LOAD_FROM_DIRECTORY = QC.Signal(str)
|
|
76
|
+
SIG_SELECT_OBJECTS = QC.Signal(list, str)
|
|
77
|
+
SIG_SELECT_GROUPS = QC.Signal(list, str)
|
|
78
|
+
SIG_SELECT_ALL_GROUPS = QC.Signal(str)
|
|
79
|
+
SIG_DELETE_METADATA = QC.Signal(bool, bool)
|
|
80
|
+
SIG_SWITCH_TO_PANEL = QC.Signal(str)
|
|
81
|
+
SIG_TOGGLE_AUTO_REFRESH = QC.Signal(bool)
|
|
82
|
+
SIG_TOGGLE_SHOW_TITLES = QC.Signal(bool)
|
|
83
|
+
SIG_RESET_ALL = QC.Signal()
|
|
84
|
+
SIG_SAVE_TO_H5 = QC.Signal(str)
|
|
85
|
+
SIG_OPEN_H5 = QC.Signal(list, bool, bool)
|
|
86
|
+
SIG_IMPORT_H5 = QC.Signal(str, bool)
|
|
87
|
+
SIG_CALC = QC.Signal(str, object)
|
|
88
|
+
SIG_RUN_MACRO = QC.Signal(str)
|
|
89
|
+
SIG_STOP_MACRO = QC.Signal(str)
|
|
90
|
+
SIG_IMPORT_MACRO_FROM_FILE = QC.Signal(str)
|
|
91
|
+
|
|
92
|
+
def __init__(self, win: DLMainWindow) -> None:
|
|
93
|
+
QC.QThread.__init__(self)
|
|
94
|
+
self.port: int = None
|
|
95
|
+
self.is_ready = True
|
|
96
|
+
self.server: SimpleXMLRPCServer | None = None
|
|
97
|
+
self.win = win
|
|
98
|
+
win.SIG_READY.connect(self.datalab_is_ready)
|
|
99
|
+
win.SIG_CLOSING.connect(self.shutdown_server)
|
|
100
|
+
self.SIG_CLOSE_APP.connect(win.close)
|
|
101
|
+
self.SIG_RAISE_WINDOW.connect(win.raise_window)
|
|
102
|
+
self.SIG_ADD_OBJECT.connect(win.add_object)
|
|
103
|
+
self.SIG_ADD_GROUP.connect(win.add_group)
|
|
104
|
+
self.SIG_LOAD_FROM_FILES.connect(win.load_from_files)
|
|
105
|
+
self.SIG_LOAD_FROM_DIRECTORY.connect(win.load_from_directory)
|
|
106
|
+
self.SIG_SELECT_OBJECTS.connect(win.select_objects)
|
|
107
|
+
self.SIG_SELECT_GROUPS.connect(win.select_groups)
|
|
108
|
+
self.SIG_SELECT_ALL_GROUPS.connect(lambda panel: win.select_groups(None, panel))
|
|
109
|
+
self.SIG_DELETE_METADATA.connect(win.delete_metadata)
|
|
110
|
+
self.SIG_SWITCH_TO_PANEL.connect(win.set_current_panel)
|
|
111
|
+
self.SIG_TOGGLE_AUTO_REFRESH.connect(win.toggle_auto_refresh)
|
|
112
|
+
self.SIG_TOGGLE_SHOW_TITLES.connect(win.toggle_show_titles)
|
|
113
|
+
self.SIG_RESET_ALL.connect(win.reset_all)
|
|
114
|
+
self.SIG_SAVE_TO_H5.connect(win.save_to_h5_file)
|
|
115
|
+
self.SIG_OPEN_H5.connect(win.open_h5_files)
|
|
116
|
+
self.SIG_IMPORT_H5.connect(win.import_h5_file)
|
|
117
|
+
self.SIG_CALC.connect(win.calc)
|
|
118
|
+
self.SIG_RUN_MACRO.connect(win.run_macro)
|
|
119
|
+
self.SIG_STOP_MACRO.connect(win.stop_macro)
|
|
120
|
+
self.SIG_IMPORT_MACRO_FROM_FILE.connect(win.import_macro_from_file)
|
|
121
|
+
|
|
122
|
+
def serve(self) -> None:
|
|
123
|
+
"""Start server and serve forever"""
|
|
124
|
+
with SimpleXMLRPCServer(
|
|
125
|
+
("127.0.0.1", 0), logRequests=False, allow_none=True
|
|
126
|
+
) as server:
|
|
127
|
+
self.server = server
|
|
128
|
+
server.register_introspection_functions()
|
|
129
|
+
self.register_functions(server)
|
|
130
|
+
self.port = server.server_address[1]
|
|
131
|
+
self.notify_port(self.port)
|
|
132
|
+
with execenv.context(xmlrpcport=self.port):
|
|
133
|
+
server.serve_forever()
|
|
134
|
+
|
|
135
|
+
def shutdown_server(self) -> None:
|
|
136
|
+
"""Shutdown server"""
|
|
137
|
+
if self.server is not None:
|
|
138
|
+
self.server.shutdown()
|
|
139
|
+
self.server = None
|
|
140
|
+
|
|
141
|
+
def notify_port(self, port: int) -> None:
|
|
142
|
+
"""Notify automatically attributed port.
|
|
143
|
+
|
|
144
|
+
This method is called after the server port has been automatically
|
|
145
|
+
attributed. It notifies the port number to the main window.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
port: Server port number
|
|
149
|
+
"""
|
|
150
|
+
self.SIG_SERVER_PORT.emit(port)
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def check_remote_functions(cls) -> None:
|
|
154
|
+
"""Check if all AbstractDLControl methods are implemented in RemoteServer"""
|
|
155
|
+
mlist = []
|
|
156
|
+
for method in AbstractDLControl.get_public_methods():
|
|
157
|
+
if not hasattr(cls, method):
|
|
158
|
+
mlist.append(method)
|
|
159
|
+
if mlist:
|
|
160
|
+
raise RuntimeError(f"{cls} is missing some methods: {','.join(mlist)}")
|
|
161
|
+
|
|
162
|
+
def register_functions(self, server: SimpleXMLRPCServer) -> None:
|
|
163
|
+
"""Register functions"""
|
|
164
|
+
for name in AbstractDLControl.get_public_methods():
|
|
165
|
+
server.register_function(getattr(self, name))
|
|
166
|
+
|
|
167
|
+
def run(self) -> None:
|
|
168
|
+
"""Thread execution method"""
|
|
169
|
+
if "coverage" in sys.modules:
|
|
170
|
+
# The following is required to make coverage work with threading
|
|
171
|
+
# pylint: disable=protected-access
|
|
172
|
+
sys.settrace(threading._trace_hook)
|
|
173
|
+
self.serve()
|
|
174
|
+
|
|
175
|
+
def datalab_is_ready(self) -> None:
|
|
176
|
+
"""Called when DataLab is ready to process new requests"""
|
|
177
|
+
self.is_ready = True
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def get_version() -> str:
|
|
181
|
+
"""Return DataLab public version"""
|
|
182
|
+
return datalab.__version__
|
|
183
|
+
|
|
184
|
+
def close_application(self) -> None:
|
|
185
|
+
"""Close DataLab application"""
|
|
186
|
+
self.SIG_CLOSE_APP.emit()
|
|
187
|
+
|
|
188
|
+
def raise_window(self) -> None:
|
|
189
|
+
"""Raise DataLab window"""
|
|
190
|
+
self.SIG_RAISE_WINDOW.emit()
|
|
191
|
+
|
|
192
|
+
@remote_call
|
|
193
|
+
def get_current_panel(self) -> str:
|
|
194
|
+
"""Return current panel name.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Panel name (valid values: 'signal', 'image', 'macro')
|
|
198
|
+
"""
|
|
199
|
+
return self.win.get_current_panel()
|
|
200
|
+
|
|
201
|
+
@remote_call
|
|
202
|
+
def set_current_panel(self, panel: str) -> None:
|
|
203
|
+
"""Switch to panel.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
panel: Panel name (valid values: 'signal', 'image', 'macro')
|
|
207
|
+
"""
|
|
208
|
+
self.SIG_SWITCH_TO_PANEL.emit(panel)
|
|
209
|
+
|
|
210
|
+
@remote_call
|
|
211
|
+
def toggle_auto_refresh(self, state: bool) -> None:
|
|
212
|
+
"""Toggle auto refresh state.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
state: True to enable auto refresh, False to disable it
|
|
216
|
+
"""
|
|
217
|
+
self.SIG_TOGGLE_AUTO_REFRESH.emit(state)
|
|
218
|
+
|
|
219
|
+
@remote_call
|
|
220
|
+
def toggle_show_titles(self, state: bool) -> None:
|
|
221
|
+
"""Toggle show titles state.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
state: True to enable show titles, False to disable it
|
|
225
|
+
"""
|
|
226
|
+
self.SIG_TOGGLE_SHOW_TITLES.emit(state)
|
|
227
|
+
|
|
228
|
+
@remote_call
|
|
229
|
+
def reset_all(self) -> None:
|
|
230
|
+
"""Reset all application data"""
|
|
231
|
+
self.SIG_RESET_ALL.emit()
|
|
232
|
+
|
|
233
|
+
@remote_call
|
|
234
|
+
def save_to_h5_file(self, filename: str) -> None:
|
|
235
|
+
"""Save to a DataLab HDF5 file.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
filename: HDF5 file name (with extension .h5)
|
|
239
|
+
"""
|
|
240
|
+
self.SIG_SAVE_TO_H5.emit(filename)
|
|
241
|
+
|
|
242
|
+
@remote_call
|
|
243
|
+
def open_h5_files(
|
|
244
|
+
self,
|
|
245
|
+
h5files: list[str] | None = None,
|
|
246
|
+
import_all: bool | None = None,
|
|
247
|
+
reset_all: bool | None = None,
|
|
248
|
+
) -> None:
|
|
249
|
+
"""Open a DataLab HDF5 file or import from any other HDF5 file.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
h5files: HDF5 file names. Defaults to None.
|
|
253
|
+
import_all: Import all objects from HDF5 file. Defaults to None.
|
|
254
|
+
reset_all: Reset all application data. Defaults to None.
|
|
255
|
+
"""
|
|
256
|
+
import_all = True if import_all is None else import_all
|
|
257
|
+
reset_all = False if reset_all is None else reset_all
|
|
258
|
+
self.SIG_OPEN_H5.emit(h5files, import_all, reset_all)
|
|
259
|
+
|
|
260
|
+
@remote_call
|
|
261
|
+
def import_h5_file(self, filename: str, reset_all: bool | None = None) -> None:
|
|
262
|
+
"""Open DataLab HDF5 browser to Import HDF5 file.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
filename: HDF5 file name
|
|
266
|
+
reset_all: Reset all application data. Defaults to None.
|
|
267
|
+
"""
|
|
268
|
+
reset_all = False if reset_all is None else reset_all
|
|
269
|
+
self.SIG_IMPORT_H5.emit(filename, reset_all)
|
|
270
|
+
|
|
271
|
+
@remote_call
|
|
272
|
+
def load_from_files(self, filenames: list[str]) -> None:
|
|
273
|
+
"""Open objects from files in current panel (signals/images).
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
filenames: list of file names
|
|
277
|
+
"""
|
|
278
|
+
self.SIG_LOAD_FROM_FILES.emit(filenames)
|
|
279
|
+
|
|
280
|
+
@remote_call
|
|
281
|
+
def load_from_directory(self, path: str) -> None:
|
|
282
|
+
"""Open objects from directory in current panel (signals/images).
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
path: directory path
|
|
286
|
+
"""
|
|
287
|
+
self.SIG_LOAD_FROM_DIRECTORY.emit(path)
|
|
288
|
+
|
|
289
|
+
@remote_call
|
|
290
|
+
def add_signal(
|
|
291
|
+
self,
|
|
292
|
+
title: str,
|
|
293
|
+
xbinary: Binary,
|
|
294
|
+
ybinary: Binary,
|
|
295
|
+
xunit: str = "",
|
|
296
|
+
yunit: str = "",
|
|
297
|
+
xlabel: str = "",
|
|
298
|
+
ylabel: str = "",
|
|
299
|
+
group_id: str = "",
|
|
300
|
+
set_current: bool = True,
|
|
301
|
+
) -> bool: # pylint: disable=too-many-arguments
|
|
302
|
+
"""Add signal data to DataLab.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
title: Signal title
|
|
306
|
+
xbinary: X data
|
|
307
|
+
ybinary: Y data
|
|
308
|
+
xunit: X unit. Defaults to ""
|
|
309
|
+
yunit: Y unit. Defaults to ""
|
|
310
|
+
xlabel: X label. Defaults to ""
|
|
311
|
+
ylabel: Y label. Defaults to ""
|
|
312
|
+
group_id: group id in which to add the signal. Defaults to ""
|
|
313
|
+
set_current: if True, set the added signal as current
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
True if successful
|
|
317
|
+
"""
|
|
318
|
+
xdata = utils.rpcbinary_to_array(xbinary)
|
|
319
|
+
ydata = utils.rpcbinary_to_array(ybinary)
|
|
320
|
+
signal = create_signal(title, xdata, ydata)
|
|
321
|
+
signal.xunit = xunit or "" # In case xunit is None
|
|
322
|
+
signal.yunit = yunit or "" # In case yunit is None
|
|
323
|
+
signal.xlabel = xlabel or "" # In case xlabel is None
|
|
324
|
+
signal.ylabel = ylabel or "" # In case ylabel is None
|
|
325
|
+
self.SIG_ADD_OBJECT.emit(signal, group_id, set_current)
|
|
326
|
+
return True
|
|
327
|
+
|
|
328
|
+
@remote_call
|
|
329
|
+
def add_image(
|
|
330
|
+
self,
|
|
331
|
+
title: str,
|
|
332
|
+
zbinary: Binary,
|
|
333
|
+
xunit: str = "",
|
|
334
|
+
yunit: str = "",
|
|
335
|
+
zunit: str = "",
|
|
336
|
+
xlabel: str = "",
|
|
337
|
+
ylabel: str = "",
|
|
338
|
+
zlabel: str = "",
|
|
339
|
+
group_id: str = "",
|
|
340
|
+
set_current: bool = True,
|
|
341
|
+
) -> bool: # pylint: disable=too-many-arguments
|
|
342
|
+
"""Add image data to DataLab.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
title: Image title
|
|
346
|
+
zbinary: Z data
|
|
347
|
+
xunit: X unit. Defaults to ""
|
|
348
|
+
yunit: Y unit. Defaults to ""
|
|
349
|
+
zunit: Z unit. Defaults to ""
|
|
350
|
+
xlabel: X label. Defaults to ""
|
|
351
|
+
ylabel: Y label. Defaults to ""
|
|
352
|
+
zlabel: Z label. Defaults to ""
|
|
353
|
+
group_id: group id in which to add the image. Defaults to ""
|
|
354
|
+
set_current: if True, set the added image as current
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
True if successful
|
|
358
|
+
"""
|
|
359
|
+
data = utils.rpcbinary_to_array(zbinary)
|
|
360
|
+
image = create_image(title, data)
|
|
361
|
+
image.xunit = xunit or "" # In case xunit is None
|
|
362
|
+
image.yunit = yunit or "" # In case yunit is None
|
|
363
|
+
image.zunit = zunit or "" # In case zunit is None
|
|
364
|
+
image.xlabel = xlabel or "" # In case xlabel is None
|
|
365
|
+
image.ylabel = ylabel or "" # In case ylabel is None
|
|
366
|
+
image.zlabel = zlabel or "" # In case zlabel is None
|
|
367
|
+
self.SIG_ADD_OBJECT.emit(image, group_id, set_current)
|
|
368
|
+
return True
|
|
369
|
+
|
|
370
|
+
@remote_call
|
|
371
|
+
def add_object(
|
|
372
|
+
self, obj_data: list[str], group_id: str = "", set_current: bool = True
|
|
373
|
+
) -> bool:
|
|
374
|
+
"""Add object to DataLab.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
obj_data: Object data
|
|
378
|
+
group_id: Group id in which to add the object. Defaults to ""
|
|
379
|
+
set_current: if True, set the added object as current
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
True if successful
|
|
383
|
+
"""
|
|
384
|
+
obj = utils.rpcjson_to_dataset(obj_data)
|
|
385
|
+
self.SIG_ADD_OBJECT.emit(obj, group_id, set_current)
|
|
386
|
+
return True
|
|
387
|
+
|
|
388
|
+
@remote_call
|
|
389
|
+
def get_sel_object_uuids(self, include_groups: bool = False) -> list[str]:
|
|
390
|
+
"""Return selected objects uuids.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
include_groups: If True, also return objects from selected groups.
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
List of selected objects uuids.
|
|
397
|
+
"""
|
|
398
|
+
return self.win.get_sel_object_uuids(include_groups)
|
|
399
|
+
|
|
400
|
+
@remote_call
|
|
401
|
+
def add_group(
|
|
402
|
+
self, title: str, panel: str | None = None, select: bool = False
|
|
403
|
+
) -> None:
|
|
404
|
+
"""Add group to DataLab.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
title: Group title
|
|
408
|
+
panel: Panel name (valid values: "signal", "image"). Defaults to None.
|
|
409
|
+
select: Select the group after creation. Defaults to False.
|
|
410
|
+
"""
|
|
411
|
+
self.SIG_ADD_GROUP.emit(title, panel, select)
|
|
412
|
+
|
|
413
|
+
@remote_call
|
|
414
|
+
def select_objects(
|
|
415
|
+
self,
|
|
416
|
+
selection: list[int | str],
|
|
417
|
+
panel: str | None = None,
|
|
418
|
+
) -> None:
|
|
419
|
+
"""Select objects in current panel.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
selection: List of object numbers (1 to N) or uuids to select
|
|
423
|
+
panel: panel name (valid values: "signal", "image").
|
|
424
|
+
If None, current panel is used. Defaults to None.
|
|
425
|
+
"""
|
|
426
|
+
self.SIG_SELECT_OBJECTS.emit(selection, panel)
|
|
427
|
+
|
|
428
|
+
@remote_call
|
|
429
|
+
def select_groups(
|
|
430
|
+
self, selection: list[int | str] | None = None, panel: str | None = None
|
|
431
|
+
) -> None:
|
|
432
|
+
"""Select groups in current panel.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
selection: List of group numbers (1 to N), or list of group uuids,
|
|
436
|
+
or None to select all groups. Defaults to None.
|
|
437
|
+
panel: panel name (valid values: "signal", "image").
|
|
438
|
+
If None, current panel is used. Defaults to None.
|
|
439
|
+
"""
|
|
440
|
+
if selection is None:
|
|
441
|
+
self.SIG_SELECT_ALL_GROUPS.emit(panel)
|
|
442
|
+
else:
|
|
443
|
+
self.SIG_SELECT_GROUPS.emit(selection, panel)
|
|
444
|
+
|
|
445
|
+
@remote_call
|
|
446
|
+
def delete_metadata(
|
|
447
|
+
self, refresh_plot: bool = True, keep_roi: bool = False
|
|
448
|
+
) -> None:
|
|
449
|
+
"""Delete metadata of selected objects
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
refresh_plot: Refresh plot. Defaults to True.
|
|
453
|
+
keep_roi: Keep ROI. Defaults to False.
|
|
454
|
+
"""
|
|
455
|
+
self.SIG_DELETE_METADATA.emit(refresh_plot, keep_roi)
|
|
456
|
+
|
|
457
|
+
@remote_call
|
|
458
|
+
def calc(self, name: str, param_data: list[str] | None = None) -> bool:
|
|
459
|
+
"""Call computation feature ``name``
|
|
460
|
+
|
|
461
|
+
.. note::
|
|
462
|
+
|
|
463
|
+
This calls either the processor's ``compute_<name>`` method (if it exists),
|
|
464
|
+
or the processor's ``<name>`` computation feature (if it is registered,
|
|
465
|
+
using the ``run_feature`` method).
|
|
466
|
+
It looks for the function in all panels, starting with the current one.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
name: Compute function name
|
|
470
|
+
param_data: Compute function parameters. Defaults to None.
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
True if successful, False otherwise
|
|
474
|
+
"""
|
|
475
|
+
if param_data is None:
|
|
476
|
+
param = None
|
|
477
|
+
else:
|
|
478
|
+
param = utils.rpcjson_to_dataset(param_data)
|
|
479
|
+
self.SIG_CALC.emit(name, param)
|
|
480
|
+
return True
|
|
481
|
+
|
|
482
|
+
@remote_call
|
|
483
|
+
def get_group_titles_with_object_info(
|
|
484
|
+
self,
|
|
485
|
+
) -> tuple[list[str], list[list[str]], list[list[str]]]:
|
|
486
|
+
"""Return groups titles and lists of inner objects uuids and titles.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
Groups titles, lists of inner objects uuids and titles
|
|
490
|
+
"""
|
|
491
|
+
return self.win.get_group_titles_with_object_info()
|
|
492
|
+
|
|
493
|
+
@remote_call
|
|
494
|
+
def get_object_titles(self, panel: str | None = None) -> list[str]:
|
|
495
|
+
"""Get object (signal/image) list for current panel.
|
|
496
|
+
Objects are sorted by group number and object index in group.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
panel: panel name (valid values: "signal", "image", "macro").
|
|
500
|
+
If None, current data panel is used (i.e. signal or image panel).
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
List of object titles
|
|
504
|
+
"""
|
|
505
|
+
return self.win.get_object_titles(panel)
|
|
506
|
+
|
|
507
|
+
@remote_call
|
|
508
|
+
def get_object(
|
|
509
|
+
self,
|
|
510
|
+
nb_id_title: int | str | None = None,
|
|
511
|
+
panel: str | None = None,
|
|
512
|
+
) -> list[str]:
|
|
513
|
+
"""Get object (signal/image) from index.
|
|
514
|
+
|
|
515
|
+
Args:
|
|
516
|
+
nb_id_title: Object number, or object id, or object title.
|
|
517
|
+
Defaults to None (current object).
|
|
518
|
+
panel: Panel name. Defaults to None (current panel).
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
Object
|
|
522
|
+
|
|
523
|
+
Raises:
|
|
524
|
+
KeyError: if object not found
|
|
525
|
+
"""
|
|
526
|
+
obj = self.win.get_object(nb_id_title, panel)
|
|
527
|
+
if obj is None:
|
|
528
|
+
return None
|
|
529
|
+
return utils.dataset_to_rpcjson(obj)
|
|
530
|
+
|
|
531
|
+
@remote_call
|
|
532
|
+
def get_object_uuids(
|
|
533
|
+
self, panel: str | None = None, group: int | str | None = None
|
|
534
|
+
) -> list[str]:
|
|
535
|
+
"""Get object (signal/image) uuid list for current panel.
|
|
536
|
+
Objects are sorted by group number and object index in group.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
panel: panel name (valid values: "signal", "image").
|
|
540
|
+
If None, current panel is used.
|
|
541
|
+
group: Group number, or group id, or group title.
|
|
542
|
+
Defaults to None (all groups).
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
Object uuids
|
|
546
|
+
"""
|
|
547
|
+
return self.win.get_object_uuids(panel, group)
|
|
548
|
+
|
|
549
|
+
@remote_call
|
|
550
|
+
def get_object_shapes(
|
|
551
|
+
self,
|
|
552
|
+
nb_id_title: int | str | None = None,
|
|
553
|
+
panel: str | None = None,
|
|
554
|
+
) -> list:
|
|
555
|
+
"""Get plot item shapes associated to object (signal/image).
|
|
556
|
+
|
|
557
|
+
Args:
|
|
558
|
+
nb_id_title: Object number, or object id, or object title.
|
|
559
|
+
Defaults to None (current object).
|
|
560
|
+
panel: Panel name. Defaults to None (current panel).
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
List of plot item shapes
|
|
564
|
+
"""
|
|
565
|
+
items = self.win.get_object_shapes(nb_id_title, panel)
|
|
566
|
+
return items_to_json(items)
|
|
567
|
+
|
|
568
|
+
@remote_call
|
|
569
|
+
def add_annotations_from_items(
|
|
570
|
+
self, items_json: str, refresh_plot: bool = True, panel: str | None = None
|
|
571
|
+
) -> None:
|
|
572
|
+
"""Add object annotations (annotation plot items).
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
items_json: JSON string of annotation items
|
|
576
|
+
refresh_plot: refresh plot. Defaults to True.
|
|
577
|
+
panel: panel name (valid values: "signal", "image").
|
|
578
|
+
If None, current panel is used.
|
|
579
|
+
"""
|
|
580
|
+
items = json_to_items(items_json)
|
|
581
|
+
if items:
|
|
582
|
+
self.win.add_annotations_from_items(items, refresh_plot, panel)
|
|
583
|
+
|
|
584
|
+
@remote_call
|
|
585
|
+
def add_label_with_title(
|
|
586
|
+
self, title: str | None = None, panel: str | None = None
|
|
587
|
+
) -> None:
|
|
588
|
+
"""Add a label with object title on the associated plot
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
title: Label title. Defaults to None.
|
|
592
|
+
If None, the title is the object title.
|
|
593
|
+
panel: panel name (valid values: "signal", "image").
|
|
594
|
+
If None, current panel is used.
|
|
595
|
+
"""
|
|
596
|
+
self.win.add_label_with_title(title, panel)
|
|
597
|
+
|
|
598
|
+
@remote_call
|
|
599
|
+
def run_macro(self, number_or_title: int | str | None = None) -> None:
|
|
600
|
+
"""Run macro.
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
number: Number of the macro (starting at 1). Defaults to None (run
|
|
604
|
+
current macro, or does nothing if there is no macro).
|
|
605
|
+
"""
|
|
606
|
+
self.SIG_RUN_MACRO.emit(number_or_title)
|
|
607
|
+
|
|
608
|
+
@remote_call
|
|
609
|
+
def stop_macro(self, number_or_title: int | str | None = None) -> None:
|
|
610
|
+
"""Stop macro.
|
|
611
|
+
|
|
612
|
+
Args:
|
|
613
|
+
number: Number of the macro (starting at 1). Defaults to None (stop
|
|
614
|
+
current macro, or does nothing if there is no macro).
|
|
615
|
+
"""
|
|
616
|
+
self.SIG_STOP_MACRO.emit(number_or_title)
|
|
617
|
+
|
|
618
|
+
@remote_call
|
|
619
|
+
def import_macro_from_file(self, filename: str) -> None:
|
|
620
|
+
"""Import macro from file
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
filename: Filename.
|
|
624
|
+
"""
|
|
625
|
+
self.SIG_IMPORT_MACRO_FROM_FILE.emit(filename)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
RemoteServer.check_remote_functions()
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
# === Python 2.7 client side:
|
|
632
|
+
#
|
|
633
|
+
# # See doc/remotecontrol_py27.py for an almost complete Python 2.7
|
|
634
|
+
# # implementation of RemoteClient class
|
|
635
|
+
#
|
|
636
|
+
# import io
|
|
637
|
+
# from xmlrpclib import ServerProxy, Binary
|
|
638
|
+
# import numpy as np
|
|
639
|
+
# def array_to_binary(data):
|
|
640
|
+
# """Convert NumPy array to XML-RPC Binary object, with shape and dtype"""
|
|
641
|
+
# dbytes = io.BytesIO()
|
|
642
|
+
# np.save(dbytes, data, allow_pickle=False)
|
|
643
|
+
# return Binary(dbytes.getvalue())
|
|
644
|
+
# s = ServerProxy("http://127.0.0.1:8000")
|
|
645
|
+
# data = np.array([[3, 4, 5], [7, 8, 0]], dtype=np.uint16)
|
|
646
|
+
# s.add_image("toto", array_to_binary(data))
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def get_datalab_xmlrpc_port() -> str:
|
|
650
|
+
"""Return DataLab current XML-RPC port
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
XML-RPC port
|
|
654
|
+
|
|
655
|
+
Raises:
|
|
656
|
+
ConnectionRefusedError: DataLab has not yet been executed
|
|
657
|
+
"""
|
|
658
|
+
# The following is valid only when using Python 3.9+ with DataLab
|
|
659
|
+
# installed on the client side. In any other situation, please use the
|
|
660
|
+
# ``get_datalab_xmlrpc_port`` function from doc/remotecontrol_py27.py.
|
|
661
|
+
initialize()
|
|
662
|
+
try:
|
|
663
|
+
return Conf.main.rpc_server_port.get()
|
|
664
|
+
except RuntimeError as exc:
|
|
665
|
+
raise ConnectionRefusedError("DataLab has not yet been executed") from exc
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
class RemoteClient(BaseProxy):
|
|
669
|
+
"""Object representing a proxy/client to DataLab XML-RPC server.
|
|
670
|
+
This object is used to call DataLab functions from a Python script.
|
|
671
|
+
|
|
672
|
+
Examples:
|
|
673
|
+
Here is a simple example of how to use RemoteClient in a Python script
|
|
674
|
+
or in a Jupyter notebook:
|
|
675
|
+
|
|
676
|
+
>>> from datalab.remote import RemoteClient
|
|
677
|
+
>>> proxy = RemoteClient()
|
|
678
|
+
>>> proxy.connect()
|
|
679
|
+
Connecting to DataLab XML-RPC server...OK (port: 28867)
|
|
680
|
+
>>> proxy.get_version()
|
|
681
|
+
'1.0.0'
|
|
682
|
+
>>> proxy.add_signal("toto", np.array([1., 2., 3.]), np.array([4., 5., -1.]))
|
|
683
|
+
True
|
|
684
|
+
>>> proxy.get_object_titles()
|
|
685
|
+
['toto']
|
|
686
|
+
>>> proxy["toto"]
|
|
687
|
+
<sigima.objects.signal.SignalObj at 0x7f7f1c0b4a90>
|
|
688
|
+
>>> proxy[1]
|
|
689
|
+
<sigima.objects.signal.SignalObj at 0x7f7f1c0b4a90>
|
|
690
|
+
>>> proxy[1].data
|
|
691
|
+
array([1., 2., 3.])
|
|
692
|
+
"""
|
|
693
|
+
|
|
694
|
+
def __init__(self) -> None:
|
|
695
|
+
super().__init__()
|
|
696
|
+
self.port: str | None = None
|
|
697
|
+
self._datalab: ServerProxy
|
|
698
|
+
|
|
699
|
+
def set_port(self, port: str | None = None) -> None:
|
|
700
|
+
"""Set XML-RPC port to connect to.
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
port: XML-RPC port to connect to. If None, the port is automatically
|
|
704
|
+
retrieved from DataLab configuration.
|
|
705
|
+
"""
|
|
706
|
+
execenv.print(f"Setting XML-RPC port... [input:{port}] ", end="")
|
|
707
|
+
port_str = ""
|
|
708
|
+
if port is None:
|
|
709
|
+
port = execenv.xmlrpcport
|
|
710
|
+
port_str = f"→[execenv.xmlrpcport:{port}] "
|
|
711
|
+
if port is None:
|
|
712
|
+
port = get_datalab_xmlrpc_port()
|
|
713
|
+
port_str = f"→[Conf.main.rpc_server_port:{port}] "
|
|
714
|
+
execenv.print(port_str, end="")
|
|
715
|
+
self.port = port
|
|
716
|
+
if port is None:
|
|
717
|
+
execenv.print("KO")
|
|
718
|
+
raise ConnectionRefusedError("DataLab XML-RPC port is not set")
|
|
719
|
+
execenv.print("OK")
|
|
720
|
+
|
|
721
|
+
def __connect_to_server(self) -> None:
|
|
722
|
+
"""Connect to DataLab XML-RPC server.
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
port: XML-RPC port to connect to.
|
|
726
|
+
|
|
727
|
+
Raises:
|
|
728
|
+
ConnectionRefusedError: DataLab is currently not running
|
|
729
|
+
"""
|
|
730
|
+
self._datalab = ServerProxy(f"http://127.0.0.1:{self.port}", allow_none=True)
|
|
731
|
+
try:
|
|
732
|
+
version = self.get_version()
|
|
733
|
+
except ConnectionRefusedError as exc:
|
|
734
|
+
raise ConnectionRefusedError("DataLab is currently not running") from exc
|
|
735
|
+
# If DataLab version is not compatible with this client, show a warning
|
|
736
|
+
server_ver = Version(version)
|
|
737
|
+
client_ver = Version(datalab.__version__)
|
|
738
|
+
if server_ver < client_ver:
|
|
739
|
+
warnings.warn(
|
|
740
|
+
f"DataLab server version ({server_ver}) may not be fully compatible "
|
|
741
|
+
f"with this DataLab client version ({client_ver}).\n"
|
|
742
|
+
f"Please upgrade the server to {client_ver} or higher."
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
def connect(
|
|
746
|
+
self,
|
|
747
|
+
port: str | None = None,
|
|
748
|
+
timeout: float | None = None,
|
|
749
|
+
retries: int | None = None,
|
|
750
|
+
) -> None:
|
|
751
|
+
"""Try to connect to DataLab XML-RPC server.
|
|
752
|
+
|
|
753
|
+
Args:
|
|
754
|
+
port: XML-RPC port to connect to. If not specified,
|
|
755
|
+
the port is automatically retrieved from DataLab configuration.
|
|
756
|
+
timeout: Maximum time to wait for connection in seconds. Defaults to 5.0.
|
|
757
|
+
This is the total maximum wait time, not per retry.
|
|
758
|
+
retries: Number of retries. Defaults to 10. This parameter is deprecated
|
|
759
|
+
and will be removed in a future version (kept for backward compatibility).
|
|
760
|
+
|
|
761
|
+
Raises:
|
|
762
|
+
ConnectionRefusedError: Unable to connect to DataLab
|
|
763
|
+
ValueError: Invalid timeout (must be >= 0.0)
|
|
764
|
+
ValueError: Invalid number of retries (must be >= 1)
|
|
765
|
+
"""
|
|
766
|
+
timeout = 5.0 if timeout is None else timeout
|
|
767
|
+
retries = 10 if retries is None else retries # Kept for backward compatibility
|
|
768
|
+
if timeout < 0.0:
|
|
769
|
+
raise ValueError("timeout must be >= 0.0")
|
|
770
|
+
if retries < 1:
|
|
771
|
+
raise ValueError("retries must be >= 1")
|
|
772
|
+
|
|
773
|
+
execenv.print(f"Connecting to DataLab XML-RPC server... [port:{port}] ", end="")
|
|
774
|
+
|
|
775
|
+
# Use exponential backoff for more efficient retrying
|
|
776
|
+
start_time = time.time()
|
|
777
|
+
poll_interval = 0.1 # Start with 100ms
|
|
778
|
+
max_poll_interval = 1.0 # Cap at 1 second
|
|
779
|
+
|
|
780
|
+
while True:
|
|
781
|
+
try:
|
|
782
|
+
# Try to set the port - this may fail if DataLab hasn't written its
|
|
783
|
+
# config file yet, so we retry it in the loop
|
|
784
|
+
self.set_port(port)
|
|
785
|
+
self.__connect_to_server()
|
|
786
|
+
elapsed = time.time() - start_time
|
|
787
|
+
execenv.print(f"OK (port: {self.port}, connected in {elapsed:.1f}s)")
|
|
788
|
+
return
|
|
789
|
+
except (ConnectionRefusedError, OSError) as exc:
|
|
790
|
+
# Catch both ConnectionRefusedError and OSError (includes socket errors)
|
|
791
|
+
elapsed = time.time() - start_time
|
|
792
|
+
if elapsed >= timeout:
|
|
793
|
+
execenv.print("KO")
|
|
794
|
+
raise ConnectionRefusedError(
|
|
795
|
+
f"Unable to connect to DataLab after {elapsed:.1f}s"
|
|
796
|
+
) from exc
|
|
797
|
+
# Wait before next retry with exponential backoff
|
|
798
|
+
time.sleep(poll_interval)
|
|
799
|
+
poll_interval = min(poll_interval * 1.5, max_poll_interval)
|
|
800
|
+
|
|
801
|
+
def disconnect(self) -> None:
|
|
802
|
+
"""Disconnect from DataLab XML-RPC server."""
|
|
803
|
+
# This is not mandatory with XML-RPC, but if we change protocol in the
|
|
804
|
+
# future, it may be useful to have a disconnect method.
|
|
805
|
+
self._datalab = None
|
|
806
|
+
|
|
807
|
+
def is_connected(self) -> bool:
|
|
808
|
+
"""Return True if connected to DataLab XML-RPC server."""
|
|
809
|
+
if self._datalab is not None:
|
|
810
|
+
try:
|
|
811
|
+
self.get_version()
|
|
812
|
+
return True
|
|
813
|
+
except ConnectionRefusedError:
|
|
814
|
+
self._datalab = None
|
|
815
|
+
return False
|
|
816
|
+
|
|
817
|
+
def get_method_list(self) -> list[str]:
|
|
818
|
+
"""Return list of available methods."""
|
|
819
|
+
return self._datalab.system.listMethods()
|
|
820
|
+
|
|
821
|
+
# === Following methods should match the register functions in XML-RPC server
|
|
822
|
+
|
|
823
|
+
def add_signal(
|
|
824
|
+
self,
|
|
825
|
+
title: str,
|
|
826
|
+
xdata: np.ndarray,
|
|
827
|
+
ydata: np.ndarray,
|
|
828
|
+
xunit: str = "",
|
|
829
|
+
yunit: str = "",
|
|
830
|
+
xlabel: str = "",
|
|
831
|
+
ylabel: str = "",
|
|
832
|
+
group_id: str = "",
|
|
833
|
+
set_current: bool = True,
|
|
834
|
+
) -> bool: # pylint: disable=too-many-arguments
|
|
835
|
+
"""Add signal data to DataLab.
|
|
836
|
+
|
|
837
|
+
Args:
|
|
838
|
+
title: Signal title
|
|
839
|
+
xdata: X data
|
|
840
|
+
ydata: Y data
|
|
841
|
+
xunit: X unit. Defaults to ""
|
|
842
|
+
yunit: Y unit. Defaults to ""
|
|
843
|
+
xlabel: X label. Defaults to ""
|
|
844
|
+
ylabel: Y label. Defaults to ""
|
|
845
|
+
group_id: group id in which to add the signal. Defaults to ""
|
|
846
|
+
set_current: if True, set the added signal as current
|
|
847
|
+
|
|
848
|
+
Returns:
|
|
849
|
+
True if signal was added successfully, False otherwise
|
|
850
|
+
|
|
851
|
+
Raises:
|
|
852
|
+
ValueError: Invalid xdata dtype
|
|
853
|
+
ValueError: Invalid ydata dtype
|
|
854
|
+
"""
|
|
855
|
+
obj = SignalObj()
|
|
856
|
+
obj.set_xydata(xdata, ydata)
|
|
857
|
+
obj.check_data()
|
|
858
|
+
xbinary = utils.array_to_rpcbinary(xdata)
|
|
859
|
+
ybinary = utils.array_to_rpcbinary(ydata)
|
|
860
|
+
return self._datalab.add_signal(
|
|
861
|
+
title, xbinary, ybinary, xunit, yunit, xlabel, ylabel, group_id, set_current
|
|
862
|
+
)
|
|
863
|
+
|
|
864
|
+
def add_image(
|
|
865
|
+
self,
|
|
866
|
+
title: str,
|
|
867
|
+
data: np.ndarray,
|
|
868
|
+
xunit: str = "",
|
|
869
|
+
yunit: str = "",
|
|
870
|
+
zunit: str = "",
|
|
871
|
+
xlabel: str = "",
|
|
872
|
+
ylabel: str = "",
|
|
873
|
+
zlabel: str = "",
|
|
874
|
+
group_id: str = "",
|
|
875
|
+
set_current: bool = True,
|
|
876
|
+
) -> bool: # pylint: disable=too-many-arguments
|
|
877
|
+
"""Add image data to DataLab.
|
|
878
|
+
|
|
879
|
+
Args:
|
|
880
|
+
title: Image title
|
|
881
|
+
data: Image data
|
|
882
|
+
xunit: X unit. Defaults to ""
|
|
883
|
+
yunit: Y unit. Defaults to ""
|
|
884
|
+
zunit: Z unit. Defaults to ""
|
|
885
|
+
xlabel: X label. Defaults to ""
|
|
886
|
+
ylabel: Y label. Defaults to ""
|
|
887
|
+
zlabel: Z label. Defaults to ""
|
|
888
|
+
group_id: group id in which to add the image. Defaults to ""
|
|
889
|
+
set_current: if True, set the added image as current
|
|
890
|
+
|
|
891
|
+
Returns:
|
|
892
|
+
True if image was added successfully, False otherwise
|
|
893
|
+
|
|
894
|
+
Raises:
|
|
895
|
+
ValueError: Invalid data dtype
|
|
896
|
+
"""
|
|
897
|
+
obj = ImageObj()
|
|
898
|
+
obj.data = data
|
|
899
|
+
obj.check_data()
|
|
900
|
+
zbinary = utils.array_to_rpcbinary(data)
|
|
901
|
+
return self._datalab.add_image(
|
|
902
|
+
title,
|
|
903
|
+
zbinary,
|
|
904
|
+
xunit,
|
|
905
|
+
yunit,
|
|
906
|
+
zunit,
|
|
907
|
+
xlabel,
|
|
908
|
+
ylabel,
|
|
909
|
+
zlabel,
|
|
910
|
+
group_id,
|
|
911
|
+
set_current,
|
|
912
|
+
)
|
|
913
|
+
|
|
914
|
+
def add_object(
|
|
915
|
+
self,
|
|
916
|
+
obj: SignalObj | ImageObj,
|
|
917
|
+
group_id: str = "",
|
|
918
|
+
set_current: bool = True,
|
|
919
|
+
) -> None:
|
|
920
|
+
"""Add object to DataLab.
|
|
921
|
+
|
|
922
|
+
Args:
|
|
923
|
+
obj: Signal or image object
|
|
924
|
+
group_id: group id in which to add the object. Defaults to ""
|
|
925
|
+
set_current: if True, set the added object as current
|
|
926
|
+
"""
|
|
927
|
+
obj_data = utils.dataset_to_rpcjson(obj)
|
|
928
|
+
self._datalab.add_object(obj_data, group_id, set_current)
|
|
929
|
+
|
|
930
|
+
def calc(self, name: str, param: gds.DataSet | None = None) -> None:
|
|
931
|
+
"""Call computation feature ``name``
|
|
932
|
+
|
|
933
|
+
.. note::
|
|
934
|
+
|
|
935
|
+
This calls either the processor's ``compute_<name>`` method (if it exists),
|
|
936
|
+
or the processor's ``<name>`` computation feature (if it is registered,
|
|
937
|
+
using the ``run_feature`` method).
|
|
938
|
+
It looks for the function in all panels, starting with the current one.
|
|
939
|
+
|
|
940
|
+
Args:
|
|
941
|
+
name: Compute function name
|
|
942
|
+
param: Compute function parameter. Defaults to None.
|
|
943
|
+
|
|
944
|
+
Raises:
|
|
945
|
+
ValueError: unknown function
|
|
946
|
+
"""
|
|
947
|
+
if param is None:
|
|
948
|
+
return self._datalab.calc(name)
|
|
949
|
+
return self._datalab.calc(name, utils.dataset_to_rpcjson(param))
|
|
950
|
+
|
|
951
|
+
def get_object(
|
|
952
|
+
self,
|
|
953
|
+
nb_id_title: int | str | None = None,
|
|
954
|
+
panel: str | None = None,
|
|
955
|
+
) -> SignalObj | ImageObj:
|
|
956
|
+
"""Get object (signal/image) from index.
|
|
957
|
+
|
|
958
|
+
Args:
|
|
959
|
+
nb_id_title: Object number, or object id, or object title.
|
|
960
|
+
Defaults to None (current object).
|
|
961
|
+
panel: Panel name. Defaults to None (current panel).
|
|
962
|
+
|
|
963
|
+
Returns:
|
|
964
|
+
Object
|
|
965
|
+
|
|
966
|
+
Raises:
|
|
967
|
+
KeyError: if object not found
|
|
968
|
+
"""
|
|
969
|
+
param_data = self._datalab.get_object(nb_id_title, panel)
|
|
970
|
+
if param_data is None:
|
|
971
|
+
return None
|
|
972
|
+
return utils.rpcjson_to_dataset(param_data)
|
|
973
|
+
|
|
974
|
+
def get_object_shapes(
|
|
975
|
+
self,
|
|
976
|
+
nb_id_title: int | str | None = None,
|
|
977
|
+
panel: str | None = None,
|
|
978
|
+
) -> list:
|
|
979
|
+
"""Get plot item shapes associated to object (signal/image).
|
|
980
|
+
|
|
981
|
+
Args:
|
|
982
|
+
nb_id_title: Object number, or object id, or object title.
|
|
983
|
+
Defaults to None (current object).
|
|
984
|
+
panel: Panel name. Defaults to None (current panel).
|
|
985
|
+
|
|
986
|
+
Returns:
|
|
987
|
+
List of plot item shapes
|
|
988
|
+
"""
|
|
989
|
+
items_json = self._datalab.get_object_shapes(nb_id_title, panel)
|
|
990
|
+
return json_to_items(items_json)
|
|
991
|
+
|
|
992
|
+
def add_annotations_from_items(
|
|
993
|
+
self, items: list, refresh_plot: bool = True, panel: str | None = None
|
|
994
|
+
) -> None:
|
|
995
|
+
"""Add object annotations (annotation plot items).
|
|
996
|
+
|
|
997
|
+
Args:
|
|
998
|
+
items: annotation plot items
|
|
999
|
+
refresh_plot: refresh plot. Defaults to True.
|
|
1000
|
+
panel: panel name (valid values: "signal", "image").
|
|
1001
|
+
If None, current panel is used.
|
|
1002
|
+
"""
|
|
1003
|
+
items_json = items_to_json(items)
|
|
1004
|
+
if items_json is not None:
|
|
1005
|
+
self._datalab.add_annotations_from_items(items_json, refresh_plot, panel)
|