datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datalab/__init__.py +35 -2
- datalab/adapters_metadata/__init__.py +31 -0
- datalab/adapters_metadata/base_adapter.py +316 -0
- datalab/adapters_metadata/common.py +422 -0
- datalab/adapters_metadata/geometry_adapter.py +98 -0
- datalab/adapters_metadata/table_adapter.py +84 -0
- datalab/adapters_plotpy/__init__.py +54 -0
- datalab/adapters_plotpy/annotations.py +124 -0
- datalab/adapters_plotpy/base.py +110 -0
- datalab/adapters_plotpy/converters.py +86 -0
- datalab/adapters_plotpy/factories.py +80 -0
- datalab/adapters_plotpy/objects/__init__.py +0 -0
- datalab/adapters_plotpy/objects/base.py +197 -0
- datalab/adapters_plotpy/objects/image.py +157 -0
- datalab/adapters_plotpy/objects/scalar.py +565 -0
- datalab/adapters_plotpy/objects/signal.py +264 -0
- datalab/adapters_plotpy/roi/__init__.py +0 -0
- datalab/adapters_plotpy/roi/base.py +146 -0
- datalab/adapters_plotpy/roi/factory.py +93 -0
- datalab/adapters_plotpy/roi/image.py +207 -0
- datalab/adapters_plotpy/roi/signal.py +72 -0
- datalab/app.py +98 -0
- datalab/config.py +817 -0
- datalab/control/__init__.py +0 -0
- datalab/control/baseproxy.py +776 -0
- datalab/control/proxy.py +343 -0
- datalab/control/remote.py +1005 -0
- datalab/data/doc/DataLab_en.pdf +0 -0
- datalab/data/doc/DataLab_fr.pdf +0 -0
- datalab/data/icons/analysis/delete_results.svg +109 -0
- datalab/data/icons/analysis/fw1e2.svg +156 -0
- datalab/data/icons/analysis/fwhm.svg +156 -0
- datalab/data/icons/analysis/histogram.svg +49 -0
- datalab/data/icons/analysis/peak_detect.svg +160 -0
- datalab/data/icons/analysis/plot_results.svg +151 -0
- datalab/data/icons/analysis/show_results.svg +83 -0
- datalab/data/icons/analysis/stats.svg +49 -0
- datalab/data/icons/analysis.svg +120 -0
- datalab/data/icons/apply.svg +3 -0
- datalab/data/icons/check_all.svg +15 -0
- datalab/data/icons/collapse.svg +44 -0
- datalab/data/icons/collapse_selection.svg +63 -0
- datalab/data/icons/console.svg +101 -0
- datalab/data/icons/create/1d-normal.svg +8 -0
- datalab/data/icons/create/1d-poisson.svg +9 -0
- datalab/data/icons/create/1d-uniform.svg +8 -0
- datalab/data/icons/create/1d-zero.svg +57 -0
- datalab/data/icons/create/2d-gaussian.svg +56 -0
- datalab/data/icons/create/2d-normal.svg +38 -0
- datalab/data/icons/create/2d-poisson.svg +38 -0
- datalab/data/icons/create/2d-ramp.svg +90 -0
- datalab/data/icons/create/2d-sinc.svg +62 -0
- datalab/data/icons/create/2d-uniform.svg +38 -0
- datalab/data/icons/create/2d-zero.svg +13 -0
- datalab/data/icons/create/checkerboard.svg +39 -0
- datalab/data/icons/create/cosine.svg +12 -0
- datalab/data/icons/create/exponential.svg +55 -0
- datalab/data/icons/create/gaussian.svg +12 -0
- datalab/data/icons/create/grating.svg +29 -0
- datalab/data/icons/create/linear_chirp.svg +7 -0
- datalab/data/icons/create/logistic.svg +7 -0
- datalab/data/icons/create/lorentzian.svg +12 -0
- datalab/data/icons/create/planck.svg +12 -0
- datalab/data/icons/create/polynomial.svg +7 -0
- datalab/data/icons/create/pulse.svg +12 -0
- datalab/data/icons/create/ring.svg +18 -0
- datalab/data/icons/create/sawtooth.svg +7 -0
- datalab/data/icons/create/siemens.svg +35 -0
- datalab/data/icons/create/sinc.svg +12 -0
- datalab/data/icons/create/sine.svg +7 -0
- datalab/data/icons/create/square.svg +7 -0
- datalab/data/icons/create/square_pulse.svg +7 -0
- datalab/data/icons/create/step.svg +7 -0
- datalab/data/icons/create/step_pulse.svg +12 -0
- datalab/data/icons/create/triangle.svg +7 -0
- datalab/data/icons/create/voigt.svg +12 -0
- datalab/data/icons/edit/annotations.svg +72 -0
- datalab/data/icons/edit/annotations_copy.svg +114 -0
- datalab/data/icons/edit/annotations_delete.svg +83 -0
- datalab/data/icons/edit/annotations_edit.svg +98 -0
- datalab/data/icons/edit/annotations_export.svg +85 -0
- datalab/data/icons/edit/annotations_import.svg +85 -0
- datalab/data/icons/edit/annotations_paste.svg +100 -0
- datalab/data/icons/edit/copy_titles.svg +109 -0
- datalab/data/icons/edit/delete.svg +84 -0
- datalab/data/icons/edit/delete_all.svg +214 -0
- datalab/data/icons/edit/duplicate.svg +64 -0
- datalab/data/icons/edit/goto_source.svg +60 -0
- datalab/data/icons/edit/metadata.svg +60 -0
- datalab/data/icons/edit/metadata_add.svg +80 -0
- datalab/data/icons/edit/metadata_copy.svg +96 -0
- datalab/data/icons/edit/metadata_delete.svg +62 -0
- datalab/data/icons/edit/metadata_export.svg +68 -0
- datalab/data/icons/edit/metadata_import.svg +68 -0
- datalab/data/icons/edit/metadata_paste.svg +79 -0
- datalab/data/icons/edit/move_down.svg +55 -0
- datalab/data/icons/edit/move_up.svg +54 -0
- datalab/data/icons/edit/new_group.svg +76 -0
- datalab/data/icons/edit/recompute.svg +60 -0
- datalab/data/icons/edit/rename.svg +49 -0
- datalab/data/icons/edit.svg +16 -0
- datalab/data/icons/expand.svg +44 -0
- datalab/data/icons/expand_selection.svg +63 -0
- datalab/data/icons/fit/cdf_fit.svg +56 -0
- datalab/data/icons/fit/exponential_fit.svg +55 -0
- datalab/data/icons/fit/gaussian_fit.svg +62 -0
- datalab/data/icons/fit/interactive_fit.svg +101 -0
- datalab/data/icons/fit/linear_fit.svg +57 -0
- datalab/data/icons/fit/lorentzian_fit.svg +209 -0
- datalab/data/icons/fit/multigaussian_fit.svg +85 -0
- datalab/data/icons/fit/multilorentzian_fit.svg +85 -0
- datalab/data/icons/fit/piecewiseexponential_fit.svg +209 -0
- datalab/data/icons/fit/planckian_fit.svg +62 -0
- datalab/data/icons/fit/polynomial_fit.svg +59 -0
- datalab/data/icons/fit/sigmoid_fit.svg +56 -0
- datalab/data/icons/fit/sinusoidal_fit.svg +72 -0
- datalab/data/icons/fit/twohalfgaussian_fit.svg +63 -0
- datalab/data/icons/fit/voigt_fit.svg +57 -0
- datalab/data/icons/group.svg +56 -0
- datalab/data/icons/h5/h5array.svg +59 -0
- datalab/data/icons/h5/h5attrs.svg +75 -0
- datalab/data/icons/h5/h5browser.svg +133 -0
- datalab/data/icons/h5/h5file.svg +69 -0
- datalab/data/icons/h5/h5group.svg +49 -0
- datalab/data/icons/h5/h5scalar.svg +1 -0
- datalab/data/icons/help_pdf.svg +46 -0
- datalab/data/icons/history.svg +7 -0
- datalab/data/icons/image.svg +135 -0
- datalab/data/icons/io/fileopen_directory.svg +60 -0
- datalab/data/icons/io/fileopen_h5.svg +84 -0
- datalab/data/icons/io/fileopen_ima.svg +187 -0
- datalab/data/icons/io/fileopen_py.svg +123 -0
- datalab/data/icons/io/fileopen_sig.svg +138 -0
- datalab/data/icons/io/filesave_h5.svg +97 -0
- datalab/data/icons/io/filesave_ima.svg +200 -0
- datalab/data/icons/io/filesave_py.svg +136 -0
- datalab/data/icons/io/filesave_sig.svg +151 -0
- datalab/data/icons/io/import_text.svg +144 -0
- datalab/data/icons/io/save_to_directory.svg +134 -0
- datalab/data/icons/io.svg +84 -0
- datalab/data/icons/libre-camera-flash-off.svg +1 -0
- datalab/data/icons/libre-camera-flash-on.svg +1 -0
- datalab/data/icons/libre-gui-about.svg +1 -0
- datalab/data/icons/libre-gui-action-delete.svg +1 -0
- datalab/data/icons/libre-gui-add.svg +1 -0
- datalab/data/icons/libre-gui-arrow-down.svg +1 -0
- datalab/data/icons/libre-gui-arrow-left.svg +1 -0
- datalab/data/icons/libre-gui-arrow-right.svg +1 -0
- datalab/data/icons/libre-gui-arrow-up.svg +1 -0
- datalab/data/icons/libre-gui-close.svg +40 -0
- datalab/data/icons/libre-gui-cogs.svg +1 -0
- datalab/data/icons/libre-gui-globe.svg +1 -0
- datalab/data/icons/libre-gui-help.svg +1 -0
- datalab/data/icons/libre-gui-link.svg +1 -0
- datalab/data/icons/libre-gui-menu.svg +1 -0
- datalab/data/icons/libre-gui-pencil.svg +1 -0
- datalab/data/icons/libre-gui-plugin.svg +1 -0
- datalab/data/icons/libre-gui-questions.svg +1 -0
- datalab/data/icons/libre-gui-settings.svg +1 -0
- datalab/data/icons/libre-gui-unlink.svg +1 -0
- datalab/data/icons/libre-tech-ram.svg +1 -0
- datalab/data/icons/libre-toolbox.svg +1 -0
- datalab/data/icons/logs.svg +1 -0
- datalab/data/icons/markers.svg +74 -0
- datalab/data/icons/menu.svg +13 -0
- datalab/data/icons/new_ima.svg +148 -0
- datalab/data/icons/new_sig.svg +123 -0
- datalab/data/icons/operations/abs.svg +116 -0
- datalab/data/icons/operations/arithmetic.svg +123 -0
- datalab/data/icons/operations/average.svg +124 -0
- datalab/data/icons/operations/complex_from_magnitude_phase.svg +116 -0
- datalab/data/icons/operations/complex_from_real_imag.svg +124 -0
- datalab/data/icons/operations/constant.svg +116 -0
- datalab/data/icons/operations/constant_add.svg +109 -0
- datalab/data/icons/operations/constant_divide.svg +109 -0
- datalab/data/icons/operations/constant_multiply.svg +109 -0
- datalab/data/icons/operations/constant_subtract.svg +109 -0
- datalab/data/icons/operations/convert_dtype.svg +117 -0
- datalab/data/icons/operations/convolution.svg +46 -0
- datalab/data/icons/operations/deconvolution.svg +57 -0
- datalab/data/icons/operations/derivative.svg +127 -0
- datalab/data/icons/operations/difference.svg +52 -0
- datalab/data/icons/operations/division.svg +139 -0
- datalab/data/icons/operations/exp.svg +116 -0
- datalab/data/icons/operations/flip_horizontally.svg +69 -0
- datalab/data/icons/operations/flip_vertically.svg +74 -0
- datalab/data/icons/operations/im.svg +124 -0
- datalab/data/icons/operations/integral.svg +50 -0
- datalab/data/icons/operations/inverse.svg +143 -0
- datalab/data/icons/operations/log10.svg +109 -0
- datalab/data/icons/operations/phase.svg +116 -0
- datalab/data/icons/operations/power.svg +118 -0
- datalab/data/icons/operations/product.svg +124 -0
- datalab/data/icons/operations/profile.svg +379 -0
- datalab/data/icons/operations/profile_average.svg +399 -0
- datalab/data/icons/operations/profile_radial.svg +261 -0
- datalab/data/icons/operations/profile_segment.svg +262 -0
- datalab/data/icons/operations/quadratic_difference.svg +84 -0
- datalab/data/icons/operations/re.svg +124 -0
- datalab/data/icons/operations/rotate_left.svg +72 -0
- datalab/data/icons/operations/rotate_right.svg +72 -0
- datalab/data/icons/operations/signals_to_image.svg +314 -0
- datalab/data/icons/operations/sqrt.svg +110 -0
- datalab/data/icons/operations/std.svg +124 -0
- datalab/data/icons/operations/sum.svg +102 -0
- datalab/data/icons/play_demo.svg +9 -0
- datalab/data/icons/processing/axis_transform.svg +62 -0
- datalab/data/icons/processing/bandpass.svg +79 -0
- datalab/data/icons/processing/bandstop.svg +71 -0
- datalab/data/icons/processing/binning.svg +126 -0
- datalab/data/icons/processing/clip.svg +119 -0
- datalab/data/icons/processing/detrending.svg +173 -0
- datalab/data/icons/processing/distribute_on_grid.svg +769 -0
- datalab/data/icons/processing/edge_detection.svg +46 -0
- datalab/data/icons/processing/erase.svg +1 -0
- datalab/data/icons/processing/exposure.svg +143 -0
- datalab/data/icons/processing/fourier.svg +104 -0
- datalab/data/icons/processing/highpass.svg +59 -0
- datalab/data/icons/processing/interpolation.svg +71 -0
- datalab/data/icons/processing/level_adjustment.svg +70 -0
- datalab/data/icons/processing/lowpass.svg +60 -0
- datalab/data/icons/processing/morphology.svg +49 -0
- datalab/data/icons/processing/noise_addition.svg +114 -0
- datalab/data/icons/processing/noise_reduction.svg +38 -0
- datalab/data/icons/processing/normalize.svg +84 -0
- datalab/data/icons/processing/offset_correction.svg +131 -0
- datalab/data/icons/processing/resampling1d.svg +101 -0
- datalab/data/icons/processing/resampling2d.svg +240 -0
- datalab/data/icons/processing/reset_positions.svg +185 -0
- datalab/data/icons/processing/resize.svg +9 -0
- datalab/data/icons/processing/reverse_signal_x.svg +171 -0
- datalab/data/icons/processing/stability.svg +11 -0
- datalab/data/icons/processing/swap_x_y.svg +65 -0
- datalab/data/icons/processing/thresholding.svg +63 -0
- datalab/data/icons/processing/windowing.svg +45 -0
- datalab/data/icons/properties.svg +26 -0
- datalab/data/icons/reset.svg +9 -0
- datalab/data/icons/restore.svg +40 -0
- datalab/data/icons/roi/roi.svg +76 -0
- datalab/data/icons/roi/roi_coordinate.svg +78 -0
- datalab/data/icons/roi/roi_copy.svg +112 -0
- datalab/data/icons/roi/roi_delete.svg +81 -0
- datalab/data/icons/roi/roi_export.svg +87 -0
- datalab/data/icons/roi/roi_graphical.svg +78 -0
- datalab/data/icons/roi/roi_grid.svg +67 -0
- datalab/data/icons/roi/roi_ima.svg +188 -0
- datalab/data/icons/roi/roi_import.svg +87 -0
- datalab/data/icons/roi/roi_new.svg +81 -0
- datalab/data/icons/roi/roi_new_circle.svg +95 -0
- datalab/data/icons/roi/roi_new_polygon.svg +110 -0
- datalab/data/icons/roi/roi_new_rectangle.svg +70 -0
- datalab/data/icons/roi/roi_paste.svg +98 -0
- datalab/data/icons/roi/roi_sig.svg +124 -0
- datalab/data/icons/shapes.svg +134 -0
- datalab/data/icons/signal.svg +103 -0
- datalab/data/icons/table.svg +85 -0
- datalab/data/icons/table_unavailable.svg +102 -0
- datalab/data/icons/to_signal.svg +124 -0
- datalab/data/icons/tour/next.svg +44 -0
- datalab/data/icons/tour/previous.svg +44 -0
- datalab/data/icons/tour/rewind.svg +51 -0
- datalab/data/icons/tour/stop.svg +47 -0
- datalab/data/icons/tour/tour.svg +16 -0
- datalab/data/icons/uncheck_all.svg +78 -0
- datalab/data/icons/view/curve_antialiasing.svg +50 -0
- datalab/data/icons/view/new_window.svg +98 -0
- datalab/data/icons/view/refresh-auto.svg +57 -0
- datalab/data/icons/view/refresh-manual.svg +51 -0
- datalab/data/icons/view/reset_curve_styles.svg +96 -0
- datalab/data/icons/view/show_first.svg +55 -0
- datalab/data/icons/view/show_titles.svg +46 -0
- datalab/data/icons/visualization.svg +51 -0
- datalab/data/logo/DataLab-Banner-150.png +0 -0
- datalab/data/logo/DataLab-Banner-200.png +0 -0
- datalab/data/logo/DataLab-Banner2-100.png +0 -0
- datalab/data/logo/DataLab-Splash.png +0 -0
- datalab/data/logo/DataLab-watermark.png +0 -0
- datalab/data/logo/DataLab.svg +83 -0
- datalab/data/tests/reordering_test.h5 +0 -0
- datalab/data/tutorials/fabry_perot/fabry-perot1.jpg +0 -0
- datalab/data/tutorials/fabry_perot/fabry-perot2.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_13.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_18.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_23.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_30.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_35.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_40.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_45.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_50.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_55.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_60.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_65.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_70.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_75.jpg +0 -0
- datalab/data/tutorials/laser_beam/TEM00_z_80.jpg +0 -0
- datalab/env.py +542 -0
- datalab/gui/__init__.py +89 -0
- datalab/gui/actionhandler.py +1701 -0
- datalab/gui/docks.py +473 -0
- datalab/gui/h5io.py +150 -0
- datalab/gui/macroeditor.py +310 -0
- datalab/gui/main.py +2081 -0
- datalab/gui/newobject.py +217 -0
- datalab/gui/objectview.py +766 -0
- datalab/gui/panel/__init__.py +48 -0
- datalab/gui/panel/base.py +3254 -0
- datalab/gui/panel/image.py +157 -0
- datalab/gui/panel/macro.py +607 -0
- datalab/gui/panel/signal.py +164 -0
- datalab/gui/plothandler.py +800 -0
- datalab/gui/processor/__init__.py +84 -0
- datalab/gui/processor/base.py +2456 -0
- datalab/gui/processor/catcher.py +75 -0
- datalab/gui/processor/image.py +1214 -0
- datalab/gui/processor/signal.py +755 -0
- datalab/gui/profiledialog.py +333 -0
- datalab/gui/roieditor.py +633 -0
- datalab/gui/roigrideditor.py +208 -0
- datalab/gui/settings.py +612 -0
- datalab/gui/tour.py +908 -0
- datalab/h5/__init__.py +12 -0
- datalab/h5/common.py +314 -0
- datalab/h5/generic.py +580 -0
- datalab/h5/native.py +39 -0
- datalab/h5/utils.py +95 -0
- datalab/objectmodel.py +640 -0
- datalab/plugins/_readme_.txt +9 -0
- datalab/plugins/datalab_imageformats.py +175 -0
- datalab/plugins/datalab_testdata.py +190 -0
- datalab/plugins.py +355 -0
- datalab/tests/__init__.py +199 -0
- datalab/tests/backbone/__init__.py +1 -0
- datalab/tests/backbone/config_unit_test.py +170 -0
- datalab/tests/backbone/config_versioning_unit_test.py +34 -0
- datalab/tests/backbone/dictlistserial_app_test.py +38 -0
- datalab/tests/backbone/errorcatcher_unit_test.py +69 -0
- datalab/tests/backbone/errormsgbox_unit_test.py +50 -0
- datalab/tests/backbone/execenv_unit.py +262 -0
- datalab/tests/backbone/loadtest_gdi.py +147 -0
- datalab/tests/backbone/long_callback.py +96 -0
- datalab/tests/backbone/main_app_test.py +137 -0
- datalab/tests/backbone/memory_leak.py +43 -0
- datalab/tests/backbone/procisolation1_unit.py +128 -0
- datalab/tests/backbone/procisolation2_unit.py +171 -0
- datalab/tests/backbone/procisolation_unit_test.py +22 -0
- datalab/tests/backbone/profiling_app.py +27 -0
- datalab/tests/backbone/strings_unit_test.py +65 -0
- datalab/tests/backbone/title_formatting_unit_test.py +82 -0
- datalab/tests/conftest.py +131 -0
- datalab/tests/features/__init__.py +1 -0
- datalab/tests/features/applauncher/__init__.py +1 -0
- datalab/tests/features/applauncher/launcher1_app_test.py +28 -0
- datalab/tests/features/applauncher/launcher2_app_test.py +30 -0
- datalab/tests/features/common/__init__.py +1 -0
- datalab/tests/features/common/add_metadata_app_test.py +134 -0
- datalab/tests/features/common/add_metadata_unit_test.py +267 -0
- datalab/tests/features/common/annotations_management_unit_test.py +152 -0
- datalab/tests/features/common/auto_analysis_recompute_unit_test.py +240 -0
- datalab/tests/features/common/createobject_unit_test.py +50 -0
- datalab/tests/features/common/geometry_results_app_test.py +135 -0
- datalab/tests/features/common/interactive_processing_test.py +1109 -0
- datalab/tests/features/common/io_app_test.py +75 -0
- datalab/tests/features/common/large_results_app_test.py +187 -0
- datalab/tests/features/common/metadata_all_patterns_test.py +103 -0
- datalab/tests/features/common/metadata_app_test.py +139 -0
- datalab/tests/features/common/metadata_io_unit_test.py +60 -0
- datalab/tests/features/common/misc_app_test.py +236 -0
- datalab/tests/features/common/multiple_geometry_results_unit_test.py +122 -0
- datalab/tests/features/common/multiple_table_results_unit_test.py +64 -0
- datalab/tests/features/common/operation_modes_app_test.py +392 -0
- datalab/tests/features/common/plot_results_app_test.py +278 -0
- datalab/tests/features/common/reorder_app_test.py +75 -0
- datalab/tests/features/common/result_deletion_unit_test.py +96 -0
- datalab/tests/features/common/result_merged_label_unit_test.py +154 -0
- datalab/tests/features/common/result_shape_settings_unit_test.py +223 -0
- datalab/tests/features/common/roi_plotitem_unit_test.py +64 -0
- datalab/tests/features/common/roieditor_unit_test.py +102 -0
- datalab/tests/features/common/save_to_dir_app_test.py +163 -0
- datalab/tests/features/common/save_to_dir_unit_test.py +474 -0
- datalab/tests/features/common/stat_app_test.py +40 -0
- datalab/tests/features/common/stats_tools_unit_test.py +77 -0
- datalab/tests/features/common/table_results_app_test.py +52 -0
- datalab/tests/features/common/textimport_unit_test.py +131 -0
- datalab/tests/features/common/uuid_preservation_test.py +281 -0
- datalab/tests/features/common/worker_unit_test.py +402 -0
- datalab/tests/features/control/__init__.py +1 -0
- datalab/tests/features/control/connect_dialog.py +28 -0
- datalab/tests/features/control/embedded1_unit_test.py +304 -0
- datalab/tests/features/control/embedded2_unit_test.py +52 -0
- datalab/tests/features/control/remoteclient_app_test.py +219 -0
- datalab/tests/features/control/remoteclient_unit.py +75 -0
- datalab/tests/features/control/simpleclient_unit_test.py +321 -0
- datalab/tests/features/hdf5/__init__.py +1 -0
- datalab/tests/features/hdf5/h5browser1_unit_test.py +31 -0
- datalab/tests/features/hdf5/h5browser2_unit.py +55 -0
- datalab/tests/features/hdf5/h5browser_app_test.py +77 -0
- datalab/tests/features/hdf5/h5import_app_test.py +25 -0
- datalab/tests/features/hdf5/h5importer_app_test.py +34 -0
- datalab/tests/features/image/__init__.py +1 -0
- datalab/tests/features/image/annotations_app_test.py +28 -0
- datalab/tests/features/image/annotations_unit_test.py +80 -0
- datalab/tests/features/image/average_app_test.py +46 -0
- datalab/tests/features/image/background_dialog_test.py +70 -0
- datalab/tests/features/image/blobs_app_test.py +50 -0
- datalab/tests/features/image/contour_app_test.py +42 -0
- datalab/tests/features/image/contour_fabryperot_app_test.py +51 -0
- datalab/tests/features/image/denoise_app_test.py +31 -0
- datalab/tests/features/image/distribute_on_grid_app_test.py +95 -0
- datalab/tests/features/image/edges_app_test.py +31 -0
- datalab/tests/features/image/erase_app_test.py +21 -0
- datalab/tests/features/image/fft2d_app_test.py +27 -0
- datalab/tests/features/image/flatfield_app_test.py +40 -0
- datalab/tests/features/image/geometry_transform_unit_test.py +396 -0
- datalab/tests/features/image/imagetools_app_test.py +51 -0
- datalab/tests/features/image/imagetools_unit_test.py +27 -0
- datalab/tests/features/image/load_app_test.py +73 -0
- datalab/tests/features/image/morph_app_test.py +32 -0
- datalab/tests/features/image/offsetcorrection_app_test.py +30 -0
- datalab/tests/features/image/peak2d_app_test.py +53 -0
- datalab/tests/features/image/profile_app_test.py +73 -0
- datalab/tests/features/image/profile_dialog_test.py +56 -0
- datalab/tests/features/image/roi_app_test.py +98 -0
- datalab/tests/features/image/roi_circ_app_test.py +62 -0
- datalab/tests/features/image/roi_manipulation_app_test.py +268 -0
- datalab/tests/features/image/roigrid_unit_test.py +60 -0
- datalab/tests/features/image/side_by_side_app_test.py +52 -0
- datalab/tests/features/macro/__init__.py +1 -0
- datalab/tests/features/macro/macro_app_test.py +28 -0
- datalab/tests/features/macro/macroeditor_unit_test.py +102 -0
- datalab/tests/features/signal/__init__.py +1 -0
- datalab/tests/features/signal/baseline_dialog_test.py +53 -0
- datalab/tests/features/signal/deltax_dialog_unit_test.py +34 -0
- datalab/tests/features/signal/fft1d_app_test.py +26 -0
- datalab/tests/features/signal/filter_app_test.py +44 -0
- datalab/tests/features/signal/fitdialog_unit_test.py +50 -0
- datalab/tests/features/signal/interpolation_app_test.py +110 -0
- datalab/tests/features/signal/loadbigsignal_app_test.py +80 -0
- datalab/tests/features/signal/multiple_rois_unit_test.py +132 -0
- datalab/tests/features/signal/pulse_features_app_test.py +118 -0
- datalab/tests/features/signal/pulse_features_roi_app_test.py +55 -0
- datalab/tests/features/signal/roi_app_test.py +78 -0
- datalab/tests/features/signal/roi_manipulation_app_test.py +261 -0
- datalab/tests/features/signal/select_xy_cursor_unit_test.py +46 -0
- datalab/tests/features/signal/signalpeakdetection_dialog_test.py +33 -0
- datalab/tests/features/signal/signals_to_image_app_test.py +98 -0
- datalab/tests/features/signal/xarray_compat_app_test.py +128 -0
- datalab/tests/features/tour_unit_test.py +22 -0
- datalab/tests/features/utilities/__init__.py +1 -0
- datalab/tests/features/utilities/installconf_unit_test.py +21 -0
- datalab/tests/features/utilities/logview_app_test.py +21 -0
- datalab/tests/features/utilities/logview_error.py +24 -0
- datalab/tests/features/utilities/logview_unit_test.py +21 -0
- datalab/tests/features/utilities/memstatus_app_test.py +42 -0
- datalab/tests/features/utilities/settings_unit_test.py +88 -0
- datalab/tests/scenarios/__init__.py +1 -0
- datalab/tests/scenarios/beautiful_app.py +121 -0
- datalab/tests/scenarios/common.py +463 -0
- datalab/tests/scenarios/demo.py +212 -0
- datalab/tests/scenarios/example_app_test.py +47 -0
- datalab/tests/scenarios/scenario_h5_app_test.py +75 -0
- datalab/tests/scenarios/scenario_ima1_app_test.py +34 -0
- datalab/tests/scenarios/scenario_ima2_app_test.py +34 -0
- datalab/tests/scenarios/scenario_mac_app_test.py +58 -0
- datalab/tests/scenarios/scenario_sig1_app_test.py +36 -0
- datalab/tests/scenarios/scenario_sig2_app_test.py +35 -0
- datalab/utils/__init__.py +1 -0
- datalab/utils/conf.py +304 -0
- datalab/utils/dephash.py +105 -0
- datalab/utils/qthelpers.py +633 -0
- datalab/utils/strings.py +34 -0
- datalab/utils/tests.py +0 -0
- datalab/widgets/__init__.py +1 -0
- datalab/widgets/connection.py +138 -0
- datalab/widgets/filedialog.py +91 -0
- datalab/widgets/fileviewer.py +84 -0
- datalab/widgets/fitdialog.py +788 -0
- datalab/widgets/h5browser.py +1048 -0
- datalab/widgets/imagebackground.py +111 -0
- datalab/widgets/instconfviewer.py +175 -0
- datalab/widgets/logviewer.py +80 -0
- datalab/widgets/signalbaseline.py +90 -0
- datalab/widgets/signalcursor.py +208 -0
- datalab/widgets/signaldeltax.py +151 -0
- datalab/widgets/signalpeak.py +199 -0
- datalab/widgets/status.py +249 -0
- datalab/widgets/textimport.py +786 -0
- datalab/widgets/warningerror.py +223 -0
- datalab/widgets/wizard.py +286 -0
- datalab_platform-1.0.1.dist-info/METADATA +121 -0
- datalab_platform-1.0.1.dist-info/RECORD +494 -0
- datalab_platform-0.0.1.dev0.dist-info/METADATA +0 -67
- datalab_platform-0.0.1.dev0.dist-info/RECORD +0 -7
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/WHEEL +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/entry_points.txt +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Plot handler
|
|
5
|
+
============
|
|
6
|
+
|
|
7
|
+
The :mod:`datalab.gui.plothandler` module provides plot handlers for signal
|
|
8
|
+
and image panels, that is, classes handling `PlotPy` plot items for representing
|
|
9
|
+
signals and images.
|
|
10
|
+
|
|
11
|
+
Signal plot handler
|
|
12
|
+
-------------------
|
|
13
|
+
|
|
14
|
+
.. autoclass:: SignalPlotHandler
|
|
15
|
+
:members:
|
|
16
|
+
:inherited-members:
|
|
17
|
+
|
|
18
|
+
Image plot handler
|
|
19
|
+
------------------
|
|
20
|
+
|
|
21
|
+
.. autoclass:: ImagePlotHandler
|
|
22
|
+
:members:
|
|
23
|
+
:inherited-members:
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import hashlib
|
|
31
|
+
from collections.abc import Iterator
|
|
32
|
+
from typing import TYPE_CHECKING, Generic
|
|
33
|
+
from weakref import WeakKeyDictionary
|
|
34
|
+
|
|
35
|
+
import numpy as np
|
|
36
|
+
from plotpy.constants import PlotType
|
|
37
|
+
from plotpy.items import CurveItem, GridItem, LegendBoxItem, MaskedXYImageItem
|
|
38
|
+
from plotpy.plot import PlotOptions
|
|
39
|
+
from qtpy import QtWidgets as QW
|
|
40
|
+
from qwt import QwtScaleDraw
|
|
41
|
+
from sigima.objects import ImageObj, SignalObj, TypeObj
|
|
42
|
+
|
|
43
|
+
from datalab.adapters_metadata import GeometryAdapter, TableAdapter
|
|
44
|
+
from datalab.adapters_plotpy import TypePlotItem, create_adapter_from_object
|
|
45
|
+
from datalab.adapters_plotpy.objects.scalar import MergedResultPlotPyAdapter
|
|
46
|
+
from datalab.config import Conf, _
|
|
47
|
+
from datalab.objectmodel import get_uuid
|
|
48
|
+
from datalab.utils.qthelpers import block_signals, create_progress_bar
|
|
49
|
+
|
|
50
|
+
if TYPE_CHECKING:
|
|
51
|
+
from plotpy.items import LabelItem
|
|
52
|
+
from plotpy.plot import PlotWidget
|
|
53
|
+
|
|
54
|
+
from datalab.gui.panel.base import BaseDataPanel
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def calc_data_hash(obj: SignalObj | ImageObj) -> str:
|
|
58
|
+
"""Calculate a hash for a SignalObj | ImageObj object's data"""
|
|
59
|
+
return hashlib.sha1(np.ascontiguousarray(obj.data)).hexdigest()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class BasePlotHandler(Generic[TypeObj, TypePlotItem]): # type: ignore
|
|
63
|
+
"""Object handling plot items associated to objects (signals/images)"""
|
|
64
|
+
|
|
65
|
+
PLOT_TYPE: PlotType | None = None # Replaced in subclasses
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
panel: BaseDataPanel,
|
|
70
|
+
plotwidget: PlotWidget,
|
|
71
|
+
) -> None:
|
|
72
|
+
self.panel = panel
|
|
73
|
+
self.plotwidget = plotwidget
|
|
74
|
+
self.plot = plotwidget.get_plot()
|
|
75
|
+
|
|
76
|
+
# Plot items: key = object uuid, value = plot item
|
|
77
|
+
self.__plotitems: dict[str, TypePlotItem] = {}
|
|
78
|
+
|
|
79
|
+
self.__shapeitems = []
|
|
80
|
+
self.__cached_hashes: WeakKeyDictionary[TypeObj, list[int]] = (
|
|
81
|
+
WeakKeyDictionary()
|
|
82
|
+
)
|
|
83
|
+
self.__auto_refresh = False
|
|
84
|
+
self.__show_first_only = False
|
|
85
|
+
# Mapping from object UUID to merged result adapter
|
|
86
|
+
# The merged adapter consolidates all result labels for an object
|
|
87
|
+
self.__merged_result_adapters: dict[str, MergedResultPlotPyAdapter] = {}
|
|
88
|
+
|
|
89
|
+
def __len__(self) -> int:
|
|
90
|
+
"""Return number of items"""
|
|
91
|
+
return len(self.__plotitems)
|
|
92
|
+
|
|
93
|
+
def __getitem__(self, oid: str) -> TypePlotItem:
|
|
94
|
+
"""Return item associated to object uuid"""
|
|
95
|
+
try:
|
|
96
|
+
return self.__plotitems[oid]
|
|
97
|
+
except KeyError as exc:
|
|
98
|
+
# Item does not exist: this may happen when "auto refresh" is disabled
|
|
99
|
+
# (object has been added to model but the corresponding plot item has not
|
|
100
|
+
# been created yet)
|
|
101
|
+
if not self.__auto_refresh:
|
|
102
|
+
self.refresh_plot(oid, True, force=True, only_visible=False)
|
|
103
|
+
return self.__plotitems[oid]
|
|
104
|
+
# Item does not exist and auto refresh is enabled: this should not happen
|
|
105
|
+
raise exc
|
|
106
|
+
|
|
107
|
+
def get(self, key: str, default: TypePlotItem | None = None) -> TypePlotItem | None:
|
|
108
|
+
"""Return item associated to object uuid.
|
|
109
|
+
If the key is not found, default is returned if given,
|
|
110
|
+
otherwise None is returned."""
|
|
111
|
+
return self.__plotitems.get(key, default)
|
|
112
|
+
|
|
113
|
+
def get_obj_from_item(self, item: TypePlotItem) -> TypeObj | None:
|
|
114
|
+
"""Return object associated to plot item
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
item: plot item
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Object associated to plot item
|
|
121
|
+
"""
|
|
122
|
+
for obj in self.panel.objmodel:
|
|
123
|
+
if self.get(get_uuid(obj)) is item:
|
|
124
|
+
return obj
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def __setitem__(self, oid: str, item: TypePlotItem) -> None:
|
|
128
|
+
"""Set item associated to object uuid"""
|
|
129
|
+
self.__plotitems[oid] = item
|
|
130
|
+
|
|
131
|
+
def __iter__(self) -> Iterator[TypePlotItem]:
|
|
132
|
+
"""Return an iterator over plothandler values (plot items)"""
|
|
133
|
+
return iter(self.__plotitems.values())
|
|
134
|
+
|
|
135
|
+
def remove_item(self, oid: str) -> None:
|
|
136
|
+
"""Remove plot item associated to object uuid"""
|
|
137
|
+
try:
|
|
138
|
+
item = self.__plotitems.pop(oid)
|
|
139
|
+
except KeyError:
|
|
140
|
+
# Item does not exist: this may happen when "auto refresh" is disabled
|
|
141
|
+
# (object has been added to model but the corresponding plot item has not
|
|
142
|
+
# been created yet).
|
|
143
|
+
# This may also happen after opening a project, then immediately selecting
|
|
144
|
+
# a group containing more than one object: plot item would have been created
|
|
145
|
+
# only for the first object in group, and this exception would be raised
|
|
146
|
+
# for the second one (which does not have a plot item yet).
|
|
147
|
+
return
|
|
148
|
+
self.plot.del_item(item)
|
|
149
|
+
|
|
150
|
+
def clear(self) -> None:
|
|
151
|
+
"""Clear plot items"""
|
|
152
|
+
self.__plotitems = {}
|
|
153
|
+
self.__merged_result_adapters = {}
|
|
154
|
+
self.cleanup_dataview()
|
|
155
|
+
self.remove_all_shape_items()
|
|
156
|
+
|
|
157
|
+
def add_shapes(self, oid: str, do_autoscale: bool = False) -> None:
|
|
158
|
+
"""Add geometric shape items associated to computed results and annotations,
|
|
159
|
+
for the object with the given uuid"""
|
|
160
|
+
obj = self.panel.objmodel[oid]
|
|
161
|
+
if obj.metadata:
|
|
162
|
+
obj_adapter = create_adapter_from_object(obj)
|
|
163
|
+
items = list(obj_adapter.iterate_shape_items(editable=False))
|
|
164
|
+
|
|
165
|
+
# Collect all result adapters
|
|
166
|
+
results = list(TableAdapter.iterate_from_obj(obj)) + list(
|
|
167
|
+
GeometryAdapter.iterate_from_obj(obj)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Create or update merged result adapter
|
|
171
|
+
if results:
|
|
172
|
+
merged_adapter = MergedResultPlotPyAdapter(results, obj)
|
|
173
|
+
self.__merged_result_adapters[oid] = merged_adapter
|
|
174
|
+
|
|
175
|
+
# Get the merged label
|
|
176
|
+
merged_label = merged_adapter.get_merged_label()
|
|
177
|
+
if merged_label is not None:
|
|
178
|
+
# Set initial visibility based on configuration
|
|
179
|
+
merged_label.setVisible(Conf.view.show_result_label.get())
|
|
180
|
+
items.append(merged_label)
|
|
181
|
+
|
|
182
|
+
# Add other items from the merged adapter (e.g., geometric shapes)
|
|
183
|
+
items.extend(merged_adapter.get_other_items())
|
|
184
|
+
else:
|
|
185
|
+
# No results, remove any existing adapter
|
|
186
|
+
self.__merged_result_adapters.pop(oid, None)
|
|
187
|
+
|
|
188
|
+
if items:
|
|
189
|
+
if do_autoscale:
|
|
190
|
+
self.plot.do_autoscale()
|
|
191
|
+
# Performance optimization: block `plotpy.plot.BasePlot` signals, add
|
|
192
|
+
# all items except the last one, unblock signals, then add the last one
|
|
193
|
+
# (this avoids some unnecessary refresh process by PlotPy)
|
|
194
|
+
with block_signals(self.plot):
|
|
195
|
+
with create_progress_bar(
|
|
196
|
+
self.panel, _("Creating geometric shapes"), max_=len(items) - 1
|
|
197
|
+
) as progress:
|
|
198
|
+
for i_item, item in enumerate(items[:-1]):
|
|
199
|
+
progress.setValue(i_item + 1)
|
|
200
|
+
if progress.wasCanceled():
|
|
201
|
+
break
|
|
202
|
+
self.plot.add_item(item)
|
|
203
|
+
self.__shapeitems.append(item)
|
|
204
|
+
# Only process events every 10 items to keep UI responsive
|
|
205
|
+
# without killing performance
|
|
206
|
+
if (i_item + 1) % 10 == 0:
|
|
207
|
+
QW.QApplication.processEvents()
|
|
208
|
+
self.plot.add_item(items[-1])
|
|
209
|
+
self.__shapeitems.append(items[-1])
|
|
210
|
+
|
|
211
|
+
def update_resultproperty_from_plot_item(self, item: LabelItem) -> None:
|
|
212
|
+
"""Update result properties from merged label plot item.
|
|
213
|
+
|
|
214
|
+
When the merged results label is moved, update the stored position
|
|
215
|
+
in the merged result adapter.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
item: Merged results label item
|
|
219
|
+
"""
|
|
220
|
+
# Find the merged adapter that owns this label
|
|
221
|
+
for merged_adapter in self.__merged_result_adapters.values():
|
|
222
|
+
if merged_adapter.get_cached_label() is item:
|
|
223
|
+
# Update the adapter's metadata with the new position
|
|
224
|
+
merged_adapter.update_obj_metadata_from_item(item)
|
|
225
|
+
break
|
|
226
|
+
|
|
227
|
+
def remove_all_shape_items(self) -> None:
|
|
228
|
+
"""Remove all geometric shapes associated to result items"""
|
|
229
|
+
if set(self.__shapeitems).issubset(set(self.plot.items)):
|
|
230
|
+
self.plot.del_items(self.__shapeitems)
|
|
231
|
+
self.__shapeitems = []
|
|
232
|
+
# Clear cached labels in merged result adapters since they were removed
|
|
233
|
+
for merged_adapter in self.__merged_result_adapters.values():
|
|
234
|
+
merged_adapter.invalidate_cached_label()
|
|
235
|
+
|
|
236
|
+
def refresh_all_shape_items(self) -> None:
|
|
237
|
+
"""Refresh all geometric shapes to apply new style parameters.
|
|
238
|
+
|
|
239
|
+
This method is called when shape/marker visualization settings
|
|
240
|
+
are changed in the Settings dialog. It removes and recreates all shape
|
|
241
|
+
items to apply the new parameters.
|
|
242
|
+
"""
|
|
243
|
+
# Get all object IDs that have shape items
|
|
244
|
+
oids_with_shapes = list(self.__merged_result_adapters.keys())
|
|
245
|
+
|
|
246
|
+
if not oids_with_shapes:
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# Remove all existing shape items
|
|
250
|
+
self.remove_all_shape_items()
|
|
251
|
+
|
|
252
|
+
# Recreate shape items for all objects with the new settings
|
|
253
|
+
for oid in oids_with_shapes:
|
|
254
|
+
self.add_shapes(oid, do_autoscale=False)
|
|
255
|
+
|
|
256
|
+
self.plot.replot()
|
|
257
|
+
|
|
258
|
+
def toggle_result_label_visibility(self, show: bool) -> None:
|
|
259
|
+
"""Toggle the visibility of all merged result labels on the plot.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
show: True to show the labels, False to hide them
|
|
263
|
+
"""
|
|
264
|
+
for merged_adapter in self.__merged_result_adapters.values():
|
|
265
|
+
label = merged_adapter.get_cached_label()
|
|
266
|
+
if label is not None:
|
|
267
|
+
label.setVisible(show)
|
|
268
|
+
self.plot.replot()
|
|
269
|
+
|
|
270
|
+
def __add_item_to_plot(self, oid: str) -> TypePlotItem:
|
|
271
|
+
"""Make plot item and add it to plot.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
oid: object uuid
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Plot item
|
|
278
|
+
"""
|
|
279
|
+
obj = self.panel.objmodel[oid]
|
|
280
|
+
self.__cached_hashes[obj] = calc_data_hash(obj)
|
|
281
|
+
item: TypePlotItem = create_adapter_from_object(obj).make_item()
|
|
282
|
+
item.set_readonly(True)
|
|
283
|
+
self[oid] = item
|
|
284
|
+
self.plot.add_item(item)
|
|
285
|
+
return item
|
|
286
|
+
|
|
287
|
+
def __update_item_on_plot(self, oid: str, just_show: bool = False) -> None:
|
|
288
|
+
"""Update plot item.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
oid: object uuid
|
|
292
|
+
just_show: if True, only show the item (do not update it).
|
|
293
|
+
Defaults to False.
|
|
294
|
+
"""
|
|
295
|
+
if not just_show:
|
|
296
|
+
obj = self.panel.objmodel[oid]
|
|
297
|
+
cached_hash = self.__cached_hashes.get(obj)
|
|
298
|
+
new_hash = calc_data_hash(obj)
|
|
299
|
+
data_changed = cached_hash is None or cached_hash != new_hash
|
|
300
|
+
self.__cached_hashes[obj] = new_hash
|
|
301
|
+
adapter = create_adapter_from_object(obj)
|
|
302
|
+
adapter.update_item(self[oid], data_changed=data_changed)
|
|
303
|
+
|
|
304
|
+
def set_auto_refresh(self, auto_refresh: bool) -> None:
|
|
305
|
+
"""Set auto refresh mode.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
auto_refresh: if True, refresh plot items automatically
|
|
309
|
+
"""
|
|
310
|
+
self.__auto_refresh = auto_refresh
|
|
311
|
+
if auto_refresh:
|
|
312
|
+
self.refresh_plot("selected")
|
|
313
|
+
|
|
314
|
+
def set_show_first_only(self, show_first_only: bool) -> None:
|
|
315
|
+
"""Set show first only mode.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
show_first_only: if True, show only the first selected item
|
|
319
|
+
"""
|
|
320
|
+
self.__show_first_only = show_first_only
|
|
321
|
+
if self.__auto_refresh:
|
|
322
|
+
self.refresh_plot("selected")
|
|
323
|
+
|
|
324
|
+
def get_existing_oids(self) -> list[str]:
|
|
325
|
+
"""Get existing object uuids.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
List of object uuids that have a plot item associated to them.
|
|
329
|
+
"""
|
|
330
|
+
return list(self.__plotitems.keys())
|
|
331
|
+
|
|
332
|
+
def reduce_shown_oids(self, oids: list[str]) -> list[str]:
|
|
333
|
+
"""Reduce the number of shown objects to visible items only. The base
|
|
334
|
+
implementation is to show only the first selected item if the option
|
|
335
|
+
"Show first only" is enabled.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
oids: list of object uuids
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Reduced list of object uuids
|
|
342
|
+
"""
|
|
343
|
+
if self.__show_first_only:
|
|
344
|
+
return oids[:1]
|
|
345
|
+
return oids
|
|
346
|
+
|
|
347
|
+
def refresh_plot(
|
|
348
|
+
self,
|
|
349
|
+
what: str,
|
|
350
|
+
update_items: bool = True,
|
|
351
|
+
force: bool = False,
|
|
352
|
+
only_visible: bool = True,
|
|
353
|
+
only_existing: bool = False,
|
|
354
|
+
) -> None:
|
|
355
|
+
"""Refresh plot.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
what: string describing the objects to refresh.
|
|
359
|
+
Valid values are "selected" (refresh the selected objects),
|
|
360
|
+
"all" (refresh all objects), "existing" (refresh existing plot items),
|
|
361
|
+
or an object uuid.
|
|
362
|
+
update_items: if True, update the items.
|
|
363
|
+
If False, only show the items (do not update them).
|
|
364
|
+
Defaults to True.
|
|
365
|
+
force: if True, force refresh even if auto refresh is disabled.
|
|
366
|
+
Defaults to False.
|
|
367
|
+
only_visible: if True, only refresh visible items. Defaults to True.
|
|
368
|
+
Visible items are the ones that are not hidden by other items or the items
|
|
369
|
+
except the first one if the option "Show first only" is enabled.
|
|
370
|
+
This is useful for images, where the last image is the one that is shown.
|
|
371
|
+
If False, all items are refreshed.
|
|
372
|
+
only_existing: if True, only refresh existing items. Defaults to False.
|
|
373
|
+
Existing items are the ones that have already been created and are
|
|
374
|
+
associated to the object uuid. If False, create new items for the
|
|
375
|
+
objects that do not have an item yet.
|
|
376
|
+
|
|
377
|
+
Raises:
|
|
378
|
+
ValueError: if `what` is not a valid value
|
|
379
|
+
"""
|
|
380
|
+
if not self.__auto_refresh and not force:
|
|
381
|
+
return
|
|
382
|
+
if what == "selected":
|
|
383
|
+
# Refresh selected objects
|
|
384
|
+
oids = self.panel.objview.get_sel_object_uuids(include_groups=True)
|
|
385
|
+
if len(oids) == 1:
|
|
386
|
+
self.cleanup_dataview()
|
|
387
|
+
self.remove_all_shape_items()
|
|
388
|
+
for item in self:
|
|
389
|
+
if item is not None:
|
|
390
|
+
item.hide()
|
|
391
|
+
elif what == "existing":
|
|
392
|
+
# Refresh existing objects
|
|
393
|
+
oids = self.get_existing_oids()
|
|
394
|
+
elif what == "all":
|
|
395
|
+
# Refresh all objects
|
|
396
|
+
oids = self.panel.objmodel.get_object_ids()
|
|
397
|
+
else:
|
|
398
|
+
# Refresh a single object defined by its uuid
|
|
399
|
+
oids = [what]
|
|
400
|
+
try:
|
|
401
|
+
# Check if this is a valid object uuid
|
|
402
|
+
self.panel.objmodel.get_objects(oids)
|
|
403
|
+
except KeyError as exc:
|
|
404
|
+
raise ValueError(f"Invalid value for `what`: {what}") from exc
|
|
405
|
+
|
|
406
|
+
# Initialize titles and scales dictionaries
|
|
407
|
+
title_keys = ("title", "xlabel", "ylabel", "zlabel", "xunit", "yunit", "zunit")
|
|
408
|
+
titles_dict = {}
|
|
409
|
+
autoscale = False
|
|
410
|
+
scale_keys = (
|
|
411
|
+
"xscalelog",
|
|
412
|
+
"xscalemin",
|
|
413
|
+
"xscalemax",
|
|
414
|
+
"yscalelog",
|
|
415
|
+
"yscalemin",
|
|
416
|
+
"yscalemax",
|
|
417
|
+
)
|
|
418
|
+
scales_dict = {}
|
|
419
|
+
|
|
420
|
+
if oids:
|
|
421
|
+
if what != "existing" and only_visible:
|
|
422
|
+
# Remove hidden items from the list of objects to refresh
|
|
423
|
+
oids = self.reduce_shown_oids(oids)
|
|
424
|
+
with create_progress_bar(
|
|
425
|
+
self.panel, _("Creating plot items"), max_=len(oids)
|
|
426
|
+
) as progress:
|
|
427
|
+
# Iterate over objects
|
|
428
|
+
for i_obj, oid in enumerate(oids):
|
|
429
|
+
progress.setValue(i_obj + 1)
|
|
430
|
+
if progress.wasCanceled():
|
|
431
|
+
break
|
|
432
|
+
obj = self.panel.objmodel[oid]
|
|
433
|
+
|
|
434
|
+
# Collecting titles information
|
|
435
|
+
for key in title_keys:
|
|
436
|
+
title = getattr(obj, key, "")
|
|
437
|
+
value = titles_dict.get(key)
|
|
438
|
+
if value is None:
|
|
439
|
+
titles_dict[key] = title
|
|
440
|
+
elif value != title:
|
|
441
|
+
titles_dict[key] = ""
|
|
442
|
+
|
|
443
|
+
# Collecting scales information
|
|
444
|
+
autoscale = autoscale or obj.autoscale
|
|
445
|
+
for key in scale_keys:
|
|
446
|
+
scale = getattr(obj, key, None)
|
|
447
|
+
if scale is not None:
|
|
448
|
+
cmp = min if "min" in key else max
|
|
449
|
+
scales_dict[key] = cmp(scales_dict.get(key, scale), scale)
|
|
450
|
+
|
|
451
|
+
# Update or add item to plot
|
|
452
|
+
item = self.get(oid)
|
|
453
|
+
if item is None:
|
|
454
|
+
if only_existing:
|
|
455
|
+
continue
|
|
456
|
+
item = self.__add_item_to_plot(oid)
|
|
457
|
+
else:
|
|
458
|
+
self.__update_item_on_plot(oid, just_show=not update_items)
|
|
459
|
+
if what != "existing" or item.isVisible():
|
|
460
|
+
self.plot.set_item_visible(item, True, replot=False)
|
|
461
|
+
self.plot.set_active_item(item)
|
|
462
|
+
item.unselect()
|
|
463
|
+
|
|
464
|
+
# Add geometric shapes
|
|
465
|
+
self.add_shapes(oid, do_autoscale=autoscale)
|
|
466
|
+
|
|
467
|
+
self.plot.replot()
|
|
468
|
+
|
|
469
|
+
else:
|
|
470
|
+
# No object to refresh: clean up titles
|
|
471
|
+
for key in title_keys:
|
|
472
|
+
titles_dict[key] = ""
|
|
473
|
+
|
|
474
|
+
# Set titles
|
|
475
|
+
tdict = titles_dict
|
|
476
|
+
tdict["ylabel"] = (tdict["ylabel"], tdict.pop("zlabel"))
|
|
477
|
+
tdict["yunit"] = (tdict["yunit"], tdict.pop("zunit"))
|
|
478
|
+
self.plot.set_titles(**titles_dict)
|
|
479
|
+
|
|
480
|
+
# Set scales
|
|
481
|
+
replot = False
|
|
482
|
+
for axis_name, axis in (("bottom", "x"), ("left", "y")):
|
|
483
|
+
axis_id = self.plot.get_axis_id(axis_name)
|
|
484
|
+
scalelog = scales_dict.get(f"{axis}scalelog")
|
|
485
|
+
if scalelog is not None:
|
|
486
|
+
new_scale = "log" if scalelog else "lin"
|
|
487
|
+
self.plot.set_axis_scale(axis_id, new_scale, autoscale=False)
|
|
488
|
+
replot = True
|
|
489
|
+
if autoscale:
|
|
490
|
+
self.plot.do_autoscale()
|
|
491
|
+
else:
|
|
492
|
+
for axis_name, axis in (("bottom", "x"), ("left", "y")):
|
|
493
|
+
axis_id = self.plot.get_axis_id(axis_name)
|
|
494
|
+
new_vmin = scales_dict.get(f"{axis}scalemin")
|
|
495
|
+
new_vmax = scales_dict.get(f"{axis}scalemax")
|
|
496
|
+
if new_vmin is not None or new_vmax is not None:
|
|
497
|
+
self.plot.do_autoscale(replot=False, axis_id=axis_id)
|
|
498
|
+
vmin, vmax = self.plot.get_axis_limits(axis_id)
|
|
499
|
+
new_vmin = new_vmin if new_vmin is not None else vmin
|
|
500
|
+
new_vmax = new_vmax if new_vmax is not None else vmax
|
|
501
|
+
self.plot.set_axis_limits(axis_id, new_vmin, new_vmax)
|
|
502
|
+
replot = True
|
|
503
|
+
if replot:
|
|
504
|
+
self.plot.replot()
|
|
505
|
+
|
|
506
|
+
def cleanup_dataview(self) -> None:
|
|
507
|
+
"""Clean up data view"""
|
|
508
|
+
# Find items to remove (items that are not in the handler and not special items)
|
|
509
|
+
# Skip shape items since they're managed separately and checking membership
|
|
510
|
+
# for many items is expensive
|
|
511
|
+
shape_items_set = set(self.__shapeitems)
|
|
512
|
+
items_to_remove = [
|
|
513
|
+
item
|
|
514
|
+
for item in self.plot.items[:]
|
|
515
|
+
if item not in shape_items_set
|
|
516
|
+
and item not in self
|
|
517
|
+
and not isinstance(item, (LegendBoxItem, GridItem))
|
|
518
|
+
]
|
|
519
|
+
# Delete items one by one with error handling for items already removed
|
|
520
|
+
for item in items_to_remove:
|
|
521
|
+
try:
|
|
522
|
+
self.plot.del_item(item)
|
|
523
|
+
except ValueError:
|
|
524
|
+
# Item was already removed (e.g., by detach())
|
|
525
|
+
pass
|
|
526
|
+
|
|
527
|
+
def get_plot_options(self) -> PlotOptions:
|
|
528
|
+
"""Return standard signal/image plot options"""
|
|
529
|
+
return PlotOptions(
|
|
530
|
+
type=self.PLOT_TYPE,
|
|
531
|
+
xlabel=self.plot.get_axis_title("bottom"),
|
|
532
|
+
ylabel=self.plot.get_axis_title("left"),
|
|
533
|
+
xunit=self.plot.get_axis_unit("bottom"),
|
|
534
|
+
yunit=self.plot.get_axis_unit("left"),
|
|
535
|
+
show_axes_tab=False,
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
class SignalPlotHandler(BasePlotHandler[SignalObj, CurveItem]):
|
|
540
|
+
"""Object handling signal plot items, plot dialogs, plot options"""
|
|
541
|
+
|
|
542
|
+
PLOT_TYPE = PlotType.CURVE
|
|
543
|
+
|
|
544
|
+
def toggle_anti_aliasing(self, state: bool) -> None:
|
|
545
|
+
"""Toggle anti-aliasing
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
state: if True, enable anti-aliasing
|
|
549
|
+
"""
|
|
550
|
+
self.plot.set_antialiasing(state)
|
|
551
|
+
self.plot.replot()
|
|
552
|
+
|
|
553
|
+
def get_plot_options(self) -> PlotOptions:
|
|
554
|
+
"""Return standard signal/image plot options"""
|
|
555
|
+
options = super().get_plot_options()
|
|
556
|
+
options.curve_antialiasing = self.plot.antialiased
|
|
557
|
+
return options
|
|
558
|
+
|
|
559
|
+
def refresh_plot(
|
|
560
|
+
self,
|
|
561
|
+
what: str,
|
|
562
|
+
update_items: bool = True,
|
|
563
|
+
force: bool = False,
|
|
564
|
+
only_visible: bool = True,
|
|
565
|
+
only_existing: bool = False,
|
|
566
|
+
) -> None:
|
|
567
|
+
"""Refresh plot and configure datetime axis if needed.
|
|
568
|
+
|
|
569
|
+
This override adds automatic datetime axis configuration when at least one
|
|
570
|
+
of the displayed signals has a datetime X-axis.
|
|
571
|
+
|
|
572
|
+
Args:
|
|
573
|
+
what: string describing the objects to refresh
|
|
574
|
+
update_items: if True, update the items
|
|
575
|
+
force: if True, force refresh even if auto refresh is disabled
|
|
576
|
+
only_visible: if True, only refresh visible items
|
|
577
|
+
only_existing: if True, only refresh existing items
|
|
578
|
+
"""
|
|
579
|
+
# Call parent implementation
|
|
580
|
+
super().refresh_plot(what, update_items, force, only_visible, only_existing)
|
|
581
|
+
|
|
582
|
+
# Check if any visible signal has datetime X-axis
|
|
583
|
+
has_datetime = False
|
|
584
|
+
datetime_format = None
|
|
585
|
+
for item in self:
|
|
586
|
+
if item is not None and item.isVisible():
|
|
587
|
+
obj = self.get_obj_from_item(item)
|
|
588
|
+
if obj is not None and obj.is_x_datetime():
|
|
589
|
+
has_datetime = True
|
|
590
|
+
# Get format from signal metadata, or use configured default
|
|
591
|
+
datetime_format = obj.metadata.get("x_datetime_format")
|
|
592
|
+
if datetime_format is None:
|
|
593
|
+
# Use configured format based on time unit
|
|
594
|
+
unit = obj.xunit if obj.xunit else "s"
|
|
595
|
+
if unit in ("ns", "us", "ms"):
|
|
596
|
+
datetime_format = Conf.view.sig_datetime_format_ms.get(
|
|
597
|
+
"%H:%M:%S.%f"
|
|
598
|
+
)
|
|
599
|
+
else:
|
|
600
|
+
datetime_format = Conf.view.sig_datetime_format_s.get(
|
|
601
|
+
"%H:%M:%S"
|
|
602
|
+
)
|
|
603
|
+
break
|
|
604
|
+
|
|
605
|
+
# Configure X-axis for datetime or restore default
|
|
606
|
+
if has_datetime and datetime_format is not None:
|
|
607
|
+
self.plot.set_axis_datetime("bottom", format=datetime_format)
|
|
608
|
+
else:
|
|
609
|
+
# Restore default scale draw (remove datetime formatting)
|
|
610
|
+
self.plot.setAxisScaleDraw(self.plot.get_axis_id("bottom"), QwtScaleDraw())
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
class ImagePlotHandler(BasePlotHandler[ImageObj, MaskedXYImageItem]):
|
|
614
|
+
"""Object handling image plot items, plot dialogs, plot options"""
|
|
615
|
+
|
|
616
|
+
PLOT_TYPE = PlotType.IMAGE
|
|
617
|
+
|
|
618
|
+
def reduce_shown_oids(self, oids: list[str]) -> list[str]:
|
|
619
|
+
"""Reduce the number of shown objects to visible items only. The base
|
|
620
|
+
implementation is to show only the first selected item if the option
|
|
621
|
+
"Show first only" is enabled.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
oids: list of object uuids
|
|
625
|
+
|
|
626
|
+
Returns:
|
|
627
|
+
Reduced list of object uuids
|
|
628
|
+
"""
|
|
629
|
+
oids = super().reduce_shown_oids(oids)
|
|
630
|
+
|
|
631
|
+
# For Image View, we show only the last image (which is the highest z-order
|
|
632
|
+
# plot item) if more than one image is selected, if last image has no
|
|
633
|
+
# transparency and if the other images are all completely covered by the last
|
|
634
|
+
# image.
|
|
635
|
+
# TODO: [P4] Enhance this algorithm to handle more complex cases
|
|
636
|
+
# (not sure it's worth it)
|
|
637
|
+
if len(oids) > 1:
|
|
638
|
+
# Get objects associated to the oids
|
|
639
|
+
objs = self.panel.objmodel.get_objects(oids)
|
|
640
|
+
# First condition is about the image transparency
|
|
641
|
+
last_obj = objs[-1]
|
|
642
|
+
alpha_cond = (
|
|
643
|
+
last_obj.get_metadata_option("alpha", 1.0) == 1.0
|
|
644
|
+
and last_obj.get_metadata_option("alpha_function", 0) == 0
|
|
645
|
+
)
|
|
646
|
+
if alpha_cond:
|
|
647
|
+
# Second condition is about the image size and position
|
|
648
|
+
geom_cond = True
|
|
649
|
+
for obj in objs[:-1]:
|
|
650
|
+
# Handle both uniform and non-uniform coordinates
|
|
651
|
+
if last_obj.is_uniform_coords and obj.is_uniform_coords:
|
|
652
|
+
# Both have uniform coordinates, use old logic
|
|
653
|
+
geom_cond = (
|
|
654
|
+
geom_cond
|
|
655
|
+
and last_obj.x0 <= obj.x0
|
|
656
|
+
and last_obj.y0 <= obj.y0
|
|
657
|
+
and last_obj.x0 + last_obj.width >= obj.x0 + obj.width
|
|
658
|
+
and last_obj.y0 + last_obj.height >= obj.y0 + obj.height
|
|
659
|
+
)
|
|
660
|
+
else:
|
|
661
|
+
# At least one has non-uniform coordinates,
|
|
662
|
+
# use extent comparison
|
|
663
|
+
last_x0 = (
|
|
664
|
+
last_obj.x0
|
|
665
|
+
if last_obj.is_uniform_coords
|
|
666
|
+
else last_obj.xcoords[0]
|
|
667
|
+
)
|
|
668
|
+
last_y0 = (
|
|
669
|
+
last_obj.y0
|
|
670
|
+
if last_obj.is_uniform_coords
|
|
671
|
+
else last_obj.ycoords[0]
|
|
672
|
+
)
|
|
673
|
+
last_x1 = (
|
|
674
|
+
last_obj.x0 + last_obj.width
|
|
675
|
+
if last_obj.is_uniform_coords
|
|
676
|
+
else last_obj.xcoords[-1]
|
|
677
|
+
)
|
|
678
|
+
last_y1 = (
|
|
679
|
+
last_obj.y0 + last_obj.height
|
|
680
|
+
if last_obj.is_uniform_coords
|
|
681
|
+
else last_obj.ycoords[-1]
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
obj_x0 = obj.x0 if obj.is_uniform_coords else obj.xcoords[0]
|
|
685
|
+
obj_y0 = obj.y0 if obj.is_uniform_coords else obj.ycoords[0]
|
|
686
|
+
obj_x1 = (
|
|
687
|
+
obj.x0 + obj.width
|
|
688
|
+
if obj.is_uniform_coords
|
|
689
|
+
else obj.xcoords[-1]
|
|
690
|
+
)
|
|
691
|
+
obj_y1 = (
|
|
692
|
+
obj.y0 + obj.height
|
|
693
|
+
if obj.is_uniform_coords
|
|
694
|
+
else obj.ycoords[-1]
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
geom_cond = (
|
|
698
|
+
geom_cond
|
|
699
|
+
and last_x0 <= obj_x0
|
|
700
|
+
and last_y0 <= obj_y0
|
|
701
|
+
and last_x1 >= obj_x1
|
|
702
|
+
and last_y1 >= obj_y1
|
|
703
|
+
)
|
|
704
|
+
if not geom_cond:
|
|
705
|
+
break
|
|
706
|
+
if geom_cond:
|
|
707
|
+
oids = oids[-1:]
|
|
708
|
+
return oids
|
|
709
|
+
|
|
710
|
+
def refresh_plot(
|
|
711
|
+
self,
|
|
712
|
+
what: str,
|
|
713
|
+
update_items: bool = True,
|
|
714
|
+
force: bool = False,
|
|
715
|
+
only_visible: bool = True,
|
|
716
|
+
only_existing: bool = False,
|
|
717
|
+
) -> None:
|
|
718
|
+
"""Refresh plot.
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
what: string describing the objects to refresh.
|
|
722
|
+
Valid values are "selected" (refresh the selected objects),
|
|
723
|
+
"all" (refresh all objects), "existing" (refresh existing plot items),
|
|
724
|
+
or an object uuid.
|
|
725
|
+
update_items: if True, update the items.
|
|
726
|
+
If False, only show the items (do not update them).
|
|
727
|
+
Defaults to True.
|
|
728
|
+
force: if True, force refresh even if auto refresh is disabled.
|
|
729
|
+
Defaults to False.
|
|
730
|
+
only_visible: if True, only refresh visible items. Defaults to True.
|
|
731
|
+
Visible items are the ones that are not hidden by other items or the items
|
|
732
|
+
except the first one if the option "Show first only" is enabled.
|
|
733
|
+
This is useful for images, where the last image is the one that is shown.
|
|
734
|
+
If False, all items are refreshed.
|
|
735
|
+
only_existing: if True, only refresh existing items. Defaults to False.
|
|
736
|
+
Existing items are the ones that have already been created and are
|
|
737
|
+
associated to the object uuid. If False, create new items for the
|
|
738
|
+
objects that do not have an item yet.
|
|
739
|
+
|
|
740
|
+
Raises:
|
|
741
|
+
ValueError: if `what` is not a valid value
|
|
742
|
+
"""
|
|
743
|
+
super().refresh_plot(
|
|
744
|
+
what=what,
|
|
745
|
+
update_items=update_items,
|
|
746
|
+
force=force,
|
|
747
|
+
only_visible=only_visible,
|
|
748
|
+
only_existing=only_existing,
|
|
749
|
+
)
|
|
750
|
+
self.plotwidget.contrast.setVisible(Conf.view.show_contrast.get(True))
|
|
751
|
+
plot = self.plotwidget.get_plot()
|
|
752
|
+
new_aspect_ratio = current_aspect_ratio = plot.get_aspect_ratio()
|
|
753
|
+
new_lock = current_lock = plot.lock_aspect_ratio
|
|
754
|
+
if Conf.view.ima_aspect_ratio_1_1.get():
|
|
755
|
+
# Lock aspect ratio to 1:1
|
|
756
|
+
new_aspect_ratio = 1.0
|
|
757
|
+
new_lock = True
|
|
758
|
+
else:
|
|
759
|
+
# Use physical pixel size to set aspect ratio
|
|
760
|
+
# Determine which objects to check based on the 'what' parameter
|
|
761
|
+
if what == "selected":
|
|
762
|
+
# Use selected objects to determine aspect ratio
|
|
763
|
+
oids = self.panel.objview.get_sel_object_uuids(include_groups=True)
|
|
764
|
+
elif what == "existing":
|
|
765
|
+
# Use existing objects
|
|
766
|
+
oids = self.get_existing_oids()
|
|
767
|
+
elif what == "all":
|
|
768
|
+
# Use all objects
|
|
769
|
+
oids = self.panel.objmodel.get_object_ids()
|
|
770
|
+
else:
|
|
771
|
+
# Single object by uuid
|
|
772
|
+
oids = [what]
|
|
773
|
+
|
|
774
|
+
# Reduce to visible items and check aspect ratio
|
|
775
|
+
for oid in reversed(self.reduce_shown_oids(oids)):
|
|
776
|
+
if oid in self.get_existing_oids() and self.get(oid).isVisible():
|
|
777
|
+
obj: ImageObj = self.panel.objmodel[oid]
|
|
778
|
+
if obj.is_uniform_coords:
|
|
779
|
+
new_aspect_ratio = obj.dx / obj.dy
|
|
780
|
+
new_lock = True
|
|
781
|
+
else:
|
|
782
|
+
new_lock = False
|
|
783
|
+
break
|
|
784
|
+
if new_aspect_ratio != current_aspect_ratio or new_lock != current_lock:
|
|
785
|
+
# Update aspect ratio only if it has changed
|
|
786
|
+
plot.set_aspect_ratio(new_aspect_ratio, lock=new_lock)
|
|
787
|
+
plot.do_autoscale()
|
|
788
|
+
|
|
789
|
+
def cleanup_dataview(self) -> None:
|
|
790
|
+
"""Clean up data view"""
|
|
791
|
+
for widget in (self.plotwidget.xcsw, self.plotwidget.ycsw):
|
|
792
|
+
widget.hide()
|
|
793
|
+
super().cleanup_dataview()
|
|
794
|
+
|
|
795
|
+
def get_plot_options(self) -> PlotOptions:
|
|
796
|
+
"""Return standard signal/image plot options"""
|
|
797
|
+
options = super().get_plot_options()
|
|
798
|
+
options.zlabel = self.plot.get_axis_title("right")
|
|
799
|
+
options.zunit = self.plot.get_axis_unit("right")
|
|
800
|
+
return options
|