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,565 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
PlotPy Adapter Scalar Module
|
|
5
|
+
----------------------------
|
|
6
|
+
|
|
7
|
+
This module contains adapters for scalar results (GeometryResult, TableResult)
|
|
8
|
+
to avoid circular imports with the base and factories modules.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections.abc import Iterable
|
|
16
|
+
from typing import TYPE_CHECKING, Literal
|
|
17
|
+
|
|
18
|
+
import guidata.dataset as gds
|
|
19
|
+
import numpy as np
|
|
20
|
+
from guidata.configtools import get_font
|
|
21
|
+
from plotpy.builder import make
|
|
22
|
+
from plotpy.items import (
|
|
23
|
+
AnnotatedCircle,
|
|
24
|
+
AnnotatedEllipse,
|
|
25
|
+
AnnotatedPoint,
|
|
26
|
+
AnnotatedRectangle,
|
|
27
|
+
AnnotatedSegment,
|
|
28
|
+
AnnotatedShape,
|
|
29
|
+
LabelItem,
|
|
30
|
+
Marker,
|
|
31
|
+
PolygonShape,
|
|
32
|
+
)
|
|
33
|
+
from sigima.objects.base import BaseObj
|
|
34
|
+
from sigima.objects.scalar import KindShape
|
|
35
|
+
from sigima.objects.signal import SignalObj
|
|
36
|
+
from sigima.tools import coordinates
|
|
37
|
+
from sigima.tools.signal import pulse
|
|
38
|
+
|
|
39
|
+
from datalab.adapters_metadata import (
|
|
40
|
+
GeometryAdapter,
|
|
41
|
+
TableAdapter,
|
|
42
|
+
resultadapter_to_html,
|
|
43
|
+
)
|
|
44
|
+
from datalab.adapters_plotpy.base import (
|
|
45
|
+
config_annotated_shape,
|
|
46
|
+
items_to_json,
|
|
47
|
+
json_to_items,
|
|
48
|
+
set_plot_item_editable,
|
|
49
|
+
)
|
|
50
|
+
from datalab.config import PLOTPY_CONF, Conf, _
|
|
51
|
+
|
|
52
|
+
if TYPE_CHECKING:
|
|
53
|
+
from plotpy.styles import ShapeParam
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ResultPlotPyAdapter:
|
|
57
|
+
"""Adapter for converting `sigima` table or geometry adapters to PlotPy
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
result: Table or geometry adapter
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, result_adapter: TableAdapter | GeometryAdapter) -> None:
|
|
64
|
+
self.result_adapter = result_adapter
|
|
65
|
+
|
|
66
|
+
def get_other_items(self, obj: BaseObj) -> list: # pylint: disable=unused-argument
|
|
67
|
+
"""Return other items associated to this result (excluding label item)
|
|
68
|
+
|
|
69
|
+
Those items are not serialized to JSON.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
obj: object (signal/image)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List of other items
|
|
76
|
+
"""
|
|
77
|
+
return []
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class GeometryPlotPyAdapter(ResultPlotPyAdapter):
|
|
81
|
+
"""Adapter for converting `sigima` geometry adapters to PlotPy
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
result: Geometry adapter
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
AssertionError: invalid argument
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, result_adapter: GeometryAdapter) -> None:
|
|
91
|
+
assert isinstance(result_adapter, GeometryAdapter)
|
|
92
|
+
super().__init__(result_adapter)
|
|
93
|
+
|
|
94
|
+
def iterate_shape_items(
|
|
95
|
+
self, fmt: str, lbl: bool, prefix: Literal["s", "i"]
|
|
96
|
+
) -> Iterable:
|
|
97
|
+
"""Iterate over metadata shape plot items.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
fmt: numeric format (e.g. "%.3f")
|
|
101
|
+
lbl: if True, show shape labels
|
|
102
|
+
prefix: "s" for signal, "i" for image
|
|
103
|
+
|
|
104
|
+
Yields:
|
|
105
|
+
Plot item
|
|
106
|
+
"""
|
|
107
|
+
max_shapes = Conf.view.max_shapes_to_draw.get(200)
|
|
108
|
+
total_coords = len(self.result_adapter.result.coords)
|
|
109
|
+
|
|
110
|
+
# Yield shapes up to the maximum limit
|
|
111
|
+
for idx, coords in enumerate(self.result_adapter.result.coords):
|
|
112
|
+
if idx >= max_shapes:
|
|
113
|
+
break
|
|
114
|
+
yield self.create_shape_item(coords, fmt, lbl, prefix)
|
|
115
|
+
|
|
116
|
+
# If shapes were truncated, create a warning label
|
|
117
|
+
if total_coords > max_shapes:
|
|
118
|
+
warning_text = "⚠ " + _("Only %d out of %d shapes are displayed") % (
|
|
119
|
+
max_shapes,
|
|
120
|
+
total_coords,
|
|
121
|
+
)
|
|
122
|
+
warning_label = make.label(warning_text, "BR", (0, 0), "BR")
|
|
123
|
+
warning_label.labelparam.font.bold = True
|
|
124
|
+
warning_label.labelparam.font.size = 10
|
|
125
|
+
warning_label.labelparam.bgalpha = 0.8
|
|
126
|
+
warning_label.labelparam.bgcolor = "#ff9800" # Orange background
|
|
127
|
+
warning_label.labelparam.textcolor = "#000000" # Black text
|
|
128
|
+
warning_label.labelparam.update_item(warning_label)
|
|
129
|
+
warning_label.set_readonly(True)
|
|
130
|
+
yield warning_label
|
|
131
|
+
|
|
132
|
+
def create_shape_item(
|
|
133
|
+
self, coords: np.ndarray, fmt: str, lbl: bool, prefix: Literal["s", "i"]
|
|
134
|
+
) -> (
|
|
135
|
+
AnnotatedPoint
|
|
136
|
+
| Marker
|
|
137
|
+
| AnnotatedRectangle
|
|
138
|
+
| AnnotatedCircle
|
|
139
|
+
| AnnotatedSegment
|
|
140
|
+
| AnnotatedEllipse
|
|
141
|
+
| PolygonShape
|
|
142
|
+
| None
|
|
143
|
+
):
|
|
144
|
+
"""Create individual shape item from coordinates
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
coords: coordinate array
|
|
148
|
+
fmt: numeric format (e.g. "%.3f")
|
|
149
|
+
lbl: if True, show shape labels
|
|
150
|
+
prefix: "s" for signal, "i" for image
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Plot item
|
|
154
|
+
"""
|
|
155
|
+
if self.result_adapter.result.kind == KindShape.POINT:
|
|
156
|
+
assert len(coords) == 2, "Coordinates must be a 2-element array"
|
|
157
|
+
x0, y0 = coords
|
|
158
|
+
item = AnnotatedPoint(x0, y0)
|
|
159
|
+
sparam: ShapeParam = item.shape.shapeparam
|
|
160
|
+
sparam.symbol.marker = "Ellipse"
|
|
161
|
+
sparam.symbol.size = 6
|
|
162
|
+
sparam.sel_symbol.marker = "Ellipse"
|
|
163
|
+
sparam.sel_symbol.size = 6
|
|
164
|
+
aparam = item.annotationparam
|
|
165
|
+
aparam.title = self.result_adapter.title
|
|
166
|
+
sparam.update_item(item.shape)
|
|
167
|
+
aparam.update_item(item)
|
|
168
|
+
elif self.result_adapter.result.kind == KindShape.MARKER:
|
|
169
|
+
assert len(coords) == 2, "Coordinates must be a 2-element array"
|
|
170
|
+
x0, y0 = coords
|
|
171
|
+
item = self.__make_marker_item(x0, y0, fmt)
|
|
172
|
+
elif self.result_adapter.result.kind == KindShape.RECTANGLE:
|
|
173
|
+
assert len(coords) == 4, "Coordinates must be a 4-element array"
|
|
174
|
+
x0, y0, dx, dy = coords
|
|
175
|
+
item = make.annotated_rectangle(
|
|
176
|
+
x0, y0, x0 + dx, y0 + dy, title=self.result_adapter.title
|
|
177
|
+
)
|
|
178
|
+
elif self.result_adapter.result.kind == KindShape.CIRCLE:
|
|
179
|
+
assert len(coords) == 3, "Coordinates must be a 3-element array"
|
|
180
|
+
xc, yc, r = coords
|
|
181
|
+
x0, y0, x1, y1 = coordinates.circle_to_diameter(xc, yc, r)
|
|
182
|
+
item = make.annotated_circle(
|
|
183
|
+
x0, y0, x1, y1, title=self.result_adapter.title
|
|
184
|
+
)
|
|
185
|
+
elif self.result_adapter.result.kind == KindShape.SEGMENT:
|
|
186
|
+
assert len(coords) == 4, "Coordinates must be a 4-element array"
|
|
187
|
+
x0, y0, x1, y1 = coords
|
|
188
|
+
item = make.annotated_segment(
|
|
189
|
+
x0, y0, x1, y1, title=self.result_adapter.title
|
|
190
|
+
)
|
|
191
|
+
elif self.result_adapter.result.kind == KindShape.ELLIPSE:
|
|
192
|
+
assert len(coords) == 5, "Coordinates must be a 5-element array"
|
|
193
|
+
xc, yc, a, b, t = coords
|
|
194
|
+
coords = coordinates.ellipse_to_diameters(xc, yc, a, b, t)
|
|
195
|
+
x0, y0, x1, y1, x2, y2, x3, y3 = coords
|
|
196
|
+
item = make.annotated_ellipse(
|
|
197
|
+
x0, y0, x1, y1, x2, y2, x3, y3, title=self.result_adapter.title
|
|
198
|
+
)
|
|
199
|
+
elif self.result_adapter.result.kind == KindShape.POLYGON:
|
|
200
|
+
assert len(coords) >= 6, "Coordinates must be at least 6-element array"
|
|
201
|
+
assert len(coords) % 2 == 0, "Coordinates must be even-length array"
|
|
202
|
+
x, y = coords[::2], coords[1::2]
|
|
203
|
+
# Filter out NaN coordinates to avoid performance issues
|
|
204
|
+
# Polygons are padded with NaNs to create regular arrays, but we only
|
|
205
|
+
# need to draw the valid coordinates
|
|
206
|
+
valid_mask = ~(np.isnan(x) | np.isnan(y))
|
|
207
|
+
x, y = x[valid_mask], y[valid_mask]
|
|
208
|
+
item = make.polygon(x, y, title=self.result_adapter.title, closed=False)
|
|
209
|
+
else:
|
|
210
|
+
raise NotImplementedError(
|
|
211
|
+
f"Unsupported shape kind: {self.result_adapter.result.kind}"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if isinstance(item, AnnotatedShape):
|
|
215
|
+
config_annotated_shape(item, fmt, lbl, "results", f"{prefix}/annotation")
|
|
216
|
+
# Apply settings for annotated shapes (except AnnotatedPoint)
|
|
217
|
+
if not isinstance(item, AnnotatedPoint):
|
|
218
|
+
if prefix == "s":
|
|
219
|
+
config_param = Conf.view.sig_shape_param.get()
|
|
220
|
+
else:
|
|
221
|
+
config_param = Conf.view.ima_shape_param.get()
|
|
222
|
+
shape_param: ShapeParam = item.shape.shapeparam
|
|
223
|
+
gds.update_dataset(shape_param, config_param)
|
|
224
|
+
shape_param.update_item(item.shape)
|
|
225
|
+
|
|
226
|
+
if isinstance(item, Marker):
|
|
227
|
+
item.set_style("results", f"{prefix}/marker")
|
|
228
|
+
# Apply cursor/marker settings from config
|
|
229
|
+
if prefix == "s":
|
|
230
|
+
config_param = Conf.view.sig_marker_param.get()
|
|
231
|
+
else:
|
|
232
|
+
config_param = Conf.view.ima_marker_param.get()
|
|
233
|
+
param = item.markerparam
|
|
234
|
+
gds.update_dataset(param, config_param)
|
|
235
|
+
param.update_item(item)
|
|
236
|
+
|
|
237
|
+
set_plot_item_editable(item, False)
|
|
238
|
+
return item
|
|
239
|
+
|
|
240
|
+
def __make_marker_item(self, x0: float, y0: float, fmt: str) -> Marker:
|
|
241
|
+
"""Make marker item
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
x0: x coordinate
|
|
245
|
+
y0: y coordinate
|
|
246
|
+
fmt: numeric format (e.g. '%.3f')
|
|
247
|
+
"""
|
|
248
|
+
if np.isnan(x0):
|
|
249
|
+
mstyle = "-"
|
|
250
|
+
|
|
251
|
+
def label(x, y): # pylint: disable=unused-argument
|
|
252
|
+
return (self.result_adapter.title + ": " + fmt) % y
|
|
253
|
+
|
|
254
|
+
elif np.isnan(y0):
|
|
255
|
+
mstyle = "|"
|
|
256
|
+
|
|
257
|
+
def label(x, y): # pylint: disable=unused-argument
|
|
258
|
+
return (self.result_adapter.title + ": " + fmt) % x
|
|
259
|
+
|
|
260
|
+
else:
|
|
261
|
+
mstyle = "+"
|
|
262
|
+
txt = self.result_adapter.title + ": (" + fmt + ", " + fmt + ")"
|
|
263
|
+
|
|
264
|
+
def label(x, y):
|
|
265
|
+
return txt % (x, y)
|
|
266
|
+
|
|
267
|
+
marker = make.marker(position=(x0, y0), markerstyle=mstyle, label_cb=label)
|
|
268
|
+
return marker
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def create_pulse_segment(
|
|
272
|
+
x0: float, y0: float, x1: float, y1: float, label: str
|
|
273
|
+
) -> AnnotatedSegment:
|
|
274
|
+
"""Create a signal segment item for pulse visualization.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
x0: X-coordinate of the start point
|
|
278
|
+
y0: Y-coordinate of the start point
|
|
279
|
+
x1: X-coordinate of the end point
|
|
280
|
+
y1: Y-coordinate of the end point
|
|
281
|
+
label: Label for the segment
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Annotated segment item styled for pulse visualization
|
|
285
|
+
"""
|
|
286
|
+
item = make.annotated_segment(x0, y0, x1, y1, label, show_computations=False)
|
|
287
|
+
|
|
288
|
+
# Configure label appearance similar to Sigima's vistools
|
|
289
|
+
item.label.labelparam.bgalpha = 0.5
|
|
290
|
+
item.label.labelparam.anchor = "T"
|
|
291
|
+
item.label.labelparam.yc = 10
|
|
292
|
+
item.label.labelparam.update_item(item.label)
|
|
293
|
+
|
|
294
|
+
# Configure segment appearance
|
|
295
|
+
param = item.shape.shapeparam
|
|
296
|
+
param.line.color = "#33ff00" # Green color for baselines/plateaus
|
|
297
|
+
param.line.width = 5
|
|
298
|
+
param.symbol.facecolor = "#26be00"
|
|
299
|
+
param.symbol.edgecolor = "#33ff00"
|
|
300
|
+
param.symbol.marker = "Ellipse"
|
|
301
|
+
param.symbol.size = 11
|
|
302
|
+
param.update_item(item.shape)
|
|
303
|
+
|
|
304
|
+
# Make non-interactive
|
|
305
|
+
item.set_movable(False)
|
|
306
|
+
item.set_resizable(False)
|
|
307
|
+
item.set_selectable(False)
|
|
308
|
+
|
|
309
|
+
return item
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def create_pulse_crossing_marker(
|
|
313
|
+
orientation: Literal["h", "v"], position: float, label: str
|
|
314
|
+
) -> Marker:
|
|
315
|
+
"""Create a crossing marker for pulse visualization.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
orientation: 'h' for horizontal, 'v' for vertical cursor
|
|
319
|
+
position: Position of the cursor along the relevant axis
|
|
320
|
+
label: Label for the cursor
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Marker item styled for crossing visualization
|
|
324
|
+
"""
|
|
325
|
+
if orientation == "h":
|
|
326
|
+
cursor = make.hcursor(position, label=label)
|
|
327
|
+
elif orientation == "v":
|
|
328
|
+
cursor = make.vcursor(position, label=label)
|
|
329
|
+
else:
|
|
330
|
+
raise ValueError("Orientation must be 'h' or 'v'")
|
|
331
|
+
|
|
332
|
+
# Configure appearance similar to Sigima's vistools
|
|
333
|
+
cursor.set_movable(False)
|
|
334
|
+
cursor.set_selectable(False)
|
|
335
|
+
cursor.markerparam.line.color = "#a7ff33" # Light green
|
|
336
|
+
cursor.markerparam.line.width = 3
|
|
337
|
+
cursor.markerparam.symbol.marker = "NoSymbol"
|
|
338
|
+
cursor.markerparam.text.textcolor = "#ffffff"
|
|
339
|
+
cursor.markerparam.text.background_color = "#000000"
|
|
340
|
+
cursor.markerparam.text.background_alpha = 0.5
|
|
341
|
+
cursor.markerparam.text.font.bold = True
|
|
342
|
+
cursor.markerparam.update_item(cursor)
|
|
343
|
+
|
|
344
|
+
return cursor
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def are_values_valid(values: list[float | None]) -> bool:
|
|
348
|
+
"""Check if all values are valid (not None or nan)
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
values: list of values
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
True if all values are valid, False otherwise
|
|
355
|
+
"""
|
|
356
|
+
for v in values:
|
|
357
|
+
if v is None or (isinstance(v, float) and np.isnan(v)):
|
|
358
|
+
return False
|
|
359
|
+
return True
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class TablePlotPyAdapter(ResultPlotPyAdapter):
|
|
363
|
+
"""Adapter for converting `sigima` table adapters to PlotPy
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
result: Table adapter
|
|
367
|
+
|
|
368
|
+
Raises:
|
|
369
|
+
AssertionError: invalid argument
|
|
370
|
+
"""
|
|
371
|
+
|
|
372
|
+
def __init__(self, result_adapter: TableAdapter) -> None:
|
|
373
|
+
assert isinstance(result_adapter, TableAdapter)
|
|
374
|
+
super().__init__(result_adapter)
|
|
375
|
+
|
|
376
|
+
def get_other_items(self, obj: BaseObj) -> list:
|
|
377
|
+
"""Return other items associated to this result (excluding label item)
|
|
378
|
+
|
|
379
|
+
Those items are not serialized to JSON.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
obj: object (signal/image)
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
List of other items
|
|
386
|
+
"""
|
|
387
|
+
items = []
|
|
388
|
+
if self.result_adapter.result.is_pulse_features():
|
|
389
|
+
pulse_items = self.create_pulse_visualization_items(obj)
|
|
390
|
+
items.extend(pulse_items)
|
|
391
|
+
return items
|
|
392
|
+
|
|
393
|
+
def create_pulse_visualization_items(
|
|
394
|
+
self, obj: SignalObj
|
|
395
|
+
) -> list[AnnotatedSegment | Marker]:
|
|
396
|
+
"""Create pulse visualization items from table data.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
obj: Signal object containing the pulse data
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
List of PlotPy items for pulse visualization
|
|
403
|
+
"""
|
|
404
|
+
items = []
|
|
405
|
+
df = self.result_adapter.to_dataframe()
|
|
406
|
+
for _index, row in df.iterrows():
|
|
407
|
+
# Start baseline
|
|
408
|
+
xs0, xs1 = row["xstartmin"], row["xstartmax"]
|
|
409
|
+
ys = pulse.get_range_mean_y(obj.x, obj.y, (xs0, xs1))
|
|
410
|
+
if are_values_valid([xs0, xs1, ys]):
|
|
411
|
+
items.append(create_pulse_segment(xs0, ys, xs1, ys, "Start baseline"))
|
|
412
|
+
# End baseline
|
|
413
|
+
xe0, xe1 = row["xendmin"], row["xendmax"]
|
|
414
|
+
ye = pulse.get_range_mean_y(obj.x, obj.y, (xe0, xe1))
|
|
415
|
+
if are_values_valid([xe0, xe1, ye]):
|
|
416
|
+
items.append(create_pulse_segment(xe0, ye, xe1, ye, "End baseline"))
|
|
417
|
+
if "xplateaumin" in row and "xplateaumax" in row:
|
|
418
|
+
xp0, xp1 = row["xplateaumin"], row["xplateaumax"]
|
|
419
|
+
yp = pulse.get_range_mean_y(obj.x, obj.y, (xp0, xp1))
|
|
420
|
+
if are_values_valid([xp0, xp1, yp]):
|
|
421
|
+
items.append(create_pulse_segment(xp0, yp, xp1, yp, "Plateau"))
|
|
422
|
+
for metric in ("x0", "x50", "x100"):
|
|
423
|
+
if metric in row:
|
|
424
|
+
x = row[metric]
|
|
425
|
+
metric_str = metric.replace("x", "x|<sub>") + "%</sub>"
|
|
426
|
+
if are_values_valid([x]):
|
|
427
|
+
items.append(create_pulse_crossing_marker("v", x, metric_str))
|
|
428
|
+
return items
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class MergedResultPlotPyAdapter:
|
|
432
|
+
"""Adapter for merging multiple result adapters into a single label.
|
|
433
|
+
|
|
434
|
+
This adapter manages a merged label that displays all results for a given object.
|
|
435
|
+
Instead of creating individual labels for each result (which causes overlapping),
|
|
436
|
+
it creates a single label with all results concatenated as HTML.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
result_adapters: List of result adapters (GeometryAdapter or TableAdapter)
|
|
440
|
+
obj: Signal or image object associated with the results
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
def __init__(
|
|
444
|
+
self,
|
|
445
|
+
result_adapters: list[GeometryAdapter | TableAdapter],
|
|
446
|
+
obj: BaseObj,
|
|
447
|
+
) -> None:
|
|
448
|
+
self.result_adapters = result_adapters
|
|
449
|
+
self.obj = obj
|
|
450
|
+
self._cached_label: LabelItem | None = None
|
|
451
|
+
|
|
452
|
+
def get_cached_label(self) -> LabelItem | None:
|
|
453
|
+
"""Get the cached merged label item, if it exists.
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
Merged label item, or None if not cached
|
|
457
|
+
"""
|
|
458
|
+
return self._cached_label
|
|
459
|
+
|
|
460
|
+
def invalidate_cached_label(self) -> None:
|
|
461
|
+
"""Invalidate the cached merged label item."""
|
|
462
|
+
self._cached_label = None
|
|
463
|
+
|
|
464
|
+
@property
|
|
465
|
+
def item_json(self) -> str | None:
|
|
466
|
+
"""JSON representation of the merged label item.
|
|
467
|
+
|
|
468
|
+
The position is stored in all result adapters so they stay in sync.
|
|
469
|
+
"""
|
|
470
|
+
if self.result_adapters:
|
|
471
|
+
return self.result_adapters[0].get_applicative_attr("item_json")
|
|
472
|
+
return None
|
|
473
|
+
|
|
474
|
+
@item_json.setter
|
|
475
|
+
def item_json(self, value: str | None) -> None:
|
|
476
|
+
"""Set JSON representation of the merged label item.
|
|
477
|
+
|
|
478
|
+
The position is stored in all result adapters to keep them synchronized.
|
|
479
|
+
"""
|
|
480
|
+
for result_adapter in self.result_adapters:
|
|
481
|
+
result_adapter.set_applicative_attr("item_json", value)
|
|
482
|
+
|
|
483
|
+
def create_merged_label(self) -> LabelItem | None:
|
|
484
|
+
"""Create a single merged label from all result adapters.
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
Merged label item, or None if no results
|
|
488
|
+
"""
|
|
489
|
+
if not self.result_adapters:
|
|
490
|
+
return None
|
|
491
|
+
|
|
492
|
+
# Create the label with merged content
|
|
493
|
+
merged_html = resultadapter_to_html(self.result_adapters, self.obj)
|
|
494
|
+
item = make.label(merged_html, "TL", (0, 0), "TL", title="Results")
|
|
495
|
+
font = get_font(PLOTPY_CONF, "results", "label/font")
|
|
496
|
+
item.set_style("results", "label")
|
|
497
|
+
item.labelparam.font.update_param(font)
|
|
498
|
+
item.labelparam.update_item(item)
|
|
499
|
+
|
|
500
|
+
# Make label read-only (user cannot delete it to remove individual results)
|
|
501
|
+
item.set_readonly(True)
|
|
502
|
+
|
|
503
|
+
self._cached_label = item
|
|
504
|
+
return item
|
|
505
|
+
|
|
506
|
+
def get_merged_label(self) -> LabelItem | None:
|
|
507
|
+
"""Get the merged label, creating it if necessary or updating if it exists.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
Merged label item, or None if no results
|
|
511
|
+
"""
|
|
512
|
+
if not self.result_adapters:
|
|
513
|
+
self._cached_label = None
|
|
514
|
+
return None
|
|
515
|
+
|
|
516
|
+
# Try to restore existing label from stored JSON position
|
|
517
|
+
if self.item_json and self._cached_label is None:
|
|
518
|
+
stored_item = json_to_items(self.item_json)[0]
|
|
519
|
+
if isinstance(stored_item, LabelItem):
|
|
520
|
+
# Update the stored item with current merged content
|
|
521
|
+
merged_html = resultadapter_to_html(self.result_adapters, self.obj)
|
|
522
|
+
stored_item.set_text(merged_html)
|
|
523
|
+
stored_item.set_readonly(True)
|
|
524
|
+
self._cached_label = stored_item
|
|
525
|
+
return stored_item
|
|
526
|
+
|
|
527
|
+
# Update existing cached label if present
|
|
528
|
+
if self._cached_label is not None:
|
|
529
|
+
merged_html = resultadapter_to_html(self.result_adapters, self.obj)
|
|
530
|
+
self._cached_label.set_text(merged_html)
|
|
531
|
+
return self._cached_label
|
|
532
|
+
|
|
533
|
+
# Create new label
|
|
534
|
+
return self.create_merged_label()
|
|
535
|
+
|
|
536
|
+
def update_obj_metadata_from_item(self, item: LabelItem) -> None:
|
|
537
|
+
"""Update all result adapters' metadata with the label item position.
|
|
538
|
+
|
|
539
|
+
Args:
|
|
540
|
+
item: Merged label item (after user moved it)
|
|
541
|
+
"""
|
|
542
|
+
if item is not None:
|
|
543
|
+
self.item_json = items_to_json([item])
|
|
544
|
+
# Update all result adapters in the object's metadata
|
|
545
|
+
for result_adapter in self.result_adapters:
|
|
546
|
+
result_adapter.add_to(self.obj)
|
|
547
|
+
|
|
548
|
+
def get_other_items(self) -> list:
|
|
549
|
+
"""Return other items from all result adapters (e.g., geometric shapes).
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
List of all other items from all result adapters
|
|
553
|
+
"""
|
|
554
|
+
items = []
|
|
555
|
+
for result_adapter in self.result_adapters:
|
|
556
|
+
if isinstance(result_adapter, GeometryAdapter):
|
|
557
|
+
plotpy_adapter = GeometryPlotPyAdapter(result_adapter)
|
|
558
|
+
elif isinstance(result_adapter, TableAdapter):
|
|
559
|
+
plotpy_adapter = TablePlotPyAdapter(result_adapter)
|
|
560
|
+
else:
|
|
561
|
+
raise NotImplementedError(
|
|
562
|
+
f"Unsupported result adapter type: {type(result_adapter)}"
|
|
563
|
+
)
|
|
564
|
+
items.extend(plotpy_adapter.get_other_items(self.obj))
|
|
565
|
+
return items
|