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,1109 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Interactive re-processing feature tests
|
|
5
|
+
|
|
6
|
+
Tests the end-to-end interactive processing workflow where users can
|
|
7
|
+
modify processing parameters and re-apply them to regenerate the result.
|
|
8
|
+
|
|
9
|
+
This includes:
|
|
10
|
+
- Parameter modification and re-application
|
|
11
|
+
- Parameter serialization/deserialization
|
|
12
|
+
- Processing metadata storage for 1-to-1 operations
|
|
13
|
+
- Handling of operations with and without parameters
|
|
14
|
+
|
|
15
|
+
Note: metadata_all_patterns_test.py verifies metadata storage for all
|
|
16
|
+
processing patterns (1-to-1, 2-to-1, n-to-1).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import numpy as np
|
|
24
|
+
from guidata.dataset import json_to_dataset
|
|
25
|
+
from guidata.qthelpers import qt_app_context, qt_wait
|
|
26
|
+
from sigima.objects import Gauss2DParam, GaussParam, create_image_roi
|
|
27
|
+
from sigima.params import (
|
|
28
|
+
BinningParam,
|
|
29
|
+
ConstantParam,
|
|
30
|
+
GaussianParam,
|
|
31
|
+
MovingAverageParam,
|
|
32
|
+
SignalsToImageParam,
|
|
33
|
+
)
|
|
34
|
+
from sigima.proc.image import RadialProfileParam
|
|
35
|
+
|
|
36
|
+
from datalab.gui.newobject import CREATION_PARAMETERS_OPTION
|
|
37
|
+
from datalab.gui.processor.base import PROCESSING_PARAMETERS_OPTION
|
|
38
|
+
from datalab.objectmodel import get_uuid
|
|
39
|
+
from datalab.tests import datalab_test_app_context
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_signal_interactive_processing():
|
|
43
|
+
"""Test interactive processing for signals"""
|
|
44
|
+
with qt_app_context():
|
|
45
|
+
with datalab_test_app_context() as win:
|
|
46
|
+
panel = win.signalpanel
|
|
47
|
+
processor = panel.processor
|
|
48
|
+
|
|
49
|
+
# Create a test signal
|
|
50
|
+
panel.new_object()
|
|
51
|
+
signal = panel.objview.get_current_object()
|
|
52
|
+
assert signal is not None
|
|
53
|
+
|
|
54
|
+
# Apply a Gaussian filter (which has parameters)
|
|
55
|
+
param = GaussianParam.create(sigma=2.0)
|
|
56
|
+
processor.compute_1_to_1(
|
|
57
|
+
processor.get_feature("gaussian_filter").function,
|
|
58
|
+
param=param,
|
|
59
|
+
title="Gaussian filter",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Get the filtered signal
|
|
63
|
+
filtered_sig = panel.objview.get_current_object()
|
|
64
|
+
assert filtered_sig is not None
|
|
65
|
+
assert filtered_sig != signal
|
|
66
|
+
|
|
67
|
+
# Check that processing metadata was stored
|
|
68
|
+
assert PROCESSING_PARAMETERS_OPTION in filtered_sig.get_metadata_options()
|
|
69
|
+
option_dict = filtered_sig.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
70
|
+
|
|
71
|
+
# Verify metadata content
|
|
72
|
+
assert option_dict["source_uuid"] == get_uuid(signal)
|
|
73
|
+
assert option_dict["func_name"] == "gaussian_filter"
|
|
74
|
+
|
|
75
|
+
# Verify the parameter can be deserialized
|
|
76
|
+
stored_param = json_to_dataset(option_dict["param_json"])
|
|
77
|
+
assert stored_param.sigma == 2.0
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_image_interactive_processing():
|
|
81
|
+
"""Test interactive processing for images"""
|
|
82
|
+
with qt_app_context():
|
|
83
|
+
with datalab_test_app_context() as win:
|
|
84
|
+
panel = win.imagepanel
|
|
85
|
+
processor = panel.processor
|
|
86
|
+
|
|
87
|
+
# Create a test image
|
|
88
|
+
panel.new_object()
|
|
89
|
+
image = panel.objview.get_current_object()
|
|
90
|
+
assert image is not None
|
|
91
|
+
|
|
92
|
+
# Apply a moving average filter (which has parameters)
|
|
93
|
+
param = MovingAverageParam.create(n=5)
|
|
94
|
+
processor.compute_1_to_1(
|
|
95
|
+
processor.get_feature("moving_average").function,
|
|
96
|
+
param=param,
|
|
97
|
+
title="Moving average",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Get the filtered image
|
|
101
|
+
filtered_ima = panel.objview.get_current_object()
|
|
102
|
+
assert filtered_ima is not None
|
|
103
|
+
assert filtered_ima != image
|
|
104
|
+
|
|
105
|
+
# Check that processing metadata was stored
|
|
106
|
+
assert PROCESSING_PARAMETERS_OPTION in filtered_ima.get_metadata_options()
|
|
107
|
+
option_dict = filtered_ima.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
108
|
+
|
|
109
|
+
# Verify metadata content
|
|
110
|
+
assert option_dict["source_uuid"] == get_uuid(image)
|
|
111
|
+
assert option_dict["func_name"] == "moving_average"
|
|
112
|
+
|
|
113
|
+
# Verify the parameter can be deserialized
|
|
114
|
+
stored_param = json_to_dataset(option_dict["param_json"])
|
|
115
|
+
assert stored_param.n == 5
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_processing_without_parameters():
|
|
119
|
+
"""Test that processing without parameters doesn't store metadata"""
|
|
120
|
+
with qt_app_context():
|
|
121
|
+
with datalab_test_app_context() as win:
|
|
122
|
+
panel = win.signalpanel
|
|
123
|
+
processor = panel.processor
|
|
124
|
+
|
|
125
|
+
# Create a test signal
|
|
126
|
+
panel.new_object()
|
|
127
|
+
signal = panel.objview.get_current_object()
|
|
128
|
+
assert signal is not None
|
|
129
|
+
|
|
130
|
+
# Apply absolute value (which has NO parameters)
|
|
131
|
+
processor.compute_1_to_1(
|
|
132
|
+
processor.get_feature("absolute").function,
|
|
133
|
+
title="Absolute value",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Get the result signal
|
|
137
|
+
result_signal = panel.objview.get_current_object()
|
|
138
|
+
assert result_signal is not None
|
|
139
|
+
assert result_signal != signal
|
|
140
|
+
|
|
141
|
+
# Check that processing metadata was NOT stored
|
|
142
|
+
assert PROCESSING_PARAMETERS_OPTION in result_signal.get_metadata_options()
|
|
143
|
+
option_dict = result_signal.get_metadata_option(
|
|
144
|
+
PROCESSING_PARAMETERS_OPTION
|
|
145
|
+
)
|
|
146
|
+
assert "param_json" not in option_dict
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_recompute():
|
|
150
|
+
"""Test recompute feature for signals"""
|
|
151
|
+
with qt_app_context():
|
|
152
|
+
with datalab_test_app_context() as win:
|
|
153
|
+
panel = win.signalpanel
|
|
154
|
+
processor = panel.processor
|
|
155
|
+
|
|
156
|
+
# Create a test signal
|
|
157
|
+
panel.new_object()
|
|
158
|
+
signal = panel.objview.get_current_object()
|
|
159
|
+
signal_uuid = get_uuid(signal)
|
|
160
|
+
|
|
161
|
+
# Apply a Gaussian filter with initial parameters
|
|
162
|
+
param = GaussianParam.create(sigma=2.0)
|
|
163
|
+
processor.run_feature("gaussian_filter", param=param)
|
|
164
|
+
filtered_sig = panel.objview.get_current_object()
|
|
165
|
+
original_data = filtered_sig.y.copy()
|
|
166
|
+
|
|
167
|
+
# Recompute with different input signal data
|
|
168
|
+
constant = 1.23098765
|
|
169
|
+
signal.y += constant
|
|
170
|
+
panel.recompute_processing()
|
|
171
|
+
|
|
172
|
+
assert np.allclose(filtered_sig.y, original_data + constant)
|
|
173
|
+
|
|
174
|
+
# Verify metadata is correct
|
|
175
|
+
assert PROCESSING_PARAMETERS_OPTION in filtered_sig.get_metadata_options()
|
|
176
|
+
option_dict = filtered_sig.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
177
|
+
assert option_dict["source_uuid"] == signal_uuid
|
|
178
|
+
assert option_dict["func_name"] == "gaussian_filter"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_apply_creation_parameters_signal():
|
|
182
|
+
"""Test apply_creation_parameters for signals"""
|
|
183
|
+
with qt_app_context():
|
|
184
|
+
with datalab_test_app_context() as win:
|
|
185
|
+
panel = win.signalpanel
|
|
186
|
+
objprop = panel.objprop
|
|
187
|
+
|
|
188
|
+
# Create a signal with specific parameters
|
|
189
|
+
param = GaussParam.create(mu=250.0, sigma=20.0, a=100.0, y0=0.0, size=500)
|
|
190
|
+
panel.new_object(param=param, edit=False)
|
|
191
|
+
signal = panel.objview.get_current_object()
|
|
192
|
+
assert signal is not None
|
|
193
|
+
signal_uuid = get_uuid(signal)
|
|
194
|
+
|
|
195
|
+
# Verify the Creation tab was set up
|
|
196
|
+
assert objprop.creation_param_editor is not None
|
|
197
|
+
original_data = signal.y.copy()
|
|
198
|
+
|
|
199
|
+
# Modify the creation parameters in the editor
|
|
200
|
+
editor = objprop.creation_param_editor
|
|
201
|
+
# Change the Gaussian parameters to get a predictable result
|
|
202
|
+
editor.dataset.a = 200.0 # Double the amplitude from 100.0 to 200.0
|
|
203
|
+
|
|
204
|
+
# Apply the new creation parameters
|
|
205
|
+
objprop.apply_creation_parameters()
|
|
206
|
+
|
|
207
|
+
# Verify the signal was updated in-place (same UUID)
|
|
208
|
+
updated_signal = panel.objview.get_current_object()
|
|
209
|
+
assert get_uuid(updated_signal) == signal_uuid
|
|
210
|
+
|
|
211
|
+
# Get the updated creation parameters from metadata
|
|
212
|
+
creation_param_json = updated_signal.get_metadata_option(
|
|
213
|
+
CREATION_PARAMETERS_OPTION
|
|
214
|
+
)
|
|
215
|
+
updated_param = json_to_dataset(creation_param_json)
|
|
216
|
+
|
|
217
|
+
# Verify the parameter was actually updated in metadata
|
|
218
|
+
assert updated_param.a == 200.0
|
|
219
|
+
|
|
220
|
+
# Verify the data has changed
|
|
221
|
+
# Since we're working with very small Gaussian values,
|
|
222
|
+
# just verify they're different
|
|
223
|
+
assert not np.array_equal(updated_signal.y, original_data)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_apply_creation_parameters_image():
|
|
227
|
+
"""Test apply_creation_parameters for images"""
|
|
228
|
+
with qt_app_context():
|
|
229
|
+
with datalab_test_app_context() as win:
|
|
230
|
+
panel = win.imagepanel
|
|
231
|
+
objprop = panel.objprop
|
|
232
|
+
|
|
233
|
+
# Create an image with specific parameters (using a derived class
|
|
234
|
+
# of NewImageParam) to ensure creation parameters are stored in metadata
|
|
235
|
+
param = Gauss2DParam.create(x0=50.0, y0=50.0, sigma=10.0, a=100.0)
|
|
236
|
+
panel.new_object(param=param, edit=False)
|
|
237
|
+
image = panel.objview.get_current_object()
|
|
238
|
+
assert image is not None
|
|
239
|
+
|
|
240
|
+
# Verify the Creation tab was set up
|
|
241
|
+
assert objprop.creation_param_editor is not None
|
|
242
|
+
original_data = image.data.copy()
|
|
243
|
+
|
|
244
|
+
# Modify the parameters in the editor to create a visibly different image
|
|
245
|
+
editor = objprop.creation_param_editor
|
|
246
|
+
# Change the amplitude to make it clearly different
|
|
247
|
+
editor.dataset.a = 200.0 # Double the amplitude from 100.0 to 200.0
|
|
248
|
+
|
|
249
|
+
# Apply the new parameters
|
|
250
|
+
objprop.apply_creation_parameters()
|
|
251
|
+
|
|
252
|
+
# Verify the image was updated in-place (same UUID)
|
|
253
|
+
updated_image = panel.objview.get_current_object()
|
|
254
|
+
assert get_uuid(updated_image) == get_uuid(image)
|
|
255
|
+
|
|
256
|
+
# Verify the data has changed (amplitude doubled)
|
|
257
|
+
assert not np.array_equal(updated_image.data, original_data)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_no_duplicate_creation_tabs():
|
|
261
|
+
"""Test that applying creation parameters multiple times doesn't create
|
|
262
|
+
duplicate tabs.
|
|
263
|
+
|
|
264
|
+
This test verifies the fix for the bug where clicking "Apply" in the
|
|
265
|
+
Creation tab would create a new Creation tab instead of reusing the
|
|
266
|
+
existing one. It also verifies that the Creation tab remains current
|
|
267
|
+
after applying changes.
|
|
268
|
+
"""
|
|
269
|
+
with qt_app_context():
|
|
270
|
+
with datalab_test_app_context() as win:
|
|
271
|
+
panel = win.imagepanel
|
|
272
|
+
objprop = panel.objprop
|
|
273
|
+
|
|
274
|
+
# Create an image with creation parameters
|
|
275
|
+
param = Gauss2DParam.create(x0=50.0, y0=50.0, sigma=10.0, a=100.0)
|
|
276
|
+
panel.new_object(param=param, edit=False)
|
|
277
|
+
image = panel.objview.get_current_object()
|
|
278
|
+
assert image is not None
|
|
279
|
+
|
|
280
|
+
# Verify Creation tab was set up
|
|
281
|
+
assert objprop.creation_param_editor is not None
|
|
282
|
+
assert objprop.creation_scroll is not None
|
|
283
|
+
|
|
284
|
+
# Count how many Creation tabs exist initially using the widget reference
|
|
285
|
+
initial_index = objprop.tabwidget.indexOf(objprop.creation_scroll)
|
|
286
|
+
assert initial_index >= 0, "Creation tab should be present"
|
|
287
|
+
|
|
288
|
+
# Count tabs by checking if they reference the same scroll widget
|
|
289
|
+
initial_count = sum(
|
|
290
|
+
1
|
|
291
|
+
for i in range(objprop.tabwidget.count())
|
|
292
|
+
if objprop.tabwidget.widget(i) is objprop.creation_scroll
|
|
293
|
+
)
|
|
294
|
+
assert initial_count == 1, "Should have exactly one Creation tab initially"
|
|
295
|
+
|
|
296
|
+
# Apply creation parameters multiple times
|
|
297
|
+
editor = objprop.creation_param_editor
|
|
298
|
+
for amplitude in [150.0, 200.0, 250.0]:
|
|
299
|
+
editor.dataset.a = amplitude
|
|
300
|
+
objprop.apply_creation_parameters()
|
|
301
|
+
|
|
302
|
+
# Wait for the deferred setup_creation_tab to complete
|
|
303
|
+
qt_wait(0.1)
|
|
304
|
+
|
|
305
|
+
# Verify that creation_scroll reference still exists
|
|
306
|
+
assert objprop.creation_scroll is not None
|
|
307
|
+
|
|
308
|
+
# Count Creation tabs again - should still be just one
|
|
309
|
+
creation_count = sum(
|
|
310
|
+
1
|
|
311
|
+
for i in range(objprop.tabwidget.count())
|
|
312
|
+
if objprop.tabwidget.widget(i) is objprop.creation_scroll
|
|
313
|
+
)
|
|
314
|
+
assert creation_count == 1, (
|
|
315
|
+
f"Should still have exactly one Creation tab after "
|
|
316
|
+
f"applying amplitude={amplitude}"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Verify that the Creation tab is the current tab
|
|
320
|
+
assert objprop.tabwidget.currentWidget() is objprop.creation_scroll, (
|
|
321
|
+
f"Creation tab should remain current after "
|
|
322
|
+
f"applying amplitude={amplitude}"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def test_no_creation_parameters_for_base_classes():
|
|
327
|
+
"""Test that creation parameters are NOT stored for base classes
|
|
328
|
+
|
|
329
|
+
This test verifies the behavior introduced by the patch that only stores
|
|
330
|
+
creation parameters for derived classes of NewSignalParam/NewImageParam,
|
|
331
|
+
not for the base classes themselves.
|
|
332
|
+
"""
|
|
333
|
+
with qt_app_context():
|
|
334
|
+
with datalab_test_app_context() as win:
|
|
335
|
+
# Test with signals
|
|
336
|
+
signal_panel = win.signalpanel
|
|
337
|
+
signal_objprop = signal_panel.objprop
|
|
338
|
+
|
|
339
|
+
# Create a signal using default new_object() (uses base NewSignalParam)
|
|
340
|
+
signal_panel.new_object(edit=False)
|
|
341
|
+
signal = signal_panel.objview.get_current_object()
|
|
342
|
+
assert signal is not None
|
|
343
|
+
|
|
344
|
+
# Verify the Creation tab was NOT set up (no creation parameters stored)
|
|
345
|
+
assert signal_objprop.creation_param_editor is None
|
|
346
|
+
|
|
347
|
+
# Verify that CREATION_PARAMETERS_OPTION is not in metadata
|
|
348
|
+
assert CREATION_PARAMETERS_OPTION not in signal.get_metadata_options()
|
|
349
|
+
|
|
350
|
+
# Test with images
|
|
351
|
+
image_panel = win.imagepanel
|
|
352
|
+
image_objprop = image_panel.objprop
|
|
353
|
+
|
|
354
|
+
# Create an image using default new_object() (uses base NewImageParam)
|
|
355
|
+
image_panel.new_object(edit=False)
|
|
356
|
+
image = image_panel.objview.get_current_object()
|
|
357
|
+
assert image is not None
|
|
358
|
+
|
|
359
|
+
# Verify the Creation tab was NOT set up (no creation parameters stored)
|
|
360
|
+
assert image_objprop.creation_param_editor is None
|
|
361
|
+
|
|
362
|
+
# Verify that CREATION_PARAMETERS_OPTION is not in metadata
|
|
363
|
+
assert CREATION_PARAMETERS_OPTION not in image.get_metadata_options()
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def test_apply_processing_parameters_signal():
|
|
367
|
+
"""Test apply_processing_parameters for signals"""
|
|
368
|
+
with qt_app_context():
|
|
369
|
+
with datalab_test_app_context() as win:
|
|
370
|
+
panel = win.signalpanel
|
|
371
|
+
processor = panel.processor
|
|
372
|
+
objprop = panel.objprop
|
|
373
|
+
|
|
374
|
+
# Create a test signal with some structure
|
|
375
|
+
param = GaussParam.create(mu=250.0, sigma=20.0, a=100.0, y0=10.0, size=500)
|
|
376
|
+
panel.new_object(param=param, edit=False)
|
|
377
|
+
signal = panel.objview.get_current_object()
|
|
378
|
+
assert signal is not None
|
|
379
|
+
signal_uuid = get_uuid(signal)
|
|
380
|
+
original_signal_data = signal.y.copy()
|
|
381
|
+
|
|
382
|
+
# Apply addition_constant with initial value
|
|
383
|
+
v0 = 5.0
|
|
384
|
+
processor.run_feature("addition_constant", ConstantParam.create(value=v0))
|
|
385
|
+
|
|
386
|
+
# Get the processed signal
|
|
387
|
+
processed_sig = panel.objview.get_current_object()
|
|
388
|
+
assert processed_sig is not None
|
|
389
|
+
processed_uuid = get_uuid(processed_sig)
|
|
390
|
+
|
|
391
|
+
# Verify initial constant was applied: data should be original + 5.0
|
|
392
|
+
assert np.allclose(processed_sig.y, original_signal_data + v0)
|
|
393
|
+
|
|
394
|
+
# Select the processed signal to trigger setup_processing_tab
|
|
395
|
+
panel.objview.set_current_object(processed_sig)
|
|
396
|
+
|
|
397
|
+
# Verify the Processing tab was set up
|
|
398
|
+
assert objprop.processing_param_editor is not None
|
|
399
|
+
|
|
400
|
+
# Modify the processing parameters
|
|
401
|
+
editor = objprop.processing_param_editor
|
|
402
|
+
# Change constant from 5.0 to 15.0
|
|
403
|
+
editor.dataset.value = v1 = 15.0
|
|
404
|
+
|
|
405
|
+
# Apply the new processing parameters
|
|
406
|
+
report = objprop.apply_processing_parameters()
|
|
407
|
+
|
|
408
|
+
# Verify the operation succeeded
|
|
409
|
+
assert report.success, f"Reprocessing failed: {report.message}"
|
|
410
|
+
assert report.obj_uuid == processed_uuid
|
|
411
|
+
|
|
412
|
+
# Verify the object UUID didn't change (in-place update)
|
|
413
|
+
assert get_uuid(processed_sig) == processed_uuid
|
|
414
|
+
|
|
415
|
+
# Verify the new constant was applied: data should now be original + 15.0
|
|
416
|
+
assert np.allclose(processed_sig.y, original_signal_data + v1)
|
|
417
|
+
|
|
418
|
+
# Verify metadata still points to the same source
|
|
419
|
+
pp_dict = processed_sig.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
420
|
+
assert pp_dict["source_uuid"] == signal_uuid
|
|
421
|
+
assert pp_dict["func_name"] == "addition_constant"
|
|
422
|
+
|
|
423
|
+
# Verify the parameter was updated
|
|
424
|
+
stored_param = json_to_dataset(pp_dict["param_json"])
|
|
425
|
+
assert stored_param.value == v1
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def test_apply_processing_parameters_image():
|
|
429
|
+
"""Test apply_processing_parameters for images"""
|
|
430
|
+
with qt_app_context():
|
|
431
|
+
with datalab_test_app_context() as win:
|
|
432
|
+
panel = win.imagepanel
|
|
433
|
+
processor = panel.processor
|
|
434
|
+
objprop = panel.objprop
|
|
435
|
+
|
|
436
|
+
# Create a default test image
|
|
437
|
+
panel.new_object(edit=False)
|
|
438
|
+
image = panel.objview.get_current_object()
|
|
439
|
+
assert image is not None
|
|
440
|
+
image_uuid = get_uuid(image)
|
|
441
|
+
original_image_data = image.data.copy()
|
|
442
|
+
|
|
443
|
+
# Apply addition_constant with initial value
|
|
444
|
+
v0 = 7.0
|
|
445
|
+
processor.run_feature("addition_constant", ConstantParam.create(value=v0))
|
|
446
|
+
|
|
447
|
+
# Get the processed image
|
|
448
|
+
processed_ima = panel.objview.get_current_object()
|
|
449
|
+
assert processed_ima is not None
|
|
450
|
+
processed_uuid = get_uuid(processed_ima)
|
|
451
|
+
|
|
452
|
+
# Verify initial constant was applied: data should be original + 7.0
|
|
453
|
+
assert np.allclose(processed_ima.data, original_image_data + v0)
|
|
454
|
+
|
|
455
|
+
# Select the processed image to trigger setup_processing_tab
|
|
456
|
+
panel.objview.set_current_object(processed_ima)
|
|
457
|
+
|
|
458
|
+
# Verify the Processing tab was set up
|
|
459
|
+
assert objprop.processing_param_editor is not None
|
|
460
|
+
|
|
461
|
+
# Modify the processing parameters
|
|
462
|
+
editor = objprop.processing_param_editor
|
|
463
|
+
# Change constant from 7.0 to 20.0
|
|
464
|
+
editor.dataset.value = v1 = 20.0
|
|
465
|
+
|
|
466
|
+
# Apply the new processing parameters
|
|
467
|
+
report = objprop.apply_processing_parameters()
|
|
468
|
+
|
|
469
|
+
# Verify the operation succeeded
|
|
470
|
+
assert report.success, f"Reprocessing failed: {report.message}"
|
|
471
|
+
assert report.obj_uuid == processed_uuid
|
|
472
|
+
|
|
473
|
+
# Verify the object UUID didn't change (in-place update)
|
|
474
|
+
assert get_uuid(processed_ima) == processed_uuid
|
|
475
|
+
|
|
476
|
+
# Verify the new constant was applied: data should now be original + 20.0
|
|
477
|
+
assert np.allclose(processed_ima.data, original_image_data + v1)
|
|
478
|
+
|
|
479
|
+
# Verify metadata still points to the same source
|
|
480
|
+
pp_dict = processed_ima.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
481
|
+
assert pp_dict["source_uuid"] == image_uuid
|
|
482
|
+
assert pp_dict["func_name"] == "addition_constant"
|
|
483
|
+
|
|
484
|
+
# Verify the parameter was updated
|
|
485
|
+
stored_param = json_to_dataset(pp_dict["param_json"])
|
|
486
|
+
assert stored_param.value == v1
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def test_no_duplicate_processing_tabs():
|
|
490
|
+
"""Test that applying processing parameters multiple times doesn't create
|
|
491
|
+
duplicate tabs.
|
|
492
|
+
|
|
493
|
+
This test verifies the fix for the bug where clicking "Apply" in the
|
|
494
|
+
Processing tab would create a new Processing tab instead of reusing the
|
|
495
|
+
existing one. It also verifies that the Processing tab remains current
|
|
496
|
+
after applying changes.
|
|
497
|
+
"""
|
|
498
|
+
with qt_app_context():
|
|
499
|
+
with datalab_test_app_context() as win:
|
|
500
|
+
panel = win.imagepanel
|
|
501
|
+
processor = panel.processor
|
|
502
|
+
objprop = panel.objprop
|
|
503
|
+
|
|
504
|
+
# Create a default test image
|
|
505
|
+
panel.new_object(edit=False)
|
|
506
|
+
image = panel.objview.get_current_object()
|
|
507
|
+
assert image is not None
|
|
508
|
+
|
|
509
|
+
# Apply addition_constant with initial value
|
|
510
|
+
v0 = 7.0
|
|
511
|
+
processor.run_feature("addition_constant", ConstantParam.create(value=v0))
|
|
512
|
+
|
|
513
|
+
# Get the processed image
|
|
514
|
+
processed_ima = panel.objview.get_current_object()
|
|
515
|
+
assert processed_ima is not None
|
|
516
|
+
|
|
517
|
+
# Select the processed image to trigger setup_processing_tab
|
|
518
|
+
panel.objview.set_current_object(processed_ima)
|
|
519
|
+
|
|
520
|
+
# Verify Processing tab was set up
|
|
521
|
+
assert objprop.processing_param_editor is not None
|
|
522
|
+
assert objprop.processing_scroll is not None
|
|
523
|
+
|
|
524
|
+
# Count how many Processing tabs exist initially
|
|
525
|
+
initial_index = objprop.tabwidget.indexOf(objprop.processing_scroll)
|
|
526
|
+
assert initial_index >= 0, "Processing tab should be present"
|
|
527
|
+
|
|
528
|
+
# Count tabs by checking if they reference the same scroll widget
|
|
529
|
+
initial_count = sum(
|
|
530
|
+
1
|
|
531
|
+
for i in range(objprop.tabwidget.count())
|
|
532
|
+
if objprop.tabwidget.widget(i) is objprop.processing_scroll
|
|
533
|
+
)
|
|
534
|
+
assert initial_count == 1, (
|
|
535
|
+
"Should have exactly one Processing tab initially"
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
# Apply processing parameters multiple times
|
|
539
|
+
editor = objprop.processing_param_editor
|
|
540
|
+
for value in [10.0, 15.0, 20.0]:
|
|
541
|
+
editor.dataset.value = value
|
|
542
|
+
report = objprop.apply_processing_parameters()
|
|
543
|
+
assert report.success
|
|
544
|
+
|
|
545
|
+
# Wait for the deferred setup_processing_tab to complete
|
|
546
|
+
qt_wait(0.1)
|
|
547
|
+
|
|
548
|
+
# Verify that processing_scroll reference still exists
|
|
549
|
+
assert objprop.processing_scroll is not None
|
|
550
|
+
|
|
551
|
+
# Count Processing tabs again - should still be just one
|
|
552
|
+
processing_count = sum(
|
|
553
|
+
1
|
|
554
|
+
for i in range(objprop.tabwidget.count())
|
|
555
|
+
if objprop.tabwidget.widget(i) is objprop.processing_scroll
|
|
556
|
+
)
|
|
557
|
+
assert processing_count == 1, (
|
|
558
|
+
f"Should still have exactly one Processing tab after "
|
|
559
|
+
f"applying value={value}"
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
# Verify that the Processing tab is the current tab
|
|
563
|
+
assert objprop.tabwidget.currentWidget() is objprop.processing_scroll, (
|
|
564
|
+
f"Processing tab should remain current after applying value={value}"
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def test_apply_processing_parameters_missing_source():
|
|
569
|
+
"""Test apply_processing_parameters when source object is missing"""
|
|
570
|
+
with qt_app_context():
|
|
571
|
+
with datalab_test_app_context() as win:
|
|
572
|
+
panel = win.signalpanel
|
|
573
|
+
processor = panel.processor
|
|
574
|
+
objprop = panel.objprop
|
|
575
|
+
|
|
576
|
+
# Create a test signal with actual data
|
|
577
|
+
param = GaussParam.create(mu=250.0, sigma=20.0, a=100.0, y0=10.0, size=500)
|
|
578
|
+
panel.new_object(param=param, edit=False)
|
|
579
|
+
signal = panel.objview.get_current_object()
|
|
580
|
+
|
|
581
|
+
# Apply a Gaussian filter
|
|
582
|
+
filter_param = GaussianParam.create(sigma=2.0)
|
|
583
|
+
processor.compute_1_to_1(
|
|
584
|
+
processor.get_feature("gaussian_filter").function,
|
|
585
|
+
param=filter_param,
|
|
586
|
+
title="Gaussian filter",
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
# Get the filtered signal
|
|
590
|
+
filtered_sig = panel.objview.get_current_object()
|
|
591
|
+
|
|
592
|
+
# Delete the source signal
|
|
593
|
+
panel.objview.set_current_object(signal)
|
|
594
|
+
panel.remove_object(force=True)
|
|
595
|
+
|
|
596
|
+
# Select the filtered signal
|
|
597
|
+
panel.objview.set_current_object(filtered_sig)
|
|
598
|
+
|
|
599
|
+
# Try to apply processing parameters
|
|
600
|
+
report = objprop.apply_processing_parameters(interactive=False)
|
|
601
|
+
|
|
602
|
+
# Verify the operation failed with appropriate message
|
|
603
|
+
assert not report.success
|
|
604
|
+
# Check for English or French message
|
|
605
|
+
assert (
|
|
606
|
+
"no longer exists" in report.message.lower()
|
|
607
|
+
or "n'existe plus" in report.message.lower()
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def test_cross_panel_image_to_signal():
|
|
612
|
+
"""Test cross-panel processing: Image → Signal (radial profile)"""
|
|
613
|
+
with qt_app_context():
|
|
614
|
+
with datalab_test_app_context() as win:
|
|
615
|
+
image_panel = win.imagepanel
|
|
616
|
+
signal_panel = win.signalpanel
|
|
617
|
+
image_processor = image_panel.processor
|
|
618
|
+
|
|
619
|
+
# Create a test image with a Gaussian peak
|
|
620
|
+
image_param = Gauss2DParam.create(
|
|
621
|
+
x0=50.0, y0=50.0, sigma=10.0, a=100.0, height=100, width=100
|
|
622
|
+
)
|
|
623
|
+
image_panel.new_object(param=image_param, edit=False)
|
|
624
|
+
image = image_panel.objview.get_current_object()
|
|
625
|
+
assert image is not None
|
|
626
|
+
image_uuid = get_uuid(image)
|
|
627
|
+
|
|
628
|
+
# Apply radial_profile (Image → Signal cross-panel computation)
|
|
629
|
+
profile_param = RadialProfileParam.create(x0=50, y0=50)
|
|
630
|
+
image_processor.run_feature("radial_profile", param=profile_param)
|
|
631
|
+
|
|
632
|
+
# Verify the result is now in the signal panel
|
|
633
|
+
signal = signal_panel.objview.get_current_object()
|
|
634
|
+
assert signal is not None
|
|
635
|
+
|
|
636
|
+
# Check that processing metadata was stored
|
|
637
|
+
assert PROCESSING_PARAMETERS_OPTION in signal.get_metadata_options()
|
|
638
|
+
option_dict = signal.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
639
|
+
|
|
640
|
+
# Verify metadata content
|
|
641
|
+
assert option_dict["source_uuid"] == image_uuid
|
|
642
|
+
assert option_dict["func_name"] == "radial_profile"
|
|
643
|
+
assert option_dict["pattern"] == "1-to-1"
|
|
644
|
+
|
|
645
|
+
# Verify the parameter can be deserialized
|
|
646
|
+
stored_param = json_to_dataset(option_dict["param_json"])
|
|
647
|
+
assert stored_param.x0 == 50
|
|
648
|
+
assert stored_param.y0 == 50
|
|
649
|
+
|
|
650
|
+
# Test that the Processing tab is set up correctly
|
|
651
|
+
# (even though source is in a different panel)
|
|
652
|
+
signal_panel.objview.set_current_object(signal)
|
|
653
|
+
assert signal_panel.objprop.processing_param_editor is not None
|
|
654
|
+
|
|
655
|
+
# Test modifying processing parameters
|
|
656
|
+
editor = signal_panel.objprop.processing_param_editor
|
|
657
|
+
# Change the center position
|
|
658
|
+
editor.dataset.x0 = 40
|
|
659
|
+
editor.dataset.y0 = 40
|
|
660
|
+
|
|
661
|
+
# Apply the new processing parameters
|
|
662
|
+
report = signal_panel.objprop.apply_processing_parameters()
|
|
663
|
+
|
|
664
|
+
# Verify the operation succeeded
|
|
665
|
+
assert report.success, f"Cross-panel reprocessing failed: {report.message}"
|
|
666
|
+
|
|
667
|
+
# Verify the parameter was updated in metadata
|
|
668
|
+
updated_dict = signal.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
669
|
+
updated_param = json_to_dataset(updated_dict["param_json"])
|
|
670
|
+
assert updated_param.x0 == 40
|
|
671
|
+
assert updated_param.y0 == 40
|
|
672
|
+
|
|
673
|
+
# Test recompute feature with modified source image
|
|
674
|
+
image.data *= 2.0 # Double the image intensity
|
|
675
|
+
original_signal_data = signal.y.copy()
|
|
676
|
+
|
|
677
|
+
# Recompute the radial profile
|
|
678
|
+
signal_panel.recompute_processing()
|
|
679
|
+
|
|
680
|
+
# The signal should have changed (doubled intensity)
|
|
681
|
+
assert not np.allclose(signal.y, original_signal_data)
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def test_cross_panel_image_to_signal_group():
|
|
685
|
+
"""Test cross-panel processing with groups: Image group → Signal group"""
|
|
686
|
+
with qt_app_context():
|
|
687
|
+
with datalab_test_app_context() as win:
|
|
688
|
+
image_panel = win.imagepanel
|
|
689
|
+
signal_panel = win.signalpanel
|
|
690
|
+
image_processor = image_panel.processor
|
|
691
|
+
|
|
692
|
+
# Create a group of test images
|
|
693
|
+
group_name = "Test Images"
|
|
694
|
+
group = image_panel.add_group(group_name)
|
|
695
|
+
group_id = get_uuid(group)
|
|
696
|
+
|
|
697
|
+
# Create multiple test images in the group
|
|
698
|
+
n_images = 3
|
|
699
|
+
for i in range(n_images):
|
|
700
|
+
image_param = Gauss2DParam.create(
|
|
701
|
+
x0=50.0 + i * 5,
|
|
702
|
+
y0=50.0 + i * 5,
|
|
703
|
+
sigma=10.0,
|
|
704
|
+
a=100.0,
|
|
705
|
+
height=100,
|
|
706
|
+
width=100,
|
|
707
|
+
)
|
|
708
|
+
image_panel.new_object(param=image_param, edit=False)
|
|
709
|
+
# The new objects are automatically added to the current group
|
|
710
|
+
|
|
711
|
+
# Select the entire group
|
|
712
|
+
image_panel.objview.set_current_item_id(group_id, extend=False)
|
|
713
|
+
|
|
714
|
+
# Get the initial number of groups in signal panel
|
|
715
|
+
signal_groups_before = len(signal_panel.objmodel.get_groups())
|
|
716
|
+
|
|
717
|
+
# Apply radial_profile to the entire group (Image → Signal cross-panel)
|
|
718
|
+
profile_param = RadialProfileParam.create(x0=50, y0=50)
|
|
719
|
+
image_processor.run_feature("radial_profile", param=profile_param)
|
|
720
|
+
|
|
721
|
+
# Verify a NEW group was created in the signal panel
|
|
722
|
+
signal_groups_after = len(signal_panel.objmodel.get_groups())
|
|
723
|
+
assert signal_groups_after == signal_groups_before + 1, (
|
|
724
|
+
"Expected a new group to be created for cross-panel computation results"
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
# Verify that the new group contains the correct number of signals
|
|
728
|
+
signal_groups = signal_panel.objmodel.get_groups()
|
|
729
|
+
new_group = signal_groups[-1] # Get the last (newly created) group
|
|
730
|
+
signals_in_group = new_group.get_objects()
|
|
731
|
+
assert len(signals_in_group) == n_images, (
|
|
732
|
+
f"Expected {n_images} signals in new group, got {len(signals_in_group)}"
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
# Verify the group name follows the expected pattern
|
|
736
|
+
assert "radial_profile" in new_group.title.lower(), (
|
|
737
|
+
f"Expected group name to contain 'radial_profile', "
|
|
738
|
+
f"got '{new_group.title}'"
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def test_cross_panel_signal_to_image():
|
|
743
|
+
"""Test cross-panel processing: Signal → Image (combine signals into image)"""
|
|
744
|
+
with qt_app_context():
|
|
745
|
+
with datalab_test_app_context() as win:
|
|
746
|
+
signal_panel = win.signalpanel
|
|
747
|
+
image_panel = win.imagepanel
|
|
748
|
+
signal_processor = signal_panel.processor
|
|
749
|
+
|
|
750
|
+
# Create multiple test signals
|
|
751
|
+
n_signals = 5
|
|
752
|
+
signal_uuids = []
|
|
753
|
+
for i in range(n_signals):
|
|
754
|
+
signal_param = GaussParam.create(
|
|
755
|
+
mu=250.0 + i * 10, sigma=20.0, a=100.0, y0=float(i), size=500
|
|
756
|
+
)
|
|
757
|
+
signal_panel.new_object(param=signal_param, edit=False)
|
|
758
|
+
signal = signal_panel.objview.get_current_object()
|
|
759
|
+
signal_uuids.append(get_uuid(signal))
|
|
760
|
+
|
|
761
|
+
# Select all signals
|
|
762
|
+
for uuid in signal_uuids:
|
|
763
|
+
signal_panel.objview.set_current_item_id(uuid, extend=True)
|
|
764
|
+
|
|
765
|
+
# Combine signals into image (Signal → Image cross-panel computation)
|
|
766
|
+
sti_param = SignalsToImageParam.create(
|
|
767
|
+
orientation="columns", normalize=True
|
|
768
|
+
)
|
|
769
|
+
signal_processor.run_feature("signals_to_image", param=sti_param)
|
|
770
|
+
|
|
771
|
+
# Verify the result is now in the image panel
|
|
772
|
+
image = image_panel.objview.get_current_object()
|
|
773
|
+
assert image is not None
|
|
774
|
+
|
|
775
|
+
# Check that processing metadata was stored
|
|
776
|
+
assert PROCESSING_PARAMETERS_OPTION in image.get_metadata_options()
|
|
777
|
+
option_dict = image.get_metadata_option(PROCESSING_PARAMETERS_OPTION)
|
|
778
|
+
|
|
779
|
+
# Verify metadata content for n-to-1 pattern
|
|
780
|
+
assert option_dict["func_name"] == "signals_to_image"
|
|
781
|
+
assert option_dict["pattern"] == "n-to-1"
|
|
782
|
+
assert len(option_dict["source_uuids"]) == n_signals
|
|
783
|
+
assert all(uuid in signal_uuids for uuid in option_dict["source_uuids"])
|
|
784
|
+
|
|
785
|
+
# Verify the parameter can be deserialized
|
|
786
|
+
stored_param = json_to_dataset(option_dict["param_json"])
|
|
787
|
+
assert stored_param.orientation == "columns"
|
|
788
|
+
assert stored_param.normalize is True
|
|
789
|
+
|
|
790
|
+
# Test that the Processing tab is NOT set up for n-to-1 pattern
|
|
791
|
+
# (interactive processing only works for 1-to-1 pattern)
|
|
792
|
+
image_panel.objview.set_current_object(image)
|
|
793
|
+
assert image_panel.objprop.processing_param_editor is None
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def test_select_source_objects_same_panel():
|
|
797
|
+
"""Test selecting source objects within the same panel"""
|
|
798
|
+
with qt_app_context():
|
|
799
|
+
with datalab_test_app_context() as win:
|
|
800
|
+
panel = win.signalpanel
|
|
801
|
+
processor = panel.processor
|
|
802
|
+
|
|
803
|
+
# Create a source signal
|
|
804
|
+
panel.new_object(edit=False)
|
|
805
|
+
source_signal = panel.objview.get_current_object()
|
|
806
|
+
source_uuid = get_uuid(source_signal)
|
|
807
|
+
|
|
808
|
+
# Apply a filter to create a processed signal
|
|
809
|
+
param = GaussianParam.create(sigma=2.0)
|
|
810
|
+
processor.run_feature("gaussian_filter", param=param)
|
|
811
|
+
|
|
812
|
+
# Get the filtered signal
|
|
813
|
+
filtered_signal = panel.objview.get_current_object()
|
|
814
|
+
assert filtered_signal is not None
|
|
815
|
+
|
|
816
|
+
# Clear selection
|
|
817
|
+
panel.objview.clearSelection()
|
|
818
|
+
|
|
819
|
+
# Select only the filtered signal
|
|
820
|
+
panel.objview.set_current_item_id(get_uuid(filtered_signal), extend=False)
|
|
821
|
+
|
|
822
|
+
# Call select_source_objects
|
|
823
|
+
panel.select_source_objects()
|
|
824
|
+
|
|
825
|
+
# Verify that the source signal is now selected
|
|
826
|
+
selected_uuids = panel.objview.get_sel_object_uuids()
|
|
827
|
+
assert source_uuid in selected_uuids
|
|
828
|
+
assert len(selected_uuids) == 1
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
def test_select_source_objects_cross_panel():
|
|
832
|
+
"""Test selecting source objects across panels (cross-panel computation)"""
|
|
833
|
+
with qt_app_context():
|
|
834
|
+
with datalab_test_app_context() as win:
|
|
835
|
+
image_panel = win.imagepanel
|
|
836
|
+
signal_panel = win.signalpanel
|
|
837
|
+
image_processor = image_panel.processor
|
|
838
|
+
|
|
839
|
+
# Create a source image
|
|
840
|
+
image_param = Gauss2DParam.create(
|
|
841
|
+
x0=50.0, y0=50.0, sigma=10.0, a=100.0, height=100, width=100
|
|
842
|
+
)
|
|
843
|
+
image_panel.new_object(param=image_param, edit=False)
|
|
844
|
+
image = image_panel.objview.get_current_object()
|
|
845
|
+
image_uuid = get_uuid(image)
|
|
846
|
+
|
|
847
|
+
# Apply radial_profile (Image → Signal)
|
|
848
|
+
profile_param = RadialProfileParam.create(x0=50, y0=50)
|
|
849
|
+
image_processor.run_feature("radial_profile", param=profile_param)
|
|
850
|
+
|
|
851
|
+
# Verify we're now in the signal panel
|
|
852
|
+
signal = signal_panel.objview.get_current_object()
|
|
853
|
+
assert signal is not None
|
|
854
|
+
|
|
855
|
+
# Clear selection in signal panel
|
|
856
|
+
signal_panel.objview.clearSelection()
|
|
857
|
+
|
|
858
|
+
# Select only the radial profile signal
|
|
859
|
+
signal_panel.objview.set_current_item_id(get_uuid(signal), extend=False)
|
|
860
|
+
|
|
861
|
+
# Call select_source_objects - should switch to image panel
|
|
862
|
+
signal_panel.select_source_objects()
|
|
863
|
+
|
|
864
|
+
# Verify that we switched to the image panel
|
|
865
|
+
# (the current panel in the main window should now be image panel)
|
|
866
|
+
assert win.get_current_panel() == "image"
|
|
867
|
+
|
|
868
|
+
# Verify that the source image is now selected in the image panel
|
|
869
|
+
selected_uuids = image_panel.objview.get_sel_object_uuids()
|
|
870
|
+
assert image_uuid in selected_uuids
|
|
871
|
+
assert len(selected_uuids) == 1
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def test_select_source_objects_multiple_sources():
|
|
875
|
+
"""Test selecting multiple source objects (n-to-1 processing)"""
|
|
876
|
+
with qt_app_context():
|
|
877
|
+
with datalab_test_app_context() as win:
|
|
878
|
+
panel = win.signalpanel
|
|
879
|
+
processor = panel.processor
|
|
880
|
+
|
|
881
|
+
# Create multiple source signals
|
|
882
|
+
n_signals = 3
|
|
883
|
+
source_uuids = []
|
|
884
|
+
for _i in range(n_signals):
|
|
885
|
+
panel.new_object(edit=False)
|
|
886
|
+
signal = panel.objview.get_current_object()
|
|
887
|
+
source_uuids.append(get_uuid(signal))
|
|
888
|
+
|
|
889
|
+
# Select all source signals
|
|
890
|
+
for uuid in source_uuids:
|
|
891
|
+
panel.objview.set_current_item_id(uuid, extend=True)
|
|
892
|
+
|
|
893
|
+
# Apply addition operation (n-to-1)
|
|
894
|
+
processor.run_feature("addition")
|
|
895
|
+
|
|
896
|
+
# Get the result signal
|
|
897
|
+
result_signal = panel.objview.get_current_object()
|
|
898
|
+
assert result_signal is not None
|
|
899
|
+
|
|
900
|
+
# Clear selection
|
|
901
|
+
panel.objview.clearSelection()
|
|
902
|
+
|
|
903
|
+
# Select only the result signal
|
|
904
|
+
panel.objview.set_current_item_id(get_uuid(result_signal), extend=False)
|
|
905
|
+
|
|
906
|
+
# Call select_source_objects
|
|
907
|
+
panel.select_source_objects()
|
|
908
|
+
|
|
909
|
+
# Verify that all source signals are now selected
|
|
910
|
+
selected_uuids = panel.objview.get_sel_object_uuids()
|
|
911
|
+
assert len(selected_uuids) == n_signals
|
|
912
|
+
assert all(uuid in selected_uuids for uuid in source_uuids)
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def test_select_source_objects_deleted_source():
|
|
916
|
+
"""Test selecting source objects when source has been deleted"""
|
|
917
|
+
with qt_app_context():
|
|
918
|
+
with datalab_test_app_context() as win:
|
|
919
|
+
panel = win.signalpanel
|
|
920
|
+
processor = panel.processor
|
|
921
|
+
|
|
922
|
+
# Create a source signal
|
|
923
|
+
panel.new_object(edit=False)
|
|
924
|
+
source_signal = panel.objview.get_current_object()
|
|
925
|
+
|
|
926
|
+
# Apply a filter
|
|
927
|
+
param = GaussianParam.create(sigma=2.0)
|
|
928
|
+
processor.run_feature("gaussian_filter", param=param)
|
|
929
|
+
|
|
930
|
+
# Get the filtered signal
|
|
931
|
+
filtered_signal = panel.objview.get_current_object()
|
|
932
|
+
|
|
933
|
+
# Delete the source signal
|
|
934
|
+
panel.objview.set_current_object(source_signal)
|
|
935
|
+
panel.remove_object(force=True)
|
|
936
|
+
|
|
937
|
+
# Select the filtered signal
|
|
938
|
+
panel.objview.clearSelection()
|
|
939
|
+
panel.objview.set_current_item_id(get_uuid(filtered_signal), extend=False)
|
|
940
|
+
|
|
941
|
+
# Call select_source_objects - should show warning
|
|
942
|
+
# (This won't raise an exception, just show a message box in GUI)
|
|
943
|
+
panel.select_source_objects()
|
|
944
|
+
|
|
945
|
+
# Verify that no objects are selected (source was deleted)
|
|
946
|
+
selected_uuids = panel.objview.get_sel_object_uuids()
|
|
947
|
+
# The filtered signal should still be selected
|
|
948
|
+
assert get_uuid(filtered_signal) in selected_uuids
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
def test_roi_mask_invalidation_on_size_change():
|
|
952
|
+
"""Test that ROI masks are invalidated when image size changes.
|
|
953
|
+
|
|
954
|
+
This test verifies the fix for the bug where modifying creation parameters
|
|
955
|
+
that change the image dimensions doesn't invalidate the ROI mask cache,
|
|
956
|
+
resulting in corrupted ROI display.
|
|
957
|
+
|
|
958
|
+
Reproduction steps:
|
|
959
|
+
1. Create a 2D gaussian image
|
|
960
|
+
2. Add a rectangular ROI
|
|
961
|
+
3. Change the size of the image (increase width by 50%)
|
|
962
|
+
4. Verify that the ROI mask is properly invalidated and recomputed
|
|
963
|
+
"""
|
|
964
|
+
with qt_app_context():
|
|
965
|
+
with datalab_test_app_context() as win:
|
|
966
|
+
panel = win.imagepanel
|
|
967
|
+
objprop = panel.objprop
|
|
968
|
+
|
|
969
|
+
# Step 1: Create a 2D gaussian image with specific dimensions
|
|
970
|
+
param = Gauss2DParam.create(
|
|
971
|
+
height=100, width=100, x0=50.0, y0=50.0, sigma=10.0, a=100.0
|
|
972
|
+
)
|
|
973
|
+
panel.new_object(param=param, edit=False)
|
|
974
|
+
image = panel.objview.get_current_object()
|
|
975
|
+
assert image is not None
|
|
976
|
+
assert image.data.shape == (100, 100)
|
|
977
|
+
|
|
978
|
+
# Step 2: Add a rectangular ROI
|
|
979
|
+
roi = create_image_roi("rectangle", [20, 20, 40, 40])
|
|
980
|
+
image.roi = roi
|
|
981
|
+
|
|
982
|
+
# Verify the ROI mask is created and cached
|
|
983
|
+
mask_before = image.maskdata
|
|
984
|
+
assert mask_before is not None
|
|
985
|
+
assert mask_before.shape == (100, 100)
|
|
986
|
+
|
|
987
|
+
# Step 3: Change the image dimensions (increase width by 50%)
|
|
988
|
+
editor = objprop.creation_param_editor
|
|
989
|
+
assert editor is not None
|
|
990
|
+
editor.dataset.width = 150 # Change from 100 to 150
|
|
991
|
+
|
|
992
|
+
# Apply the new parameters
|
|
993
|
+
objprop.apply_creation_parameters()
|
|
994
|
+
|
|
995
|
+
# Step 4: Verify the image was resized
|
|
996
|
+
updated_image = panel.objview.get_current_object()
|
|
997
|
+
assert get_uuid(updated_image) == get_uuid(image)
|
|
998
|
+
assert updated_image.data.shape == (100, 150)
|
|
999
|
+
|
|
1000
|
+
# Step 5: Verify the ROI mask was invalidated and will be recomputed
|
|
1001
|
+
# with the new dimensions
|
|
1002
|
+
mask_after = updated_image.maskdata
|
|
1003
|
+
assert mask_after is not None
|
|
1004
|
+
assert mask_after.shape == (100, 150), (
|
|
1005
|
+
f"ROI mask shape {mask_after.shape} doesn't match "
|
|
1006
|
+
f"new image shape {updated_image.data.shape}"
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
# The mask should be different from before (different shape)
|
|
1010
|
+
assert mask_before.shape != mask_after.shape
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
def test_roi_mask_invalidation_on_processing_change():
|
|
1014
|
+
"""Test that ROI masks are invalidated when processing changes image dimensions.
|
|
1015
|
+
|
|
1016
|
+
This test verifies the fix for the bug where reprocessing with parameters
|
|
1017
|
+
that change image dimensions doesn't invalidate the ROI mask cache,
|
|
1018
|
+
resulting in corrupted ROI display.
|
|
1019
|
+
|
|
1020
|
+
Scenario:
|
|
1021
|
+
1. Create a source image
|
|
1022
|
+
2. Apply binning (reduces dimensions)
|
|
1023
|
+
3. Add ROI to the binned image
|
|
1024
|
+
4. Change binning factor (changes dimensions again)
|
|
1025
|
+
5. Verify ROI mask is properly recomputed
|
|
1026
|
+
"""
|
|
1027
|
+
with qt_app_context():
|
|
1028
|
+
with datalab_test_app_context() as win:
|
|
1029
|
+
panel = win.imagepanel
|
|
1030
|
+
objprop = panel.objprop
|
|
1031
|
+
|
|
1032
|
+
# Step 1: Create a source image
|
|
1033
|
+
param = Gauss2DParam.create(
|
|
1034
|
+
height=100, width=100, x0=50.0, y0=50.0, sigma=10.0, a=100.0
|
|
1035
|
+
)
|
|
1036
|
+
panel.new_object(param=param, edit=False)
|
|
1037
|
+
source_image = panel.objview.get_current_object()
|
|
1038
|
+
assert source_image is not None
|
|
1039
|
+
|
|
1040
|
+
# Step 2: Apply binning to reduce dimensions
|
|
1041
|
+
binning_param = BinningParam.create(sx=2, sy=2) # 100x100 -> 50x50
|
|
1042
|
+
|
|
1043
|
+
# Use the processor's run_feature method with edit=False
|
|
1044
|
+
panel.processor.run_feature("binning", binning_param, edit=False)
|
|
1045
|
+
|
|
1046
|
+
# Get the binned result (last object in the list)
|
|
1047
|
+
binned = panel.objview.get_sel_objects()[-1]
|
|
1048
|
+
assert binned.data.shape == (50, 50)
|
|
1049
|
+
|
|
1050
|
+
# Step 3: Add a rectangular ROI to the binned image
|
|
1051
|
+
roi = create_image_roi("rectangle", [10, 10, 20, 20])
|
|
1052
|
+
binned.roi = roi
|
|
1053
|
+
|
|
1054
|
+
# Verify the ROI mask is created and cached
|
|
1055
|
+
mask_before = binned.maskdata
|
|
1056
|
+
assert mask_before is not None
|
|
1057
|
+
assert mask_before.shape == (50, 50)
|
|
1058
|
+
|
|
1059
|
+
# Step 4: Change binning factor via Processing tab
|
|
1060
|
+
assert objprop.setup_processing_tab(binned)
|
|
1061
|
+
editor = objprop.processing_param_editor
|
|
1062
|
+
assert editor is not None
|
|
1063
|
+
|
|
1064
|
+
# Change binning factor from 2x2 to 4x4 (50x50 -> 25x25)
|
|
1065
|
+
editor.dataset.sx = 4
|
|
1066
|
+
editor.dataset.sy = 4
|
|
1067
|
+
|
|
1068
|
+
# Apply the new processing parameters
|
|
1069
|
+
report = objprop.apply_processing_parameters(binned)
|
|
1070
|
+
assert report.success
|
|
1071
|
+
|
|
1072
|
+
# Step 5: Verify the image was resized
|
|
1073
|
+
assert binned.data.shape == (25, 25)
|
|
1074
|
+
|
|
1075
|
+
# Step 6: Verify the ROI mask was invalidated and will be recomputed
|
|
1076
|
+
# with the new dimensions
|
|
1077
|
+
mask_after = binned.maskdata
|
|
1078
|
+
assert mask_after is not None
|
|
1079
|
+
assert mask_after.shape == (25, 25), (
|
|
1080
|
+
f"ROI mask shape {mask_after.shape} doesn't match "
|
|
1081
|
+
f"new image shape {binned.data.shape}"
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
# The mask should be different from before (different shape)
|
|
1085
|
+
assert mask_before.shape != mask_after.shape
|
|
1086
|
+
|
|
1087
|
+
|
|
1088
|
+
if __name__ == "__main__":
|
|
1089
|
+
test_signal_interactive_processing()
|
|
1090
|
+
test_image_interactive_processing()
|
|
1091
|
+
test_processing_without_parameters()
|
|
1092
|
+
test_recompute()
|
|
1093
|
+
test_apply_creation_parameters_signal()
|
|
1094
|
+
test_apply_creation_parameters_image()
|
|
1095
|
+
test_no_duplicate_creation_tabs()
|
|
1096
|
+
test_no_creation_parameters_for_base_classes()
|
|
1097
|
+
test_apply_processing_parameters_signal()
|
|
1098
|
+
test_apply_processing_parameters_image()
|
|
1099
|
+
test_no_duplicate_processing_tabs()
|
|
1100
|
+
test_apply_processing_parameters_missing_source()
|
|
1101
|
+
test_cross_panel_image_to_signal()
|
|
1102
|
+
test_cross_panel_image_to_signal_group()
|
|
1103
|
+
test_cross_panel_signal_to_image()
|
|
1104
|
+
test_select_source_objects_same_panel()
|
|
1105
|
+
test_select_source_objects_cross_panel()
|
|
1106
|
+
test_select_source_objects_multiple_sources()
|
|
1107
|
+
test_select_source_objects_deleted_source()
|
|
1108
|
+
test_roi_mask_invalidation_on_size_change()
|
|
1109
|
+
test_roi_mask_invalidation_on_processing_change()
|