datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.0__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.0.dist-info/METADATA +121 -0
- datalab_platform-1.0.0.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.0.dist-info}/WHEEL +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/entry_points.txt +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
DataLab HDF5 browser module
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import abc
|
|
12
|
+
import os
|
|
13
|
+
import os.path as osp
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
15
|
+
|
|
16
|
+
from guidata.qthelpers import (
|
|
17
|
+
add_actions,
|
|
18
|
+
create_action,
|
|
19
|
+
create_toolbutton,
|
|
20
|
+
exec_dialog,
|
|
21
|
+
get_icon,
|
|
22
|
+
get_std_icon,
|
|
23
|
+
win32_fix_title_bar_background,
|
|
24
|
+
)
|
|
25
|
+
from guidata.utils.misc import to_string
|
|
26
|
+
from guidata.widgets.arrayeditor import ArrayEditor
|
|
27
|
+
from plotpy.builder import make
|
|
28
|
+
from plotpy.plot import PlotOptions, PlotWidget
|
|
29
|
+
from qtpy import QtCore as QC
|
|
30
|
+
from qtpy import QtGui as QG
|
|
31
|
+
from qtpy import QtWidgets as QW
|
|
32
|
+
from qtpy.compat import getopenfilename
|
|
33
|
+
from sigima import ImageObj, SignalObj
|
|
34
|
+
|
|
35
|
+
from datalab.adapters_plotpy import CURVESTYLES, create_adapter_from_object
|
|
36
|
+
from datalab.config import Conf, _
|
|
37
|
+
from datalab.h5 import H5Importer
|
|
38
|
+
from datalab.utils.qthelpers import block_signals, qt_handle_error_message
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from plotpy.plot import BasePlot
|
|
42
|
+
|
|
43
|
+
from datalab.h5.common import BaseNode
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AbstractTreeWidgetMeta(type(QW.QTreeWidget), abc.ABCMeta):
|
|
47
|
+
"""Mixed metaclass to avoid conflicts"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AbstractTreeWidget(QW.QTreeWidget, metaclass=AbstractTreeWidgetMeta):
|
|
51
|
+
"""One-column tree widget with context menu, ..."""
|
|
52
|
+
|
|
53
|
+
def __init__(self, parent: QW.QWidget) -> None:
|
|
54
|
+
super().__init__(parent)
|
|
55
|
+
self.setItemsExpandable(True)
|
|
56
|
+
self.itemActivated.connect(self.activated)
|
|
57
|
+
self.itemClicked.connect(self.clicked)
|
|
58
|
+
# Setup context menu
|
|
59
|
+
self.menu = QW.QMenu(self)
|
|
60
|
+
self.collapse_all_action = None
|
|
61
|
+
self.collapse_selection_action = None
|
|
62
|
+
self.expand_all_action = None
|
|
63
|
+
self.expand_selection_action = None
|
|
64
|
+
self.common_actions = self.setup_common_actions()
|
|
65
|
+
|
|
66
|
+
self.itemSelectionChanged.connect(self.item_selection_changed)
|
|
67
|
+
self.item_selection_changed()
|
|
68
|
+
|
|
69
|
+
@abc.abstractmethod
|
|
70
|
+
def activated(self, item: QW.QTreeWidgetItem) -> None:
|
|
71
|
+
"""Double-click event"""
|
|
72
|
+
|
|
73
|
+
@abc.abstractmethod
|
|
74
|
+
def clicked(self, item: QW.QTreeWidgetItem) -> None:
|
|
75
|
+
"""Item was clicked"""
|
|
76
|
+
|
|
77
|
+
@abc.abstractmethod
|
|
78
|
+
def get_actions_from_items(
|
|
79
|
+
self, items: list[QW.QTreeWidgetItem]
|
|
80
|
+
) -> list[QW.QAction]:
|
|
81
|
+
"""Get actions from item"""
|
|
82
|
+
# Right here: add other actions if necessary (reimplement this method)
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
def setup_common_actions(self) -> list[QW.QAction]:
|
|
86
|
+
"""Setup context menu common actions"""
|
|
87
|
+
self.collapse_all_action = create_action(
|
|
88
|
+
self,
|
|
89
|
+
_("Collapse all"),
|
|
90
|
+
icon=get_icon("collapse.svg"),
|
|
91
|
+
triggered=self.collapseAll,
|
|
92
|
+
)
|
|
93
|
+
self.expand_all_action = create_action(
|
|
94
|
+
self, _("Expand all"), icon=get_icon("expand.svg"), triggered=self.expandAll
|
|
95
|
+
)
|
|
96
|
+
self.restore_action = create_action(
|
|
97
|
+
self,
|
|
98
|
+
_("Restore"),
|
|
99
|
+
tip=_("Restore original tree layout"),
|
|
100
|
+
icon=get_icon("restore.svg"),
|
|
101
|
+
triggered=self.restore,
|
|
102
|
+
)
|
|
103
|
+
self.collapse_selection_action = create_action(
|
|
104
|
+
self,
|
|
105
|
+
_("Collapse selection"),
|
|
106
|
+
icon=get_icon("collapse_selection.svg"),
|
|
107
|
+
triggered=self.collapse_selection,
|
|
108
|
+
)
|
|
109
|
+
self.expand_selection_action = create_action(
|
|
110
|
+
self,
|
|
111
|
+
_("Expand selection"),
|
|
112
|
+
icon=get_icon("expand_selection.svg"),
|
|
113
|
+
triggered=self.expand_selection,
|
|
114
|
+
)
|
|
115
|
+
return [
|
|
116
|
+
self.collapse_all_action,
|
|
117
|
+
self.expand_all_action,
|
|
118
|
+
self.restore_action,
|
|
119
|
+
None,
|
|
120
|
+
self.collapse_selection_action,
|
|
121
|
+
self.expand_selection_action,
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
def update_menu(self) -> None:
|
|
125
|
+
"""Update context menu"""
|
|
126
|
+
self.menu.clear()
|
|
127
|
+
items = self.selectedItems()
|
|
128
|
+
actions = self.get_actions_from_items(items)
|
|
129
|
+
if actions:
|
|
130
|
+
actions.append(None)
|
|
131
|
+
actions += self.common_actions
|
|
132
|
+
add_actions(self.menu, actions)
|
|
133
|
+
|
|
134
|
+
def restore(self) -> None:
|
|
135
|
+
"""Restore tree state"""
|
|
136
|
+
self.collapseAll()
|
|
137
|
+
for item in self.get_top_level_items():
|
|
138
|
+
self.expandItem(item)
|
|
139
|
+
|
|
140
|
+
def __expand_item(self, item: QW.QTreeWidgetItem) -> None: # pragma: no cover
|
|
141
|
+
"""Expand item tree branch"""
|
|
142
|
+
self.expandItem(item)
|
|
143
|
+
for index in range(item.childCount()):
|
|
144
|
+
child = item.child(index)
|
|
145
|
+
self.__expand_item(child)
|
|
146
|
+
|
|
147
|
+
def expand_selection(self) -> None: # pragma: no cover
|
|
148
|
+
"""Expand selection"""
|
|
149
|
+
items = self.selectedItems()
|
|
150
|
+
if not items:
|
|
151
|
+
items = self.get_top_level_items()
|
|
152
|
+
for item in items:
|
|
153
|
+
self.__expand_item(item)
|
|
154
|
+
if items:
|
|
155
|
+
self.scrollToItem(items[0])
|
|
156
|
+
|
|
157
|
+
def __collapse_item(self, item: QW.QTreeWidgetItem) -> None: # pragma: no cover
|
|
158
|
+
"""Collapse item tree branch"""
|
|
159
|
+
self.collapseItem(item)
|
|
160
|
+
for index in range(item.childCount()):
|
|
161
|
+
child = item.child(index)
|
|
162
|
+
self.__collapse_item(child)
|
|
163
|
+
|
|
164
|
+
def collapse_selection(self) -> None: # pragma: no cover
|
|
165
|
+
"""Collapse selection"""
|
|
166
|
+
items = self.selectedItems()
|
|
167
|
+
if not items:
|
|
168
|
+
items = self.get_top_level_items()
|
|
169
|
+
for item in items:
|
|
170
|
+
self.__collapse_item(item)
|
|
171
|
+
if items:
|
|
172
|
+
self.scrollToItem(items[0])
|
|
173
|
+
|
|
174
|
+
def item_selection_changed(self) -> None:
|
|
175
|
+
"""Item selection has changed"""
|
|
176
|
+
is_selection = len(self.selectedItems()) > 0
|
|
177
|
+
self.expand_selection_action.setEnabled(is_selection)
|
|
178
|
+
self.collapse_selection_action.setEnabled(is_selection)
|
|
179
|
+
|
|
180
|
+
def get_top_level_items(self) -> list[QW.QTreeWidgetItem]:
|
|
181
|
+
"""Iterate over top level items"""
|
|
182
|
+
return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())]
|
|
183
|
+
|
|
184
|
+
def find_all_items(self):
|
|
185
|
+
"""Find all items"""
|
|
186
|
+
return self.findItems("", QC.Qt.MatchContains | QC.Qt.MatchRecursive)
|
|
187
|
+
|
|
188
|
+
def contextMenuEvent(self, event: QG.QContextMenuEvent) -> None:
|
|
189
|
+
"""Override Qt method"""
|
|
190
|
+
self.update_menu()
|
|
191
|
+
self.menu.popup(event.globalPos())
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class H5TreeWidget(AbstractTreeWidget):
|
|
195
|
+
"""HDF5 Browser Tree Widget
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
parent: Parent widget
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
SIG_SELECTED = QC.Signal(QW.QTreeWidgetItem)
|
|
202
|
+
|
|
203
|
+
def __init__(self, parent: QW.QWidget) -> None:
|
|
204
|
+
super().__init__(parent)
|
|
205
|
+
title = _("HDF5 Browser")
|
|
206
|
+
self.setColumnCount(4)
|
|
207
|
+
self.setWindowTitle(title)
|
|
208
|
+
self.setHeaderLabels([_("Name"), _("Size"), _("Type"), _("Value")])
|
|
209
|
+
self.header().setSectionResizeMode(0, QW.QHeaderView.Stretch)
|
|
210
|
+
self.header().setStretchLastSection(False)
|
|
211
|
+
self.fnames: list[str] = []
|
|
212
|
+
self.h5importers: list[H5Importer] = []
|
|
213
|
+
|
|
214
|
+
def add_root(self, fname: str) -> None:
|
|
215
|
+
"""Add HDF5 root (new file)
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
fname: HDF5 file name
|
|
219
|
+
"""
|
|
220
|
+
self.fnames.append(osp.abspath(fname))
|
|
221
|
+
importer = H5Importer(fname)
|
|
222
|
+
self.h5importers.append(importer)
|
|
223
|
+
self.add_root_to_tree(importer)
|
|
224
|
+
# Temporarily expand all items to calculate proper column widths
|
|
225
|
+
rootitem = self.topLevelItem(self.topLevelItemCount() - 1)
|
|
226
|
+
self.expand_all_children(rootitem)
|
|
227
|
+
for col in range(4):
|
|
228
|
+
self.resizeColumnToContents(col)
|
|
229
|
+
# Restore to default state (only root and its immediate children expanded)
|
|
230
|
+
for index in range(rootitem.childCount()):
|
|
231
|
+
child = rootitem.child(index)
|
|
232
|
+
self.collapseItem(child)
|
|
233
|
+
|
|
234
|
+
def remove_root(self, fname: str) -> None:
|
|
235
|
+
"""Remove HDF5 root
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
fname: HDF5 file name
|
|
239
|
+
"""
|
|
240
|
+
index = self.fnames.index(osp.abspath(fname))
|
|
241
|
+
self.fnames.pop(index)
|
|
242
|
+
importer = self.h5importers.pop(index)
|
|
243
|
+
importer.close()
|
|
244
|
+
# Remove root item associated with file
|
|
245
|
+
item = self.topLevelItem(index)
|
|
246
|
+
self.takeTopLevelItem(index)
|
|
247
|
+
del item
|
|
248
|
+
|
|
249
|
+
def cleanup(self) -> None:
|
|
250
|
+
"""Clean up widget"""
|
|
251
|
+
for importer in self.h5importers:
|
|
252
|
+
importer.close()
|
|
253
|
+
self.fnames: list[str] = []
|
|
254
|
+
self.h5importers: list[H5Importer] = []
|
|
255
|
+
self.clear()
|
|
256
|
+
|
|
257
|
+
def __get_top_level_item(self, item: QW.QTreeWidgetItem) -> QW.QTreeWidgetItem:
|
|
258
|
+
"""Get top level item associated to item
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
item: Tree item
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Top level item
|
|
265
|
+
"""
|
|
266
|
+
while item.parent():
|
|
267
|
+
item = item.parent()
|
|
268
|
+
return item
|
|
269
|
+
|
|
270
|
+
def get_node(self, item: QW.QTreeWidgetItem) -> BaseNode:
|
|
271
|
+
"""Get HDF5 dataset associated to item
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
item: Tree item
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
HDF5 node
|
|
278
|
+
"""
|
|
279
|
+
toplevel_item = self.__get_top_level_item(item)
|
|
280
|
+
toplevel_index = self.indexOfTopLevelItem(toplevel_item)
|
|
281
|
+
node_id = item.data(0, QC.Qt.UserRole)
|
|
282
|
+
if node_id:
|
|
283
|
+
importer = self.h5importers[toplevel_index]
|
|
284
|
+
return importer.get(node_id)
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
def get_nodes(self, only_checked_items: bool = True) -> list[BaseNode]:
|
|
288
|
+
"""Get all nodes associated to checked items
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
only_checked_items: If True, only checked items are returned
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
List of HDF5 nodes
|
|
295
|
+
"""
|
|
296
|
+
datasets = []
|
|
297
|
+
for item in self.find_all_items():
|
|
298
|
+
if item.flags() & QC.Qt.ItemIsUserCheckable:
|
|
299
|
+
if only_checked_items and item.checkState(0) == 0:
|
|
300
|
+
continue
|
|
301
|
+
if item is not self.topLevelItem(0):
|
|
302
|
+
node = self.get_node(item)
|
|
303
|
+
datasets.append(node)
|
|
304
|
+
return datasets
|
|
305
|
+
|
|
306
|
+
def activated(self, item: QW.QTreeWidgetItem) -> None:
|
|
307
|
+
"""Double-click event"""
|
|
308
|
+
if item is not self.topLevelItem(0):
|
|
309
|
+
self.SIG_SELECTED.emit(item)
|
|
310
|
+
|
|
311
|
+
def clicked(self, item: QW.QTreeWidgetItem) -> None:
|
|
312
|
+
"""Click event"""
|
|
313
|
+
self.activated(item)
|
|
314
|
+
|
|
315
|
+
def get_actions_from_items(self, items): # pylint: disable=W0613
|
|
316
|
+
"""Get actions from item"""
|
|
317
|
+
return []
|
|
318
|
+
|
|
319
|
+
def is_empty(self) -> bool:
|
|
320
|
+
"""Return True if tree is empty"""
|
|
321
|
+
return len(self.find_all_items()) == 1
|
|
322
|
+
|
|
323
|
+
def is_any_item_checked(self) -> bool:
|
|
324
|
+
"""Return True if any item is checked"""
|
|
325
|
+
for item in self.find_all_items():
|
|
326
|
+
if item.checkState(0) > 0:
|
|
327
|
+
return True
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
def select_all(self, state: bool) -> None:
|
|
331
|
+
"""Select all items
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
state: If True, all items are selected
|
|
335
|
+
"""
|
|
336
|
+
for item in self.find_all_items():
|
|
337
|
+
if item.flags() & QC.Qt.ItemIsUserCheckable:
|
|
338
|
+
item.setSelected(state)
|
|
339
|
+
if state:
|
|
340
|
+
self.clicked(item)
|
|
341
|
+
|
|
342
|
+
def toggle_all(self, state: bool) -> None:
|
|
343
|
+
"""Toggle all item state from 'unchecked' to 'checked'
|
|
344
|
+
(or vice-versa)
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
state: If True, all items are checked
|
|
348
|
+
"""
|
|
349
|
+
for item in self.find_all_items():
|
|
350
|
+
if item.flags() & QC.Qt.ItemIsUserCheckable:
|
|
351
|
+
item.setCheckState(0, QC.Qt.Checked if state else QC.Qt.Unchecked)
|
|
352
|
+
|
|
353
|
+
@staticmethod
|
|
354
|
+
def __create_node(node: BaseNode) -> QW.QTreeWidgetItem:
|
|
355
|
+
"""Create tree node from HDF5 node
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
node: HDF5 node
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Tree widget node
|
|
362
|
+
"""
|
|
363
|
+
text = to_string(node.text)
|
|
364
|
+
if len(text) > 30:
|
|
365
|
+
text = text[:30] + "..."
|
|
366
|
+
treeitem = QW.QTreeWidgetItem([node.name, node.shape_str, node.dtype_str, text])
|
|
367
|
+
treeitem.setData(0, QC.Qt.UserRole, node.id)
|
|
368
|
+
if node.description:
|
|
369
|
+
for col in range(treeitem.columnCount()):
|
|
370
|
+
treeitem.setToolTip(col, node.description)
|
|
371
|
+
return treeitem
|
|
372
|
+
|
|
373
|
+
@staticmethod
|
|
374
|
+
def __recursive_popfunc(parent_item: QW.QTreeWidgetItem, node: BaseNode) -> None:
|
|
375
|
+
"""Recursive HDF5 analysis
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
parent_item: Parent tree item
|
|
379
|
+
node: HDF5 node
|
|
380
|
+
"""
|
|
381
|
+
tree_item = H5TreeWidget.__create_node(node)
|
|
382
|
+
if node.is_supported():
|
|
383
|
+
tree_item.setCheckState(0, QC.Qt.Unchecked)
|
|
384
|
+
else:
|
|
385
|
+
tree_item.setFlags(QC.Qt.ItemIsEnabled)
|
|
386
|
+
tree_item.setIcon(0, get_icon(node.icon_name))
|
|
387
|
+
parent_item.addChild(tree_item)
|
|
388
|
+
for child in node.children:
|
|
389
|
+
H5TreeWidget.__recursive_popfunc(tree_item, child)
|
|
390
|
+
|
|
391
|
+
def expand_all_children(self, item: QW.QTreeWidgetItem) -> None:
|
|
392
|
+
"""Expand all children (recursively)
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
item: Tree item
|
|
396
|
+
"""
|
|
397
|
+
self.expandItem(item)
|
|
398
|
+
for index in range(item.childCount()):
|
|
399
|
+
child = item.child(index)
|
|
400
|
+
self.expand_all_children(child)
|
|
401
|
+
|
|
402
|
+
def add_root_to_tree(self, importer: H5Importer) -> None:
|
|
403
|
+
"""Add root to tree
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
importer: HDF5 importer
|
|
407
|
+
"""
|
|
408
|
+
root = importer.root
|
|
409
|
+
rootitem = QW.QTreeWidgetItem([root.name])
|
|
410
|
+
rootitem.setToolTip(0, root.description)
|
|
411
|
+
rootitem.setData(0, QC.Qt.UserRole, root.id)
|
|
412
|
+
rootitem.setFlags(QC.Qt.ItemIsEnabled)
|
|
413
|
+
rootitem.setIcon(0, get_icon(root.icon_name))
|
|
414
|
+
self.addTopLevelItem(rootitem)
|
|
415
|
+
for node in root.children:
|
|
416
|
+
self.__recursive_popfunc(rootitem, node)
|
|
417
|
+
self.expandItem(rootitem)
|
|
418
|
+
|
|
419
|
+
def toggle_show_only_checkable_items(self, state: bool) -> None:
|
|
420
|
+
"""Show only checkable items
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
state: If True, only checkable items are shown
|
|
424
|
+
"""
|
|
425
|
+
for item in self.find_all_items():
|
|
426
|
+
item.setHidden(state)
|
|
427
|
+
if state:
|
|
428
|
+
# Iterate over checkable items and show them (and their parents)
|
|
429
|
+
for item in self.find_all_items():
|
|
430
|
+
if item.flags() & QC.Qt.ItemIsUserCheckable:
|
|
431
|
+
item.setHidden(False)
|
|
432
|
+
parent = item.parent()
|
|
433
|
+
while parent:
|
|
434
|
+
parent.setHidden(False)
|
|
435
|
+
parent = parent.parent()
|
|
436
|
+
|
|
437
|
+
def toggle_show_values(self, state: bool) -> None:
|
|
438
|
+
"""Show values
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
state: If True, values are shown
|
|
442
|
+
"""
|
|
443
|
+
# Hide or show the "Value" column
|
|
444
|
+
self.setColumnHidden(3, not state)
|
|
445
|
+
|
|
446
|
+
def set_current_file(self, fname: str) -> None:
|
|
447
|
+
"""Set current file
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
fname: HDF5 file name
|
|
451
|
+
"""
|
|
452
|
+
index = self.fnames.index(osp.abspath(fname))
|
|
453
|
+
item = self.topLevelItem(index)
|
|
454
|
+
self.setCurrentItem(item)
|
|
455
|
+
self.scrollToItem(item, QW.QAbstractItemView.PositionAtTop)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
class PlotPreview(QW.QStackedWidget):
|
|
459
|
+
"""Plot preview"""
|
|
460
|
+
|
|
461
|
+
def __init__(self, parent: QW.QWidget) -> None:
|
|
462
|
+
super().__init__(parent)
|
|
463
|
+
self.curvewidget = PlotWidget(
|
|
464
|
+
self,
|
|
465
|
+
options=PlotOptions(
|
|
466
|
+
type="curve",
|
|
467
|
+
curve_antialiasing=True,
|
|
468
|
+
show_axes_tab=False,
|
|
469
|
+
autoscale_margin_percent=Conf.view.sig_autoscale_margin_percent.get(
|
|
470
|
+
0.2
|
|
471
|
+
),
|
|
472
|
+
),
|
|
473
|
+
)
|
|
474
|
+
self.addWidget(self.curvewidget)
|
|
475
|
+
self.imagewidget = PlotWidget(
|
|
476
|
+
self,
|
|
477
|
+
options=PlotOptions(
|
|
478
|
+
type="image",
|
|
479
|
+
show_contrast=True,
|
|
480
|
+
show_axes_tab=False,
|
|
481
|
+
autoscale_margin_percent=Conf.view.ima_autoscale_margin_percent.get(
|
|
482
|
+
0.2
|
|
483
|
+
),
|
|
484
|
+
),
|
|
485
|
+
)
|
|
486
|
+
self.addWidget(self.imagewidget)
|
|
487
|
+
|
|
488
|
+
def cleanup(self) -> None:
|
|
489
|
+
"""Clean up widget"""
|
|
490
|
+
for widget in (self.imagewidget, self.curvewidget):
|
|
491
|
+
widget.get_plot().del_all_items()
|
|
492
|
+
|
|
493
|
+
def update_plot_preview(self, node: BaseNode) -> None:
|
|
494
|
+
"""Update plot preview widget"""
|
|
495
|
+
try:
|
|
496
|
+
obj = node.get_native_object()
|
|
497
|
+
except Exception as msg: # pylint: disable=broad-except
|
|
498
|
+
qt_handle_error_message(self, msg)
|
|
499
|
+
return
|
|
500
|
+
if obj is None:
|
|
501
|
+
# An error occurred while creating the object (invalid data, ...)
|
|
502
|
+
label = make.label(_("Unsupported data"), "C", (0, 0), "C")
|
|
503
|
+
plot: BasePlot = self.currentWidget().get_plot()
|
|
504
|
+
plot.del_all_items()
|
|
505
|
+
plot.add_item(label)
|
|
506
|
+
plot.replot()
|
|
507
|
+
return
|
|
508
|
+
if isinstance(obj, SignalObj):
|
|
509
|
+
obj: SignalObj
|
|
510
|
+
widget = self.curvewidget
|
|
511
|
+
else:
|
|
512
|
+
obj: ImageObj
|
|
513
|
+
widget = self.imagewidget
|
|
514
|
+
with CURVESTYLES.suspend():
|
|
515
|
+
item = create_adapter_from_object(obj).make_item()
|
|
516
|
+
plot = widget.get_plot()
|
|
517
|
+
plot.del_all_items()
|
|
518
|
+
plot.add_item(item)
|
|
519
|
+
plot.set_active_item(item)
|
|
520
|
+
item.unselect()
|
|
521
|
+
plot.do_autoscale()
|
|
522
|
+
self.setCurrentWidget(widget)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class TablePreview(QW.QWidget):
|
|
526
|
+
"""Table preview
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
title: Group title
|
|
530
|
+
parent: Parent widget
|
|
531
|
+
"""
|
|
532
|
+
|
|
533
|
+
def __init__(self, parent: QW.QWidget) -> None:
|
|
534
|
+
super().__init__(parent)
|
|
535
|
+
self.setLayout(QW.QVBoxLayout())
|
|
536
|
+
self.table = QW.QTableWidget(self)
|
|
537
|
+
self.table.setEditTriggers(QW.QAbstractItemView.NoEditTriggers)
|
|
538
|
+
self.table.horizontalHeader().setStretchLastSection(True)
|
|
539
|
+
self.layout().addWidget(self.table)
|
|
540
|
+
|
|
541
|
+
def clear(self) -> None:
|
|
542
|
+
"""Clear table"""
|
|
543
|
+
self.table.clear()
|
|
544
|
+
|
|
545
|
+
def update_table_preview(self, data: dict[str, Any]) -> None:
|
|
546
|
+
"""Update table preview widget
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
node: HDF5 node
|
|
550
|
+
"""
|
|
551
|
+
self.clear()
|
|
552
|
+
self.table.setRowCount(len(data))
|
|
553
|
+
self.table.setColumnCount(1)
|
|
554
|
+
self.table.setHorizontalHeaderLabels([_("Value")])
|
|
555
|
+
self.table.setVerticalHeaderLabels(list(data.keys()))
|
|
556
|
+
for row, value in enumerate(data.values()):
|
|
557
|
+
self.table.setItem(row, 0, QW.QTableWidgetItem(str(value)))
|
|
558
|
+
self.table.resizeRowsToContents()
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
class GroupAndAttributes(QW.QTabWidget):
|
|
562
|
+
"""Group and attributes
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
parent: Parent widget
|
|
566
|
+
show_array_callback: Callback to show array
|
|
567
|
+
"""
|
|
568
|
+
|
|
569
|
+
def __init__(self, parent: QW.QWidget, show_array_callback: Callable) -> None:
|
|
570
|
+
super().__init__(parent)
|
|
571
|
+
self.group = TablePreview(self)
|
|
572
|
+
self.addTab(self.group, get_icon("h5group.svg"), _("Group"))
|
|
573
|
+
self.attrs = TablePreview(self)
|
|
574
|
+
self.addTab(self.attrs, get_icon("h5attrs.svg"), _("Attributes"))
|
|
575
|
+
# Add a button as corner widget to show the array (if any):
|
|
576
|
+
self.__show_array_btn = create_toolbutton(
|
|
577
|
+
self,
|
|
578
|
+
icon=get_icon("show_results.svg"),
|
|
579
|
+
text=_("Show array"),
|
|
580
|
+
autoraise=False,
|
|
581
|
+
triggered=show_array_callback,
|
|
582
|
+
)
|
|
583
|
+
self.__show_array_btn.setEnabled(False)
|
|
584
|
+
self.setCornerWidget(self.__show_array_btn, QC.Qt.TopRightCorner)
|
|
585
|
+
|
|
586
|
+
def cleanup(self) -> None:
|
|
587
|
+
"""Clean up widget"""
|
|
588
|
+
self.group.clear()
|
|
589
|
+
self.attrs.clear()
|
|
590
|
+
|
|
591
|
+
def update_from_node(self, node: BaseNode) -> None:
|
|
592
|
+
"""Update widget from node
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
node: HDF5 node
|
|
596
|
+
"""
|
|
597
|
+
# Update group =================================================================
|
|
598
|
+
text = to_string(node.text)
|
|
599
|
+
if text:
|
|
600
|
+
lines = text.splitlines()[:5]
|
|
601
|
+
if len(lines) == 5:
|
|
602
|
+
lines += ["[...]"]
|
|
603
|
+
text = os.linesep.join(lines)
|
|
604
|
+
data = {
|
|
605
|
+
_("Path"): node.id,
|
|
606
|
+
_("Name"): node.name,
|
|
607
|
+
_("Description"): node.description,
|
|
608
|
+
_("Textual preview"): text,
|
|
609
|
+
# "Raw": repr(node.data),
|
|
610
|
+
}
|
|
611
|
+
self.group.update_table_preview(data)
|
|
612
|
+
|
|
613
|
+
# Update attributes ============================================================
|
|
614
|
+
self.attrs.update_table_preview(node.metadata)
|
|
615
|
+
|
|
616
|
+
# Update show array button =====================================================
|
|
617
|
+
self.__show_array_btn.setEnabled(node.IS_ARRAY)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
class H5FileSelector(QW.QWidget):
|
|
621
|
+
"""HDF5 file selector
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
parent: Parent widget
|
|
625
|
+
"""
|
|
626
|
+
|
|
627
|
+
SIG_ADD_FILENAME = QC.Signal(str)
|
|
628
|
+
SIG_REMOVE_FILENAME = QC.Signal(str)
|
|
629
|
+
SIG_CURRENT_CHANGED = QC.Signal(str)
|
|
630
|
+
|
|
631
|
+
def __init__(self, parent: QW.QWidget) -> None:
|
|
632
|
+
super().__init__(parent)
|
|
633
|
+
self.setLayout(QW.QHBoxLayout())
|
|
634
|
+
self.layout().setContentsMargins(0, 0, 0, 0)
|
|
635
|
+
self.combo = QW.QComboBox(self)
|
|
636
|
+
self.combo.currentTextChanged.connect(self.current_file_changed)
|
|
637
|
+
self.layout().addWidget(self.combo)
|
|
638
|
+
self.btn_add = create_toolbutton(
|
|
639
|
+
self,
|
|
640
|
+
icon=get_std_icon("DirOpenIcon"),
|
|
641
|
+
text=_("Open") + " ...",
|
|
642
|
+
autoraise=False,
|
|
643
|
+
triggered=lambda _checked=False: self.add_file(),
|
|
644
|
+
)
|
|
645
|
+
self.btn_add.setSizePolicy(QW.QSizePolicy.Fixed, QW.QSizePolicy.Fixed)
|
|
646
|
+
self.layout().addWidget(self.btn_add)
|
|
647
|
+
self.btn_rmv = create_toolbutton(
|
|
648
|
+
self,
|
|
649
|
+
icon=get_std_icon("DialogCloseButton"),
|
|
650
|
+
text=_("Close"),
|
|
651
|
+
autoraise=False,
|
|
652
|
+
triggered=self.remove_file,
|
|
653
|
+
)
|
|
654
|
+
self.btn_rmv.setSizePolicy(QW.QSizePolicy.Fixed, QW.QSizePolicy.Fixed)
|
|
655
|
+
self.layout().addWidget(self.btn_rmv)
|
|
656
|
+
self.btn_rmv.setEnabled(False)
|
|
657
|
+
|
|
658
|
+
def set_current_fname(self, fname: str) -> None:
|
|
659
|
+
"""Set current file name
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
fname: HDF5 file name
|
|
663
|
+
"""
|
|
664
|
+
index = self.combo.findText(fname)
|
|
665
|
+
if index >= 0:
|
|
666
|
+
self.combo.setCurrentIndex(index)
|
|
667
|
+
|
|
668
|
+
def get_current_fname(self) -> str:
|
|
669
|
+
"""Return current file name
|
|
670
|
+
|
|
671
|
+
Returns:
|
|
672
|
+
HDF5 file name
|
|
673
|
+
"""
|
|
674
|
+
return self.combo.currentText()
|
|
675
|
+
|
|
676
|
+
def current_file_changed(self, fname: str) -> None:
|
|
677
|
+
"""Current file changed
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
fname: HDF5 file name
|
|
681
|
+
"""
|
|
682
|
+
self.SIG_CURRENT_CHANGED.emit(fname)
|
|
683
|
+
|
|
684
|
+
def add_fname(self, fname: str) -> None:
|
|
685
|
+
"""Add file name
|
|
686
|
+
|
|
687
|
+
Args:
|
|
688
|
+
fname: HDF5 file name
|
|
689
|
+
"""
|
|
690
|
+
self.combo.addItem(get_icon("h5file.svg"), fname)
|
|
691
|
+
self.btn_rmv.setEnabled(True)
|
|
692
|
+
|
|
693
|
+
def remove_fname(self, fname: str) -> None:
|
|
694
|
+
"""Remove file name
|
|
695
|
+
|
|
696
|
+
Args:
|
|
697
|
+
fname: HDF5 file name
|
|
698
|
+
"""
|
|
699
|
+
index = self.combo.findText(fname)
|
|
700
|
+
if index >= 0:
|
|
701
|
+
self.combo.removeItem(index)
|
|
702
|
+
if self.combo.count() == 0:
|
|
703
|
+
self.btn_rmv.setEnabled(False)
|
|
704
|
+
|
|
705
|
+
def add_file(self, fname: str | None = None) -> None:
|
|
706
|
+
"""Browse file
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
fname: HDF5 file name. Default is None.
|
|
710
|
+
(this is used for testing only)
|
|
711
|
+
"""
|
|
712
|
+
if fname is None:
|
|
713
|
+
fname = getopenfilename(
|
|
714
|
+
self,
|
|
715
|
+
_("Select HDF5 file"),
|
|
716
|
+
"",
|
|
717
|
+
_("HDF5 files (*.h5 *.hdf5 *.hdf *.he5);;All files (*)"),
|
|
718
|
+
)[0]
|
|
719
|
+
if fname:
|
|
720
|
+
self.SIG_ADD_FILENAME.emit(osp.abspath(fname))
|
|
721
|
+
|
|
722
|
+
def remove_file(self, fname: str | None = None) -> None:
|
|
723
|
+
"""Remove file name
|
|
724
|
+
|
|
725
|
+
Args:
|
|
726
|
+
fname: HDF5 file name
|
|
727
|
+
"""
|
|
728
|
+
if fname is None:
|
|
729
|
+
fname = self.combo.currentText()
|
|
730
|
+
self.SIG_REMOVE_FILENAME.emit(fname)
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
class H5Browser(QW.QSplitter):
|
|
734
|
+
"""HDF5 Browser Widget
|
|
735
|
+
|
|
736
|
+
Args:
|
|
737
|
+
parent: Parent widget
|
|
738
|
+
"""
|
|
739
|
+
|
|
740
|
+
SIG_SELECT_NEW_FILE = QC.Signal(str)
|
|
741
|
+
SIG_REMOVE_FILE = QC.Signal(str)
|
|
742
|
+
|
|
743
|
+
def __init__(self, parent: QW.QWidget | None = None) -> None:
|
|
744
|
+
super().__init__(parent)
|
|
745
|
+
self.selector = H5FileSelector(self)
|
|
746
|
+
self.selector.SIG_ADD_FILENAME.connect(self.__add_new_file)
|
|
747
|
+
self.selector.SIG_REMOVE_FILENAME.connect(self.__remove_file)
|
|
748
|
+
self.selector.SIG_CURRENT_CHANGED.connect(self.__selector_current_file_changed)
|
|
749
|
+
self.tree = H5TreeWidget(self)
|
|
750
|
+
self.tree.SIG_SELECTED.connect(self.__item_selected_on_tree)
|
|
751
|
+
selectorandtree = QW.QFrame(self)
|
|
752
|
+
selectorandtree.setLayout(QW.QVBoxLayout())
|
|
753
|
+
selectorandtree.layout().addWidget(self.selector)
|
|
754
|
+
# Add toolbar with tree actions
|
|
755
|
+
toolbar = self.__create_toolbar()
|
|
756
|
+
selectorandtree.layout().addWidget(toolbar)
|
|
757
|
+
selectorandtree.layout().addWidget(self.tree)
|
|
758
|
+
selectorandtree.layout().setContentsMargins(0, 0, 0, 0)
|
|
759
|
+
self.addWidget(selectorandtree)
|
|
760
|
+
preview = QW.QSplitter(self)
|
|
761
|
+
preview.setOrientation(QC.Qt.Vertical)
|
|
762
|
+
self.addWidget(preview)
|
|
763
|
+
self.plotpreview = PlotPreview(self)
|
|
764
|
+
preview.addWidget(self.plotpreview)
|
|
765
|
+
self.groupandattrs = GroupAndAttributes(self, self.show_array)
|
|
766
|
+
preview.addWidget(self.groupandattrs)
|
|
767
|
+
preview.setSizes([int(self.size().height() / 2)] * 2)
|
|
768
|
+
|
|
769
|
+
def __create_toolbar(self) -> QW.QToolBar:
|
|
770
|
+
"""Create toolbar with tree actions
|
|
771
|
+
|
|
772
|
+
Returns:
|
|
773
|
+
Toolbar widget
|
|
774
|
+
"""
|
|
775
|
+
toolbar = QW.QToolBar(self)
|
|
776
|
+
toolbar.setToolButtonStyle(QC.Qt.ToolButtonTextBesideIcon)
|
|
777
|
+
toolbar.setIconSize(QC.QSize(16, 16))
|
|
778
|
+
toolbar.setStyleSheet("QToolBar { padding: 2px; spacing: 2px; }")
|
|
779
|
+
toolbar.addAction(self.tree.expand_all_action)
|
|
780
|
+
toolbar.addAction(self.tree.collapse_all_action)
|
|
781
|
+
toolbar.addAction(self.tree.restore_action)
|
|
782
|
+
toolbar.addSeparator()
|
|
783
|
+
toolbar.addAction(self.tree.expand_selection_action)
|
|
784
|
+
toolbar.addAction(self.tree.collapse_selection_action)
|
|
785
|
+
return toolbar
|
|
786
|
+
|
|
787
|
+
def open_file(self, fname: str) -> None:
|
|
788
|
+
"""Open HDF5 file
|
|
789
|
+
|
|
790
|
+
Args:
|
|
791
|
+
fname: HDF5 file name
|
|
792
|
+
"""
|
|
793
|
+
self.tree.add_root(fname)
|
|
794
|
+
self.selector.add_fname(fname)
|
|
795
|
+
|
|
796
|
+
def close_file(self, fname: str) -> None:
|
|
797
|
+
"""Close HDF5 file
|
|
798
|
+
|
|
799
|
+
Args:
|
|
800
|
+
fname: HDF5 file name
|
|
801
|
+
"""
|
|
802
|
+
self.tree.remove_root(fname)
|
|
803
|
+
self.selector.remove_fname(fname)
|
|
804
|
+
|
|
805
|
+
def __add_new_file(self, fname: str) -> None:
|
|
806
|
+
"""Add new file
|
|
807
|
+
|
|
808
|
+
Args:
|
|
809
|
+
fname: HDF5 file name
|
|
810
|
+
"""
|
|
811
|
+
self.open_file(fname)
|
|
812
|
+
self.selector.set_current_fname(fname)
|
|
813
|
+
self.SIG_SELECT_NEW_FILE.emit(fname)
|
|
814
|
+
|
|
815
|
+
def __remove_file(self, fname: str) -> None:
|
|
816
|
+
"""Remove file
|
|
817
|
+
|
|
818
|
+
Args:
|
|
819
|
+
fname: HDF5 file name
|
|
820
|
+
"""
|
|
821
|
+
self.close_file(fname)
|
|
822
|
+
self.SIG_REMOVE_FILE.emit(fname)
|
|
823
|
+
|
|
824
|
+
def cleanup(self) -> None:
|
|
825
|
+
"""Clean up widget"""
|
|
826
|
+
self.tree.cleanup()
|
|
827
|
+
self.plotpreview.cleanup()
|
|
828
|
+
|
|
829
|
+
def get_node(self, item: QW.QTreeWidgetItem | None = None) -> BaseNode:
|
|
830
|
+
"""Return (selected) dataset
|
|
831
|
+
|
|
832
|
+
Args:
|
|
833
|
+
item: Tree item
|
|
834
|
+
|
|
835
|
+
Returns:
|
|
836
|
+
HDF5 node
|
|
837
|
+
"""
|
|
838
|
+
if item is None:
|
|
839
|
+
item = self.tree.currentItem()
|
|
840
|
+
return self.tree.get_node(item)
|
|
841
|
+
|
|
842
|
+
def __item_selected_on_tree(self, item: QW.QTreeWidgetItem) -> None:
|
|
843
|
+
"""Item selected on tree
|
|
844
|
+
|
|
845
|
+
Args:
|
|
846
|
+
item: Tree item
|
|
847
|
+
"""
|
|
848
|
+
# View the selected item
|
|
849
|
+
node = self.get_node(item)
|
|
850
|
+
if node.is_supported():
|
|
851
|
+
self.plotpreview.update_plot_preview(node)
|
|
852
|
+
self.groupandattrs.update_from_node(node)
|
|
853
|
+
# Update the file selector combo box
|
|
854
|
+
with block_signals(self.selector.combo):
|
|
855
|
+
# Avoid triggering current file changed signal, which would result in
|
|
856
|
+
# loosing the current selection on the tree (side effect: "Show array"
|
|
857
|
+
# button would still be enabled if the previous node was an array, except
|
|
858
|
+
# that now the current node is not an array, thus causing an error if
|
|
859
|
+
# the user clicks on the button).
|
|
860
|
+
self.selector.set_current_fname(node.h5file.filename)
|
|
861
|
+
|
|
862
|
+
def __selector_current_file_changed(self, fname: str) -> None:
|
|
863
|
+
"""Selector current file changed
|
|
864
|
+
|
|
865
|
+
Args:
|
|
866
|
+
fname: HDF5 file name
|
|
867
|
+
"""
|
|
868
|
+
if fname:
|
|
869
|
+
self.tree.set_current_file(fname)
|
|
870
|
+
|
|
871
|
+
def show_array(self) -> None:
|
|
872
|
+
"""Show array"""
|
|
873
|
+
node = self.get_node()
|
|
874
|
+
assert node.IS_ARRAY
|
|
875
|
+
arrayeditor = ArrayEditor(self)
|
|
876
|
+
arrayeditor.setup_and_check(
|
|
877
|
+
node.data, title=node.name, readonly=True, add_title_suffix=False
|
|
878
|
+
)
|
|
879
|
+
exec_dialog(arrayeditor)
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
class H5BrowserDialog(QW.QDialog):
|
|
883
|
+
"""HDF5 Browser Dialog
|
|
884
|
+
|
|
885
|
+
Args:
|
|
886
|
+
parent: Parent widget
|
|
887
|
+
size: Dialog size
|
|
888
|
+
"""
|
|
889
|
+
|
|
890
|
+
def __init__(
|
|
891
|
+
self, parent: QW.QWidget | None = None, size: tuple[int, int] = (1150, 700)
|
|
892
|
+
) -> None:
|
|
893
|
+
super().__init__(parent)
|
|
894
|
+
self.setWindowFlags(QC.Qt.Window)
|
|
895
|
+
self.setObjectName("h5browser")
|
|
896
|
+
self.setWindowTitle(_("HDF5 Browser"))
|
|
897
|
+
self.setWindowIcon(get_icon("h5browser.svg"))
|
|
898
|
+
win32_fix_title_bar_background(self)
|
|
899
|
+
vlayout = QW.QVBoxLayout()
|
|
900
|
+
self.setLayout(vlayout)
|
|
901
|
+
self.button_layout: QW.QHBoxLayout | None = None
|
|
902
|
+
self.bbox: QW.QDialogButtonBox | None = None
|
|
903
|
+
self.nodes: list[BaseNode] = []
|
|
904
|
+
self.checkbox_show_only: QW.QCheckBox | None = None
|
|
905
|
+
self.checkbox_show_values: QW.QCheckBox | None = None
|
|
906
|
+
|
|
907
|
+
self.browser = H5Browser(self)
|
|
908
|
+
self.browser.SIG_SELECT_NEW_FILE.connect(self.select_new_file)
|
|
909
|
+
self.browser.SIG_REMOVE_FILE.connect(self.remove_file)
|
|
910
|
+
vlayout.addWidget(self.browser)
|
|
911
|
+
|
|
912
|
+
self.browser.tree.itemChanged.connect(lambda item: self.refresh_buttons())
|
|
913
|
+
|
|
914
|
+
self.install_button_layout()
|
|
915
|
+
|
|
916
|
+
self.setMinimumSize(QC.QSize(900, 500))
|
|
917
|
+
self.resize(QC.QSize(*size))
|
|
918
|
+
self.browser.setSizes([int(self.size().height() / 2)] * 2)
|
|
919
|
+
self.refresh_buttons()
|
|
920
|
+
|
|
921
|
+
def accept(self) -> None:
|
|
922
|
+
"""Accept changes"""
|
|
923
|
+
self.nodes = self.browser.tree.get_nodes()
|
|
924
|
+
QW.QDialog.accept(self)
|
|
925
|
+
|
|
926
|
+
def is_empty(self) -> bool:
|
|
927
|
+
"""Return True if tree is empty"""
|
|
928
|
+
return self.browser.tree.is_empty()
|
|
929
|
+
|
|
930
|
+
def cleanup(self) -> None:
|
|
931
|
+
"""Cleanup dialog"""
|
|
932
|
+
self.browser.cleanup()
|
|
933
|
+
|
|
934
|
+
def refresh_buttons(self) -> None:
|
|
935
|
+
"""Refresh buttons"""
|
|
936
|
+
state = self.browser.tree.is_any_item_checked()
|
|
937
|
+
self.bbox.button(QW.QDialogButtonBox.Ok).setEnabled(state)
|
|
938
|
+
|
|
939
|
+
def show_only_checkable_items(self, state: int) -> None:
|
|
940
|
+
"""Show only checkable items
|
|
941
|
+
|
|
942
|
+
Args:
|
|
943
|
+
state: If True, only checkable items are shown
|
|
944
|
+
"""
|
|
945
|
+
self.browser.tree.toggle_show_only_checkable_items(state)
|
|
946
|
+
fname = self.browser.selector.get_current_fname()
|
|
947
|
+
if fname:
|
|
948
|
+
self.browser.tree.set_current_file(fname)
|
|
949
|
+
|
|
950
|
+
def __finalize_setup(self) -> None:
|
|
951
|
+
"""Finalize setup"""
|
|
952
|
+
tree = self.browser.tree
|
|
953
|
+
tree.toggle_show_only_checkable_items(self.checkbox_show_only.isChecked())
|
|
954
|
+
tree.toggle_show_values(self.checkbox_show_values.isChecked())
|
|
955
|
+
|
|
956
|
+
def open_file(self, fname: str) -> None:
|
|
957
|
+
"""Open file
|
|
958
|
+
|
|
959
|
+
Args:
|
|
960
|
+
fname: HDF5 file name
|
|
961
|
+
"""
|
|
962
|
+
self.browser.open_file(fname)
|
|
963
|
+
self.__finalize_setup()
|
|
964
|
+
|
|
965
|
+
def open_files(self, fnames: list[str]) -> None:
|
|
966
|
+
"""Open files
|
|
967
|
+
|
|
968
|
+
Args:
|
|
969
|
+
fnames: HDF5 file names
|
|
970
|
+
"""
|
|
971
|
+
for fname in fnames:
|
|
972
|
+
self.browser.open_file(fname)
|
|
973
|
+
self.__finalize_setup()
|
|
974
|
+
|
|
975
|
+
def select_new_file(self, fname: str) -> None: # pylint:disable=unused-argument
|
|
976
|
+
"""Select new file
|
|
977
|
+
|
|
978
|
+
Args:
|
|
979
|
+
fname: HDF5 file name
|
|
980
|
+
"""
|
|
981
|
+
self.__finalize_setup()
|
|
982
|
+
self.refresh_buttons()
|
|
983
|
+
|
|
984
|
+
def remove_file(self, fname: str) -> None: # pylint:disable=unused-argument
|
|
985
|
+
"""Remove file
|
|
986
|
+
|
|
987
|
+
Args:
|
|
988
|
+
fname: HDF5 file name
|
|
989
|
+
"""
|
|
990
|
+
self.refresh_buttons()
|
|
991
|
+
|
|
992
|
+
def get_all_nodes(self) -> list[BaseNode]:
|
|
993
|
+
"""Return all supported datasets
|
|
994
|
+
|
|
995
|
+
Returns:
|
|
996
|
+
List of HDF5 nodes
|
|
997
|
+
"""
|
|
998
|
+
return self.browser.tree.get_nodes(only_checked_items=False)
|
|
999
|
+
|
|
1000
|
+
def get_nodes(self) -> list[BaseNode]:
|
|
1001
|
+
"""Return datasets
|
|
1002
|
+
|
|
1003
|
+
Returns:
|
|
1004
|
+
List of HDF5 nodes
|
|
1005
|
+
"""
|
|
1006
|
+
return self.nodes
|
|
1007
|
+
|
|
1008
|
+
def install_button_layout(self) -> None:
|
|
1009
|
+
"""Install button layout"""
|
|
1010
|
+
bbox = QW.QDialogButtonBox(QW.QDialogButtonBox.Ok | QW.QDialogButtonBox.Cancel)
|
|
1011
|
+
bbox.accepted.connect(self.accept)
|
|
1012
|
+
bbox.rejected.connect(self.reject)
|
|
1013
|
+
|
|
1014
|
+
btn_check_all = create_toolbutton(
|
|
1015
|
+
self,
|
|
1016
|
+
icon=get_icon("check_all.svg"),
|
|
1017
|
+
text=_("Check all"),
|
|
1018
|
+
autoraise=False,
|
|
1019
|
+
shortcut=QG.QKeySequence.SelectAll,
|
|
1020
|
+
triggered=lambda checked=True: self.browser.tree.toggle_all(checked),
|
|
1021
|
+
)
|
|
1022
|
+
btn_uncheck_all = create_toolbutton(
|
|
1023
|
+
self,
|
|
1024
|
+
icon=get_icon("uncheck_all.svg"),
|
|
1025
|
+
text=_("Uncheck all"),
|
|
1026
|
+
autoraise=False,
|
|
1027
|
+
triggered=lambda checked=False: self.browser.tree.toggle_all(checked),
|
|
1028
|
+
)
|
|
1029
|
+
self.checkbox_show_only = QW.QCheckBox(_("Show only supported data"))
|
|
1030
|
+
self.checkbox_show_only.stateChanged.connect(self.show_only_checkable_items)
|
|
1031
|
+
self.checkbox_show_values = QW.QCheckBox(_("Show values"))
|
|
1032
|
+
self.checkbox_show_values.stateChanged.connect(
|
|
1033
|
+
self.browser.tree.toggle_show_values
|
|
1034
|
+
)
|
|
1035
|
+
|
|
1036
|
+
self.button_layout = QW.QHBoxLayout()
|
|
1037
|
+
self.button_layout.addWidget(self.checkbox_show_only)
|
|
1038
|
+
self.button_layout.addWidget(self.checkbox_show_values)
|
|
1039
|
+
self.button_layout.addSpacing(10)
|
|
1040
|
+
self.button_layout.addWidget(btn_check_all)
|
|
1041
|
+
self.button_layout.addWidget(btn_uncheck_all)
|
|
1042
|
+
self.button_layout.addStretch()
|
|
1043
|
+
self.button_layout.addWidget(bbox)
|
|
1044
|
+
self.bbox = bbox
|
|
1045
|
+
|
|
1046
|
+
vlayout: QW.QVBoxLayout = self.layout()
|
|
1047
|
+
vlayout.addSpacing(10)
|
|
1048
|
+
vlayout.addLayout(self.button_layout)
|