datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datalab/__init__.py +35 -2
- datalab/adapters_metadata/__init__.py +31 -0
- datalab/adapters_metadata/base_adapter.py +316 -0
- datalab/adapters_metadata/common.py +422 -0
- datalab/adapters_metadata/geometry_adapter.py +98 -0
- datalab/adapters_metadata/table_adapter.py +84 -0
- datalab/adapters_plotpy/__init__.py +54 -0
- datalab/adapters_plotpy/annotations.py +124 -0
- datalab/adapters_plotpy/base.py +110 -0
- datalab/adapters_plotpy/converters.py +86 -0
- datalab/adapters_plotpy/factories.py +80 -0
- datalab/adapters_plotpy/objects/__init__.py +0 -0
- datalab/adapters_plotpy/objects/base.py +197 -0
- datalab/adapters_plotpy/objects/image.py +157 -0
- datalab/adapters_plotpy/objects/scalar.py +565 -0
- datalab/adapters_plotpy/objects/signal.py +264 -0
- datalab/adapters_plotpy/roi/__init__.py +0 -0
- datalab/adapters_plotpy/roi/base.py +146 -0
- datalab/adapters_plotpy/roi/factory.py +93 -0
- datalab/adapters_plotpy/roi/image.py +207 -0
- datalab/adapters_plotpy/roi/signal.py +72 -0
- datalab/app.py +98 -0
- datalab/config.py +817 -0
- datalab/control/__init__.py +0 -0
- datalab/control/baseproxy.py +776 -0
- datalab/control/proxy.py +343 -0
- datalab/control/remote.py +1005 -0
- datalab/data/doc/DataLab_en.pdf +0 -0
- datalab/data/doc/DataLab_fr.pdf +0 -0
- datalab/data/icons/analysis/delete_results.svg +109 -0
- datalab/data/icons/analysis/fw1e2.svg +156 -0
- datalab/data/icons/analysis/fwhm.svg +156 -0
- datalab/data/icons/analysis/histogram.svg +49 -0
- datalab/data/icons/analysis/peak_detect.svg +160 -0
- datalab/data/icons/analysis/plot_results.svg +151 -0
- datalab/data/icons/analysis/show_results.svg +83 -0
- datalab/data/icons/analysis/stats.svg +49 -0
- datalab/data/icons/analysis.svg +120 -0
- datalab/data/icons/apply.svg +3 -0
- datalab/data/icons/check_all.svg +15 -0
- datalab/data/icons/collapse.svg +44 -0
- datalab/data/icons/collapse_selection.svg +63 -0
- datalab/data/icons/console.svg +101 -0
- datalab/data/icons/create/1d-normal.svg +8 -0
- datalab/data/icons/create/1d-poisson.svg +9 -0
- datalab/data/icons/create/1d-uniform.svg +8 -0
- datalab/data/icons/create/1d-zero.svg +57 -0
- datalab/data/icons/create/2d-gaussian.svg +56 -0
- datalab/data/icons/create/2d-normal.svg +38 -0
- datalab/data/icons/create/2d-poisson.svg +38 -0
- datalab/data/icons/create/2d-ramp.svg +90 -0
- datalab/data/icons/create/2d-sinc.svg +62 -0
- datalab/data/icons/create/2d-uniform.svg +38 -0
- datalab/data/icons/create/2d-zero.svg +13 -0
- datalab/data/icons/create/checkerboard.svg +39 -0
- datalab/data/icons/create/cosine.svg +12 -0
- datalab/data/icons/create/exponential.svg +55 -0
- datalab/data/icons/create/gaussian.svg +12 -0
- datalab/data/icons/create/grating.svg +29 -0
- datalab/data/icons/create/linear_chirp.svg +7 -0
- datalab/data/icons/create/logistic.svg +7 -0
- datalab/data/icons/create/lorentzian.svg +12 -0
- datalab/data/icons/create/planck.svg +12 -0
- datalab/data/icons/create/polynomial.svg +7 -0
- datalab/data/icons/create/pulse.svg +12 -0
- datalab/data/icons/create/ring.svg +18 -0
- datalab/data/icons/create/sawtooth.svg +7 -0
- datalab/data/icons/create/siemens.svg +35 -0
- datalab/data/icons/create/sinc.svg +12 -0
- datalab/data/icons/create/sine.svg +7 -0
- datalab/data/icons/create/square.svg +7 -0
- datalab/data/icons/create/square_pulse.svg +7 -0
- datalab/data/icons/create/step.svg +7 -0
- datalab/data/icons/create/step_pulse.svg +12 -0
- datalab/data/icons/create/triangle.svg +7 -0
- datalab/data/icons/create/voigt.svg +12 -0
- datalab/data/icons/edit/annotations.svg +72 -0
- datalab/data/icons/edit/annotations_copy.svg +114 -0
- datalab/data/icons/edit/annotations_delete.svg +83 -0
- datalab/data/icons/edit/annotations_edit.svg +98 -0
- datalab/data/icons/edit/annotations_export.svg +85 -0
- datalab/data/icons/edit/annotations_import.svg +85 -0
- datalab/data/icons/edit/annotations_paste.svg +100 -0
- datalab/data/icons/edit/copy_titles.svg +109 -0
- datalab/data/icons/edit/delete.svg +84 -0
- datalab/data/icons/edit/delete_all.svg +214 -0
- datalab/data/icons/edit/duplicate.svg +64 -0
- datalab/data/icons/edit/goto_source.svg +60 -0
- datalab/data/icons/edit/metadata.svg +60 -0
- datalab/data/icons/edit/metadata_add.svg +80 -0
- datalab/data/icons/edit/metadata_copy.svg +96 -0
- datalab/data/icons/edit/metadata_delete.svg +62 -0
- datalab/data/icons/edit/metadata_export.svg +68 -0
- datalab/data/icons/edit/metadata_import.svg +68 -0
- datalab/data/icons/edit/metadata_paste.svg +79 -0
- datalab/data/icons/edit/move_down.svg +55 -0
- datalab/data/icons/edit/move_up.svg +54 -0
- datalab/data/icons/edit/new_group.svg +76 -0
- datalab/data/icons/edit/recompute.svg +60 -0
- datalab/data/icons/edit/rename.svg +49 -0
- datalab/data/icons/edit.svg +16 -0
- datalab/data/icons/expand.svg +44 -0
- datalab/data/icons/expand_selection.svg +63 -0
- datalab/data/icons/fit/cdf_fit.svg +56 -0
- datalab/data/icons/fit/exponential_fit.svg +55 -0
- datalab/data/icons/fit/gaussian_fit.svg +62 -0
- datalab/data/icons/fit/interactive_fit.svg +101 -0
- datalab/data/icons/fit/linear_fit.svg +57 -0
- datalab/data/icons/fit/lorentzian_fit.svg +209 -0
- datalab/data/icons/fit/multigaussian_fit.svg +85 -0
- datalab/data/icons/fit/multilorentzian_fit.svg +85 -0
- datalab/data/icons/fit/piecewiseexponential_fit.svg +209 -0
- datalab/data/icons/fit/planckian_fit.svg +62 -0
- datalab/data/icons/fit/polynomial_fit.svg +59 -0
- datalab/data/icons/fit/sigmoid_fit.svg +56 -0
- datalab/data/icons/fit/sinusoidal_fit.svg +72 -0
- datalab/data/icons/fit/twohalfgaussian_fit.svg +63 -0
- datalab/data/icons/fit/voigt_fit.svg +57 -0
- datalab/data/icons/group.svg +56 -0
- datalab/data/icons/h5/h5array.svg +59 -0
- datalab/data/icons/h5/h5attrs.svg +75 -0
- datalab/data/icons/h5/h5browser.svg +133 -0
- datalab/data/icons/h5/h5file.svg +69 -0
- datalab/data/icons/h5/h5group.svg +49 -0
- datalab/data/icons/h5/h5scalar.svg +1 -0
- datalab/data/icons/help_pdf.svg +46 -0
- datalab/data/icons/history.svg +7 -0
- datalab/data/icons/image.svg +135 -0
- datalab/data/icons/io/fileopen_directory.svg +60 -0
- datalab/data/icons/io/fileopen_h5.svg +84 -0
- datalab/data/icons/io/fileopen_ima.svg +187 -0
- datalab/data/icons/io/fileopen_py.svg +123 -0
- datalab/data/icons/io/fileopen_sig.svg +138 -0
- datalab/data/icons/io/filesave_h5.svg +97 -0
- datalab/data/icons/io/filesave_ima.svg +200 -0
- datalab/data/icons/io/filesave_py.svg +136 -0
- datalab/data/icons/io/filesave_sig.svg +151 -0
- datalab/data/icons/io/import_text.svg +144 -0
- datalab/data/icons/io/save_to_directory.svg +134 -0
- datalab/data/icons/io.svg +84 -0
- datalab/data/icons/libre-camera-flash-off.svg +1 -0
- datalab/data/icons/libre-camera-flash-on.svg +1 -0
- datalab/data/icons/libre-gui-about.svg +1 -0
- datalab/data/icons/libre-gui-action-delete.svg +1 -0
- datalab/data/icons/libre-gui-add.svg +1 -0
- datalab/data/icons/libre-gui-arrow-down.svg +1 -0
- datalab/data/icons/libre-gui-arrow-left.svg +1 -0
- datalab/data/icons/libre-gui-arrow-right.svg +1 -0
- datalab/data/icons/libre-gui-arrow-up.svg +1 -0
- datalab/data/icons/libre-gui-close.svg +40 -0
- datalab/data/icons/libre-gui-cogs.svg +1 -0
- datalab/data/icons/libre-gui-globe.svg +1 -0
- datalab/data/icons/libre-gui-help.svg +1 -0
- datalab/data/icons/libre-gui-link.svg +1 -0
- datalab/data/icons/libre-gui-menu.svg +1 -0
- datalab/data/icons/libre-gui-pencil.svg +1 -0
- datalab/data/icons/libre-gui-plugin.svg +1 -0
- datalab/data/icons/libre-gui-questions.svg +1 -0
- datalab/data/icons/libre-gui-settings.svg +1 -0
- datalab/data/icons/libre-gui-unlink.svg +1 -0
- datalab/data/icons/libre-tech-ram.svg +1 -0
- datalab/data/icons/libre-toolbox.svg +1 -0
- datalab/data/icons/logs.svg +1 -0
- datalab/data/icons/markers.svg +74 -0
- datalab/data/icons/menu.svg +13 -0
- datalab/data/icons/new_ima.svg +148 -0
- datalab/data/icons/new_sig.svg +123 -0
- datalab/data/icons/operations/abs.svg +116 -0
- datalab/data/icons/operations/arithmetic.svg +123 -0
- datalab/data/icons/operations/average.svg +124 -0
- datalab/data/icons/operations/complex_from_magnitude_phase.svg +116 -0
- datalab/data/icons/operations/complex_from_real_imag.svg +124 -0
- datalab/data/icons/operations/constant.svg +116 -0
- datalab/data/icons/operations/constant_add.svg +109 -0
- datalab/data/icons/operations/constant_divide.svg +109 -0
- datalab/data/icons/operations/constant_multiply.svg +109 -0
- datalab/data/icons/operations/constant_subtract.svg +109 -0
- datalab/data/icons/operations/convert_dtype.svg +117 -0
- datalab/data/icons/operations/convolution.svg +46 -0
- datalab/data/icons/operations/deconvolution.svg +57 -0
- datalab/data/icons/operations/derivative.svg +127 -0
- datalab/data/icons/operations/difference.svg +52 -0
- datalab/data/icons/operations/division.svg +139 -0
- datalab/data/icons/operations/exp.svg +116 -0
- datalab/data/icons/operations/flip_horizontally.svg +69 -0
- datalab/data/icons/operations/flip_vertically.svg +74 -0
- datalab/data/icons/operations/im.svg +124 -0
- datalab/data/icons/operations/integral.svg +50 -0
- datalab/data/icons/operations/inverse.svg +143 -0
- datalab/data/icons/operations/log10.svg +109 -0
- datalab/data/icons/operations/phase.svg +116 -0
- datalab/data/icons/operations/power.svg +118 -0
- datalab/data/icons/operations/product.svg +124 -0
- datalab/data/icons/operations/profile.svg +379 -0
- datalab/data/icons/operations/profile_average.svg +399 -0
- datalab/data/icons/operations/profile_radial.svg +261 -0
- datalab/data/icons/operations/profile_segment.svg +262 -0
- datalab/data/icons/operations/quadratic_difference.svg +84 -0
- datalab/data/icons/operations/re.svg +124 -0
- datalab/data/icons/operations/rotate_left.svg +72 -0
- datalab/data/icons/operations/rotate_right.svg +72 -0
- datalab/data/icons/operations/signals_to_image.svg +314 -0
- datalab/data/icons/operations/sqrt.svg +110 -0
- datalab/data/icons/operations/std.svg +124 -0
- datalab/data/icons/operations/sum.svg +102 -0
- datalab/data/icons/play_demo.svg +9 -0
- datalab/data/icons/processing/axis_transform.svg +62 -0
- datalab/data/icons/processing/bandpass.svg +79 -0
- datalab/data/icons/processing/bandstop.svg +71 -0
- datalab/data/icons/processing/binning.svg +126 -0
- datalab/data/icons/processing/clip.svg +119 -0
- datalab/data/icons/processing/detrending.svg +173 -0
- datalab/data/icons/processing/distribute_on_grid.svg +769 -0
- datalab/data/icons/processing/edge_detection.svg +46 -0
- datalab/data/icons/processing/erase.svg +1 -0
- datalab/data/icons/processing/exposure.svg +143 -0
- datalab/data/icons/processing/fourier.svg +104 -0
- datalab/data/icons/processing/highpass.svg +59 -0
- datalab/data/icons/processing/interpolation.svg +71 -0
- datalab/data/icons/processing/level_adjustment.svg +70 -0
- datalab/data/icons/processing/lowpass.svg +60 -0
- datalab/data/icons/processing/morphology.svg +49 -0
- datalab/data/icons/processing/noise_addition.svg +114 -0
- datalab/data/icons/processing/noise_reduction.svg +38 -0
- datalab/data/icons/processing/normalize.svg +84 -0
- datalab/data/icons/processing/offset_correction.svg +131 -0
- datalab/data/icons/processing/resampling1d.svg +101 -0
- datalab/data/icons/processing/resampling2d.svg +240 -0
- datalab/data/icons/processing/reset_positions.svg +185 -0
- datalab/data/icons/processing/resize.svg +9 -0
- datalab/data/icons/processing/reverse_signal_x.svg +171 -0
- datalab/data/icons/processing/stability.svg +11 -0
- datalab/data/icons/processing/swap_x_y.svg +65 -0
- datalab/data/icons/processing/thresholding.svg +63 -0
- datalab/data/icons/processing/windowing.svg +45 -0
- datalab/data/icons/properties.svg +26 -0
- datalab/data/icons/reset.svg +9 -0
- datalab/data/icons/restore.svg +40 -0
- datalab/data/icons/roi/roi.svg +76 -0
- datalab/data/icons/roi/roi_coordinate.svg +78 -0
- datalab/data/icons/roi/roi_copy.svg +112 -0
- datalab/data/icons/roi/roi_delete.svg +81 -0
- datalab/data/icons/roi/roi_export.svg +87 -0
- datalab/data/icons/roi/roi_graphical.svg +78 -0
- datalab/data/icons/roi/roi_grid.svg +67 -0
- datalab/data/icons/roi/roi_ima.svg +188 -0
- datalab/data/icons/roi/roi_import.svg +87 -0
- datalab/data/icons/roi/roi_new.svg +81 -0
- datalab/data/icons/roi/roi_new_circle.svg +95 -0
- datalab/data/icons/roi/roi_new_polygon.svg +110 -0
- datalab/data/icons/roi/roi_new_rectangle.svg +70 -0
- datalab/data/icons/roi/roi_paste.svg +98 -0
- datalab/data/icons/roi/roi_sig.svg +124 -0
- datalab/data/icons/shapes.svg +134 -0
- datalab/data/icons/signal.svg +103 -0
- datalab/data/icons/table.svg +85 -0
- datalab/data/icons/table_unavailable.svg +102 -0
- datalab/data/icons/to_signal.svg +124 -0
- datalab/data/icons/tour/next.svg +44 -0
- datalab/data/icons/tour/previous.svg +44 -0
- datalab/data/icons/tour/rewind.svg +51 -0
- datalab/data/icons/tour/stop.svg +47 -0
- datalab/data/icons/tour/tour.svg +16 -0
- datalab/data/icons/uncheck_all.svg +78 -0
- datalab/data/icons/view/curve_antialiasing.svg +50 -0
- datalab/data/icons/view/new_window.svg +98 -0
- datalab/data/icons/view/refresh-auto.svg +57 -0
- datalab/data/icons/view/refresh-manual.svg +51 -0
- datalab/data/icons/view/reset_curve_styles.svg +96 -0
- datalab/data/icons/view/show_first.svg +55 -0
- datalab/data/icons/view/show_titles.svg +46 -0
- datalab/data/icons/visualization.svg +51 -0
- datalab/data/logo/DataLab-Banner-150.png +0 -0
- datalab/data/logo/DataLab-Banner-200.png +0 -0
- datalab/data/logo/DataLab-Banner2-100.png +0 -0
- datalab/data/logo/DataLab-Splash.png +0 -0
- datalab/data/logo/DataLab-watermark.png +0 -0
- datalab/data/logo/DataLab.svg +83 -0
- datalab/data/tests/reordering_test.h5 +0 -0
- datalab/data/tutorials/fabry_perot/fabry-perot1.jpg +0 -0
- datalab/data/tutorials/fabry_perot/fabry-perot2.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_13.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_18.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_23.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_30.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_35.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_40.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_45.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_50.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_55.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_60.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_65.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_70.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_75.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_80.jpg +0 -0
- datalab/env.py +542 -0
- datalab/gui/__init__.py +89 -0
- datalab/gui/actionhandler.py +1701 -0
- datalab/gui/docks.py +473 -0
- datalab/gui/h5io.py +150 -0
- datalab/gui/macroeditor.py +310 -0
- datalab/gui/main.py +2081 -0
- datalab/gui/newobject.py +217 -0
- datalab/gui/objectview.py +766 -0
- datalab/gui/panel/__init__.py +48 -0
- datalab/gui/panel/base.py +3254 -0
- datalab/gui/panel/image.py +157 -0
- datalab/gui/panel/macro.py +607 -0
- datalab/gui/panel/signal.py +164 -0
- datalab/gui/plothandler.py +800 -0
- datalab/gui/processor/__init__.py +84 -0
- datalab/gui/processor/base.py +2456 -0
- datalab/gui/processor/catcher.py +75 -0
- datalab/gui/processor/image.py +1214 -0
- datalab/gui/processor/signal.py +755 -0
- datalab/gui/profiledialog.py +333 -0
- datalab/gui/roieditor.py +633 -0
- datalab/gui/roigrideditor.py +208 -0
- datalab/gui/settings.py +612 -0
- datalab/gui/tour.py +908 -0
- datalab/h5/__init__.py +12 -0
- datalab/h5/common.py +314 -0
- datalab/h5/generic.py +580 -0
- datalab/h5/native.py +39 -0
- datalab/h5/utils.py +95 -0
- datalab/objectmodel.py +640 -0
- datalab/plugins/_readme_.txt +9 -0
- datalab/plugins/datalab_imageformats.py +175 -0
- datalab/plugins/datalab_testdata.py +190 -0
- datalab/plugins.py +355 -0
- datalab/tests/__init__.py +199 -0
- datalab/tests/backbone/__init__.py +1 -0
- datalab/tests/backbone/config_unit_test.py +170 -0
- datalab/tests/backbone/config_versioning_unit_test.py +34 -0
- datalab/tests/backbone/dictlistserial_app_test.py +38 -0
- datalab/tests/backbone/errorcatcher_unit_test.py +69 -0
- datalab/tests/backbone/errormsgbox_unit_test.py +50 -0
- datalab/tests/backbone/execenv_unit.py +262 -0
- datalab/tests/backbone/loadtest_gdi.py +147 -0
- datalab/tests/backbone/long_callback.py +96 -0
- datalab/tests/backbone/main_app_test.py +137 -0
- datalab/tests/backbone/memory_leak.py +43 -0
- datalab/tests/backbone/procisolation1_unit.py +128 -0
- datalab/tests/backbone/procisolation2_unit.py +171 -0
- datalab/tests/backbone/procisolation_unit_test.py +22 -0
- datalab/tests/backbone/profiling_app.py +27 -0
- datalab/tests/backbone/strings_unit_test.py +65 -0
- datalab/tests/backbone/title_formatting_unit_test.py +82 -0
- datalab/tests/conftest.py +131 -0
- datalab/tests/features/__init__.py +1 -0
- datalab/tests/features/applauncher/__init__.py +1 -0
- datalab/tests/features/applauncher/launcher1_app_test.py +28 -0
- datalab/tests/features/applauncher/launcher2_app_test.py +30 -0
- datalab/tests/features/common/__init__.py +1 -0
- datalab/tests/features/common/add_metadata_app_test.py +134 -0
- datalab/tests/features/common/add_metadata_unit_test.py +267 -0
- datalab/tests/features/common/annotations_management_unit_test.py +152 -0
- datalab/tests/features/common/auto_analysis_recompute_unit_test.py +240 -0
- datalab/tests/features/common/createobject_unit_test.py +50 -0
- datalab/tests/features/common/geometry_results_app_test.py +135 -0
- datalab/tests/features/common/interactive_processing_test.py +1109 -0
- datalab/tests/features/common/io_app_test.py +75 -0
- datalab/tests/features/common/large_results_app_test.py +187 -0
- datalab/tests/features/common/metadata_all_patterns_test.py +103 -0
- datalab/tests/features/common/metadata_app_test.py +139 -0
- datalab/tests/features/common/metadata_io_unit_test.py +60 -0
- datalab/tests/features/common/misc_app_test.py +236 -0
- datalab/tests/features/common/multiple_geometry_results_unit_test.py +122 -0
- datalab/tests/features/common/multiple_table_results_unit_test.py +64 -0
- datalab/tests/features/common/operation_modes_app_test.py +392 -0
- datalab/tests/features/common/plot_results_app_test.py +278 -0
- datalab/tests/features/common/reorder_app_test.py +75 -0
- datalab/tests/features/common/result_deletion_unit_test.py +96 -0
- datalab/tests/features/common/result_merged_label_unit_test.py +154 -0
- datalab/tests/features/common/result_shape_settings_unit_test.py +223 -0
- datalab/tests/features/common/roi_plotitem_unit_test.py +64 -0
- datalab/tests/features/common/roieditor_unit_test.py +102 -0
- datalab/tests/features/common/save_to_dir_app_test.py +163 -0
- datalab/tests/features/common/save_to_dir_unit_test.py +474 -0
- datalab/tests/features/common/stat_app_test.py +40 -0
- datalab/tests/features/common/stats_tools_unit_test.py +77 -0
- datalab/tests/features/common/table_results_app_test.py +52 -0
- datalab/tests/features/common/textimport_unit_test.py +131 -0
- datalab/tests/features/common/uuid_preservation_test.py +281 -0
- datalab/tests/features/common/worker_unit_test.py +402 -0
- datalab/tests/features/control/__init__.py +1 -0
- datalab/tests/features/control/connect_dialog.py +28 -0
- datalab/tests/features/control/embedded1_unit_test.py +304 -0
- datalab/tests/features/control/embedded2_unit_test.py +52 -0
- datalab/tests/features/control/remoteclient_app_test.py +219 -0
- datalab/tests/features/control/remoteclient_unit.py +75 -0
- datalab/tests/features/control/simpleclient_unit_test.py +321 -0
- datalab/tests/features/hdf5/__init__.py +1 -0
- datalab/tests/features/hdf5/h5browser1_unit_test.py +31 -0
- datalab/tests/features/hdf5/h5browser2_unit.py +55 -0
- datalab/tests/features/hdf5/h5browser_app_test.py +77 -0
- datalab/tests/features/hdf5/h5import_app_test.py +25 -0
- datalab/tests/features/hdf5/h5importer_app_test.py +34 -0
- datalab/tests/features/image/__init__.py +1 -0
- datalab/tests/features/image/annotations_app_test.py +28 -0
- datalab/tests/features/image/annotations_unit_test.py +80 -0
- datalab/tests/features/image/average_app_test.py +46 -0
- datalab/tests/features/image/background_dialog_test.py +70 -0
- datalab/tests/features/image/blobs_app_test.py +50 -0
- datalab/tests/features/image/contour_app_test.py +42 -0
- datalab/tests/features/image/contour_fabryperot_app_test.py +51 -0
- datalab/tests/features/image/denoise_app_test.py +31 -0
- datalab/tests/features/image/distribute_on_grid_app_test.py +95 -0
- datalab/tests/features/image/edges_app_test.py +31 -0
- datalab/tests/features/image/erase_app_test.py +21 -0
- datalab/tests/features/image/fft2d_app_test.py +27 -0
- datalab/tests/features/image/flatfield_app_test.py +40 -0
- datalab/tests/features/image/geometry_transform_unit_test.py +396 -0
- datalab/tests/features/image/imagetools_app_test.py +51 -0
- datalab/tests/features/image/imagetools_unit_test.py +27 -0
- datalab/tests/features/image/load_app_test.py +73 -0
- datalab/tests/features/image/morph_app_test.py +32 -0
- datalab/tests/features/image/offsetcorrection_app_test.py +30 -0
- datalab/tests/features/image/peak2d_app_test.py +53 -0
- datalab/tests/features/image/profile_app_test.py +73 -0
- datalab/tests/features/image/profile_dialog_test.py +56 -0
- datalab/tests/features/image/roi_app_test.py +98 -0
- datalab/tests/features/image/roi_circ_app_test.py +62 -0
- datalab/tests/features/image/roi_manipulation_app_test.py +268 -0
- datalab/tests/features/image/roigrid_unit_test.py +60 -0
- datalab/tests/features/image/side_by_side_app_test.py +52 -0
- datalab/tests/features/macro/__init__.py +1 -0
- datalab/tests/features/macro/macro_app_test.py +28 -0
- datalab/tests/features/macro/macroeditor_unit_test.py +102 -0
- datalab/tests/features/signal/__init__.py +1 -0
- datalab/tests/features/signal/baseline_dialog_test.py +53 -0
- datalab/tests/features/signal/deltax_dialog_unit_test.py +34 -0
- datalab/tests/features/signal/fft1d_app_test.py +26 -0
- datalab/tests/features/signal/filter_app_test.py +44 -0
- datalab/tests/features/signal/fitdialog_unit_test.py +50 -0
- datalab/tests/features/signal/interpolation_app_test.py +110 -0
- datalab/tests/features/signal/loadbigsignal_app_test.py +80 -0
- datalab/tests/features/signal/multiple_rois_unit_test.py +132 -0
- datalab/tests/features/signal/pulse_features_app_test.py +118 -0
- datalab/tests/features/signal/pulse_features_roi_app_test.py +55 -0
- datalab/tests/features/signal/roi_app_test.py +78 -0
- datalab/tests/features/signal/roi_manipulation_app_test.py +261 -0
- datalab/tests/features/signal/select_xy_cursor_unit_test.py +46 -0
- datalab/tests/features/signal/signalpeakdetection_dialog_test.py +33 -0
- datalab/tests/features/signal/signals_to_image_app_test.py +98 -0
- datalab/tests/features/signal/xarray_compat_app_test.py +128 -0
- datalab/tests/features/tour_unit_test.py +22 -0
- datalab/tests/features/utilities/__init__.py +1 -0
- datalab/tests/features/utilities/installconf_unit_test.py +21 -0
- datalab/tests/features/utilities/logview_app_test.py +21 -0
- datalab/tests/features/utilities/logview_error.py +24 -0
- datalab/tests/features/utilities/logview_unit_test.py +21 -0
- datalab/tests/features/utilities/memstatus_app_test.py +42 -0
- datalab/tests/features/utilities/settings_unit_test.py +88 -0
- datalab/tests/scenarios/__init__.py +1 -0
- datalab/tests/scenarios/beautiful_app.py +121 -0
- datalab/tests/scenarios/common.py +463 -0
- datalab/tests/scenarios/demo.py +212 -0
- datalab/tests/scenarios/example_app_test.py +47 -0
- datalab/tests/scenarios/scenario_h5_app_test.py +75 -0
- datalab/tests/scenarios/scenario_ima1_app_test.py +34 -0
- datalab/tests/scenarios/scenario_ima2_app_test.py +34 -0
- datalab/tests/scenarios/scenario_mac_app_test.py +58 -0
- datalab/tests/scenarios/scenario_sig1_app_test.py +36 -0
- datalab/tests/scenarios/scenario_sig2_app_test.py +35 -0
- datalab/utils/__init__.py +1 -0
- datalab/utils/conf.py +304 -0
- datalab/utils/dephash.py +105 -0
- datalab/utils/qthelpers.py +633 -0
- datalab/utils/strings.py +34 -0
- datalab/utils/tests.py +0 -0
- datalab/widgets/__init__.py +1 -0
- datalab/widgets/connection.py +138 -0
- datalab/widgets/filedialog.py +91 -0
- datalab/widgets/fileviewer.py +84 -0
- datalab/widgets/fitdialog.py +788 -0
- datalab/widgets/h5browser.py +1048 -0
- datalab/widgets/imagebackground.py +111 -0
- datalab/widgets/instconfviewer.py +175 -0
- datalab/widgets/logviewer.py +80 -0
- datalab/widgets/signalbaseline.py +90 -0
- datalab/widgets/signalcursor.py +208 -0
- datalab/widgets/signaldeltax.py +151 -0
- datalab/widgets/signalpeak.py +199 -0
- datalab/widgets/status.py +249 -0
- datalab/widgets/textimport.py +786 -0
- datalab/widgets/warningerror.py +223 -0
- datalab/widgets/wizard.py +286 -0
- datalab_platform-1.0.1.dist-info/METADATA +121 -0
- datalab_platform-1.0.1.dist-info/RECORD +494 -0
- datalab_platform-0.0.1.dev0.dist-info/METADATA +0 -67
- datalab_platform-0.0.1.dev0.dist-info/RECORD +0 -7
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/WHEEL +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/entry_points.txt +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/top_level.txt +0 -0
datalab/h5/generic.py
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
DataLab Generic HDF5 format support
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
8
|
+
|
|
9
|
+
import h5py
|
|
10
|
+
import numpy as np
|
|
11
|
+
from guidata.utils.misc import to_string
|
|
12
|
+
from sigima.objects import create_image, create_signal
|
|
13
|
+
|
|
14
|
+
from datalab.h5 import common, utils
|
|
15
|
+
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# Encoding and Data Reading Utilities
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def safe_decode_bytes(data, fallback="<binary data>"):
|
|
22
|
+
"""Safely decode bytes to string using multiple encoding strategies."""
|
|
23
|
+
if isinstance(data, str):
|
|
24
|
+
return data
|
|
25
|
+
if not isinstance(data, bytes):
|
|
26
|
+
return str(data)
|
|
27
|
+
|
|
28
|
+
# Try encodings in order of preference
|
|
29
|
+
for encoding in ["utf-8", "latin1", "cp1252", "iso-8859-1", "ascii"]:
|
|
30
|
+
try:
|
|
31
|
+
decoded = data.decode(encoding)
|
|
32
|
+
# For legacy encodings, validate the result looks reasonable
|
|
33
|
+
if encoding in ["latin1", "cp1252", "iso-8859-1"]:
|
|
34
|
+
if _is_reasonable_text(decoded):
|
|
35
|
+
return decoded
|
|
36
|
+
else:
|
|
37
|
+
return decoded
|
|
38
|
+
except (UnicodeDecodeError, UnicodeError):
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
# Final fallback with replacement characters
|
|
42
|
+
try:
|
|
43
|
+
return data.decode("utf-8", errors="replace")
|
|
44
|
+
except Exception: # pylint: disable=broad-except
|
|
45
|
+
return fallback
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _is_reasonable_text(text):
|
|
49
|
+
"""Check if decoded text looks reasonable (mostly printable)."""
|
|
50
|
+
if not text:
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
printable_chars = sum(1 for c in text if c.isprintable() or c.isspace())
|
|
54
|
+
ratio = printable_chars / len(text)
|
|
55
|
+
|
|
56
|
+
# Accept if mostly printable or short enough to be likely text
|
|
57
|
+
return ratio >= 0.8 or len(text) < 20
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def safe_read_dataset(dset, fallback_data=None):
|
|
61
|
+
"""Safely read HDF5 dataset with encoding error handling."""
|
|
62
|
+
try:
|
|
63
|
+
return dset[()]
|
|
64
|
+
except UnicodeDecodeError:
|
|
65
|
+
# Try alternative reading strategies for problematic encodings
|
|
66
|
+
return _try_alternative_read(dset, fallback_data)
|
|
67
|
+
except (TypeError, ValueError, OSError):
|
|
68
|
+
return fallback_data
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _try_alternative_read(dset, fallback_data):
|
|
72
|
+
"""Try alternative strategies to read datasets with encoding issues."""
|
|
73
|
+
strategies = [
|
|
74
|
+
lambda d: d.asstr()[()], # Try string conversion
|
|
75
|
+
lambda d: d.astype("S")[()], # Try reading as bytes
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
for strategy in strategies:
|
|
79
|
+
try:
|
|
80
|
+
return strategy(dset)
|
|
81
|
+
except Exception: # pylint: disable=broad-except
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
return fallback_data
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# =============================================================================
|
|
88
|
+
# Text Formatting Utilities
|
|
89
|
+
# =============================================================================
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def format_text_data(data):
|
|
93
|
+
"""Format various types of data for text display."""
|
|
94
|
+
if data is None:
|
|
95
|
+
return "<unreadable data>"
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
return to_string(data)
|
|
99
|
+
except (UnicodeDecodeError, UnicodeError):
|
|
100
|
+
return _handle_encoding_issues(data)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _handle_encoding_issues(data):
|
|
104
|
+
"""Handle data with encoding issues."""
|
|
105
|
+
if isinstance(data, bytes):
|
|
106
|
+
return safe_decode_bytes(data)
|
|
107
|
+
|
|
108
|
+
if isinstance(data, np.ndarray):
|
|
109
|
+
if data.dtype.kind in ["S", "a", "U"]: # String arrays
|
|
110
|
+
return _format_string_array(data)
|
|
111
|
+
if data.dtype.names: # Compound data
|
|
112
|
+
return _format_compound_data(data)
|
|
113
|
+
|
|
114
|
+
return f"<data with encoding issues: {type(data)}>"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _format_string_array(data):
|
|
118
|
+
"""Format string arrays with encoding handling."""
|
|
119
|
+
try:
|
|
120
|
+
if data.size == 1:
|
|
121
|
+
return safe_decode_bytes(data.item())
|
|
122
|
+
# Show first few elements
|
|
123
|
+
items = []
|
|
124
|
+
for i, item in enumerate(data.flat):
|
|
125
|
+
if i >= 5:
|
|
126
|
+
items.append("...")
|
|
127
|
+
break
|
|
128
|
+
items.append(safe_decode_bytes(item))
|
|
129
|
+
return f"[{', '.join(items)}]"
|
|
130
|
+
except Exception: # pylint: disable=broad-except
|
|
131
|
+
return f"<string array: {data.shape} {data.dtype}>"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _format_compound_data(data):
|
|
135
|
+
"""Format compound data with encoding handling."""
|
|
136
|
+
try:
|
|
137
|
+
result_parts = []
|
|
138
|
+
for field_name in data.dtype.names:
|
|
139
|
+
field_data = data[field_name]
|
|
140
|
+
if hasattr(field_data, "item"):
|
|
141
|
+
field_data = field_data.item()
|
|
142
|
+
|
|
143
|
+
if isinstance(field_data, bytes):
|
|
144
|
+
field_value = safe_decode_bytes(field_data)
|
|
145
|
+
else:
|
|
146
|
+
field_value = str(field_data)
|
|
147
|
+
|
|
148
|
+
result_parts.append(f"{field_name}: {field_value}")
|
|
149
|
+
|
|
150
|
+
return f"({', '.join(result_parts)})"
|
|
151
|
+
except Exception: # pylint: disable=broad-except
|
|
152
|
+
return f"<compound data: {data.dtype}>"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# =============================================================================
|
|
156
|
+
# Base Node Class
|
|
157
|
+
# =============================================================================
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class BaseGenericNode(common.BaseNode):
|
|
161
|
+
"""Base class for generic HDF5 data nodes with encoding support."""
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def match(cls, dset):
|
|
165
|
+
"""Return True if h5 dataset matches this node pattern."""
|
|
166
|
+
return not isinstance(dset, h5py.Group)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def icon_name(self):
|
|
170
|
+
"""Icon name associated to node."""
|
|
171
|
+
return "h5scalar.svg"
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def data(self):
|
|
175
|
+
"""Data associated to node, if available."""
|
|
176
|
+
return safe_read_dataset(self.dset, fallback_data=None)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def dtype_str(self):
|
|
180
|
+
"""Return string representation of node data type."""
|
|
181
|
+
try:
|
|
182
|
+
return str(self.dset.dtype)
|
|
183
|
+
except (UnicodeDecodeError, TypeError, ValueError):
|
|
184
|
+
if self.data is None:
|
|
185
|
+
return "unknown"
|
|
186
|
+
try:
|
|
187
|
+
return str(self.data.dtype)
|
|
188
|
+
except Exception: # pylint: disable=broad-except
|
|
189
|
+
return "unknown"
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def text(self):
|
|
193
|
+
"""Return node textual representation."""
|
|
194
|
+
return format_text_data(self.data)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# =============================================================================
|
|
198
|
+
# Specialized Node Classes
|
|
199
|
+
# =============================================================================
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class GenericScalarNode(BaseGenericNode):
|
|
203
|
+
"""Node for scalar HDF5 data."""
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def match(cls, dset):
|
|
207
|
+
"""Match scalar numeric data."""
|
|
208
|
+
if not super().match(dset):
|
|
209
|
+
return False
|
|
210
|
+
data = safe_read_dataset(dset)
|
|
211
|
+
return (
|
|
212
|
+
data is not None
|
|
213
|
+
and isinstance(data, np.generic)
|
|
214
|
+
and utils.is_supported_num_dtype(data)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class GenericTextNode(BaseGenericNode):
|
|
219
|
+
"""Node for text/string HDF5 data."""
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def match(cls, dset):
|
|
223
|
+
"""Match text or string data."""
|
|
224
|
+
if not super().match(dset):
|
|
225
|
+
return False
|
|
226
|
+
data = safe_read_dataset(dset)
|
|
227
|
+
if data is None:
|
|
228
|
+
# Try to match based on dtype for unreadable data
|
|
229
|
+
try:
|
|
230
|
+
dtype = dset.dtype
|
|
231
|
+
return dtype.kind in ["S", "a", "U"] or "str" in str(dtype)
|
|
232
|
+
except Exception: # pylint: disable=broad-except
|
|
233
|
+
return False
|
|
234
|
+
return isinstance(data, bytes) or utils.is_supported_str_dtype(data)
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def dtype_str(self):
|
|
238
|
+
"""Return simplified dtype for text data."""
|
|
239
|
+
return "string"
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def text(self):
|
|
243
|
+
"""Return formatted text with special handling for single arrays."""
|
|
244
|
+
if self.data is None:
|
|
245
|
+
return "<unreadable data>"
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
if utils.is_single_str_array(self.data):
|
|
249
|
+
item = self.data[0]
|
|
250
|
+
return safe_decode_bytes(item) if isinstance(item, bytes) else str(item)
|
|
251
|
+
return format_text_data(self.data)
|
|
252
|
+
except (UnicodeDecodeError, UnicodeError):
|
|
253
|
+
return _handle_text_encoding_issues(self.data)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _handle_text_encoding_issues(data):
|
|
257
|
+
"""Handle encoding issues specific to text nodes."""
|
|
258
|
+
if isinstance(data, bytes):
|
|
259
|
+
return safe_decode_bytes(data)
|
|
260
|
+
if isinstance(data, np.ndarray) and data.dtype.kind in ["S", "a"]:
|
|
261
|
+
try:
|
|
262
|
+
if data.size == 1:
|
|
263
|
+
return safe_decode_bytes(data.item())
|
|
264
|
+
decoded = [safe_decode_bytes(item) for item in data.flat]
|
|
265
|
+
return str(decoded[:10]) # Show first 10 elements
|
|
266
|
+
except Exception: # pylint: disable=broad-except
|
|
267
|
+
return f"<string data: {data.shape}>"
|
|
268
|
+
return "<text with encoding issues>"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class GenericArrayNode(BaseGenericNode):
|
|
272
|
+
"""Node for array HDF5 data, including numeric arrays from compound data."""
|
|
273
|
+
|
|
274
|
+
IS_ARRAY = True
|
|
275
|
+
|
|
276
|
+
@classmethod
|
|
277
|
+
def match(cls, dset):
|
|
278
|
+
"""Match numeric array data, including convertible compound data."""
|
|
279
|
+
if not super().match(dset):
|
|
280
|
+
return False
|
|
281
|
+
data = safe_read_dataset(dset)
|
|
282
|
+
|
|
283
|
+
if data is None:
|
|
284
|
+
return False
|
|
285
|
+
|
|
286
|
+
# First check direct numeric arrays
|
|
287
|
+
if (
|
|
288
|
+
utils.is_supported_num_dtype(data)
|
|
289
|
+
and isinstance(data, np.ndarray)
|
|
290
|
+
and len(data.shape) in (1, 2)
|
|
291
|
+
):
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
# Then check compound data that can be converted to numeric arrays
|
|
295
|
+
if (
|
|
296
|
+
isinstance(data, np.ndarray)
|
|
297
|
+
and hasattr(data.dtype, "names")
|
|
298
|
+
and data.dtype.names is not None
|
|
299
|
+
):
|
|
300
|
+
return cls._can_convert_compound_to_numeric(data)
|
|
301
|
+
|
|
302
|
+
return False
|
|
303
|
+
|
|
304
|
+
@classmethod
|
|
305
|
+
def _can_convert_compound_to_numeric(cls, data):
|
|
306
|
+
"""Check if compound data can be converted to a supported numeric array."""
|
|
307
|
+
try:
|
|
308
|
+
numeric_array = cls.extract_numeric_from_compound(data)
|
|
309
|
+
return (
|
|
310
|
+
numeric_array is not None
|
|
311
|
+
and utils.is_supported_num_dtype(numeric_array)
|
|
312
|
+
and isinstance(numeric_array, np.ndarray)
|
|
313
|
+
and len(numeric_array.shape) in (1, 2)
|
|
314
|
+
)
|
|
315
|
+
except Exception: # pylint: disable=broad-except
|
|
316
|
+
return False
|
|
317
|
+
|
|
318
|
+
@classmethod
|
|
319
|
+
def extract_numeric_from_compound(cls, data):
|
|
320
|
+
"""Extract a numeric array from compound data."""
|
|
321
|
+
if not (hasattr(data.dtype, "names") and data.dtype.names):
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
# Find ALL fields and check if they are numeric
|
|
325
|
+
all_fields = list(data.dtype.names)
|
|
326
|
+
numeric_fields = []
|
|
327
|
+
for field_name in all_fields:
|
|
328
|
+
field_dtype = data.dtype.fields[field_name][0]
|
|
329
|
+
if np.issubdtype(field_dtype, np.number):
|
|
330
|
+
numeric_fields.append(field_name)
|
|
331
|
+
|
|
332
|
+
# Only convert if ALL fields are numeric (preserve all information)
|
|
333
|
+
# or if there's a single numeric field and no important string data
|
|
334
|
+
if len(numeric_fields) == 0:
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
if len(numeric_fields) != len(all_fields):
|
|
338
|
+
# Mixed data - check if non-numeric fields contain meaningful data
|
|
339
|
+
for field_name in all_fields:
|
|
340
|
+
if field_name not in numeric_fields:
|
|
341
|
+
field_data = data[field_name]
|
|
342
|
+
# If there's meaningful string data, don't convert
|
|
343
|
+
if cls._has_meaningful_string_data(field_data):
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
# If single numeric field, extract it directly
|
|
348
|
+
if len(numeric_fields) == 1:
|
|
349
|
+
return data[numeric_fields[0]]
|
|
350
|
+
|
|
351
|
+
# Multiple numeric fields: stack them if compatible shapes
|
|
352
|
+
field_data = [data[field] for field in numeric_fields]
|
|
353
|
+
|
|
354
|
+
# Check if all fields have the same shape
|
|
355
|
+
shapes = [arr.shape for arr in field_data]
|
|
356
|
+
if len(set(shapes)) == 1:
|
|
357
|
+
# Stack along new axis to create 2D array
|
|
358
|
+
return np.stack(field_data, axis=-1)
|
|
359
|
+
|
|
360
|
+
except Exception: # pylint: disable=broad-except
|
|
361
|
+
pass
|
|
362
|
+
return None
|
|
363
|
+
|
|
364
|
+
@classmethod
|
|
365
|
+
def _has_meaningful_string_data(cls, field_data):
|
|
366
|
+
"""Check if string field contains meaningful data worth preserving."""
|
|
367
|
+
try:
|
|
368
|
+
if hasattr(field_data, "flat"):
|
|
369
|
+
# Check if most entries are non-empty and meaningful
|
|
370
|
+
non_empty_count = 0
|
|
371
|
+
for item in field_data.flat:
|
|
372
|
+
if isinstance(item, bytes):
|
|
373
|
+
decoded = item.decode("utf-8", errors="ignore").strip()
|
|
374
|
+
if decoded and len(decoded) > 0:
|
|
375
|
+
non_empty_count += 1
|
|
376
|
+
elif isinstance(item, str) and item.strip():
|
|
377
|
+
non_empty_count += 1
|
|
378
|
+
|
|
379
|
+
# If most entries have meaningful content, preserve it
|
|
380
|
+
return non_empty_count / field_data.size > 0.5
|
|
381
|
+
return True # Default to preserving unknown string data
|
|
382
|
+
except Exception: # pylint: disable=broad-except
|
|
383
|
+
return True # Conservative: preserve if we can't determine
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def data(self):
|
|
387
|
+
"""Data associated to node, if available."""
|
|
388
|
+
raw_data = safe_read_dataset(self.dset, fallback_data=None)
|
|
389
|
+
|
|
390
|
+
# If this is compound data, try to extract numeric array
|
|
391
|
+
if (
|
|
392
|
+
raw_data is not None
|
|
393
|
+
and isinstance(raw_data, np.ndarray)
|
|
394
|
+
and hasattr(raw_data.dtype, "names")
|
|
395
|
+
and raw_data.dtype.names is not None
|
|
396
|
+
):
|
|
397
|
+
numeric_data = self.extract_numeric_from_compound(raw_data)
|
|
398
|
+
if numeric_data is not None:
|
|
399
|
+
return numeric_data
|
|
400
|
+
|
|
401
|
+
return raw_data
|
|
402
|
+
|
|
403
|
+
def is_supported(self) -> bool:
|
|
404
|
+
"""Return True if node is associated to supported data"""
|
|
405
|
+
return self.data.size > 1
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
def __is_signal(self):
|
|
409
|
+
"""Return True if array represents a signal"""
|
|
410
|
+
shape = self.data.shape
|
|
411
|
+
return len(shape) == 1 or shape[0] in (1, 2) or shape[1] in (1, 2)
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def icon_name(self):
|
|
415
|
+
"""Icon name associated to node"""
|
|
416
|
+
if self.is_supported():
|
|
417
|
+
return "signal.svg" if self.__is_signal else "image.svg"
|
|
418
|
+
return "h5array.svg"
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def shape_str(self):
|
|
422
|
+
"""Return string representation of node shape, if any"""
|
|
423
|
+
return " x ".join([str(size) for size in self.data.shape])
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def dtype_str(self):
|
|
427
|
+
"""Return string representation of node data type, if any"""
|
|
428
|
+
return str(self.data.dtype)
|
|
429
|
+
|
|
430
|
+
@property
|
|
431
|
+
def text(self):
|
|
432
|
+
"""Return node textual representation"""
|
|
433
|
+
return str(self.data)
|
|
434
|
+
|
|
435
|
+
def create_native_object(self):
|
|
436
|
+
"""Create native object, if supported"""
|
|
437
|
+
if self.__is_signal:
|
|
438
|
+
obj = create_signal(self.object_title)
|
|
439
|
+
try:
|
|
440
|
+
self.set_signal_data(obj)
|
|
441
|
+
except ValueError:
|
|
442
|
+
obj = None
|
|
443
|
+
else:
|
|
444
|
+
obj = create_image(self.object_title)
|
|
445
|
+
try:
|
|
446
|
+
self.set_image_data(obj)
|
|
447
|
+
except ValueError:
|
|
448
|
+
obj = None
|
|
449
|
+
return obj
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class GenericCompoundNode(BaseGenericNode):
|
|
453
|
+
"""Node for compound/structured HDF5 data that can't convert to numeric arrays."""
|
|
454
|
+
|
|
455
|
+
IS_ARRAY = True
|
|
456
|
+
|
|
457
|
+
@classmethod
|
|
458
|
+
def match(cls, dset):
|
|
459
|
+
"""Match compound/structured data that cannot be converted to numeric arrays."""
|
|
460
|
+
if not super().match(dset):
|
|
461
|
+
return False
|
|
462
|
+
|
|
463
|
+
data = safe_read_dataset(dset)
|
|
464
|
+
if data is None:
|
|
465
|
+
# Try to match based on dtype if we can't read the data
|
|
466
|
+
try:
|
|
467
|
+
return dset.dtype.names is not None
|
|
468
|
+
except Exception: # pylint: disable=broad-except
|
|
469
|
+
return False
|
|
470
|
+
|
|
471
|
+
# Check if it's compound data (structured array)
|
|
472
|
+
if not (
|
|
473
|
+
isinstance(data, np.ndarray)
|
|
474
|
+
and hasattr(data.dtype, "names")
|
|
475
|
+
and data.dtype.names is not None
|
|
476
|
+
):
|
|
477
|
+
return False
|
|
478
|
+
|
|
479
|
+
# IMPORTANT: Only match if GenericArrayNode cannot handle this data
|
|
480
|
+
# Try to convert to a numeric array first
|
|
481
|
+
if cls._can_convert_to_numeric_array(data):
|
|
482
|
+
return False # Let GenericArrayNode handle it
|
|
483
|
+
|
|
484
|
+
return True # We handle compound data that can't be converted
|
|
485
|
+
|
|
486
|
+
@classmethod
|
|
487
|
+
def _can_convert_to_numeric_array(cls, data):
|
|
488
|
+
"""Check if compound data can be converted to a numeric array."""
|
|
489
|
+
try:
|
|
490
|
+
# Try to extract numeric fields and create a pure numeric array
|
|
491
|
+
numeric_array = cls.extract_numeric_array(data)
|
|
492
|
+
if numeric_array is None:
|
|
493
|
+
return False
|
|
494
|
+
|
|
495
|
+
# Check if the resulting array would be supported by GenericArrayNode
|
|
496
|
+
return (
|
|
497
|
+
utils.is_supported_num_dtype(numeric_array)
|
|
498
|
+
and isinstance(numeric_array, np.ndarray)
|
|
499
|
+
and len(numeric_array.shape) in (1, 2)
|
|
500
|
+
)
|
|
501
|
+
except Exception: # pylint: disable=broad-except
|
|
502
|
+
return False
|
|
503
|
+
|
|
504
|
+
@classmethod
|
|
505
|
+
def extract_numeric_array(cls, data):
|
|
506
|
+
"""Try to extract a pure numeric array from compound data."""
|
|
507
|
+
# Use the same logic as GenericArrayNode
|
|
508
|
+
return GenericArrayNode.extract_numeric_from_compound(data)
|
|
509
|
+
|
|
510
|
+
@property
|
|
511
|
+
def dtype_str(self):
|
|
512
|
+
"""Return detailed compound dtype information."""
|
|
513
|
+
try:
|
|
514
|
+
dtype = self.dset.dtype
|
|
515
|
+
if dtype.names:
|
|
516
|
+
field_info = []
|
|
517
|
+
for name in dtype.names:
|
|
518
|
+
field_dtype = dtype.fields[name][0]
|
|
519
|
+
field_info.append(f"{name}: {field_dtype}")
|
|
520
|
+
return f"compound({', '.join(field_info)})"
|
|
521
|
+
return str(dtype)
|
|
522
|
+
except Exception: # pylint: disable=broad-except
|
|
523
|
+
return super().dtype_str
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def text(self):
|
|
527
|
+
"""Return formatted compound data."""
|
|
528
|
+
if self.data is None:
|
|
529
|
+
return "<unreadable compound data>"
|
|
530
|
+
try:
|
|
531
|
+
return self._format_compound_data()
|
|
532
|
+
except Exception: # pylint: disable=broad-except
|
|
533
|
+
return f"<compound data: {self.data.shape} records>"
|
|
534
|
+
|
|
535
|
+
def _format_compound_data(self):
|
|
536
|
+
"""Format compound data for display."""
|
|
537
|
+
if not (hasattr(self.data.dtype, "names") and self.data.dtype.names):
|
|
538
|
+
return super().text
|
|
539
|
+
if self.data.size == 1:
|
|
540
|
+
return self._format_single_record()
|
|
541
|
+
return self._format_multiple_records()
|
|
542
|
+
|
|
543
|
+
def _format_single_record(self):
|
|
544
|
+
"""Format a single compound record."""
|
|
545
|
+
parts = []
|
|
546
|
+
for field_name in self.data.dtype.names:
|
|
547
|
+
field_value = self.data[field_name].item()
|
|
548
|
+
if isinstance(field_value, bytes):
|
|
549
|
+
field_value = safe_decode_bytes(field_value)
|
|
550
|
+
parts.append(f"{field_name}: {field_value}")
|
|
551
|
+
return f"({', '.join(parts)})"
|
|
552
|
+
|
|
553
|
+
def _format_multiple_records(self):
|
|
554
|
+
"""Format multiple compound records."""
|
|
555
|
+
records = []
|
|
556
|
+
for i, record in enumerate(self.data.flat):
|
|
557
|
+
if i >= 3: # Show max 3 records
|
|
558
|
+
records.append("...")
|
|
559
|
+
break
|
|
560
|
+
|
|
561
|
+
parts = []
|
|
562
|
+
for field_name in self.data.dtype.names:
|
|
563
|
+
field_value = record[field_name]
|
|
564
|
+
if isinstance(field_value, bytes):
|
|
565
|
+
field_value = safe_decode_bytes(field_value)
|
|
566
|
+
parts.append(f"{field_name}: {field_value}")
|
|
567
|
+
records.append(f"({', '.join(parts)})")
|
|
568
|
+
|
|
569
|
+
return f"[{', '.join(records)}]"
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
# =============================================================================
|
|
573
|
+
# Node Registration
|
|
574
|
+
# =============================================================================
|
|
575
|
+
|
|
576
|
+
# Register all node types with the factory
|
|
577
|
+
common.NODE_FACTORY.register(GenericScalarNode, is_generic=True)
|
|
578
|
+
common.NODE_FACTORY.register(GenericTextNode, is_generic=True)
|
|
579
|
+
common.NODE_FACTORY.register(GenericArrayNode, is_generic=True)
|
|
580
|
+
common.NODE_FACTORY.register(GenericCompoundNode, is_generic=True)
|
datalab/h5/native.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
DataLab Native HDF5 I/O module
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from guidata.io import HDF5Reader, HDF5Writer
|
|
12
|
+
|
|
13
|
+
import datalab
|
|
14
|
+
|
|
15
|
+
DATALAB_VERSION_NAME = "DataLab_Version"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NativeH5Writer(HDF5Writer):
|
|
19
|
+
"""DataLab signal/image objects HDF5 guidata Dataset Writer class
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
filename (str): HDF5 file name
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, filename: str) -> None:
|
|
26
|
+
super().__init__(filename)
|
|
27
|
+
self.h5[DATALAB_VERSION_NAME] = datalab.__version__
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class NativeH5Reader(HDF5Reader):
|
|
31
|
+
"""DataLab signal/image objects HDF5 guidata dataset Writer class
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
filename (str): HDF5 file name
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, filename: str) -> None:
|
|
38
|
+
super().__init__(filename)
|
|
39
|
+
self.version = self.h5[DATALAB_VERSION_NAME]
|